kopia lustrzana https://github.com/weetmuts/wmbusmeters
Exit and print info when background rtl_wmbus process fails to start.
rodzic
7aae7aa6c2
commit
43f0d9b051
18
CHANGES
18
CHANGES
|
@ -1,7 +1,21 @@
|
||||||
|
|
||||||
Verison 0.9.3: 2019-03-20
|
Version 0.9.4: 2019-04-03
|
||||||
|
|
||||||
Added initial support for the Tauron Amiplus electricity meter type (amiplus).
|
The device auto can now detect an rtlsdr dongle and start
|
||||||
|
rtl_sdr|rtl_wmbus properly. It can only detecht the rtlsdr
|
||||||
|
dongle if the new udev rule has been installed, which will
|
||||||
|
create the symlink /dev/rtlsdr when the dongle is inserted.
|
||||||
|
|
||||||
|
Added the meter vendor Echelon to the generic amiplus meter type.
|
||||||
|
(The Echelon meter seems to be a standard electricity meter with a
|
||||||
|
wmbus addon sourced from Develco.)
|
||||||
|
|
||||||
|
Version 0.9.3: 2019-03-20
|
||||||
|
|
||||||
|
Added initial support for the generic Tauron Amiplus electricity meter type (amiplus).
|
||||||
|
This is actually a generic meter type, that will match the meter vendors
|
||||||
|
that provide meters under the Amiplus brand to Taurn. The first vendor to
|
||||||
|
be supported is from Apator.
|
||||||
|
|
||||||
Added support for the at-wmbus-16-2 snap on meter (apator162).
|
Added support for the at-wmbus-16-2 snap on meter (apator162).
|
||||||
Unfortunately it uses a vendor specific protocol,
|
Unfortunately it uses a vendor specific protocol,
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -20,7 +20,7 @@
|
||||||
# make DEBUG=true
|
# make DEBUG=true
|
||||||
# make DEBUG=true HOST=arm
|
# make DEBUG=true HOST=arm
|
||||||
|
|
||||||
VERSION=0.9.2
|
VERSION=0.9.4
|
||||||
|
|
||||||
ifeq "$(HOST)" "arm"
|
ifeq "$(HOST)" "arm"
|
||||||
CXX=arm-linux-gnueabihf-g++
|
CXX=arm-linux-gnueabihf-g++
|
||||||
|
|
42
README.md
42
README.md
|
@ -17,7 +17,7 @@ The program runs on GNU/Linux, MacOSX and Raspberry Pi.
|
||||||
|
|
||||||
# Run as a daemon
|
# Run as a daemon
|
||||||
|
|
||||||
Remove the wmbus dongle (im871a or amb8465) from your computer.
|
Remove the wmbus dongle (im871a,amb8465) or the generic rtlsdr dongle (RTL2838) from your computer.
|
||||||
|
|
||||||
`make; sudo make install` will install wmbusmeters as a daemon that starts
|
`make; sudo make install` will install wmbusmeters as a daemon that starts
|
||||||
automatically when an appropriate wmbus usb dongle is inserted in the computer.
|
automatically when an appropriate wmbus usb dongle is inserted in the computer.
|
||||||
|
@ -29,9 +29,11 @@ Check the config file /etc/wmbusmeters.conf:
|
||||||
loglevel=normal
|
loglevel=normal
|
||||||
device=auto
|
device=auto
|
||||||
logtelegrams=false
|
logtelegrams=false
|
||||||
|
format=json
|
||||||
meterfiles=/var/log/wmbusmeters/meter_readings
|
meterfiles=/var/log/wmbusmeters/meter_readings
|
||||||
|
meterfilesaction=overwrite
|
||||||
logfile=/var/log/wmbusmeters/wmbusmeters.log
|
logfile=/var/log/wmbusmeters/wmbusmeters.log
|
||||||
shell=/usr/bin/mosquitto_pub -h localhost -t wmbusmeters -m "$METER_JSON"
|
shell=/usr/bin/mosquitto_pub -h localhost -t wmbusmeters/$METER_ID -m "$METER_JSON"
|
||||||
```
|
```
|
||||||
|
|
||||||
Then add a meter file in /etc/wmbusmeters.d/MyTapWater
|
Then add a meter file in /etc/wmbusmeters.d/MyTapWater
|
||||||
|
@ -44,6 +46,8 @@ key=00112233445566778899AABBCCDDEEFF
|
||||||
|
|
||||||
Now plugin your wmbus dongle. Wmbusmeters should start automatically,
|
Now plugin your wmbus dongle. Wmbusmeters should start automatically,
|
||||||
check with `tail -f /var/log/syslog` and `tail -f /var/log/wmbusmeters/wmbusmeters.log`
|
check with `tail -f /var/log/syslog` and `tail -f /var/log/wmbusmeters/wmbusmeters.log`
|
||||||
|
(If you are using an rtlsdr dongle, then make sure the binaries /usr/bin/rtl_sdr and
|
||||||
|
/usr/bin/rtl_wmbus exists and are executable.)
|
||||||
|
|
||||||
The latest reading of the meter can also be found here: /var/log/wmbusmeters/meter_readings/MyTapWater
|
The latest reading of the meter can also be found here: /var/log/wmbusmeters/meter_readings/MyTapWater
|
||||||
|
|
||||||
|
@ -70,7 +74,7 @@ The files/dir should then be located here:
|
||||||
# Running without config files, good for experimentation and test.
|
# Running without config files, good for experimentation and test.
|
||||||
|
|
||||||
```
|
```
|
||||||
wmbusmeters version: 0.9.2
|
wmbusmeters version: 0.9.4
|
||||||
Usage: wmbusmeters {options} <device> ( [meter_name] [meter_type] [meter_id] [meter_key] )*
|
Usage: wmbusmeters {options} <device> ( [meter_name] [meter_type] [meter_id] [meter_key] )*
|
||||||
|
|
||||||
As <options> you can use:
|
As <options> you can use:
|
||||||
|
@ -91,19 +95,18 @@ As <options> you can use:
|
||||||
--useconfig=<dir> load config files from dir/etc
|
--useconfig=<dir> load config files from dir/etc
|
||||||
--verbose for more information
|
--verbose for more information
|
||||||
|
|
||||||
As a <device> you can use:
|
As a <device> you can use: auto
|
||||||
|
which will look for the links /dev/im87a,/dev/amb8475 and /dev/rtlsdr (the
|
||||||
|
links are automatically generated by udev if you have run the install scripts.)
|
||||||
|
and start wmbusmeters with the proper tty device or rtlwmbus background process.
|
||||||
|
|
||||||
"/dev/ttyUSB" to which a im871a/amb8465 dongle is attached,
|
As a <device> you can also use: the exact /dev/ttyUSB0 to your dongle if you do not want
|
||||||
or you can specify auto and wmbusmeters will look for a suitable dongle
|
to install the udev rule.
|
||||||
on the device links /dev/im871a and /dev/amb8465.
|
|
||||||
|
|
||||||
"rtlwmbus:868.95M" to have wmbusmeters spawn:
|
As a <device> you can also use: rtlwmbus
|
||||||
"rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus"
|
to spawn the background process: "rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus"
|
||||||
(you might have to tweak 868.95M to nearby frequencies depending
|
You can also use: rtlwmbus:868.9M to use this fq instead. Fq tuning can sometimes
|
||||||
on the rtl-sdr dongle you are using)
|
be necessary. Or you can specify the entire background process command line: "rtlwmbus:<commandline>"
|
||||||
|
|
||||||
"rtlwmbus:<commandline>" to have wmbusmeters spawn
|
|
||||||
that commandline instead, the output is expected to be like rtl_wmbus.
|
|
||||||
|
|
||||||
As meter quadruples you specify:
|
As meter quadruples you specify:
|
||||||
<meter_name> a mnemonic for this particular meter
|
<meter_name> a mnemonic for this particular meter
|
||||||
|
@ -123,7 +126,7 @@ Supported heat cost allocator:
|
||||||
Qundis Q caloric (qcaloric)
|
Qundis Q caloric (qcaloric)
|
||||||
|
|
||||||
Supported electricity meters:
|
Supported electricity meters:
|
||||||
Tauron Amiplus (amiplus) (almost complete)
|
Tauron Amiplus (amiplus) (includes vendor apator and echelon)
|
||||||
|
|
||||||
Work in progress:
|
Work in progress:
|
||||||
Heat meter Kamstrup Multical 302 (multical302)
|
Heat meter Kamstrup Multical 302 (multical302)
|
||||||
|
@ -132,7 +135,7 @@ Electricity meter Kamstrup Omnipower (omnipower)
|
||||||
|
|
||||||
The wmbus dongles imst871a and amb8465 can only listen on one type of wmbus telegrams at a time.
|
The wmbus dongles imst871a and amb8465 can only listen on one type of wmbus telegrams at a time.
|
||||||
So you can listen to multiple meters as long as they all require the same radio mode C1 or T1.
|
So you can listen to multiple meters as long as they all require the same radio mode C1 or T1.
|
||||||
If you use rtl-sdr/rtl_wmbus, then you can listen to both C1 and T1 at the same time.
|
If you use rtlwmbus, then you can listen to both C1 and T1 at the same time.
|
||||||
|
|
||||||
# Usage examples
|
# Usage examples
|
||||||
|
|
||||||
|
@ -160,9 +163,10 @@ Example format json output:
|
||||||
|
|
||||||
`{"media":"heat","meter":"multical302","name":"MyHeater","id":"22222222","total_kwh":0.000,"total_volume_m3":0.000,"current_kw":"0.000","timestamp":"2018-02-08T09:07:22Z"}`
|
`{"media":"heat","meter":"multical302","name":"MyHeater","id":"22222222","total_kwh":0.000,"total_volume_m3":0.000,"current_kw":"0.000","timestamp":"2018-02-08T09:07:22Z"}`
|
||||||
|
|
||||||
Example format fields output:
|
Example format fields output and use rtlsdr dongle with rtlwmbus tuned to 868.9MHz instead of the
|
||||||
|
default 868.95MHz.
|
||||||
|
|
||||||
`wmbusmeters --format=fields auto GreenhouseWater multical21 33333333 ""`
|
`wmbusmeters --format=fields rtlwmbus:868.9M GreenhouseWater multical21 33333333 ""`
|
||||||
|
|
||||||
`GreenhouseTapWater;33333333;9999.099;77.712;0.000;11;31;;2018-03-05 12:10.24`
|
`GreenhouseTapWater;33333333;9999.099;77.712;0.000;11;31;;2018-03-05 12:10.24`
|
||||||
|
|
||||||
|
@ -197,7 +201,7 @@ If the meter does not use encryption of its meter data, then enter an empty key
|
||||||
`wmbusmeters --format=json --meterfiles auto MyTapWater multical21 12345678 ""`
|
`wmbusmeters --format=json --meterfiles auto MyTapWater multical21 12345678 ""`
|
||||||
|
|
||||||
If you have a Multical21 meter and you have received a KEM file and its password,
|
If you have a Multical21 meter and you have received a KEM file and its password,
|
||||||
from your water municipality, then you can use the XMLExtract.java utility to get
|
from your water municipality, then you can use the utils/XMLExtract.java utility to get
|
||||||
the meter key from the KEM file. You need to unzip the the KEM file first though,
|
the meter key from the KEM file. You need to unzip the the KEM file first though,
|
||||||
if it is zipped.
|
if it is zipped.
|
||||||
|
|
||||||
|
|
|
@ -181,10 +181,13 @@ StopWhenUnneeded=true
|
||||||
[Service]
|
[Service]
|
||||||
Type=forking
|
Type=forking
|
||||||
PrivateTmp=yes
|
PrivateTmp=yes
|
||||||
#Restart=always
|
|
||||||
RestartSec=1
|
|
||||||
User=wmbusmeters
|
User=wmbusmeters
|
||||||
Group=wmbusmeters
|
Group=wmbusmeters
|
||||||
|
Restart=always
|
||||||
|
RestartSec=1
|
||||||
|
StartLimitIntervalSec=10
|
||||||
|
StartLimitInterval=10
|
||||||
|
StartLimitBurst=3
|
||||||
|
|
||||||
# Run ExecStartPre with root-permissions
|
# Run ExecStartPre with root-permissions
|
||||||
|
|
||||||
|
@ -219,6 +222,7 @@ then
|
||||||
cat <<EOF > $ROOT/etc/udev/rules.d/99-wmbus-usb-serial.rules
|
cat <<EOF > $ROOT/etc/udev/rules.d/99-wmbus-usb-serial.rules
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60",SYMLINK+="im871a",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters.service"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60",SYMLINK+="im871a",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters.service"
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001",SYMLINK+="amb8465",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters.service"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001",SYMLINK+="amb8465",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters.service"
|
||||||
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838",SYMLINK+="rtlsdr",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters.service"
|
||||||
EOF
|
EOF
|
||||||
echo udev: installed $ROOT/etc/udev/rules.d/99-wmbus-usb-serial.rules
|
echo udev: installed $ROOT/etc/udev/rules.d/99-wmbus-usb-serial.rules
|
||||||
else
|
else
|
||||||
|
|
34
src/main.cc
34
src/main.cc
|
@ -68,20 +68,18 @@ As <options> you can use:
|
||||||
--useconfig=<dir> load config files from dir/etc
|
--useconfig=<dir> load config files from dir/etc
|
||||||
--verbose for more information
|
--verbose for more information
|
||||||
|
|
||||||
As a <device> you can use:
|
As a <device> you can use: auto
|
||||||
|
which will look for the links /dev/im87a,/dev/amb8475 and /dev/rtlsdr (the
|
||||||
|
links are automatically generated by udev if you have run the install scripts.)
|
||||||
|
and start wmbusmeters with the proper tty device or rtlwmbus background process.
|
||||||
|
|
||||||
"/dev/ttyUSB" to which a im871a/amb8465 dongle is attached,
|
As a <device> you can also use: the exact /dev/ttyUSB0 to your dongle if you do not want
|
||||||
or you can specify auto and wmbusmeters will look for a suitable dongle
|
to install the udev rule.
|
||||||
on the device links /dev/im871a and /dev/amb8465.
|
|
||||||
|
|
||||||
"rtlwmbus:868.95M" to have wmbusmeters spawn:
|
As a <device> you can also use: rtlwmbus
|
||||||
"rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus"
|
to spawn the background process: \"rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus\"
|
||||||
(you might have to tweak 868.95M to nearby frequencies depending
|
You can also use: rtlwmbus:868.9M to use this fq instead. Fq tuning can sometimes
|
||||||
on the rtl-sdr dongle you are using, also when run as a daemon,
|
be necessary. Or you can specify the entire background process command line: \"rtlwmbus:<commandline>\"
|
||||||
it uses /usr/bin/rtl_sdr and /usr/bin/rtl_wmbus instead.)
|
|
||||||
|
|
||||||
"rtlwmbus:<commandline>" to have wmbusmeters spawn
|
|
||||||
that commandline instead, its output is expected to be like rtl_wmbus.
|
|
||||||
|
|
||||||
As meter quadruples you specify:
|
As meter quadruples you specify:
|
||||||
<meter_name> a mnemonic for this particular meter
|
<meter_name> a mnemonic for this particular meter
|
||||||
|
@ -195,14 +193,12 @@ void startUsingCommandline(Configuration *config)
|
||||||
command = prefix+"rtl_sdr -f "+freq+" -s 16000000 - 2>/dev/null | "+prefix+"rtl_wmbus";
|
command = prefix+"rtl_sdr -f "+freq+" -s 16000000 - 2>/dev/null | "+prefix+"rtl_wmbus";
|
||||||
}
|
}
|
||||||
verbose("(rtlwmbus) using command: %s\n", command.c_str());
|
verbose("(rtlwmbus) using command: %s\n", command.c_str());
|
||||||
/* This will have to wait until I can differentiate between
|
|
||||||
shell childs that did mqtt/curl exits and the serial shell exit.
|
|
||||||
onChild([command,&manager](){
|
|
||||||
warning("(rtlwmbus) child process exited! Command was: \"%s\"\n", command.c_str());
|
|
||||||
manager.get()->stop();
|
|
||||||
});*/
|
|
||||||
|
|
||||||
wmbus = openRTLWMBUS(command, manager.get());
|
wmbus = openRTLWMBUS(command, manager.get(),
|
||||||
|
[command](){
|
||||||
|
warning("(rtlwmbus) child process exited! "
|
||||||
|
"Command was: \"%s\"\n", command.c_str());
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DEVICE_UNKNOWN:
|
case DEVICE_UNKNOWN:
|
||||||
|
|
|
@ -44,7 +44,8 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager {
|
||||||
~SerialCommunicationManagerImp() { }
|
~SerialCommunicationManagerImp() { }
|
||||||
|
|
||||||
unique_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate);
|
unique_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate);
|
||||||
unique_ptr<SerialDevice> createSerialDeviceCommand(string command, vector<string> args, vector<string> envs);
|
unique_ptr<SerialDevice> createSerialDeviceCommand(string command, vector<string> args, vector<string> envs,
|
||||||
|
function<void()> on_exit);
|
||||||
|
|
||||||
void listenTo(SerialDevice *sd, function<void()> cb);
|
void listenTo(SerialDevice *sd, function<void()> cb);
|
||||||
void stop();
|
void stop();
|
||||||
|
@ -69,6 +70,7 @@ private:
|
||||||
struct SerialDeviceImp : public SerialDevice {
|
struct SerialDeviceImp : public SerialDevice {
|
||||||
|
|
||||||
int fd() { return fd_; }
|
int fd() { return fd_; }
|
||||||
|
bool working() { return true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
@ -197,8 +199,11 @@ int SerialDeviceTTY::receive(vector<uchar> *data)
|
||||||
return num_read;
|
return num_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SerialDeviceCommand : public SerialDeviceImp {
|
struct SerialDeviceCommand : public SerialDeviceImp
|
||||||
SerialDeviceCommand(string command, vector<string> args, vector<string> envs, SerialCommunicationManagerImp *manager);
|
{
|
||||||
|
SerialDeviceCommand(string command, vector<string> args, vector<string> envs,
|
||||||
|
SerialCommunicationManagerImp *manager,
|
||||||
|
function<void()> on_exit);
|
||||||
~SerialDeviceCommand();
|
~SerialDeviceCommand();
|
||||||
|
|
||||||
bool open(bool fail_if_not_ok);
|
bool open(bool fail_if_not_ok);
|
||||||
|
@ -206,6 +211,8 @@ struct SerialDeviceCommand : public SerialDeviceImp {
|
||||||
bool send(vector<uchar> &data);
|
bool send(vector<uchar> &data);
|
||||||
int receive(vector<uchar> *data);
|
int receive(vector<uchar> *data);
|
||||||
int fd() { return fd_; }
|
int fd() { return fd_; }
|
||||||
|
bool working();
|
||||||
|
|
||||||
SerialCommunicationManager *manager() { return manager_; }
|
SerialCommunicationManager *manager() { return manager_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -219,16 +226,20 @@ struct SerialDeviceCommand : public SerialDeviceImp {
|
||||||
pthread_mutex_t write_lock_ = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t write_lock_ = PTHREAD_MUTEX_INITIALIZER;
|
||||||
pthread_mutex_t read_lock_ = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t read_lock_ = PTHREAD_MUTEX_INITIALIZER;
|
||||||
SerialCommunicationManagerImp *manager_;
|
SerialCommunicationManagerImp *manager_;
|
||||||
|
function<void()> on_exit_;
|
||||||
};
|
};
|
||||||
|
|
||||||
SerialDeviceCommand::SerialDeviceCommand(string command,
|
SerialDeviceCommand::SerialDeviceCommand(string command,
|
||||||
vector<string> args,
|
vector<string> args,
|
||||||
vector<string> envs,
|
vector<string> envs,
|
||||||
SerialCommunicationManagerImp *manager) {
|
SerialCommunicationManagerImp *manager,
|
||||||
|
function<void()> on_exit)
|
||||||
|
{
|
||||||
command_ = command;
|
command_ = command;
|
||||||
args_ = args;
|
args_ = args;
|
||||||
envs_ = envs;
|
envs_ = envs;
|
||||||
manager_ = manager;
|
manager_ = manager;
|
||||||
|
on_exit_ = on_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
SerialDeviceCommand::~SerialDeviceCommand()
|
SerialDeviceCommand::~SerialDeviceCommand()
|
||||||
|
@ -247,6 +258,12 @@ bool SerialDeviceCommand::open(bool fail_if_not_ok)
|
||||||
|
|
||||||
void SerialDeviceCommand::close()
|
void SerialDeviceCommand::close()
|
||||||
{
|
{
|
||||||
|
if (pid_ == 0 && fd_ == -1) return;
|
||||||
|
if (pid_ && stillRunning(pid_))
|
||||||
|
{
|
||||||
|
stopBackgroundShell(pid_);
|
||||||
|
pid_ = 0;
|
||||||
|
}
|
||||||
::flock(fd_, LOCK_UN);
|
::flock(fd_, LOCK_UN);
|
||||||
::close(fd_);
|
::close(fd_);
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
|
@ -254,6 +271,16 @@ void SerialDeviceCommand::close()
|
||||||
verbose("(serialcmd) closed %s\n", command_.c_str());
|
verbose("(serialcmd) closed %s\n", command_.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SerialDeviceCommand::working()
|
||||||
|
{
|
||||||
|
if (!pid_) return false;
|
||||||
|
bool r = stillRunning(pid_);
|
||||||
|
if (r) return true;
|
||||||
|
close();
|
||||||
|
on_exit_();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool SerialDeviceCommand::send(vector<uchar> &data)
|
bool SerialDeviceCommand::send(vector<uchar> &data)
|
||||||
{
|
{
|
||||||
if (data.size() == 0) return true;
|
if (data.size() == 0) return true;
|
||||||
|
@ -328,7 +355,7 @@ SerialCommunicationManagerImp::SerialCommunicationManagerImp(time_t exit_after_s
|
||||||
running_ = true;
|
running_ = true;
|
||||||
max_fd_ = 0;
|
max_fd_ = 0;
|
||||||
pthread_create(&thread_, NULL, startLoop, this);
|
pthread_create(&thread_, NULL, startLoop, this);
|
||||||
//running_ = (rc == 0);
|
wakeMeUpOnSigChld(thread_);
|
||||||
start_time_ = time(NULL);
|
start_time_ = time(NULL);
|
||||||
exit_after_seconds_ = exit_after_seconds;
|
exit_after_seconds_ = exit_after_seconds;
|
||||||
}
|
}
|
||||||
|
@ -344,9 +371,10 @@ unique_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceTTY(st
|
||||||
|
|
||||||
unique_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceCommand(string command,
|
unique_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceCommand(string command,
|
||||||
vector<string> args,
|
vector<string> args,
|
||||||
vector<string> envs)
|
vector<string> envs,
|
||||||
|
function<void()> on_exit)
|
||||||
{
|
{
|
||||||
return unique_ptr<SerialDevice>(new SerialDeviceCommand(command, args, envs, this));
|
return unique_ptr<SerialDevice>(new SerialDeviceCommand(command, args, envs, this, on_exit));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerialCommunicationManagerImp::listenTo(SerialDevice *sd, function<void()> cb) {
|
void SerialCommunicationManagerImp::listenTo(SerialDevice *sd, function<void()> cb) {
|
||||||
|
@ -364,7 +392,7 @@ void SerialCommunicationManagerImp::stop()
|
||||||
|
|
||||||
void SerialCommunicationManagerImp::waitForStop()
|
void SerialCommunicationManagerImp::waitForStop()
|
||||||
{
|
{
|
||||||
while (running_) { usleep(1000*1000);}
|
while (running_) { usleep(1000*1000); }
|
||||||
pthread_kill(thread_, SIGUSR1);
|
pthread_kill(thread_, SIGUSR1);
|
||||||
pthread_join(thread_, NULL);
|
pthread_join(thread_, NULL);
|
||||||
for (SerialDevice *d : devices_) {
|
for (SerialDevice *d : devices_) {
|
||||||
|
@ -405,7 +433,7 @@ void *SerialCommunicationManagerImp::eventLoop() {
|
||||||
FD_SET(d->fd(), &readfds);
|
FD_SET(d->fd(), &readfds);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timeval timeout { 3600, 0 };
|
struct timeval timeout { 10, 0 };
|
||||||
|
|
||||||
if (exit_after_seconds_ > 0) {
|
if (exit_after_seconds_ > 0) {
|
||||||
time_t curr = time(NULL);
|
time_t curr = time(NULL);
|
||||||
|
@ -431,6 +459,12 @@ void *SerialCommunicationManagerImp::eventLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (SerialDevice *d : devices_) {
|
||||||
|
if (!d->working()) {
|
||||||
|
stop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
verbose("(serialtty) event loop stopped!\n");
|
verbose("(serialtty) event loop stopped!\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
11
src/serial.h
11
src/serial.h
|
@ -29,19 +29,24 @@ using namespace std;
|
||||||
|
|
||||||
struct SerialCommunicationManager;
|
struct SerialCommunicationManager;
|
||||||
|
|
||||||
struct SerialDevice {
|
struct SerialDevice
|
||||||
|
{
|
||||||
virtual bool open(bool fail_if_not_ok) = 0;
|
virtual bool open(bool fail_if_not_ok) = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
virtual bool send(std::vector<uchar> &data) = 0;
|
virtual bool send(std::vector<uchar> &data) = 0;
|
||||||
virtual int receive(std::vector<uchar> *data) = 0;
|
virtual int receive(std::vector<uchar> *data) = 0;
|
||||||
virtual int fd() = 0;
|
virtual int fd() = 0;
|
||||||
|
virtual bool working() = 0;
|
||||||
virtual SerialCommunicationManager *manager() = 0;
|
virtual SerialCommunicationManager *manager() = 0;
|
||||||
virtual ~SerialDevice() = default;
|
virtual ~SerialDevice() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SerialCommunicationManager {
|
struct SerialCommunicationManager
|
||||||
|
{
|
||||||
virtual unique_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate) = 0;
|
virtual unique_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate) = 0;
|
||||||
virtual unique_ptr<SerialDevice> createSerialDeviceCommand(string command, vector<string> args, vector<string> envs) = 0;
|
virtual unique_ptr<SerialDevice> createSerialDeviceCommand(string command, vector<string> args,
|
||||||
|
vector<string> envs,
|
||||||
|
function<void()> on_exit) = 0;
|
||||||
virtual void listenTo(SerialDevice *sd, function<void()> cb) = 0;
|
virtual void listenTo(SerialDevice *sd, function<void()> cb) = 0;
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
virtual void waitForStop() = 0;
|
virtual void waitForStop() = 0;
|
||||||
|
|
82
src/shell.cc
82
src/shell.cc
|
@ -37,11 +37,11 @@ void invokeShell(string program, vector<string> args, vector<string> envs)
|
||||||
strcpy(p, program.c_str());
|
strcpy(p, program.c_str());
|
||||||
argv[0] = p;
|
argv[0] = p;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
debug("exec \"%s\"\n", program.c_str());
|
debug("(shell) exec \"%s\"\n", program.c_str());
|
||||||
for (auto &a : args) {
|
for (auto &a : args) {
|
||||||
argv[i] = a.c_str();
|
argv[i] = a.c_str();
|
||||||
i++;
|
i++;
|
||||||
debug("arg \"%s\"\n", a.c_str());
|
debug("(shell) arg \"%s\"\n", a.c_str());
|
||||||
}
|
}
|
||||||
argv[i] = NULL;
|
argv[i] = NULL;
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ void invokeShell(string program, vector<string> args, vector<string> envs)
|
||||||
for (auto &e : envs) {
|
for (auto &e : envs) {
|
||||||
env[i] = e.c_str();
|
env[i] = e.c_str();
|
||||||
i++;
|
i++;
|
||||||
debug("env \"%s\"\n", e.c_str());
|
debug("(shell) env \"%s\"\n", e.c_str());
|
||||||
}
|
}
|
||||||
env[i] = NULL;
|
env[i] = NULL;
|
||||||
|
|
||||||
|
@ -68,20 +68,20 @@ void invokeShell(string program, vector<string> args, vector<string> envs)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
perror("Execvp failed:");
|
perror("Execvp failed:");
|
||||||
error("Invoking shell %s failed!\n", program.c_str());
|
error("(shell) invoking %s failed!\n", program.c_str());
|
||||||
} else {
|
} else {
|
||||||
if (pid == -1) {
|
if (pid == -1) {
|
||||||
error("Could not fork!\n");
|
error("(shell) could not fork!\n");
|
||||||
}
|
}
|
||||||
debug("waiting for child %d.\n", pid);
|
debug("(shell) waiting for child %d to complete.\n", pid);
|
||||||
// Wait for the child to finish!
|
// Wait for the child to finish!
|
||||||
waitpid(pid, &status, 0);
|
waitpid(pid, &status, 0);
|
||||||
if (WIFEXITED(status)) {
|
if (WIFEXITED(status)) {
|
||||||
// Child exited properly.
|
// Child exited properly.
|
||||||
int rc = WEXITSTATUS(status);
|
int rc = WEXITSTATUS(status);
|
||||||
debug("%s: return code %d\n", program.c_str(), rc);
|
debug("(shell) %s: return code %d\n", program.c_str(), rc);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
warning("%s exited with non-zero return code: %d\n", program.c_str(), rc);
|
warning("(shell) %s exited with non-zero return code: %d\n", program.c_str(), rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,11 +96,11 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
|
||||||
strcpy(p, program.c_str());
|
strcpy(p, program.c_str());
|
||||||
argv[0] = p;
|
argv[0] = p;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
debug("exec background \"%s\"\n", program.c_str());
|
debug("(bgshell) exec background \"%s\"\n", program.c_str());
|
||||||
for (auto &a : args) {
|
for (auto &a : args) {
|
||||||
argv[i] = a.c_str();
|
argv[i] = a.c_str();
|
||||||
i++;
|
i++;
|
||||||
debug("arg \"%s\"\n", a.c_str());
|
debug("(bgshell) arg \"%s\"\n", a.c_str());
|
||||||
}
|
}
|
||||||
argv[i] = NULL;
|
argv[i] = NULL;
|
||||||
|
|
||||||
|
@ -110,12 +110,12 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
|
||||||
for (auto &e : envs) {
|
for (auto &e : envs) {
|
||||||
env[i] = e.c_str();
|
env[i] = e.c_str();
|
||||||
i++;
|
i++;
|
||||||
debug("env \"%s\"\n", e.c_str());
|
debug("(bgshell) env \"%s\"\n", e.c_str());
|
||||||
}
|
}
|
||||||
env[i] = NULL;
|
env[i] = NULL;
|
||||||
|
|
||||||
if (pipe(link) == -1) {
|
if (pipe(link) == -1) {
|
||||||
error("Could not create pipe!\n");
|
error("(bgshell) could not create pipe!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
*pid = fork();
|
*pid = fork();
|
||||||
|
@ -138,7 +138,7 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
perror("Execvp failed:");
|
perror("Execvp failed:");
|
||||||
warning("Invoking shell %s failed!\n", program.c_str());
|
error("(bgshell) invoking %s failed!\n", program.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,21 +147,65 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool stillRunning(int pid)
|
||||||
|
{
|
||||||
|
if (pid == 0) return false;
|
||||||
|
int status;
|
||||||
|
int p = waitpid(pid, &status, WNOHANG);
|
||||||
|
if (p == 0) {
|
||||||
|
// The pid has not exited yet.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p < 0) {
|
||||||
|
// No pid to wait for.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
// Child exited properly.
|
||||||
|
int rc = WEXITSTATUS(status);
|
||||||
|
debug("(bgshell) %d exited with return code %d\n", pid, rc);
|
||||||
|
}
|
||||||
|
else if (WIFSIGNALED(status)) {
|
||||||
|
// Child forcefully terminated
|
||||||
|
debug("(bgshell) %d terminated due to signal %d\n", pid, WTERMSIG(status));
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// Exited for other reasons, whatever those may be.
|
||||||
|
debug("(bgshell) %d exited\n", pid);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void stopBackgroundShell(int pid)
|
void stopBackgroundShell(int pid)
|
||||||
{
|
{
|
||||||
assert(pid > 0);
|
assert(pid > 0);
|
||||||
|
|
||||||
kill(pid, SIGINT);
|
int rc = kill(pid, SIGINT);
|
||||||
int status;
|
if (rc < 0) {
|
||||||
debug("waiting for child %d.\n", pid);
|
debug("(bgshell) could not sigint pid %d, exited already?\n", pid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Wait for the child to finish!
|
// Wait for the child to finish!
|
||||||
waitpid(pid, &status, 0);
|
debug("(bgshell) sent sigint, now waiting for child %d to exit.\n", pid);
|
||||||
|
int status;
|
||||||
|
int p = waitpid(pid, &status, 0);
|
||||||
|
if (p < 0) {
|
||||||
|
debug("(bgshell) cannot stop pid %d, exited already?\n", pid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (WIFEXITED(status)) {
|
if (WIFEXITED(status)) {
|
||||||
// Child exited properly.
|
// Child exited properly.
|
||||||
int rc = WEXITSTATUS(status);
|
int rc = WEXITSTATUS(status);
|
||||||
debug("bgshell: return code %d\n", rc);
|
debug("(bgshell) return code %d\n", rc);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
warning("bgshell: exited with non-zero return code: %d\n", rc);
|
warning("(bgshell) exited with non-zero return code: %d\n", rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (WIFSIGNALED(status)) {
|
||||||
|
// Child forcefully terminated
|
||||||
|
debug("(bgshell) %d terminated due to signal %d\n", pid, WTERMSIG(status));
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
debug("(bgshell) %d exited\n", pid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,5 @@ using namespace std;
|
||||||
|
|
||||||
void invokeShell(string program, vector<string> args, vector<string> envs);
|
void invokeShell(string program, vector<string> args, vector<string> envs);
|
||||||
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);
|
||||||
void stopBackgroundShell(int pid);
|
void stopBackgroundShell(int pid);
|
||||||
|
|
20
src/util.cc
20
src/util.cc
|
@ -39,19 +39,22 @@ void exitHandler(int signum)
|
||||||
if (exit_handler) exit_handler();
|
if (exit_handler) exit_handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
function<void()> child_handler;
|
pthread_t wake_me_up_on_sig_chld_ {};
|
||||||
|
|
||||||
void childProcessDied(int signum)
|
void wakeMeUpOnSigChld(pthread_t t)
|
||||||
{
|
{
|
||||||
if (child_handler) child_handler();
|
wake_me_up_on_sig_chld_ = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
void doNothing(int signum) {
|
void doNothing(int signum)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void onChild(function<void()> cb)
|
void signalMyself(int signum)
|
||||||
{
|
{
|
||||||
child_handler = cb;
|
if (wake_me_up_on_sig_chld_) {
|
||||||
|
pthread_kill(wake_me_up_on_sig_chld_, SIGUSR1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onExit(function<void()> cb)
|
void onExit(function<void()> cb)
|
||||||
|
@ -70,14 +73,15 @@ void onExit(function<void()> cb)
|
||||||
sigaction (SIGTERM, NULL, &old_action);
|
sigaction (SIGTERM, NULL, &old_action);
|
||||||
if (old_action.sa_handler != SIG_IGN) sigaction (SIGTERM, &new_action, NULL);
|
if (old_action.sa_handler != SIG_IGN) sigaction (SIGTERM, &new_action, NULL);
|
||||||
|
|
||||||
new_action.sa_handler = childProcessDied;
|
new_action.sa_handler = signalMyself;
|
||||||
|
sigemptyset (&new_action.sa_mask);
|
||||||
|
new_action.sa_flags = 0;
|
||||||
sigaction (SIGCHLD, NULL, &old_action);
|
sigaction (SIGCHLD, NULL, &old_action);
|
||||||
if (old_action.sa_handler != SIG_IGN) sigaction (SIGCHLD, &new_action, NULL);
|
if (old_action.sa_handler != SIG_IGN) sigaction (SIGCHLD, &new_action, NULL);
|
||||||
|
|
||||||
new_action.sa_handler = doNothing;
|
new_action.sa_handler = doNothing;
|
||||||
sigemptyset (&new_action.sa_mask);
|
sigemptyset (&new_action.sa_mask);
|
||||||
new_action.sa_flags = 0;
|
new_action.sa_flags = 0;
|
||||||
|
|
||||||
sigaction (SIGUSR1, NULL, &old_action);
|
sigaction (SIGUSR1, NULL, &old_action);
|
||||||
if (old_action.sa_handler != SIG_IGN) sigaction(SIGUSR1, &new_action, NULL);
|
if (old_action.sa_handler != SIG_IGN) sigaction(SIGUSR1, &new_action, NULL);
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include<vector>
|
#include<vector>
|
||||||
|
|
||||||
void onExit(std::function<void()> cb);
|
void onExit(std::function<void()> cb);
|
||||||
void onChild(std::function<void()> cb);
|
void wakeMeUpOnSigChld(pthread_t t);
|
||||||
|
|
||||||
typedef unsigned char uchar;
|
typedef unsigned char uchar;
|
||||||
|
|
||||||
|
|
|
@ -271,6 +271,7 @@ string mediaTypeJSON(int a_field_device_type)
|
||||||
|
|
||||||
bool detectIM871A(string device, SerialCommunicationManager *handler);
|
bool detectIM871A(string device, SerialCommunicationManager *handler);
|
||||||
bool detectAMB8465(string device, SerialCommunicationManager *handler);
|
bool detectAMB8465(string device, SerialCommunicationManager *handler);
|
||||||
|
bool detectRTLSDR(string device, SerialCommunicationManager *handler);
|
||||||
|
|
||||||
bool existsButWrongGroup(string device)
|
bool existsButWrongGroup(string device)
|
||||||
{
|
{
|
||||||
|
@ -317,6 +318,13 @@ pair<MBusDeviceType,string> detectMBusDevice(string device, SerialCommunicationM
|
||||||
error("You are not in the same group as the device /dev/amb8465\n");
|
error("You are not in the same group as the device /dev/amb8465\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (detectRTLSDR("/dev/rtlsdr", handler))
|
||||||
|
{
|
||||||
|
return { DEVICE_RTLWMBUS, "rtlwmbus" };
|
||||||
|
} else if (existsButWrongGroup("/dev/rtlsdr")) {
|
||||||
|
error("You are not in the same group as the device /dev/rtlsdr\n");
|
||||||
|
}
|
||||||
|
|
||||||
return { DEVICE_UNKNOWN, "" };
|
return { DEVICE_UNKNOWN, "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,8 @@ pair<MBusDeviceType,string> detectMBusDevice(string device, SerialCommunicationM
|
||||||
unique_ptr<WMBus> openIM871A(string device, SerialCommunicationManager *manager);
|
unique_ptr<WMBus> openIM871A(string device, SerialCommunicationManager *manager);
|
||||||
unique_ptr<WMBus> openAMB8465(string device, SerialCommunicationManager *manager);
|
unique_ptr<WMBus> openAMB8465(string device, SerialCommunicationManager *manager);
|
||||||
struct WMBusSimulator;
|
struct WMBusSimulator;
|
||||||
unique_ptr<WMBus> openRTLWMBUS(string device, SerialCommunicationManager *manager);
|
unique_ptr<WMBus> openRTLWMBUS(string device, SerialCommunicationManager *manager,
|
||||||
|
std::function<void()> on_exit);
|
||||||
unique_ptr<WMBus> openSimulator(string file, SerialCommunicationManager *manager);
|
unique_ptr<WMBus> openSimulator(string file, SerialCommunicationManager *manager);
|
||||||
|
|
||||||
string manufacturer(int m_field);
|
string manufacturer(int m_field);
|
||||||
|
|
|
@ -20,10 +20,12 @@
|
||||||
|
|
||||||
#include<assert.h>
|
#include<assert.h>
|
||||||
#include<fcntl.h>
|
#include<fcntl.h>
|
||||||
|
#include<grp.h>
|
||||||
#include<pthread.h>
|
#include<pthread.h>
|
||||||
#include<semaphore.h>
|
#include<semaphore.h>
|
||||||
#include<string.h>
|
#include<string.h>
|
||||||
#include<sys/errno.h>
|
#include<sys/errno.h>
|
||||||
|
#include<sys/stat.h>
|
||||||
#include<sys/types.h>
|
#include<sys/types.h>
|
||||||
#include<unistd.h>
|
#include<unistd.h>
|
||||||
|
|
||||||
|
@ -60,13 +62,14 @@ private:
|
||||||
SerialCommunicationManager *manager_ {};
|
SerialCommunicationManager *manager_ {};
|
||||||
};
|
};
|
||||||
|
|
||||||
unique_ptr<WMBus> openRTLWMBUS(string command, SerialCommunicationManager *manager)
|
unique_ptr<WMBus> openRTLWMBUS(string command, SerialCommunicationManager *manager,
|
||||||
|
function<void()> on_exit)
|
||||||
{
|
{
|
||||||
vector<string> args;
|
vector<string> args;
|
||||||
vector<string> envs;
|
vector<string> envs;
|
||||||
args.push_back("-c");
|
args.push_back("-c");
|
||||||
args.push_back(command);
|
args.push_back(command);
|
||||||
auto serial = manager->createSerialDeviceCommand("/bin/sh", args, envs);
|
auto serial = manager->createSerialDeviceCommand("/bin/sh", args, envs, on_exit);
|
||||||
WMBusRTLWMBUS *imp = new WMBusRTLWMBUS(std::move(serial), manager);
|
WMBusRTLWMBUS *imp = new WMBusRTLWMBUS(std::move(serial), manager);
|
||||||
return unique_ptr<WMBus>(imp);
|
return unique_ptr<WMBus>(imp);
|
||||||
}
|
}
|
||||||
|
@ -189,3 +192,20 @@ FrameStatus WMBusRTLWMBUS::checkRTLWMBUSFrame(vector<uchar> &data,
|
||||||
|
|
||||||
return FullFrame;
|
return FullFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool detectRTLSDR(string device, SerialCommunicationManager *manager)
|
||||||
|
{
|
||||||
|
// No more advanced test than that the /dev/rtlsdr link exists.
|
||||||
|
struct stat sb;
|
||||||
|
int rc = stat(device.c_str(), &sb);
|
||||||
|
if (rc) return false;
|
||||||
|
|
||||||
|
struct group *g = getgrgid(sb.st_gid);
|
||||||
|
if (g && getegid() != g->gr_gid)
|
||||||
|
{
|
||||||
|
// Our group is not the same as the device.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue