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).
 | 
			
		||||
Unfortunately it uses a vendor specific protocol,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										2
									
								
								Makefile
								
								
								
								
							| 
						 | 
				
			
			@ -20,7 +20,7 @@
 | 
			
		|||
# make DEBUG=true
 | 
			
		||||
# make DEBUG=true HOST=arm
 | 
			
		||||
 | 
			
		||||
VERSION=0.9.2
 | 
			
		||||
VERSION=0.9.4
 | 
			
		||||
 | 
			
		||||
ifeq "$(HOST)" "arm"
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
device=auto
 | 
			
		||||
logtelegrams=false
 | 
			
		||||
format=json
 | 
			
		||||
meterfiles=/var/log/wmbusmeters/meter_readings
 | 
			
		||||
meterfilesaction=overwrite
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +46,8 @@ key=00112233445566778899AABBCCDDEEFF
 | 
			
		|||
 | 
			
		||||
Now plugin your wmbus dongle. Wmbusmeters should start automatically,
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +74,7 @@ The files/dir should then be located here:
 | 
			
		|||
# 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] )*
 | 
			
		||||
 | 
			
		||||
As <options> you can use:
 | 
			
		||||
| 
						 | 
				
			
			@ -91,19 +95,18 @@ As <options> you can use:
 | 
			
		|||
    --useconfig=<dir> load config files from dir/etc
 | 
			
		||||
    --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,
 | 
			
		||||
or you can specify auto and wmbusmeters will look for a suitable dongle
 | 
			
		||||
on the device links /dev/im871a and /dev/amb8465.
 | 
			
		||||
As a <device> you can also use: the exact /dev/ttyUSB0 to your dongle if you do not want
 | 
			
		||||
to install the udev rule.
 | 
			
		||||
 | 
			
		||||
"rtlwmbus:868.95M" to have wmbusmeters spawn:
 | 
			
		||||
"rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus"
 | 
			
		||||
(you might have to tweak 868.95M to nearby frequencies depending
 | 
			
		||||
on the rtl-sdr dongle you are using)
 | 
			
		||||
 | 
			
		||||
"rtlwmbus:<commandline>" to have wmbusmeters spawn
 | 
			
		||||
that commandline instead, the output is expected to be like rtl_wmbus.
 | 
			
		||||
As a <device> you can also use: rtlwmbus
 | 
			
		||||
to spawn the background process: "rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus"
 | 
			
		||||
You can also use: rtlwmbus:868.9M to use this fq instead. Fq tuning can sometimes
 | 
			
		||||
be necessary. Or you can specify the entire background process command line: "rtlwmbus:<commandline>"
 | 
			
		||||
 | 
			
		||||
As meter quadruples you specify:
 | 
			
		||||
<meter_name> a mnemonic for this particular meter
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +126,7 @@ Supported heat cost allocator:
 | 
			
		|||
Qundis Q caloric (qcaloric)
 | 
			
		||||
 | 
			
		||||
Supported electricity meters:
 | 
			
		||||
Tauron Amiplus (amiplus) (almost complete)
 | 
			
		||||
Tauron Amiplus (amiplus) (includes vendor apator and echelon)
 | 
			
		||||
 | 
			
		||||
Work in progress:
 | 
			
		||||
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.
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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"}`
 | 
			
		||||
 | 
			
		||||
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`
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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 ""`
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
if it is zipped.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -181,10 +181,13 @@ StopWhenUnneeded=true
 | 
			
		|||
[Service]
 | 
			
		||||
Type=forking
 | 
			
		||||
PrivateTmp=yes
 | 
			
		||||
#Restart=always
 | 
			
		||||
RestartSec=1
 | 
			
		||||
User=wmbusmeters
 | 
			
		||||
Group=wmbusmeters
 | 
			
		||||
Restart=always
 | 
			
		||||
RestartSec=1
 | 
			
		||||
StartLimitIntervalSec=10
 | 
			
		||||
StartLimitInterval=10
 | 
			
		||||
