Alarm test work again.

pull/156/head
Fredrik Öhrström 2020-09-18 20:05:59 +02:00
rodzic 802e62cbbd
commit 891e3f4228
20 zmienionych plików z 264 dodań i 113 usunięć

26
CHANGES
Wyświetl plik

@ -1,6 +1,32 @@
IMPORTANT CHANGES THAT MIGHT AFFECT YOU!
** No udev rules
Wmbusmeters new default behaviour is to NOT exit if no wmbus
device is found. It stays running all the time even if no
wmbus device is detected. As soon as a device is inserted
it will detect/configure and start using the device.
When reading stdin and files, then wmbusmeters will exit
as soon as there is no more data.
This means that the udev rules no longer are needed and must be
removed. This will be done automatically by the install script.
** Use stderr for logging, stdout for data.
wmbusmeters now uses STDERR as default for info/verbose/debug/trace output.
This will not affect you if you run wmbusmeters as a daemon,
but if you start wmbusmeters yourself you should check where stderr is going.
To use the old style add --usestdoutforlogging.
** New features
To list the shell envs for a meter do --listenvs=multical21 To list the shell envs for a meter do --listenvs=multical21
To list the fields avilable for a meter do --listfields=multical21 To list the fields avilable for a meter do --listfields=multical21
To list all meters do --listmeters
To search for a meter do --listmeters=water or --listmeters=multi
Version 0.9.36: 2020-09-08 Version 0.9.36: 2020-09-08

Wyświetl plik

@ -142,6 +142,9 @@ Usage: wmbusmeters {options} <device>{:suffix} ( [meter_name] [meter_type]{:<mod
As <options> you can use: As <options> you can use:
--addconversions=<unit>+ add conversion to these units to json and meter env variables (GJ) --addconversions=<unit>+ add conversion to these units to json and meter env variables (GJ)
--alarmexpectedactivity=mon-fri(08-17) Specify when the timeout is tested, default is mon-sun(00-23)
--alarmshell=<cmdline> invokes cmdline when an alarm triggers
--alarmtimeout=<time> Expect a telegram to arrive within <time> seconds, eg 60s, 60m, 24h during expected activity.
--debug for a lot of information --debug for a lot of information
--exitafter=<time> exit program after time, eg 20h, 10m 5s --exitafter=<time> exit program after time, eg 20h, 10m 5s
--format=<hr/json/fields> for human readable, json or semicolon separated fields --format=<hr/json/fields> for human readable, json or semicolon separated fields

Wyświetl plik

@ -74,8 +74,6 @@ echo man page: installed "$ROOT"/usr/share/man/man1/wmbusmeters.1.gz
## Create wmbusmeters user ## Create wmbusmeters user
## ##
ID=$(id -u wmbusmeters 2>/dev/null)
if [ -f "$ROOT"/usr/sbin/nologin ] if [ -f "$ROOT"/usr/sbin/nologin ]
then then
USERSHELL="$ROOT/usr/sbin/nologin" USERSHELL="$ROOT/usr/sbin/nologin"
@ -88,15 +86,17 @@ fi
if [ "$ADDUSER" = "true" ] if [ "$ADDUSER" = "true" ]
then then
# Create the wmbusmeters group, if it does not already exist.
groupadd -f wmbusmeters
ID=$(id -u wmbusmeters 2>/dev/null)
if [ -z "$ID" ] if [ -z "$ID" ]
then then
# Create the wmbusmeters group, if it does not already exist.
groupadd -f wmbusmeters
# Create the wmbusmeters user # Create the wmbusmeters user
useradd --system --shell $USERSHELL -g wmbusmeters --groups dialout wmbusmeters useradd --system --shell $USERSHELL -g wmbusmeters wmbusmeters
echo user: added wmbusmeters echo user: added wmbusmeters
else else
echo user: wmbusmeters unmodified echo user: wmbusmeters already exists
fi fi
if [ "$(groups wmbusmeters | grep -o dialout)" = "" ] if [ "$(groups wmbusmeters | grep -o dialout)" = "" ]
@ -107,6 +107,16 @@ then
else else
echo user: wmbusmeters already added to dialout echo user: wmbusmeters already added to dialout
fi fi
if [ "$(groups wmbusmeters | grep -o plugdev)" = "" ]
then
# Add the wmbusmeters user to plugdev
usermod -a -G plugdev wmbusmeters
echo user: added wmbusmeters to plugdev group
else
echo user: wmbusmeters already added to plugdev
fi
if [ ! -z "$SUDO_USER" ] if [ ! -z "$SUDO_USER" ]
then then
if [ "$(groups $SUDO_USER | grep -o wmbusmeters)" = "" ] if [ "$(groups $SUDO_USER | grep -o wmbusmeters)" = "" ]
@ -236,7 +246,7 @@ fi
# This means that wmbusmeters will rely on the conf file device setting. # This means that wmbusmeters will rely on the conf file device setting.
cat <<'EOF' > $CURR_WMBS cat <<'EOF' > $CURR_WMBS
[Unit] [Unit]
Description="wmbusmeters service (no udev trigger)" Description="wmbusmeters service"
Documentation=https://github.com/weetmuts/wmbusmeters Documentation=https://github.com/weetmuts/wmbusmeters
Documentation=man:wmbusmeters(1) Documentation=man:wmbusmeters(1)
After=network.target After=network.target

Wyświetl plik

@ -1,4 +1,4 @@
telegram=|2A442D2C998734761B168D2091D37CAC21576C78|02FF207100041308190000441308190000615B7F616713|+0 telegram=|2A442D2C998734761B168D2091D37CAC21576C78|02FF207100041308190000441308190000615B7F616713|+0
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.408,"target_m3":6.408,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"} {"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.408,"target_m3":6.408,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
telegram=|2A442D2C998734761B168D2091D37CAC21576C78|02FF207100041308190000441308190000615B7F616713|+3 telegram=|2A442D2C998734761B168D2091D37CAC21576C78|02FF207100041308190000441308190000615B7F616713|+5
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.408,"target_m3":6.408,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"} {"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.408,"target_m3":6.408,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}

Wyświetl plik

@ -72,7 +72,6 @@ LIST_OF_WMBUS_RECEIVERS
bool detectIfRoot(); bool detectIfRoot();
string userName(); string userName();
bool detectIfMemberOfGroup(string group); bool detectIfMemberOfGroup(string group);
void detectProcesses(string cmd, vector<int> *pids);
void detectWMBUSReceiver(); void detectWMBUSReceiver();
void resetWMBUSReceiver(); void resetWMBUSReceiver();
void probeFor(string type, AccessCheck(*func)(string,Detected*,SerialCommunicationManager*)); void probeFor(string type, AccessCheck(*func)(string,Detected*,SerialCommunicationManager*));
@ -359,24 +358,6 @@ bool detectIfMemberOfGroup(string group)
return false; return false;
} }
void detectProcesses(string cmd, vector<int> *pids)
{
vector<string> args;
vector<string> envs;
args.push_back(cmd);
string out;
invokeShellCaptureOutput("/bin/pidof", args, envs, &out, true);
char buf[out.size()+1];
strcpy(buf, out.c_str());
char *pch;
pch = strtok (buf," \n");
while (pch != NULL)
{
pids->push_back(atoi(pch));
pch = strtok (NULL, " \n");
}
}
void stopDaemon() void stopDaemon()
{ {

Wyświetl plik

@ -20,6 +20,7 @@
#include"meters.h" #include"meters.h"
#include"printer.h" #include"printer.h"
#include"serial.h" #include"serial.h"
#include"shell.h"
#include"util.h" #include"util.h"
#include"version.h" #include"version.h"
#include"wmbus.h" #include"wmbus.h"
@ -49,6 +50,7 @@ void logStartInformation(Configuration *config);
bool start(Configuration *config); bool start(Configuration *config);
void startUsingConfigFiles(string root, bool is_daemon, string device_override, string listento_override); void startUsingConfigFiles(string root, bool is_daemon, string device_override, string listento_override);
void startDaemon(string pid_file, string device_override, string listento_override); // Will use config files. void startDaemon(string pid_file, string device_override, string listento_override); // Will use config files.
void checkIfMultipleWmbusmetersRunning();
void list_shell_envs(Configuration *config, string meter_type); void list_shell_envs(Configuration *config, string meter_type);
void list_fields(Configuration *config, string meter_type); void list_fields(Configuration *config, string meter_type);
void list_meters(Configuration *config); void list_meters(Configuration *config);
@ -81,6 +83,9 @@ set<string> not_swradio_wmbus_devices_;
// it has been read, do not open it again! // it has been read, do not open it again!
set<string> do_not_open_file_again_; set<string> do_not_open_file_again_;
// Store simulation files here.
set<string> simulation_files_;
// Rendering the telegrams to json,fields or shell calls is // Rendering the telegrams to json,fields or shell calls is
// done by the printer. // done by the printer.
unique_ptr<Printer> printer_; unique_ptr<Printer> printer_;
@ -150,14 +155,15 @@ provided you with this binary. Read the full license for all details.
const char *short_manual = const char *short_manual =
#include"short_manual.h" #include"short_manual.h"
puts(short_manual); puts(short_manual);
exit(0);
} }
else
if (config->daemon) if (config->daemon)
{ {
startDaemon(config->pid_file, config->device_override, config->listento_override); startDaemon(config->pid_file, config->device_override, config->listento_override);
exit(0); exit(0);
} }
else
if (config->useconfig) if (config->useconfig)
{ {
startUsingConfigFiles(config->config_root, false, config->device_override, config->listento_override); startUsingConfigFiles(config->config_root, false, config->device_override, config->listento_override);
@ -492,6 +498,20 @@ void remove_lost_swradio_devices_from_ignore_list(vector<string> &devices)
} }
} }
void check_statuses()
{
LOCK("(main)", "check_statuses", devices_lock_);
vector<WMBus*> not_working;
for (auto &w : wmbus_devices_)
{
if (w->isWorking())
{
w->checkStatus();
}
}
UNLOCK("(main)", "check_statuses", devices_lock_);
}
void check_for_dead_wmbus_devices(Configuration *config) void check_for_dead_wmbus_devices(Configuration *config)
{ {
trace("[MAIN] checking for dead wmbus devices...\n"); trace("[MAIN] checking for dead wmbus devices...\n");
@ -503,9 +523,9 @@ void check_for_dead_wmbus_devices(Configuration *config)
if (!w->isWorking()) if (!w->isWorking())
{ {
not_working.push_back(w.get()); not_working.push_back(w.get());
if (!config->use_auto_detect) if (config->use_auto_detect)
{ {
notice("Lost %s closing %s\n", w->device().c_str(), toString(w->type())); info("Lost %s closing %s\n", w->device().c_str(), toString(w->type()));
} }
} }
} }
@ -529,7 +549,8 @@ void check_for_dead_wmbus_devices(Configuration *config)
{ {
if (!printed_warning_) if (!printed_warning_)
{ {
info("(main) no wmbus device detected, waiting for a device to be plugged in.\n"); info("No wmbus device detected, waiting for a device to be plugged in.\n");
checkIfMultipleWmbusmetersRunning();
printed_warning_ = true; printed_warning_ = true;
} }
} }
@ -546,7 +567,7 @@ void open_wmbus_device(Configuration *config, string how, string device, Detecte
// A newly plugged in device has been manually configured or automatically detected! Start using it! // A newly plugged in device has been manually configured or automatically detected! Start using it!
if (config->use_auto_detect) if (config->use_auto_detect)
{ {
notice("Detected %s %s on %s\n", how.c_str(), toString(detected->type), device.c_str()); notice("Configure %s on %s\n", how.c_str(), toString(detected->type), device.c_str());
} }
else else
{ {
@ -562,6 +583,11 @@ void open_wmbus_device(Configuration *config, string how, string device, Detecte
bool simulated = detected->type == WMBusDeviceType::DEVICE_SIMULATOR; bool simulated = detected->type == WMBusDeviceType::DEVICE_SIMULATOR;
wmbus->onTelegram([&, simulated](vector<uchar> data){return meter_manager_->handleTelegram(data, simulated);}); wmbus->onTelegram([&, simulated](vector<uchar> data){return meter_manager_->handleTelegram(data, simulated);});
wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity);
if (detected->type == DEVICE_SIMULATOR)
{
debug("(main) added %s to files\n", detected->device.file.c_str());
simulation_files_.insert(detected->device.file);
}
UNLOCK("(main)", "perform_auto_scan_of_devices", devices_lock_); UNLOCK("(main)", "perform_auto_scan_of_devices", devices_lock_);
} }
@ -593,11 +619,11 @@ void perform_auto_scan_of_serial_devices(Configuration *config)
// A modem, an android phone, a teletype Model 33, etc.... // A modem, an android phone, a teletype Model 33, etc....
// Mark this serial device as unknown, to avoid repeated detection attempts. // Mark this serial device as unknown, to avoid repeated detection attempts.
not_serial_wmbus_devices_.insert(device); not_serial_wmbus_devices_.insert(device);
info("(main) ignoring %s, it does not respond as any of the supported wmbus devices.\n", device.c_str()); verbose("(main) ignoring %s, it does not respond as any of the supported wmbus devices.\n", device.c_str());
} }
else else
{ {
open_wmbus_device(config, "auto scan detected", device, &detected); open_wmbus_device(config, "during auto scan", device, &detected);
} }
} }
} }
@ -630,12 +656,12 @@ void perform_auto_scan_of_swradio_devices(Configuration *config)
{ {
// We cannot access this swradio device. // We cannot access this swradio device.
not_swradio_wmbus_devices_.insert(device); not_swradio_wmbus_devices_.insert(device);
info("(main) ignoring swradio %s since it is unavailable.\n", device.c_str()); verbose("(main) ignoring swradio %s since it is unavailable.\n", device.c_str());
} }
else else
{ {
detected.device.file = device; detected.device.file = device;
open_wmbus_device(config, "auto scan detected", device, &detected); open_wmbus_device(config, "during auto scan", device, &detected);
} }
} }
} }
@ -659,7 +685,11 @@ void detectAndConfigureWMBusDevices(Configuration *config)
trace("(main) %s already configured\n", sd->device().c_str()); trace("(main) %s already configured\n", sd->device().c_str());
continue; continue;
} }
if (simulation_files_.count(device.file) > 0)
{
debug("(main) %s already configured as simulation\n", device.file.c_str());
continue;
}
if (do_not_open_file_again_.count(device.file) == 0) if (do_not_open_file_again_.count(device.file) == 0)
{ {
Detected detected = detectWMBusDeviceSetting(device.file, Detected detected = detectWMBusDeviceSetting(device.file,
@ -674,7 +704,7 @@ void detectAndConfigureWMBusDevices(Configuration *config)
// Only read stdin and files once! // Only read stdin and files once!
do_not_open_file_again_.insert(device.file); do_not_open_file_again_.insert(device.file);
} }
open_wmbus_device(config, "manual configuration", device.str(), &detected); open_wmbus_device(config, "using manual setting", device.str(), &detected);
} }
} }
else else
@ -745,6 +775,7 @@ bool start(Configuration *config)
// If our software unexpectedly exits, then stop the manager, to try // If our software unexpectedly exits, then stop the manager, to try
// to achive a nice shutdown. // to achive a nice shutdown.
onExit(call(serial_manager_.get(),stop)); onExit(call(serial_manager_.get(),stop));
serial_manager_->eachEventLooping([]() { check_statuses(); });
// Create the printer object that knows how to translate // Create the printer object that knows how to translate
// telegrams into json, fields that are written into log files // telegrams into json, fields that are written into log files
@ -778,10 +809,14 @@ bool start(Configuration *config)
if (!config->use_auto_detect) if (!config->use_auto_detect)
{ {
serial_manager_->expectDevicesToWork(); if (simulation_files_.size() == 0)
{
serial_manager_->expectDevicesToWork();
}
if (wmbus_devices_.size() == 0) if (wmbus_devices_.size() == 0)
{ {
notice("(main) no wmbus device configured! Exiting.\n"); notice("No wmbus device configured! Exiting.\n");
checkIfMultipleWmbusmetersRunning();
serial_manager_->stop(); serial_manager_->stop();
} }
} }
@ -789,7 +824,8 @@ bool start(Configuration *config)
{ {
if (wmbus_devices_.size() == 0) if (wmbus_devices_.size() == 0)
{ {
notice("(main) no wmbus device detected, waiting for a device to be plugged in.\n"); notice("No wmbus device detected, waiting for a device to be plugged in.\n");
checkIfMultipleWmbusmetersRunning();
} }
} }
@ -947,3 +983,29 @@ void startUsingConfigFiles(string root, bool is_daemon, string device_override,
} }
while (restart); while (restart);
} }
void checkIfMultipleWmbusmetersRunning()
{
pid_t my_pid = getpid();
vector<int> daemons;
detectProcesses("wmbusmetersd", &daemons);
for (int i : daemons)
{
if (i != my_pid)
{
info("Notice! Wmbusmeters daemon (pid %d) is running and it might hog any wmbus devices.\n", i);
}
}
vector<int> processes;
detectProcesses("wmbusmeters", &processes);
for (int i : processes)
{
if (i != my_pid)
{
info("Notice! Other wmbusmeters (pid %d) is running and it might hog any wmbus devices.\n", i);
}
}
}

