kopia lustrzana https://github.com/weetmuts/wmbusmeters
Alarm test work again.
rodzic
802e62cbbd
commit
891e3f4228
26
CHANGES
26
CHANGES
|
@ -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 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
|
||||
|
||||
|
|
|
@ -142,6 +142,9 @@ Usage: wmbusmeters {options} <device>{:suffix} ( [meter_name] [meter_type]{:<mod
|
|||
As <options> you can use:
|
||||
|
||||
--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
|
||||
--exitafter=<time> exit program after time, eg 20h, 10m 5s
|
||||
--format=<hr/json/fields> for human readable, json or semicolon separated fields
|
||||
|
|
24
install.sh
24
install.sh
|
@ -74,8 +74,6 @@ echo man page: installed "$ROOT"/usr/share/man/man1/wmbusmeters.1.gz
|
|||
## Create wmbusmeters user
|
||||
##
|
||||
|
||||
ID=$(id -u wmbusmeters 2>/dev/null)
|
||||
|
||||
if [ -f "$ROOT"/usr/sbin/nologin ]
|
||||
then
|
||||
USERSHELL="$ROOT/usr/sbin/nologin"
|
||||
|
@ -87,16 +85,18 @@ else
|
|||
fi
|
||||
|
||||
if [ "$ADDUSER" = "true" ]
|
||||
then
|
||||
if [ -z "$ID" ]
|
||||
then
|
||||
# Create the wmbusmeters group, if it does not already exist.
|
||||
groupadd -f wmbusmeters
|
||||
|
||||
ID=$(id -u wmbusmeters 2>/dev/null)
|
||||
if [ -z "$ID" ]
|
||||
then
|
||||
# Create the wmbusmeters user
|
||||
useradd --system --shell $USERSHELL -g wmbusmeters --groups dialout wmbusmeters
|
||||
useradd --system --shell $USERSHELL -g wmbusmeters wmbusmeters
|
||||
echo user: added wmbusmeters
|
||||
else
|
||||
echo user: wmbusmeters unmodified
|
||||
echo user: wmbusmeters already exists
|
||||
fi
|
||||
|
||||
if [ "$(groups wmbusmeters | grep -o dialout)" = "" ]
|
||||
|
@ -107,6 +107,16 @@ then
|
|||
else
|
||||
echo user: wmbusmeters already added to dialout
|
||||
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" ]
|
||||
then
|
||||
if [ "$(groups $SUDO_USER | grep -o wmbusmeters)" = "" ]
|
||||
|
@ -236,7 +246,7 @@ fi
|
|||
# This means that wmbusmeters will rely on the conf file device setting.
|
||||
cat <<'EOF' > $CURR_WMBS
|
||||
[Unit]
|
||||
Description="wmbusmeters service (no udev trigger)"
|
||||
Description="wmbusmeters service"
|
||||
Documentation=https://github.com/weetmuts/wmbusmeters
|
||||
Documentation=man:wmbusmeters(1)
|
||||
After=network.target
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
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"}
|
||||
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"}
|
||||
|
|
19
src/admin.cc
19
src/admin.cc
|
@ -72,7 +72,6 @@ LIST_OF_WMBUS_RECEIVERS
|
|||
bool detectIfRoot();
|
||||
string userName();
|
||||
bool detectIfMemberOfGroup(string group);
|
||||
void detectProcesses(string cmd, vector<int> *pids);
|
||||
void detectWMBUSReceiver();
|
||||
void resetWMBUSReceiver();
|
||||
void probeFor(string type, AccessCheck(*func)(string,Detected*,SerialCommunicationManager*));
|
||||
|
@ -359,24 +358,6 @@ bool detectIfMemberOfGroup(string group)
|
|||
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()
|
||||
{
|
||||
|
|
90
src/main.cc
90
src/main.cc
|
@ -20,6 +20,7 @@
|
|||
#include"meters.h"
|
||||
#include"printer.h"
|
||||
#include"serial.h"
|
||||
#include"shell.h"
|
||||
#include"util.h"
|
||||
#include"version.h"
|
||||
#include"wmbus.h"
|
||||
|
@ -49,6 +50,7 @@ void logStartInformation(Configuration *config);
|
|||
bool start(Configuration *config);
|
||||
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 checkIfMultipleWmbusmetersRunning();
|
||||
void list_shell_envs(Configuration *config, string meter_type);
|
||||
void list_fields(Configuration *config, string meter_type);
|
||||
void list_meters(Configuration *config);
|
||||
|
@ -81,6 +83,9 @@ set<string> not_swradio_wmbus_devices_;
|
|||
// it has been read, do not open it 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
|
||||
// done by the 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 =
|
||||
#include"short_manual.h"
|
||||
puts(short_manual);
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
|
||||
if (config->daemon)
|
||||
{
|
||||
startDaemon(config->pid_file, config->device_override, config->listento_override);
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
|
||||
if (config->useconfig)
|
||||
{
|
||||
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)
|
||||
{
|
||||
trace("[MAIN] checking for dead wmbus devices...\n");
|
||||
|
@ -503,9 +523,9 @@ void check_for_dead_wmbus_devices(Configuration *config)
|
|||
if (!w->isWorking())
|
||||
{
|
||||
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_)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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!
|
||||
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
|
||||
{
|
||||
|
@ -562,6 +583,11 @@ void open_wmbus_device(Configuration *config, string how, string device, Detecte
|
|||
bool simulated = detected->type == WMBusDeviceType::DEVICE_SIMULATOR;
|
||||
wmbus->onTelegram([&, simulated](vector<uchar> data){return meter_manager_->handleTelegram(data, simulated);});
|
||||
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_);
|
||||
}
|
||||
|
||||
|
@ -593,11 +619,11 @@ void perform_auto_scan_of_serial_devices(Configuration *config)
|
|||
// A modem, an android phone, a teletype Model 33, etc....
|
||||
// Mark this serial device as unknown, to avoid repeated detection attempts.
|
||||
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
|
||||
{
|
||||
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.
|
||||
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
|
||||
{
|
||||
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());
|
||||
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)
|
||||
{
|
||||
Detected detected = detectWMBusDeviceSetting(device.file,
|
||||
|
@ -674,7 +704,7 @@ void detectAndConfigureWMBusDevices(Configuration *config)
|
|||
// Only read stdin and files once!
|
||||
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
|
||||
|
@ -745,6 +775,7 @@ bool start(Configuration *config)
|
|||
// If our software unexpectedly exits, then stop the manager, to try
|
||||
// to achive a nice shutdown.
|
||||
onExit(call(serial_manager_.get(),stop));
|
||||
serial_manager_->eachEventLooping([]() { check_statuses(); });
|
||||
|
||||
// Create the printer object that knows how to translate
|
||||
// telegrams into json, fields that are written into log files
|
||||
|
@ -777,11 +808,15 @@ bool start(Configuration *config)
|
|||
detectAndConfigureWMBusDevices(config);
|
||||
|
||||
if (!config->use_auto_detect)
|
||||
{
|
||||
if (simulation_files_.size() == 0)
|
||||
{
|
||||
serial_manager_->expectDevicesToWork();
|
||||
}
|
||||
if (wmbus_devices_.size() == 0)
|
||||
{
|
||||
notice("(main) no wmbus device configured! Exiting.\n");
|
||||
notice("No wmbus device configured! Exiting.\n");
|
||||
checkIfMultipleWmbusmetersRunning();
|
||||
serial_manager_->stop();
|
||||
}
|
||||
}
|
||||
|
@ -789,7 +824,8 @@ bool start(Configuration *config)
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,8 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager
|
|||
|
||||
void listenTo(SerialDevice *sd, function<void()> cb);
|
||||
void onDisappear(SerialDevice *sd, function<void()> cb);
|
||||
void eachEventLooping(function<void()> cb);
|
||||
|
||||
void expectDevicesToWork();
|
||||
void stop();
|
||||
void startEventLoop();
|
||||
|
@ -121,17 +123,18 @@ private:
|
|||
pthread_mutex_t devices_lock_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
vector<SerialDeviceImp*> devices_;
|
||||
vector<Timer> timers_;
|
||||
pthread_mutex_t timers_lock_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
// pthread_mutex_t timers_lock_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
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()
|
||||
{
|
||||
// Stop the loop.
|
||||
stop();
|
||||
// Close all managed devices (not yet closed)
|
||||
closeAll();
|
||||
// Stop the event loop.
|
||||
stop();
|
||||
// Grab the event_loop_lock. This can only be done when the eventLoop has stopped running.
|
||||
LOCK("(serial)", "destructor", event_loop_lock_);
|
||||
// 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;
|
||||
}
|
||||
|
||||
void SerialCommunicationManagerImp::eachEventLooping(function<void()> cb)
|
||||
{
|
||||
on_event_looping_ = cb;
|
||||
}
|
||||
|
||||
void SerialCommunicationManagerImp::expectDevicesToWork()
|
||||
{
|
||||
debug("(serial) expecting devices to work\n");
|
||||
|
@ -770,14 +778,15 @@ void SerialCommunicationManagerImp::waitForStop()
|
|||
}
|
||||
usleep(1000*1000);
|
||||
}
|
||||
debug("(serial) closing %d devices\n", devices_.size());
|
||||
closeAll();
|
||||
|
||||
if (signalsInstalled())
|
||||
{
|
||||
if (select_thread_) pthread_kill(select_thread_, SIGUSR1);
|
||||
}
|
||||
pthread_join(select_thread_, NULL);
|
||||
|
||||
debug("(serial) closing %d devices\n", devices_.size());
|
||||
closeAll();
|
||||
}
|
||||
|
||||
bool SerialCommunicationManagerImp::isRunning()
|
||||
|
@ -848,19 +857,28 @@ void SerialCommunicationManagerImp::closeAll()
|
|||
void SerialCommunicationManagerImp::executeTimerCallbacks()
|
||||
{
|
||||
time_t curr = time(NULL);
|
||||
LOCK("(serial)", "executeTimerCallbacks", timers_lock_);
|
||||
vector<Timer> timers_copy = timers_;
|
||||
UNLOCK("(serial)", "executeTimerCallbacks", timers_lock_);
|
||||
vector<Timer> to_be_called;
|
||||
|
||||
for (Timer &t : timers_copy)
|
||||
LOCK("(serial)", "executeTimerCallbacks", devices_lock_);
|
||||
|
||||
for (Timer &t : timers_)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
|
@ -1006,10 +1024,13 @@ void *SerialCommunicationManagerImp::eventLoop()
|
|||
}
|
||||
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)
|
||||
{
|
||||
debug("(serial) non working devices found, exiting.\n");
|
||||
|
@ -1121,17 +1142,17 @@ SerialCommunicationManager::~SerialCommunicationManager()
|
|||
int SerialCommunicationManagerImp::startRegularCallback(string name, int seconds, function<void()> callback)
|
||||
{
|
||||
Timer t = { (int)timers_.size(), seconds, time(NULL), callback, name };
|
||||
LOCK("(serial)", "startRegularCallback", timers_lock_);
|
||||
LOCK("(serial)", "startRegularCallback", devices_lock_);
|
||||
timers_.push_back(t);
|
||||
UNLOCK("(serial)", "startRegularCallback", timers_lock_);
|
||||
debug("(serial) registered regular callback %d %s every %d seconds\n", t.id, name.c_str(), seconds);
|
||||
UNLOCK("(serial)", "startRegularCallback", devices_lock_);
|
||||
debug("(serial) registered regular callback %s(%d) every %d seconds\n", name.c_str(), t.id, seconds);
|
||||
return t.id;
|
||||
}
|
||||
|
||||
void SerialCommunicationManagerImp::stopRegularCallback(int 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)
|
||||
{
|
||||
if ((*i).id == id)
|
||||
|
@ -1140,7 +1161,7 @@ void SerialCommunicationManagerImp::stopRegularCallback(int id)
|
|||
break;
|
||||
}
|
||||
}
|
||||
UNLOCK("(serial)", "startRegularCallback", timers_lock_);
|
||||
UNLOCK("(serial)", "stopRegularCallback", devices_lock_);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -79,6 +79,8 @@ struct SerialCommunicationManager
|
|||
virtual void listenTo(SerialDevice *sd, function<void()> cb) = 0;
|
||||
// Invoke cb callback when the serial device has disappeared!
|
||||
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.
|
||||
// But if you expect configured devices to work, then
|
||||
// the manager will exit when there are no working devices.
|
||||
|
|
19
src/shell.cc
19
src/shell.cc
|
@ -325,3 +325,22 @@ bool invokeShellCaptureOutput(string program, vector<string> args, vector<string
|
|||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 stillRunning(int pid);
|
||||
void stopBackgroundShell(int pid);
|
||||
void detectProcesses(string cmd, vector<int> *pids);
|
||||
|
|
|
@ -333,6 +333,7 @@ int test_linkmodes()
|
|||
}
|
||||
debug("test7 OK\n\n");
|
||||
|
||||
manager->stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
// Default select timeout one second.
|
||||
#define SELECT_TIMEOUT 1
|
||||
|
||||
// Default checkStatus callback frequency every 60 seconds, when an alarmtimeout has been set.
|
||||
#define CHECKSTATUS_TIMER 60
|
||||
// When running internal tests on timeouts use 2 seconds instead.
|
||||
#define CHECKSTATUS_TIMER_INTERNAL_TESTING 2
|
||||
// Default checkStatus callback frequency every 2 seconds, when an alarmtimeout has been set.
|
||||
#define CHECKSTATUS_TIMER 2
|
||||
|
||||
#endif
|
||||
|
|
51
src/wmbus.cc
51
src/wmbus.cc
|
@ -3262,7 +3262,6 @@ bool Telegram::findFormatBytesFromKnownMeterSignatures(vector<uchar> *format_byt
|
|||
|
||||
WMBusCommonImplementation::~WMBusCommonImplementation()
|
||||
{
|
||||
manager_->stopRegularCallback(regular_cb_id_);
|
||||
debug("(wmbus) deleted %s\n", toString(type()));
|
||||
}
|
||||
|
||||
|
@ -3277,12 +3276,14 @@ WMBusCommonImplementation::WMBusCommonImplementation(WMBusDeviceType t,
|
|||
// Initialize timeout from now.
|
||||
last_received_ = time(NULL);
|
||||
|
||||
/*
|
||||
// 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";
|
||||
if (serial != NULL) info = serial_->device();
|
||||
string alarm_id = "CHECK_STATUS "+string(toString(t))+":"+info;
|
||||
regular_cb_id_ = manager_->startRegularCallback(alarm_id, default_timer, call(this,checkStatus));
|
||||
*/
|
||||
}
|
||||
|
||||
WMBusDeviceType WMBusCommonImplementation::type()
|
||||
|
@ -3394,6 +3395,7 @@ bool WMBusCommonImplementation::isWorking()
|
|||
|
||||
void WMBusCommonImplementation::checkStatus()
|
||||
{
|
||||
trace("[ALARM] check status\n");
|
||||
if (protocol_error_count_ >= 20)
|
||||
{
|
||||
string msg;
|
||||
|
@ -3416,30 +3418,39 @@ void WMBusCommonImplementation::checkStatus()
|
|||
time_t now = time(NULL);
|
||||
time_t then = now - timeout_;
|
||||
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;
|
||||
}
|
||||
|
||||
last_received_ = time(NULL);
|
||||
|
||||
// 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,
|
||||
// otherwise we will always get an alarm when we enter the expected activity period.
|
||||
if (isInsideTimePeriod(now, expected_activity_) &&
|
||||
isInsideTimePeriod(then, expected_activity_))
|
||||
if (!(isInsideTimePeriod(now, 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;
|
||||
localtime_r(&nowt, &nowtm);
|
||||
localtime_r(&now, &nowtm);
|
||||
|
||||
string now = strdatetime(&nowtm);
|
||||
string nowtxt = strdatetime(&nowtm);
|
||||
|
||||
string msg;
|
||||
strprintf(msg, "%d seconds of inactivity resetting %s %s "
|
||||
"(timeout %ds expected %s now %s)",
|
||||
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);
|
||||
|
||||
|
@ -3455,20 +3466,26 @@ void WMBusCommonImplementation::checkStatus()
|
|||
manager_->stop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("(wmbus) Hit timeout(%d s) but no expected activity!\n", timeout_);
|
||||
}
|
||||
// Fake last received to restart the timeout.
|
||||
last_received_ = time(NULL);
|
||||
}
|
||||
|
||||
void WMBusCommonImplementation::setTimeout(int seconds, string expected_activity)
|
||||
{
|
||||
assert(seconds >= 0);
|
||||
|
||||
timeout_ = seconds;
|
||||
if (expected_activity == "")
|
||||
{
|
||||
expected_activity = "mon-sun(00-23)";
|
||||
}
|
||||
expected_activity_ = expected_activity;
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -61,7 +61,9 @@ struct WMBusIM871A : public virtual WMBusCommonImplementation
|
|||
void simulate() { }
|
||||
|
||||
WMBusIM871A(unique_ptr<SerialDevice> serial, SerialCommunicationManager *manager);
|
||||
~WMBusIM871A() { }
|
||||
~WMBusIM871A() {
|
||||
manager_->onDisappear(this->serial(), NULL);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -56,6 +56,11 @@ struct WMBusRTLWMBUS : public virtual WMBusCommonImplementation
|
|||
void simulate();
|
||||
|
||||
WMBusRTLWMBUS(unique_ptr<SerialDevice> serial, SerialCommunicationManager *manager);
|
||||
~WMBusRTLWMBUS()
|
||||
{
|
||||
manager_->listenTo(this->serial(), NULL);
|
||||
manager_->onDisappear(this->serial(), NULL);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -177,7 +177,11 @@ void WMBusSimulator::simulate()
|
|||
curr = time(NULL);
|
||||
if (curr > start_time + rel_time) break;
|
||||
usleep(1000*1000);
|
||||
if (!manager_->isRunning()) break;
|
||||
if (!manager_->isRunning())
|
||||
{
|
||||
debug("(simulator) exiting early\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,8 +70,6 @@ struct WMBusCommonImplementation : public virtual WMBus
|
|||
bool link_modes_configured_ {};
|
||||
LinkModeSet link_modes_ {};
|
||||
|
||||
int regular_cb_id_;
|
||||
|
||||
unique_ptr<SerialDevice> serial_;
|
||||
};
|
||||
|
||||
|
|
11
test.sh
11
test.sh
|
@ -78,11 +78,10 @@ if [ "$?" != "0" ]; then RC="1"; fi
|
|||
tests/test_serial_bads.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
#if [ "$(uname)" = "Linux" ]
|
||||
#then
|
||||
# tests/test_alarm.sh $PROG
|
||||
# if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
#fi
|
||||
if [ "$(uname)" = "Linux" ]
|
||||
then
|
||||
tests/test_alarm.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
fi
|
||||
|
||||
exit $RC
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
loglevel=debug
|
||||
loglevel=normal
|
||||
# Set internaltesting=true to shorten times for test to finish in reasonable time.
|
||||
internaltesting=true
|
||||
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.
|
||||
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!
|
||||
alarmtimeout=1s
|
||||
alarmtimeout=4s
|
||||
# Only sound the alarm if the timeout is reached when the radio is actually
|
||||
# expected to be transmitting. Some meters disable transmissions during nights
|
||||
# and weekends. Change this to mon-fri(08-19)
|
||||
|
|
|
@ -12,10 +12,10 @@ echo "RUNNING $TESTNAME ..."
|
|||
> /tmp/wmbusmeters_telegram_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
|
||||
(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
|
||||
EOF
|
||||
|
||||
|
@ -25,16 +25,18 @@ METER =={"media":"cold water","meter":"multical21","name":"Water","id":"76348799
|
|||
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
|
||||
|
||||
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" ]
|
||||
then
|
||||
echo ERROR STDOUT: $TESTNAME
|
||||
echo ERROR STDERR: $TESTNAME
|
||||
echo -----------------
|
||||
diff $TEST/test_output.txt $TEST/test_expected.txt
|
||||
diff $TEST/test_responses.txt $TEST/test_expected.txt
|
||||
echo -----------------
|
||||
TESTRESULT="ERROR"
|
||||
fi
|
||||
|
|
Ładowanie…
Reference in New Issue