StartLimitBurst=3
 | 
			
		||||
 | 
			
		||||
# Run ExecStartPre with root-permissions
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -219,6 +222,7 @@ then
 | 
			
		|||
    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}=="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
 | 
			
		||||
    echo udev: installed $ROOT/etc/udev/rules.d/99-wmbus-usb-serial.rules
 | 
			
		||||
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
 | 
			
		||||
    --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,
 | 
			
		||||
or you can specify auto and wmbusmeters will look for a suitable dongle
 | 
			
		||||
on the device links /dev/im871a and /dev/amb8465.
 | 
			
		||||
As a <device> you can also use: the exact /dev/ttyUSB0 to your dongle if you do not want
 | 
			
		||||
to install the udev rule.
 | 
			
		||||
 | 
			
		||||
"rtlwmbus:868.95M" to have wmbusmeters spawn:
 | 
			
		||||
"rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus"
 | 
			
		||||
(you might have to tweak 868.95M to nearby frequencies depending
 | 
			
		||||
on the rtl-sdr dongle you are using, also when run as a daemon,
 | 
			
		||||
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 a <device> you can also use: rtlwmbus
 | 
			
		||||
to spawn the background process: \"rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus\"
 | 
			
		||||
You can also use: rtlwmbus:868.9M to use this fq instead. Fq tuning can sometimes
 | 
			
		||||
be necessary. Or you can specify the entire background process command line: \"rtlwmbus:<commandline>\"
 | 
			
		||||
 | 
			
		||||
As meter quadruples you specify:
 | 
			
		||||
<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";
 | 
			
		||||
        }
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
    case DEVICE_UNKNOWN:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,7 +44,8 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager {
 | 
			
		|||
    ~SerialCommunicationManagerImp() { }
 | 
			
		||||
 | 
			
		||||
    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 stop();
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +70,7 @@ private:
 | 
			
		|||
struct SerialDeviceImp : public SerialDevice {
 | 
			
		||||
 | 
			
		||||
    int fd() { return fd_; }
 | 
			
		||||
    bool working() { return true; }
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -197,8 +199,11 @@ int SerialDeviceTTY::receive(vector<uchar> *data)
 | 
			
		|||
    return num_read;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct SerialDeviceCommand : public SerialDeviceImp {
 | 
			
		||||
    SerialDeviceCommand(string command, vector<string> args, vector<string> envs, SerialCommunicationManagerImp *manager);
 | 
			
		||||
struct SerialDeviceCommand : public SerialDeviceImp
 | 
			
		||||
{
 | 
			
		||||
    SerialDeviceCommand(string command, vector<string> args, vector<string> envs,
 | 
			
		||||
                        SerialCommunicationManagerImp *manager,
 | 
			
		||||
                        function<void()> on_exit);
 | 
			
		||||
    ~SerialDeviceCommand();
 | 
			
		||||
 | 
			
		||||
    bool open(bool fail_if_not_ok);
 | 
			
		||||
| 
						 | 
				
			
			@ -206,6 +211,8 @@ struct SerialDeviceCommand : public SerialDeviceImp {
 | 
			
		|||
    bool send(vector<uchar> &data);
 | 
			
		||||
    int receive(vector<uchar> *data);
 | 
			
		||||
    int fd() { return fd_; }
 | 
			
		||||
    bool working();
 | 
			
		||||
 | 
			
		||||
    SerialCommunicationManager *manager() { return manager_; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
| 
						 | 
				
			
			@ -219,16 +226,20 @@ struct SerialDeviceCommand : public SerialDeviceImp {
 | 
			
		|||
    pthread_mutex_t write_lock_ = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
    pthread_mutex_t read_lock_ = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
    SerialCommunicationManagerImp *manager_;
 | 
			
		||||
    function<void()> on_exit_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
SerialDeviceCommand::SerialDeviceCommand(string command,
 | 
			
		||||
                                         vector<string> args,
 | 
			
		||||
                                         vector<string> envs,
 | 
			
		||||
                                         SerialCommunicationManagerImp *manager) {
 | 
			
		||||
                                         SerialCommunicationManagerImp *manager,
 | 
			
		||||
                                         function<void()> on_exit)
 | 
			
		||||
{
 | 
			
		||||
    command_ = command;
 | 
			
		||||
    args_ = args;
 | 
			
		||||
    envs_ = envs;
 | 
			
		||||
    manager_ = manager;
 | 
			
		||||
    on_exit_ = on_exit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SerialDeviceCommand::~SerialDeviceCommand()
 | 
			
		||||
| 
						 | 
				
			
			@ -247,6 +258,12 @@ bool SerialDeviceCommand::open(bool fail_if_not_ok)
 | 
			
		|||
 | 
			
		||||
void SerialDeviceCommand::close()
 | 
			
		||||
{
 | 
			
		||||
    if (pid_ == 0 && fd_ == -1) return;
 | 
			
		||||
    if (pid_ && stillRunning(pid_))
 | 
			
		||||
    {
 | 
			
		||||
        stopBackgroundShell(pid_);
 | 
			
		||||
        pid_ = 0;
 | 
			
		||||
    }
 | 
			
		||||
    ::flock(fd_, LOCK_UN);
 | 
			
		||||
    ::close(fd_);
 | 
			
		||||
    fd_ = -1;
 | 
			
		||||
| 
						 | 
				
			
			@ -254,6 +271,16 @@ void SerialDeviceCommand::close()
 | 
			
		|||
    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)
 | 
			
		||||
{
 | 
			
		||||
    if (data.size() == 0) return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -328,7 +355,7 @@ SerialCommunicationManagerImp::SerialCommunicationManagerImp(time_t exit_after_s
 | 
			
		|||
    running_ = true;
 | 
			
		||||
    max_fd_ = 0;
 | 
			
		||||
    pthread_create(&thread_, NULL, startLoop, this);
 | 
			
		||||
    //running_ = (rc == 0);
 | 
			
		||||
    wakeMeUpOnSigChld(thread_);
 | 
			
		||||
    start_time_ = time(NULL);
 | 
			
		||||
    exit_after_seconds_ = exit_after_seconds;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -344,9 +371,10 @@ unique_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceTTY(st
 | 
			
		|||
 | 
			
		||||
unique_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceCommand(string command,
 | 
			
		||||
                                                                                  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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -364,7 +392,7 @@ void SerialCommunicationManagerImp::stop()
 | 
			
		|||
 | 
			
		||||
void SerialCommunicationManagerImp::waitForStop()
 | 
			
		||||
{
 | 
			
		||||
    while (running_) { usleep(1000*1000);}
 | 
			
		||||
    while (running_) { usleep(1000*1000); }
 | 
			
		||||
    pthread_kill(thread_, SIGUSR1);
 | 
			
		||||
    pthread_join(thread_, NULL);
 | 
			
		||||
    for (SerialDevice *d : devices_) {
 | 
			
		||||
| 
						 | 
				
			
			@ -405,7 +433,7 @@ void *SerialCommunicationManagerImp::eventLoop() {
 | 
			
		|||
            FD_SET(d->fd(), &readfds);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        struct timeval timeout { 3600, 0 };
 | 
			
		||||
        struct timeval timeout { 10, 0 };
 | 
			
		||||
 | 
			
		||||
        if (exit_after_seconds_ > 0) {
 | 
			
		||||
            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");
 | 
			
		||||
    return NULL;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								src/serial.h
								
								
								
								
							
							
						
						
									
										11
									
								
								src/serial.h
								
								
								
								
							| 
						 | 
				
			
			@ -29,19 +29,24 @@ using namespace std;
 | 
			
		|||
 | 
			
		||||
struct SerialCommunicationManager;
 | 
			
		||||
 | 
			
		||||
struct SerialDevice {
 | 
			
		||||
struct SerialDevice
 | 
			
		||||
{
 | 
			
		||||
    virtual bool open(bool fail_if_not_ok) = 0;
 | 
			
		||||
    virtual void close() = 0;
 | 
			
		||||
    virtual bool send(std::vector<uchar> &data) = 0;
 | 
			
		||||
    virtual int receive(std::vector<uchar> *data) = 0;
 | 
			
		||||
    virtual int fd() = 0;
 | 
			
		||||
    virtual bool working() = 0;
 | 
			
		||||
    virtual SerialCommunicationManager *manager() = 0;
 | 
			
		||||
    virtual ~SerialDevice() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SerialCommunicationManager {
 | 
			
		||||
struct SerialCommunicationManager
 | 
			
		||||
{
 | 
			
		||||
    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 stop() = 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());
 | 
			
		||||
    argv[0] = p;
 | 
			
		||||
    int i = 1;
 | 
			
		||||
    debug("exec \"%s\"\n", program.c_str());
 | 
			
		||||
    debug("(shell) exec \"%s\"\n", program.c_str());
 | 
			
		||||
    for (auto &a : args) {
 | 
			
		||||
        argv[i] = a.c_str();
 | 
			
		||||
        i++;
 | 
			
		||||
        debug("arg \"%s\"\n", a.c_str());
 | 
			
		||||
        debug("(shell) arg \"%s\"\n", a.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    argv[i] = NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +51,7 @@ void invokeShell(string program, vector<string> args, vector<string> envs)
 | 
			
		|||
    for (auto &e : envs) {
 | 
			
		||||
        env[i] = e.c_str();
 | 
			
		||||
        i++;
 | 
			
		||||
        debug("env \"%s\"\n", e.c_str());
 | 
			
		||||
        debug("(shell) env \"%s\"\n", e.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    env[i] = NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,20 +68,20 @@ void invokeShell(string program, vector<string> args, vector<string> envs)
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
        perror("Execvp failed:");
 | 
			
		||||
        error("Invoking shell %s failed!\n", program.c_str());
 | 
			
		||||
        error("(shell) invoking %s failed!\n", program.c_str());
 | 
			
		||||
    } else {
 | 
			
		||||
        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!
 | 
			
		||||
        waitpid(pid, &status, 0);
 | 
			
		||||
        if (WIFEXITED(status)) {
 | 
			
		||||
            // Child exited properly.
 | 
			
		||||
            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) {
 | 
			
		||||
                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());
 | 
			
		||||
    argv[0] = p;
 | 
			
		||||
    int i = 1;
 | 
			
		||||
    debug("exec background \"%s\"\n", program.c_str());
 | 
			
		||||
    debug("(bgshell) exec background \"%s\"\n", program.c_str());
 | 
			
		||||
    for (auto &a : args) {
 | 
			
		||||
        argv[i] = a.c_str();
 | 
			
		||||
        i++;
 | 
			
		||||
        debug("arg \"%s\"\n", a.c_str());
 | 
			
		||||
        debug("(bgshell) arg \"%s\"\n", a.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    argv[i] = NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -110,12 +110,12 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
 | 
			
		|||
    for (auto &e : envs) {
 | 
			
		||||
        env[i] = e.c_str();
 | 
			
		||||
        i++;
 | 
			
		||||
        debug("env \"%s\"\n", e.c_str());
 | 
			
		||||
        debug("(bgshell) env \"%s\"\n", e.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    env[i] = NULL;
 | 
			
		||||
 | 
			
		||||
    if (pipe(link) == -1) {
 | 
			
		||||
        error("Could not create pipe!\n");
 | 
			
		||||
        error("(bgshell) could not create pipe!\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *pid = fork();
 | 
			
		||||
| 
						 | 
				
			
			@ -138,7 +138,7 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
        perror("Execvp failed:");
 | 
			
		||||
        warning("Invoking shell %s failed!\n", program.c_str());
 | 
			
		||||
        error("(bgshell) invoking %s failed!\n", program.c_str());
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -147,21 +147,65 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
 | 
			
		|||
    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)
 | 
			
		||||
{
 | 
			
		||||
    assert(pid > 0);
 | 
			
		||||
 | 
			
		||||
    kill(pid, SIGINT);
 | 
			
		||||
    int status;
 | 
			
		||||
    debug("waiting for child %d.\n", pid);
 | 
			
		||||
    int rc = kill(pid, SIGINT);
 | 
			
		||||
    if (rc < 0) {
 | 
			
		||||
        debug("(bgshell) could not sigint pid %d, exited already?\n", pid);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    // 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)) {
 | 
			
		||||
        // Child exited properly.
 | 
			
		||||
        int rc = WEXITSTATUS(status);
 | 
			
		||||
        debug("bgshell: return code %d\n", rc);
 | 
			
		||||
        debug("(bgshell) return code %d\n", rc);
 | 
			
		||||
        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);
 | 
			
		||||
bool invokeBackgroundShell(string program, vector<string> args, vector<string> envs, int *out, int *pid);
 | 
			
		||||
bool stillRunning(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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
| 
						 | 
				
			
			@ -70,14 +73,15 @@ void onExit(function<void()> cb)
 | 
			
		|||
    sigaction (SIGTERM, NULL, &old_action);
 | 
			
		||||
    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);
 | 
			
		||||
    if (old_action.sa_handler != SIG_IGN) sigaction (SIGCHLD, &new_action, NULL);
 | 
			
		||||
 | 
			
		||||
    new_action.sa_handler = doNothing;
 | 
			
		||||
    sigemptyset (&new_action.sa_mask);
 | 
			
		||||
    new_action.sa_flags = 0;
 | 
			
		||||
 | 
			
		||||
    sigaction (SIGUSR1, NULL, &old_action);
 | 
			
		||||
    if (old_action.sa_handler != SIG_IGN) sigaction(SIGUSR1, &new_action, NULL);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@
 | 
			
		|||
#include<vector>
 | 
			
		||||
 | 
			
		||||
void onExit(std::function<void()> cb);
 | 
			
		||||
void onChild(std::function<void()> cb);
 | 
			
		||||
void wakeMeUpOnSigChld(pthread_t t);
 | 
			
		||||
 | 
			
		||||
typedef unsigned char uchar;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -271,6 +271,7 @@ string mediaTypeJSON(int a_field_device_type)
 | 
			
		|||
 | 
			
		||||
bool detectIM871A(string device, SerialCommunicationManager *handler);
 | 
			
		||||
bool detectAMB8465(string device, SerialCommunicationManager *handler);
 | 
			
		||||
bool detectRTLSDR(string device, SerialCommunicationManager *handler);
 | 
			
		||||
 | 
			
		||||
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");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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, "" };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -145,7 +145,8 @@ pair<MBusDeviceType,string> detectMBusDevice(string device, SerialCommunicationM
 | 
			
		|||
unique_ptr<WMBus> openIM871A(string device, SerialCommunicationManager *manager);
 | 
			
		||||
unique_ptr<WMBus> openAMB8465(string device, SerialCommunicationManager *manager);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
string manufacturer(int m_field);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,10 +20,12 @@
 | 
			
		|||
 | 
			
		||||
#include<assert.h>
 | 
			
		||||
#include<fcntl.h>
 | 
			
		||||
#include<grp.h>
 | 
			
		||||
#include<pthread.h>
 | 
			
		||||
#include<semaphore.h>
 | 
			
		||||
#include<string.h>
 | 
			
		||||
#include<sys/errno.h>
 | 
			
		||||
#include<sys/stat.h>
 | 
			
		||||
#include<sys/types.h>
 | 
			
		||||
#include<unistd.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,13 +62,14 @@ private:
 | 
			
		|||
    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> envs;
 | 
			
		||||
    args.push_back("-c");
 | 
			
		||||
    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);
 | 
			
		||||
    return unique_ptr<WMBus>(imp);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -189,3 +192,20 @@ FrameStatus WMBusRTLWMBUS::checkRTLWMBUSFrame(vector<uchar> &data,
 | 
			
		|||
 | 
			
		||||
    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