Wyświetl plik

@ -77,6 +77,8 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager
void listenTo(SerialDevice *sd, function<void()> cb); void listenTo(SerialDevice *sd, function<void()> cb);
void onDisappear(SerialDevice *sd, function<void()> cb); void onDisappear(SerialDevice *sd, function<void()> cb);
void eachEventLooping(function<void()> cb);
void expectDevicesToWork(); void expectDevicesToWork();
void stop(); void stop();
void startEventLoop(); void startEventLoop();
@ -121,17 +123,18 @@ private:
pthread_mutex_t devices_lock_ = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t devices_lock_ = PTHREAD_MUTEX_INITIALIZER;
vector<SerialDeviceImp*> devices_; vector<SerialDeviceImp*> devices_;
vector<Timer> timers_; vector<Timer> timers_;
pthread_mutex_t timers_lock_ = PTHREAD_MUTEX_INITIALIZER; // pthread_mutex_t timers_lock_ = PTHREAD_MUTEX_INITIALIZER;
bool calling_timers_ {}; bool calling_timers_ {};
pthread_mutex_t timer_thread_lock_ = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t timer_thread_lock_ = PTHREAD_MUTEX_INITIALIZER; // Only have one regular callback thread running.
function<void()> on_event_looping_;
}; };
SerialCommunicationManagerImp::~SerialCommunicationManagerImp() SerialCommunicationManagerImp::~SerialCommunicationManagerImp()
{ {
// Stop the loop.
stop();
// Close all managed devices (not yet closed) // Close all managed devices (not yet closed)
closeAll(); closeAll();
// Stop the event loop.
stop();
// Grab the event_loop_lock. This can only be done when the eventLoop has stopped running. // Grab the event_loop_lock. This can only be done when the eventLoop has stopped running.
LOCK("(serial)", "destructor", event_loop_lock_); LOCK("(serial)", "destructor", event_loop_lock_);
// Now we can be sure the eventLoop has stopped and it is safe to // Now we can be sure the eventLoop has stopped and it is safe to
@ -723,6 +726,11 @@ void SerialCommunicationManagerImp::onDisappear(SerialDevice *sd, function<void(
si->on_disappear_ = cb; si->on_disappear_ = cb;
} }
void SerialCommunicationManagerImp::eachEventLooping(function<void()> cb)
{
on_event_looping_ = cb;
}
void SerialCommunicationManagerImp::expectDevicesToWork() void SerialCommunicationManagerImp::expectDevicesToWork()
{ {
debug("(serial) expecting devices to work\n"); debug("(serial) expecting devices to work\n");
@ -770,14 +778,15 @@ void SerialCommunicationManagerImp::waitForStop()
} }
usleep(1000*1000); usleep(1000*1000);
} }
debug("(serial) closing %d devices\n", devices_.size());
closeAll();
if (signalsInstalled()) if (signalsInstalled())
{ {
if (select_thread_) pthread_kill(select_thread_, SIGUSR1); if (select_thread_) pthread_kill(select_thread_, SIGUSR1);
} }
pthread_join(select_thread_, NULL); pthread_join(select_thread_, NULL);
debug("(serial) closing %d devices\n", devices_.size());
closeAll();
} }
bool SerialCommunicationManagerImp::isRunning() bool SerialCommunicationManagerImp::isRunning()
@ -848,19 +857,28 @@ void SerialCommunicationManagerImp::closeAll()
void SerialCommunicationManagerImp::executeTimerCallbacks() void SerialCommunicationManagerImp::executeTimerCallbacks()
{ {
time_t curr = time(NULL); time_t curr = time(NULL);
LOCK("(serial)", "executeTimerCallbacks", timers_lock_); vector<Timer> to_be_called;
vector<Timer> timers_copy = timers_;
UNLOCK("(serial)", "executeTimerCallbacks", timers_lock_);
for (Timer &t : timers_copy) LOCK("(serial)", "executeTimerCallbacks", devices_lock_);
for (Timer &t : timers_)
{ {
if (t.isTime(curr)) if (t.isTime(curr))
{ {
trace("[SERIAL] invoking callback %d %s\n", t.id, t.name.c_str()); trace("[SERIAL] timer isTime! %d %s\n", t.id, t.name.c_str());
t.last_call = curr; t.last_call = curr;
t.callback(); to_be_called.push_back(t);
} }
} }
UNLOCK("(serial)", "executeTimerCallbacks", devices_lock_);
for (Timer &t : to_be_called)
{
trace("[SERIAL] invoking callback %s(%d)\n", t.name.c_str(), t.id);
t.callback();
}
} }
time_t SerialCommunicationManagerImp::calculateTimeToNearestTimerCallback(time_t now) time_t SerialCommunicationManagerImp::calculateTimeToNearestTimerCallback(time_t now)
@ -1006,10 +1024,13 @@ void *SerialCommunicationManagerImp::eventLoop()
} }
else else
{ {
debug("(serial) not starting timer thread since it is already running.\n"); trace("(serial) not starting timer thread since it is already running.\n");
} }
} }
// Invoke callback every event looping....
if (on_event_looping_) on_event_looping_();
if (non_working.size() > 0 && expect_devices_to_work_ && resetting_ == false) if (non_working.size() > 0 && expect_devices_to_work_ && resetting_ == false)
{ {
debug("(serial) non working devices found, exiting.\n"); debug("(serial) non working devices found, exiting.\n");
@ -1121,17 +1142,17 @@ SerialCommunicationManager::~SerialCommunicationManager()
int SerialCommunicationManagerImp::startRegularCallback(string name, int seconds, function<void()> callback) int SerialCommunicationManagerImp::startRegularCallback(string name, int seconds, function<void()> callback)
{ {
Timer t = { (int)timers_.size(), seconds, time(NULL), callback, name }; Timer t = { (int)timers_.size(), seconds, time(NULL), callback, name };
LOCK("(serial)", "startRegularCallback", timers_lock_); LOCK("(serial)", "startRegularCallback", devices_lock_);
timers_.push_back(t); timers_.push_back(t);
UNLOCK("(serial)", "startRegularCallback", timers_lock_); UNLOCK("(serial)", "startRegularCallback", devices_lock_);
debug("(serial) registered regular callback %d %s every %d seconds\n", t.id, name.c_str(), seconds); debug("(serial) registered regular callback %s(%d) every %d seconds\n", name.c_str(), t.id, seconds);
return t.id; return t.id;
} }
void SerialCommunicationManagerImp::stopRegularCallback(int id) void SerialCommunicationManagerImp::stopRegularCallback(int id)
{ {
debug("(serial) stopping regular callback %d\n", id); debug("(serial) stopping regular callback %d\n", id);
LOCK("(serial)", "stopRegularCallback", timers_lock_); LOCK("(serial)", "stopRegularCallback", devices_lock_);
for (auto i = timers_.begin(); i != timers_.end(); ++i) for (auto i = timers_.begin(); i != timers_.end(); ++i)
{ {
if ((*i).id == id) if ((*i).id == id)
@ -1140,7 +1161,7 @@ void SerialCommunicationManagerImp::stopRegularCallback(int id)
break; break;
} }
} }
UNLOCK("(serial)", "startRegularCallback", timers_lock_); UNLOCK("(serial)", "stopRegularCallback", devices_lock_);
} }

Wyświetl plik

@ -79,6 +79,8 @@ struct SerialCommunicationManager
virtual void listenTo(SerialDevice *sd, function<void()> cb) = 0; virtual void listenTo(SerialDevice *sd, function<void()> cb) = 0;
// Invoke cb callback when the serial device has disappeared! // Invoke cb callback when the serial device has disappeared!
virtual void onDisappear(SerialDevice *sd, function<void()> cb) = 0; virtual void onDisappear(SerialDevice *sd, function<void()> cb) = 0;
// Invoke once every select loop, typically once per second.
virtual void eachEventLooping(function<void()> cb) = 0;
// Normally the communication mananager runs for ever. // Normally the communication mananager runs for ever.
// But if you expect configured devices to work, then // But if you expect configured devices to work, then
// the manager will exit when there are no working devices. // the manager will exit when there are no working devices.

Wyświetl plik

@ -325,3 +325,22 @@ bool invokeShellCaptureOutput(string program, vector<string> args, vector<string
return true; return true;
} }
void detectProcesses(string cmd, vector<int> *pids)
{
vector<string> args;
vector<string> envs;
args.push_back(cmd);
string out;
invokeShellCaptureOutput("/bin/pidof", args, envs, &out, true);
char buf[out.size()+1];
strcpy(buf, out.c_str());
char *pch;
pch = strtok (buf," \n");
while (pch != NULL)
{
pids->push_back(atoi(pch));
pch = strtok (NULL, " \n");
}
}

Wyświetl plik

@ -25,3 +25,4 @@ bool invokeShellCaptureOutput(string program, vector<string> args, vector<string
bool invokeBackgroundShell(string program, vector<string> args, vector<string> envs, int *out, int *pid); bool invokeBackgroundShell(string program, vector<string> args, vector<string> envs, int *out, int *pid);
bool stillRunning(int pid); bool stillRunning(int pid);
void stopBackgroundShell(int pid); void stopBackgroundShell(int pid);
void detectProcesses(string cmd, vector<int> *pids);

Wyświetl plik

@ -333,6 +333,7 @@ int test_linkmodes()
} }
debug("test7 OK\n\n"); debug("test7 OK\n\n");
manager->stop();
return 0; return 0;
} }

Wyświetl plik

@ -21,9 +21,7 @@
// Default select timeout one second. // Default select timeout one second.
#define SELECT_TIMEOUT 1 #define SELECT_TIMEOUT 1
// Default checkStatus callback frequency every 60 seconds, when an alarmtimeout has been set. // Default checkStatus callback frequency every 2 seconds, when an alarmtimeout has been set.
#define CHECKSTATUS_TIMER 60 #define CHECKSTATUS_TIMER 2
// When running internal tests on timeouts use 2 seconds instead.
#define CHECKSTATUS_TIMER_INTERNAL_TESTING 2
#endif #endif

