Fix bug where a received telegram while setting the link mode during a reset caused wmbusmeters to fail.

pull/574/head
Fredrik Öhrström 2022-07-01 15:54:59 +02:00
rodzic 04e2959919
commit 6571e4e024
12 zmienionych plików z 145 dodań i 43 usunięć

Wyświetl plik

@ -120,7 +120,7 @@ struct LoRaIU880B : public virtual WMBusCommonImplementation
string getFirmwareVersion();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes()
{
return LORA_bit;
@ -259,7 +259,7 @@ void LoRaIU880B::deviceReset()
// set the link modes properly.
}
void LoRaIU880B::deviceSetLinkModes(LinkModeSet lms)
bool LoRaIU880B::deviceSetLinkModes(LinkModeSet lms)
{
/*
if (serial()->readonly()) return; // Feeding from stdin or file.
@ -293,6 +293,7 @@ void LoRaIU880B::deviceSetLinkModes(LinkModeSet lms)
bool ok = waitForResponse(DEVMGMT_MSG_SET_RADIO_MODE_RSP);
if (!ok) return; // timeout
*/
return true;
}
FrameStatus LoRaIU880B::checkIU880BFrame(vector<uchar> &data,

Wyświetl plik

@ -35,7 +35,7 @@ struct MBusRawTTY : public virtual WMBusCommonImplementation
string getDeviceUniqueId();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes() { return Any_bit; }
int numConcurrentLinkModes() { return 0; }
bool canSetLinkModes(LinkModeSet desired_modes) { return true; }
@ -117,8 +117,9 @@ void MBusRawTTY::deviceReset()
sleep(1);
}
void MBusRawTTY::deviceSetLinkModes(LinkModeSet lms)
bool MBusRawTTY::deviceSetLinkModes(LinkModeSet lms)
{
return true;
}
void MBusRawTTY::processSerialData()

Wyświetl plik

@ -4016,7 +4016,8 @@ WMBusCommonImplementation::WMBusCommonImplementation(string bus_alias,
cached_device_id_(""),
cached_device_unique_id_(""),
command_mutex_("wmbus_command_mutex"),
waiting_for_response_sem_("waiting_for_response_sem")
waiting_for_response_sem_("waiting_for_response_sem"),
receiving_buffer_mutex_("receiving_buffer_mutex")
{
// Initialize timeout from now.
last_received_ = time(NULL);
@ -4122,8 +4123,28 @@ void WMBusCommonImplementation::resetProtocolErrorCount()
void WMBusCommonImplementation::setLinkModes(LinkModeSet lms)
{
link_modes_ = lms;
deviceSetLinkModes(lms);
retrySetLinkModes(lms);
link_modes_configured_ = true;
}
void WMBusCommonImplementation::retrySetLinkModes(LinkModeSet lms)
{
int tries = 0;
for (;;)
{
bool ok = deviceSetLinkModes(lms);
if (ok) break;
if (!manager_->isRunning()) break;
tries++;
if (tries > 3)
{
string msg = tostrprintf("failed to reset wmbus device %s exiting wmbusmeters", hr().c_str());
warning("(wmbus) Permanent error, %s\n", msg.c_str());
logAlarm(Alarm::DeviceFailure, msg);
manager_->stop();
}
}
}
bool WMBusCommonImplementation::areLinkModesConfigured()
@ -4170,7 +4191,7 @@ bool WMBusCommonImplementation::reset()
resetting = true;
serial()->resetInitiated();
serial()->close();
notice_timestamp("(wmbus) resetting %s\n", device().c_str(), toString(type()));
notice_timestamp("(wmbus) resetting %s\n", hr().c_str());
// Give the device 3 seconds to shut down properly.
usleep(3000*1000);
@ -4188,15 +4209,22 @@ bool WMBusCommonImplementation::reset()
// Invoke any other device specific resets for this device.
deviceReset();
if (resetting) serial()->resetCompleted();
if (resetting)
{
serial()->resetCompleted();
}
// If init, then no link modes are configured.
// If reset, re-initialize the link modes.
if (areLinkModesConfigured())
{
deviceSetLinkModes(protectedGetLinkModes());
retrySetLinkModes(protectedGetLinkModes());
}
if (resetting)
{
notice_timestamp("(wmbus) reset completed %s\n", hr().c_str());
}
return true;
}
@ -4332,11 +4360,23 @@ void WMBusCommonImplementation::setTimeout(int seconds, string expected_activity
bool WMBusCommonImplementation::waitForResponse(int id)
{
assert(waiting_for_response_id_ == 0 && id != 0);
assert(id != 0);
if (waiting_for_response_id_ != 0)
{
error("(wmbus) bad internal state tried waitForResponse(%d) but already waiting for %d! Exiting!\n", id, waiting_for_response_id_);
}
waiting_for_response_id_ = id;
return waiting_for_response_sem_.wait();
bool ok = waiting_for_response_sem_.wait();
if (ok) return true;
// Ouch, we had a timeout. Reset the waiting for value here.
warning("(wmbus device) timeout request id %d\n", waiting_for_response_id_);
waiting_for_response_id_ = 0;
return false;
}
bool WMBusCommonImplementation::notifyResponseIsHere(int id)
@ -5484,16 +5524,22 @@ Detected detectWMBusDeviceWithFileOrHex(SpecifiedDevice &specified_device,
// Ok, we are left with a single /dev/ttyUSB0 lets talk to it
// to figure out what is connected to it.
LinkModeSet desired_linkmodes = lms;
if (specified_device.type == WMBusDeviceType::DEVICE_UNKNOWN)
{
error("You have to specify the expected device type for the tty %s\n",
specified_device.file.c_str());
}
set<WMBusDeviceType> probe_for = { specified_device.type };
Detected d = detectWMBusDeviceOnTTY(specified_device.file, probe_for, desired_linkmodes, handler);
if (specified_device.type != d.found_type &&
specified_device.type != DEVICE_UNKNOWN)
{
warning("Expected %s on %s but found %s instead, ignoring it!\n",
warning("Expected %s on %s but did not find it! Ignoring tty!\n",
toLowerCaseString(specified_device.type),
specified_device.file.c_str(),
toLowerCaseString(d.found_type));
specified_device.file.c_str());
d.found_file = specified_device.file;
d.found_type = WMBusDeviceType::DEVICE_UNKNOWN;
}

Wyświetl plik

@ -127,7 +127,7 @@ struct WMBusAmber : public virtual WMBusCommonImplementation
string getDeviceId();
string getDeviceUniqueId();
LinkModeSet getLinkModes();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
void deviceReset();
LinkModeSet supportedLinkModes()
{
@ -165,7 +165,7 @@ struct WMBusAmber : public virtual WMBusCommonImplementation
}
private:
vector<uchar> read_buffer_;
vector<uchar> read_buffer_; // Must be protected by LOCK_WMBUS_RECEIVING_BUFFER(where)
vector<uchar> request_;
vector<uchar> response_;
@ -314,9 +314,11 @@ bool WMBusAmber::getConfiguration()
return device_config_.decodeNoFrame(response_, 3);
}
void WMBusAmber::deviceSetLinkModes(LinkModeSet lms)
bool WMBusAmber::deviceSetLinkModes(LinkModeSet lms)
{
if (serial()->readonly()) return; // Feeding from stdin or file.
bool rc = false;
if (serial()->readonly()) return true; // Feeding from stdin or file.
if (!canSetLinkModes(lms))
{
@ -324,6 +326,13 @@ void WMBusAmber::deviceSetLinkModes(LinkModeSet lms)
error("(amb8465) setting link mode(s) %s is not supported for amb8465\n", modes.c_str());
}
{
// Empty the read buffer we do not want any partial data lying around
// because we expect a response to arrive.
LOCK_WMBUS_RECEIVING_BUFFER(deviceSetLinkMode_ClearBuffer);
read_buffer_.clear();
}
LOCK_WMBUS_EXECUTING_COMMAND(devicesSetLinkModes);
request_.resize(8);
@ -358,13 +367,19 @@ void WMBusAmber::deviceSetLinkModes(LinkModeSet lms)
if (sent)
{
bool ok = waitForResponse(CMD_SET_MODE_REQ | 0x80);
if (!ok)
if (ok)
{
rc = true;
}
else
{
warning("Warning! Did not get confirmation on set link mode for amb8465\n");
rc = false;
}
}
link_modes_ = lms;
return rc;
}
FrameStatus WMBusAmber::checkAMB8465Frame(vector<uchar> &data,
@ -427,7 +442,8 @@ FrameStatus WMBusAmber::checkAMB8465Frame(vector<uchar> &data,
while ((payload_len = data[offset]) < 10 || !isValidWMBusCField(data[offset+1]))
{
offset++;
if (offset + 2 >= data.size()) {
if (offset + 2 >= data.size())
{
// No sensible telegram in the buffer. Flush it!
// But not the last char, because the next char could be a valid c field.
verbose("(amb8465) no sensible telegram found, clearing buffer.\n");
@ -475,11 +491,15 @@ void WMBusAmber::processSerialData()
// Check long delay beetween rx chunks
gettimeofday(&timestamp, NULL);
if (read_buffer_.size() > 0 && timerisset(&timestamp_last_rx_)) {
LOCK_WMBUS_RECEIVING_BUFFER(processSerialData);
if (read_buffer_.size() > 0 && timerisset(&timestamp_last_rx_))
{
struct timeval chunk_time;
timersub(&timestamp, &timestamp_last_rx_, &chunk_time);
if (chunk_time.tv_sec >= 2) {
if (chunk_time.tv_sec >= 2)
{
verbose("(amb8465) rx long delay (%lds), drop incomplete telegram\n", chunk_time.tv_sec);
read_buffer_.clear();
protocolErrorDetected();

Wyświetl plik

@ -68,7 +68,8 @@ struct WMBusCommonImplementation : public virtual WMBus
void resetProtocolErrorCount();
bool areLinkModesConfigured();
// Device specific set link modes implementation.
virtual void deviceSetLinkModes(LinkModeSet lms) = 0;
void retrySetLinkModes(LinkModeSet lms);
virtual bool deviceSetLinkModes(LinkModeSet lms) = 0;
// Device specific reset code, apart from serial->open and setLinkModes.
virtual void deviceReset() = 0;
virtual void deviceClose();
@ -117,6 +118,11 @@ protected:
int waiting_for_response_id_ {};
Semaphore waiting_for_response_sem_;
bool serial_override_ {};
// Lock this mutex when you want to append to, truncate or clear the receiving buffer.
RecursiveMutex receiving_buffer_mutex_;
#define LOCK_WMBUS_RECEIVING_BUFFER(where) WITH(receiving_buffer_mutex_, receiving_buffer_mutex, where)
};
#endif

Wyświetl plik

@ -44,7 +44,7 @@ struct WMBusCUL : public virtual WMBusCommonImplementation
string getDeviceUniqueId();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes() {
return
C1_bit |
@ -149,15 +149,23 @@ void WMBusCUL::deviceReset()
// set the link modes properly.
}
void WMBusCUL::deviceSetLinkModes(LinkModeSet lms)
bool WMBusCUL::deviceSetLinkModes(LinkModeSet lms)
{
if (serial()->readonly()) return; // Feeding from stdin or file.
if (serial()->readonly()) return true; // Feeding from stdin or file.
if (!canSetLinkModes(lms))
{
string modes = lms.hr();
error("(cul) setting link mode(s) %s is not supported\n", modes.c_str());
}
{
// Empty the read buffer we do not want any partial data lying around
// because we expect a response to arrive.
LOCK_WMBUS_RECEIVING_BUFFER(deviceSetLinkMode_ClearBuffer);
read_buffer_.clear();
}
// 'brc' command: b - wmbus, r - receive, c - c mode (with t)
vector<uchar> msg(5);
msg[0] = 'b';
@ -208,6 +216,7 @@ void WMBusCUL::deviceSetLinkModes(LinkModeSet lms)
sent = serial()->send(msg);
// Any response here, or does it silently move into listening mode?
return true;
}
void WMBusCUL::simulate()
@ -227,8 +236,11 @@ void WMBusCUL::processSerialData()
{
vector<uchar> data;
// Receive and accumulated serial data until a full frame has been received.
// Receive and accumulate serial data until a full frame has been received.
serial()->receive(&data);
LOCK_WMBUS_RECEIVING_BUFFER(processSerialData);
read_buffer_.insert(read_buffer_.end(), data.begin(), data.end());
size_t frame_length;

Wyświetl plik

@ -166,7 +166,7 @@ struct WMBusIM871aIM170A : public virtual WMBusCommonImplementation
uchar getFirmwareVersion();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes()
{
if (type() == WMBusDeviceType::DEVICE_IM871A)
@ -525,9 +525,11 @@ void WMBusIM871aIM170A::deviceReset()
// set the link modes properly.
}
void WMBusIM871aIM170A::deviceSetLinkModes(LinkModeSet lms)
bool WMBusIM871aIM170A::deviceSetLinkModes(LinkModeSet lms)
{
if (serial()->readonly()) return; // Feeding from stdin or file.
bool rc = false;
if (serial()->readonly()) return true; // Feeding from stdin or file.
if (!canSetLinkModes(lms))
{
@ -581,11 +583,17 @@ void WMBusIM871aIM170A::deviceSetLinkModes(LinkModeSet lms)
if (sent)
{
bool ok = waitForResponse(DEVMGMT_MSG_SET_CONFIG_RSP);
if (!ok)
if (ok)
{
rc = true;
}
else
{
warning("Warning! Did not get confirmation on set link mode for im871a\n");
}
}
return rc;
}
FrameStatus WMBusIM871aIM170A::checkIM871AFrame(vector<uchar> &data,
@ -721,6 +729,8 @@ void WMBusIM871aIM170A::processSerialData()
// Receive and accumulated serial data until a full frame has been received.
serial()->receive(&data);
LOCK_WMBUS_RECEIVING_BUFFER(processSerialData);
read_buffer_.insert(read_buffer_.end(), data.begin(), data.end());
size_t frame_length;

Wyświetl plik

@ -35,7 +35,7 @@ struct WMBusRawTTY : public virtual WMBusCommonImplementation
string getDeviceUniqueId();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes() { return Any_bit; }
int numConcurrentLinkModes() { return 0; }
bool canSetLinkModes(LinkModeSet desired_modes) { return true; }
@ -123,8 +123,9 @@ void WMBusRawTTY::deviceReset()
{
}
void WMBusRawTTY::deviceSetLinkModes(LinkModeSet lms)
bool WMBusRawTTY::deviceSetLinkModes(LinkModeSet lms)
{
return true;
}
void WMBusRawTTY::copy(vector<uchar> *from, vector<uchar> *to)

Wyświetl plik

@ -114,7 +114,7 @@ struct WMBusRC1180 : public virtual WMBusCommonImplementation
string getDeviceUniqueId();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes() {
return
T1_bit;
@ -274,9 +274,9 @@ void WMBusRC1180::deviceReset()
// set the link modes properly.
}
void WMBusRC1180::deviceSetLinkModes(LinkModeSet lms)
bool WMBusRC1180::deviceSetLinkModes(LinkModeSet lms)
{
if (serial()->readonly()) return; // Feeding from stdin or file.
if (serial()->readonly()) return true; // Feeding from stdin or file.
if (!canSetLinkModes(lms))
{
@ -285,7 +285,7 @@ void WMBusRC1180::deviceSetLinkModes(LinkModeSet lms)
}
// Do not actually try to change the link mode, we assume it is T1.
return;
return true;
}
void WMBusRC1180::simulate()
@ -299,6 +299,8 @@ void WMBusRC1180::processSerialData()
// Receive and accumulated serial data until a full frame has been received.
serial()->receive(&data);
LOCK_WMBUS_RECEIVING_BUFFER(processSerialData);
read_buffer_.insert(read_buffer_.end(), data.begin(), data.end());
size_t frame_length;

Wyświetl plik

@ -41,7 +41,7 @@ struct WMBusRTL433 : public virtual WMBusCommonImplementation
string getDeviceUniqueId();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes() {
return
C1_bit |
@ -182,8 +182,9 @@ void WMBusRTL433::deviceReset()
{
}
void WMBusRTL433::deviceSetLinkModes(LinkModeSet lm)
bool WMBusRTL433::deviceSetLinkModes(LinkModeSet lm)
{
return true;
}
void WMBusRTL433::simulate()

Wyświetl plik

@ -41,7 +41,7 @@ struct WMBusRTLWMBUS : public virtual WMBusCommonImplementation
string getDeviceUniqueId();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes() {
return
C1_bit |
@ -216,12 +216,13 @@ void WMBusRTLWMBUS::deviceReset()
{
}
void WMBusRTLWMBUS::deviceSetLinkModes(LinkModeSet lm)
bool WMBusRTLWMBUS::deviceSetLinkModes(LinkModeSet lm)
{
LinkModeSet lms;
lms.addLinkMode(LinkMode::C1);
lms.addLinkMode(LinkMode::T1);
device_link_modes_ = lms;
return true;
}
void WMBusRTLWMBUS::simulate()

Wyświetl plik

@ -38,7 +38,7 @@ struct WMBusSimulator : public WMBusCommonImplementation
string getDeviceUniqueId();
LinkModeSet getLinkModes();
void deviceReset();
void deviceSetLinkModes(LinkModeSet lms);
bool deviceSetLinkModes(LinkModeSet lms);
LinkModeSet supportedLinkModes() { return Any_bit; }
int numConcurrentLinkModes() { return 0; }
bool canSetLinkModes(LinkModeSet lms) { return true; }
@ -106,9 +106,10 @@ void WMBusSimulator::deviceReset()
{
}
void WMBusSimulator::deviceSetLinkModes(LinkModeSet lms)
bool WMBusSimulator::deviceSetLinkModes(LinkModeSet lms)
{
link_modes_ = lms;
return true;
}
void WMBusSimulator::processSerialData()