Wyświetl plik

@ -3262,7 +3262,6 @@ bool Telegram::findFormatBytesFromKnownMeterSignatures(vector<uchar> *format_byt
WMBusCommonImplementation::~WMBusCommonImplementation() WMBusCommonImplementation::~WMBusCommonImplementation()
{ {
manager_->stopRegularCallback(regular_cb_id_);
debug("(wmbus) deleted %s\n", toString(type())); debug("(wmbus) deleted %s\n", toString(type()));
} }
@ -3277,12 +3276,14 @@ WMBusCommonImplementation::WMBusCommonImplementation(WMBusDeviceType t,
// Initialize timeout from now. // Initialize timeout from now.
last_received_ = time(NULL); last_received_ = time(NULL);
/*
// Invoke the check status once per minute. Unless internal testing, then it is every 2 seconds. // Invoke the check status once per minute. Unless internal testing, then it is every 2 seconds.
int default_timer = isInternalTestingEnabled() ? CHECKSTATUS_TIMER_INTERNAL_TESTING : CHECKSTATUS_TIMER; int default_timer = CHECKSTATUS_TIMER;
string info = "simulator"; string info = "simulator";
if (serial != NULL) info = serial_->device(); if (serial != NULL) info = serial_->device();
string alarm_id = "CHECK_STATUS "+string(toString(t))+":"+info; string alarm_id = "CHECK_STATUS "+string(toString(t))+":"+info;
regular_cb_id_ = manager_->startRegularCallback(alarm_id, default_timer, call(this,checkStatus)); regular_cb_id_ = manager_->startRegularCallback(alarm_id, default_timer, call(this,checkStatus));
*/
} }
WMBusDeviceType WMBusCommonImplementation::type() WMBusDeviceType WMBusCommonImplementation::type()
@ -3394,6 +3395,7 @@ bool WMBusCommonImplementation::isWorking()
void WMBusCommonImplementation::checkStatus() void WMBusCommonImplementation::checkStatus()
{ {
trace("[ALARM] check status\n");
if (protocol_error_count_ >= 20) if (protocol_error_count_ >= 20)
{ {
string msg; string msg;
@ -3416,58 +3418,73 @@ void WMBusCommonImplementation::checkStatus()
time_t now = time(NULL); time_t now = time(NULL);
time_t then = now - timeout_; time_t then = now - timeout_;
time_t since = now-last_received_; time_t since = now-last_received_;
if (timeout_ > 0 && since < timeout_)
// If no timeout set, just return.
if (timeout_ == 0) return;
if (since < timeout_)
{ {
trace("[WMBUS] No timeout. All ok. (%d s) Now %d seconds since last telegram was received.\n", since); trace("[WMBUS] No timeout since=%d timeout=%d. All ok.\n", since, timeout_);
return; return;
} }
last_received_ = time(NULL);
// The timeout has expired! But is the timeout expected because there should be no activity now? // The timeout has expired! But is the timeout expected because there should be no activity now?
// Also, do not sound the alarm unless we actually have a possible timeout within the expected activity, // Also, do not sound the alarm unless we actually have a possible timeout within the expected activity,
// otherwise we will always get an alarm when we enter the expected activity period. // otherwise we will always get an alarm when we enter the expected activity period.
if (isInsideTimePeriod(now, expected_activity_) && if (!(isInsideTimePeriod(now, expected_activity_) &&
isInsideTimePeriod(then, expected_activity_)) isInsideTimePeriod(then, expected_activity_)))
{ {
trace("[WMBUS] hit timeout(%d s) but this is ok, since there is no expected activity.\n", timeout_);
return;
}
time_t nowt = time(NULL); // Ok, timeout has triggered for real! Deal with it!
struct tm nowtm; struct tm nowtm;
localtime_r(&nowt, &nowtm); localtime_r(&now, &nowtm);
string now = strdatetime(&nowtm); string nowtxt = strdatetime(&nowtm);
string msg; string msg;
strprintf(msg, "%d seconds of inactivity resetting %s %s " strprintf(msg, "%d seconds of inactivity resetting %s %s "
"(timeout %ds expected %s now %s)", "(timeout %ds expected %s now %s)",
since, device().c_str(), toString(type()), since, device().c_str(), toString(type()),
timeout_, expected_activity_.c_str(), now.c_str()); timeout_, expected_activity_.c_str(), nowtxt.c_str());
logAlarm("inactivity", msg); logAlarm("inactivity", msg);
bool ok = reset(); bool ok = reset();
if (ok) if (ok)
{ {
warning("(wmbus) successfully reset wmbus device\n"); warning("(wmbus) successfully reset wmbus device\n");
}
else
{
strprintf(msg, "failed to reset wmbus device %s %s exiting wmbusmeters", device().c_str(), toString(type()));
logAlarm("device_failure", msg);
manager_->stop();
}
} }
else else
{ {
debug("(wmbus) Hit timeout(%d s) but no expected activity!\n", timeout_); strprintf(msg, "failed to reset wmbus device %s %s exiting wmbusmeters", device().c_str(), toString(type()));
logAlarm("device_failure", msg);
manager_->stop();
} }
// Fake last received to restart the timeout.
last_received_ = time(NULL);
} }
void WMBusCommonImplementation::setTimeout(int seconds, string expected_activity) void WMBusCommonImplementation::setTimeout(int seconds, string expected_activity)
{ {
assert(seconds >= 0);
timeout_ = seconds; timeout_ = seconds;
if (expected_activity == "")
{
expected_activity = "mon-sun(00-23)";
}
expected_activity_ = expected_activity; expected_activity_ = expected_activity;
debug("(wmbus) set timeout %s to \"%d\" with expected activity \"%s\"\n", toString(type_), timeout_, expected_activity_.c_str()); if (seconds > 0)
{
debug("(wmbus) set timeout %s to \"%d\" with expected activity \"%s\"\n", toString(type_), timeout_, expected_activity_.c_str());
}
else
{
debug("(wmbus) no alarm (expected activity) for %s\n", toString(type_));
}
} }
int toInt(TPLSecurityMode tsm) int toInt(TPLSecurityMode tsm)

Wyświetl plik

@ -61,7 +61,9 @@ struct WMBusIM871A : public virtual WMBusCommonImplementation
void simulate() { } void simulate() { }
WMBusIM871A(unique_ptr<SerialDevice> serial, SerialCommunicationManager *manager); WMBusIM871A(unique_ptr<SerialDevice> serial, SerialCommunicationManager *manager);
~WMBusIM871A() { } ~WMBusIM871A() {
manager_->onDisappear(this->serial(), NULL);
}
private: private:

Wyświetl plik

@ -56,6 +56,11 @@ struct WMBusRTLWMBUS : public virtual WMBusCommonImplementation
void simulate(); void simulate();
WMBusRTLWMBUS(unique_ptr<SerialDevice> serial, SerialCommunicationManager *manager); WMBusRTLWMBUS(unique_ptr<SerialDevice> serial, SerialCommunicationManager *manager);
~WMBusRTLWMBUS()
{
manager_->listenTo(this->serial(), NULL);
manager_->onDisappear(this->serial(), NULL);
}
private: private:

Wyświetl plik

@ -177,7 +177,11 @@ void WMBusSimulator::simulate()
curr = time(NULL); curr = time(NULL);
if (curr > start_time + rel_time) break; if (curr > start_time + rel_time) break;
usleep(1000*1000); usleep(1000*1000);
if (!manager_->isRunning()) break; if (!manager_->isRunning())
{
debug("(simulator) exiting early\n");
break;
}
} }
} }
} }

Wyświetl plik

@ -70,8 +70,6 @@ struct WMBusCommonImplementation : public virtual WMBus
bool link_modes_configured_ {}; bool link_modes_configured_ {};
LinkModeSet link_modes_ {}; LinkModeSet link_modes_ {};
int regular_cb_id_;
unique_ptr<SerialDevice> serial_; unique_ptr<SerialDevice> serial_;
}; };

11
test.sh
Wyświetl plik

@ -78,11 +78,10 @@ if [ "$?" != "0" ]; then RC="1"; fi
tests/test_serial_bads.sh $PROG tests/test_serial_bads.sh $PROG
if [ "$?" != "0" ]; then RC="1"; fi if [ "$?" != "0" ]; then RC="1"; fi
#if [ "$(uname)" = "Linux" ] if [ "$(uname)" = "Linux" ]
#then then
# tests/test_alarm.sh $PROG tests/test_alarm.sh $PROG
# if [ "$?" != "0" ]; then RC="1"; fi if [ "$?" != "0" ]; then RC="1"; fi
fi
#fi
exit $RC exit $RC

Wyświetl plik

@ -1,4 +1,4 @@
loglevel=debug loglevel=normal
# Set internaltesting=true to shorten times for test to finish in reasonable time. # Set internaltesting=true to shorten times for test to finish in reasonable time.
internaltesting=true internaltesting=true
device=must_be_overriden device=must_be_overriden
@ -9,7 +9,7 @@ shell=echo METER =="$METER_JSON"== >> /tmp/wmbusmeters_telegram_test
# The alarm will always be logged in the log file. # The alarm will always be logged in the log file.
alarmshell=echo ALARM_SHELL "$ALARM_TYPE" "$ALARM_MESSAGE" >> /tmp/wmbusmeters_alarm_test alarmshell=echo ALARM_SHELL "$ALARM_TYPE" "$ALARM_MESSAGE" >> /tmp/wmbusmeters_alarm_test
# Expect a received telegram no longer than 1 second since the last telegram! # Expect a received telegram no longer than 1 second since the last telegram!
alarmtimeout=1s alarmtimeout=4s
# Only sound the alarm if the timeout is reached when the radio is actually # Only sound the alarm if the timeout is reached when the radio is actually
# expected to be transmitting. Some meters disable transmissions during nights # expected to be transmitting. Some meters disable transmissions during nights
# and weekends. Change this to mon-fri(08-19) # and weekends. Change this to mon-fri(08-19)

Wyświetl plik

@ -12,10 +12,10 @@ echo "RUNNING $TESTNAME ..."
> /tmp/wmbusmeters_telegram_test > /tmp/wmbusmeters_telegram_test
> /tmp/wmbusmeters_alarm_test > /tmp/wmbusmeters_alarm_test
$PROG --useconfig=tests/config7 --device=simulations/simulation_alarm.txt | sed 's/....-..-.. ..:../1111-11-11 11:11/' > $TEST/test_output.txt $PROG --useconfig=tests/config7 --device=simulations/simulation_alarm.txt 2> $TEST/test_stderr.txt | sed 's/....-..-..T..:..:..Z/1111-11-11T11:11:11Z/' > $TEST/test_output.txt
cat > $TEST/test_expected.txt <<EOF cat > $TEST/test_expected.txt <<EOF
(alarm) inactivity: 2 seconds of inactivity resetting simulations/simulation_alarm.txt DEVICE_SIMULATOR (timeout 1s expected mon-sun(00-23) now 1111-11-11 11:11) (alarm) inactivity: 4 seconds of inactivity resetting simulations/simulation_alarm.txt DEVICE_SIMULATOR (timeout 4s expected mon-sun(00-23) now 1111-11-11 11:11)
(wmbus) successfully reset wmbus device (wmbus) successfully reset wmbus device
EOF EOF
@ -25,16 +25,18 @@ METER =={"media":"cold water","meter":"multical21","name":"Water","id":"76348799
EOF EOF
cat > /tmp/wmbusmeters_alarm_expected <<EOF cat > /tmp/wmbusmeters_alarm_expected <<EOF
ALARM_SHELL inactivity 2 seconds of inactivity resetting simulations/simulation_alarm.txt DEVICE_SIMULATOR (timeout 1s expected mon-sun(00-23) now 1111-11-11 11:11) ALARM_SHELL inactivity 4 seconds of inactivity resetting simulations/simulation_alarm.txt DEVICE_SIMULATOR (timeout 4s expected mon-sun(00-23) now 1111-11-11 11:11)
EOF EOF
REST=$(diff $TEST/test_output.txt $TEST/test_expected.txt) cat $TEST/test_stderr.txt | sed 's/now ....-..-.. ..:../now 1111-11-11 11:11/' > $TEST/test_responses.txt
REST=$(diff $TEST/test_responses.txt $TEST/test_expected.txt)
if [ ! -z "$REST" ] if [ ! -z "$REST" ]
then then
echo ERROR STDOUT: $TESTNAME echo ERROR STDERR: $TESTNAME
echo ----------------- echo -----------------
diff $TEST/test_output.txt $TEST/test_expected.txt diff $TEST/test_responses.txt $TEST/test_expected.txt
echo ----------------- echo -----------------
TESTRESULT="ERROR" TESTRESULT="ERROR"
fi fi