kopia lustrzana https://github.com/weetmuts/wmbusmeters
				
				
				
			Now rtlwmbus seems to work.
							rodzic
							
								
									dfe8c8bee5
								
							
						
					
					
						commit
						b69b27707c
					
				
							
								
								
									
										5
									
								
								CHANGES
								
								
								
								
							
							
						
						
									
										5
									
								
								CHANGES
								
								
								
								
							| 
						 | 
				
			
			@ -1,4 +1,9 @@
 | 
			
		|||
 | 
			
		||||
Version 0.9.0: 2019-02-24
 | 
			
		||||
 | 
			
		||||
Running wmbusmeters as daemon is now supported.
 | 
			
		||||
Updated README
 | 
			
		||||
 | 
			
		||||
Version 0.8.4: 2019-02-23
 | 
			
		||||
 | 
			
		||||
Add config files support and daemon mode.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Makefile
								
								
								
								
							| 
						 | 
				
			
			@ -20,7 +20,7 @@
 | 
			
		|||
# make DEBUG=true
 | 
			
		||||
# make DEBUG=true HOST=arm
 | 
			
		||||
 | 
			
		||||
VERSION=0.8.4
 | 
			
		||||
VERSION=0.9.0
 | 
			
		||||
 | 
			
		||||
ifeq "$(HOST)" "arm"
 | 
			
		||||
    CXX=arm-linux-gnueabihf-g++
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +73,7 @@ METERS_OBJS:=\
 | 
			
		|||
	$(BUILD)/wmbus.o \
 | 
			
		||||
	$(BUILD)/wmbus_amb8465.o \
 | 
			
		||||
	$(BUILD)/wmbus_im871a.o \
 | 
			
		||||
	$(BUILD)/wmbus_rtlwmbus.o \
 | 
			
		||||
	$(BUILD)/wmbus_simulator.o \
 | 
			
		||||
	$(BUILD)/wmbus_utils.o
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -194,9 +194,15 @@ unique_ptr<Configuration> parseCommandLine(int argc, char **argv) {
 | 
			
		|||
        error("Unknown option \"%s\"\n", argv[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    c->usb_device = argv[i];
 | 
			
		||||
    char *extra = strchr(argv[i], ':');
 | 
			
		||||
    if (extra) {
 | 
			
		||||
        *extra = 0;
 | 
			
		||||
        extra++;
 | 
			
		||||
        c->device_extra = extra;
 | 
			
		||||
    }
 | 
			
		||||
    c->device = argv[i];
 | 
			
		||||
    i++;
 | 
			
		||||
    if (c->usb_device.length() == 0) error("You must supply the usb device to which the wmbus dongle is connected.\n");
 | 
			
		||||
    if (c->device.length() == 0) error("You must supply the usb device to which the wmbus dongle is connected.\n");
 | 
			
		||||
 | 
			
		||||
    if ((argc-i) % 4 != 0) {
 | 
			
		||||
        error("For each meter you must supply a: name,type,id and key.\n");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,7 +94,19 @@ void handleLoglevel(Configuration *c, string loglevel)
 | 
			
		|||
 | 
			
		||||
void handleDevice(Configuration *c, string device)
 | 
			
		||||
{
 | 
			
		||||
    c->usb_device = device;
 | 
			
		||||
    // device can be:
 | 
			
		||||
    // /dev/ttyUSB00
 | 
			
		||||
    // auto
 | 
			
		||||
    // rtlwmbus:/usr/bin/rtl_sdr -f 868.9M -s 1600000 - 2>/dev/null | /usr/bin/rtl_wmbus
 | 
			
		||||
    // simulation....txt (read telegrams from file)
 | 
			
		||||
    size_t p = device.find (':');
 | 
			
		||||
    if (p != string::npos)
 | 
			
		||||
    {
 | 
			
		||||
        c->device_extra = device.substr(p+1);
 | 
			
		||||
        c->device = device.substr(0,p);
 | 
			
		||||
    } else {
 | 
			
		||||
        c->device = device;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handleLogtelegrams(Configuration *c, string logtelegrams)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,7 +65,9 @@ struct Configuration {
 | 
			
		|||
    bool list_shell_envs {};
 | 
			
		||||
    bool oneshot {};
 | 
			
		||||
    int  exitafter {}; // Seconds to exit.
 | 
			
		||||
    string usb_device;
 | 
			
		||||
    string device; // auto, /dev/ttyUSB0, simulation.txt, rtlwmbus
 | 
			
		||||
    string device_extra; // The command line that will start rtlwmbus
 | 
			
		||||
    string telegram_reader;
 | 
			
		||||
    LinkMode link_mode {};
 | 
			
		||||
    bool link_mode_set {};
 | 
			
		||||
    bool no_init {};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								src/main.cc
								
								
								
								
							
							
						
						
									
										17
									
								
								src/main.cc
								
								
								
								
							| 
						 | 
				
			
			@ -111,7 +111,10 @@ void startUsingCommandline(Configuration *config)
 | 
			
		|||
    if (config->meterfiles) {
 | 
			
		||||
        verbose("(config) store meter files in: \"%s\"\n", config->meterfiles_dir.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    verbose("(config) using usb device: %s\n", config->usb_device.c_str());
 | 
			
		||||
    verbose("(config) using device: %s\n", config->device.c_str());
 | 
			
		||||
    if (config->device_extra.length() > 0) {
 | 
			
		||||
        verbose("(config) with: %s\n", config->device_extra.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    verbose("(config) number of meters: %d\n", config->meters.size());
 | 
			
		||||
 | 
			
		||||
    auto manager = createSerialCommunicationManager(config->exitafter);
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +123,7 @@ void startUsingCommandline(Configuration *config)
 | 
			
		|||
 | 
			
		||||
    unique_ptr<WMBus> wmbus;
 | 
			
		||||
 | 
			
		||||
    auto type_and_device = detectMBusDevice(config->usb_device, manager.get());
 | 
			
		||||
    auto type_and_device = detectMBusDevice(config->device, manager.get());
 | 
			
		||||
 | 
			
		||||
    switch (type_and_device.first) {
 | 
			
		||||
    case DEVICE_IM871A:
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +138,16 @@ void startUsingCommandline(Configuration *config)
 | 
			
		|||
        verbose("(simulator) found %s\n", type_and_device.second.c_str());
 | 
			
		||||
        wmbus = openSimulator(type_and_device.second, manager.get());
 | 
			
		||||
        break;
 | 
			
		||||
    case DEVICE_RTLWMBUS:
 | 
			
		||||
    {
 | 
			
		||||
        verbose("(rtlwmbus) found %s\n", type_and_device.second.c_str());
 | 
			
		||||
        string command = config->device_extra;
 | 
			
		||||
        if (command == "") {
 | 
			
		||||
            command = "/usr/bin/rtl_sdr -f 868.9M -s 16000000 | /usr/bin/rtl_wmbus";
 | 
			
		||||
        }
 | 
			
		||||
        wmbus = openRTLWMBUS(command, manager.get());
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    case DEVICE_UNKNOWN:
 | 
			
		||||
        warning("No wmbus device found! Exiting!\n");
 | 
			
		||||
        if (config->daemon) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										158
									
								
								src/serial.cc
								
								
								
								
							
							
						
						
									
										158
									
								
								src/serial.cc
								
								
								
								
							| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
 | 
			
		||||
#include"util.h"
 | 
			
		||||
#include"serial.h"
 | 
			
		||||
#include"shell.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -34,20 +35,24 @@
 | 
			
		|||
 | 
			
		||||
static int openSerialTTY(const char *tty, int baud_rate);
 | 
			
		||||
 | 
			
		||||
struct SerialDeviceImp;
 | 
			
		||||
struct SerialDeviceTTY;
 | 
			
		||||
struct SerialDeviceCommand;
 | 
			
		||||
 | 
			
		||||
struct SerialCommunicationManagerImp : public SerialCommunicationManager {
 | 
			
		||||
    SerialCommunicationManagerImp(time_t exit_after_seconds);
 | 
			
		||||
    ~SerialCommunicationManagerImp() { }
 | 
			
		||||
 | 
			
		||||
    unique_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate);
 | 
			
		||||
    unique_ptr<SerialDevice> createSerialDeviceCommand(string command, vector<string> args, vector<string> envs);
 | 
			
		||||
 | 
			
		||||
    void listenTo(SerialDevice *sd, function<void()> cb);
 | 
			
		||||
    void stop();
 | 
			
		||||
    void waitForStop();
 | 
			
		||||
    bool isRunning();
 | 
			
		||||
 | 
			
		||||
    void opened(SerialDeviceTTY *sd);
 | 
			
		||||
    void closed(SerialDeviceTTY *sd);
 | 
			
		||||
    void opened(SerialDeviceImp *sd);
 | 
			
		||||
    void closed(SerialDeviceImp *sd);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void *eventLoop();
 | 
			
		||||
| 
						 | 
				
			
			@ -62,8 +67,13 @@ private:
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
struct SerialDeviceImp : public SerialDevice {
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
    int fd() { return fd_; }
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
 | 
			
		||||
    function<void()> on_data_;
 | 
			
		||||
    int fd_;
 | 
			
		||||
 | 
			
		||||
    friend struct SerialCommunicationManagerImp;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -76,14 +86,12 @@ struct SerialDeviceTTY : public SerialDeviceImp {
 | 
			
		|||
    void close();
 | 
			
		||||
    bool send(vector<uchar> &data);
 | 
			
		||||
    int receive(vector<uchar> *data);
 | 
			
		||||
    int fd() { return fd_; }
 | 
			
		||||
    SerialCommunicationManager *manager() { return manager_; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
    string device_;
 | 
			
		||||
    int baud_rate_ {};
 | 
			
		||||
    int fd_ {};
 | 
			
		||||
    pthread_mutex_t write_lock_ = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
    pthread_mutex_t read_lock_ = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
    SerialCommunicationManagerImp *manager_;
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +122,7 @@ bool SerialDeviceTTY::open(bool fail_if_not_ok)
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
    manager_->opened(this);
 | 
			
		||||
    verbose("(serial) opened %s\n", device_.c_str());
 | 
			
		||||
    verbose("(serialtty) opened %s\n", device_.c_str());
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +132,7 @@ void SerialDeviceTTY::close()
 | 
			
		|||
    ::close(fd_);
 | 
			
		||||
    fd_ = -1;
 | 
			
		||||
    manager_->closed(this);
 | 
			
		||||
    verbose("(serial) closed %s\n", device_.c_str());
 | 
			
		||||
    verbose("(serialtty) closed %s\n", device_.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SerialDeviceTTY::send(vector<uchar> &data)
 | 
			
		||||
| 
						 | 
				
			
			@ -189,6 +197,125 @@ int SerialDeviceTTY::receive(vector<uchar> *data)
 | 
			
		|||
    return num_read;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct SerialDeviceCommand : public SerialDeviceImp {
 | 
			
		||||
    SerialDeviceCommand(string command, vector<string> args, vector<string> envs, SerialCommunicationManagerImp *manager);
 | 
			
		||||
    ~SerialDeviceCommand();
 | 
			
		||||
 | 
			
		||||
    bool open(bool fail_if_not_ok);
 | 
			
		||||
    void close();
 | 
			
		||||
    bool send(vector<uchar> &data);
 | 
			
		||||
    int receive(vector<uchar> *data);
 | 
			
		||||
    int fd() { return fd_; }
 | 
			
		||||
    SerialCommunicationManager *manager() { return manager_; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
    string command_;
 | 
			
		||||
    int fd_ {};
 | 
			
		||||
    int pid_ {};
 | 
			
		||||
    vector<string> args_;
 | 
			
		||||
    vector<string> envs_;
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_t write_lock_ = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
    pthread_mutex_t read_lock_ = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
    SerialCommunicationManagerImp *manager_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
SerialDeviceCommand::SerialDeviceCommand(string command,
 | 
			
		||||
                                         vector<string> args,
 | 
			
		||||
                                         vector<string> envs,
 | 
			
		||||
                                         SerialCommunicationManagerImp *manager) {
 | 
			
		||||
    command_ = command;
 | 
			
		||||
    args_ = args;
 | 
			
		||||
    envs_ = envs;
 | 
			
		||||
    manager_ = manager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SerialDeviceCommand::~SerialDeviceCommand()
 | 
			
		||||
{
 | 
			
		||||
    close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SerialDeviceCommand::open(bool fail_if_not_ok)
 | 
			
		||||
{
 | 
			
		||||
    bool ok = invokeBackgroundShell("/bin/sh", args_, envs_, &fd_, 0, &pid_);
 | 
			
		||||
    if (!ok) return false;
 | 
			
		||||
    manager_->opened(this);
 | 
			
		||||
    verbose("(serialcmd) opened %s\n", command_.c_str());
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SerialDeviceCommand::close()
 | 
			
		||||
{
 | 
			
		||||
    ::flock(fd_, LOCK_UN);
 | 
			
		||||
    ::close(fd_);
 | 
			
		||||
    fd_ = -1;
 | 
			
		||||
    manager_->closed(this);
 | 
			
		||||
    verbose("(serialcmd) closed %s\n", command_.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SerialDeviceCommand::send(vector<uchar> &data)
 | 
			
		||||
{
 | 
			
		||||
    if (data.size() == 0) return true;
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&write_lock_);
 | 
			
		||||
 | 
			
		||||
    bool rc = true;
 | 
			
		||||
    int n = data.size();
 | 
			
		||||
    int written = 0;
 | 
			
		||||
    while (true) {
 | 
			
		||||
        int nw = write(fd_, &data[written], n-written);
 | 
			
		||||
        if (nw > 0) written += nw;
 | 
			
		||||
        if (nw < 0) {
 | 
			
		||||
            if (errno==EINTR) continue;
 | 
			
		||||
            rc = false;
 | 
			
		||||
            goto end;
 | 
			
		||||
        }
 | 
			
		||||
        if (written == n) break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isDebugEnabled()) {
 | 
			
		||||
        string msg = bin2hex(data);
 | 
			
		||||
        debug("(serial %s) sent \"%s\"\n", command_.c_str(), msg.c_str());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    end:
 | 
			
		||||
    pthread_mutex_unlock(&write_lock_);
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int SerialDeviceCommand::receive(vector<uchar> *data)
 | 
			
		||||
{
 | 
			
		||||
    pthread_mutex_lock(&read_lock_);
 | 
			
		||||
 | 
			
		||||
    data->clear();
 | 
			
		||||
    int available = 0;
 | 
			
		||||
    int num_read = 0;
 | 
			
		||||
 | 
			
		||||
    ioctl(fd_, FIONREAD, &available);
 | 
			
		||||
    if (!available) goto end;
 | 
			
		||||
 | 
			
		||||
    data->resize(available);
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
        int nr = read(fd_, &((*data)[num_read]), available-num_read);
 | 
			
		||||
        if (nr > 0) num_read += nr;
 | 
			
		||||
        if (nr < 0) {
 | 
			
		||||
            if (errno==EINTR) continue;
 | 
			
		||||
            goto end;
 | 
			
		||||
        }
 | 
			
		||||
        if (num_read == available) break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isDebugEnabled()) {
 | 
			
		||||
        string msg = bin2hex(*data);
 | 
			
		||||
        debug("(serialcmd) received \"%s\"\n", msg.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    end:
 | 
			
		||||
    pthread_mutex_unlock(&read_lock_);
 | 
			
		||||
    return num_read;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SerialCommunicationManagerImp::SerialCommunicationManagerImp(time_t exit_after_seconds)
 | 
			
		||||
{
 | 
			
		||||
    running_ = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -208,6 +335,13 @@ unique_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceTTY(st
 | 
			
		|||
    return unique_ptr<SerialDevice>(new SerialDeviceTTY(device, baud_rate, this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unique_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceCommand(string command,
 | 
			
		||||
                                                                                  vector<string> args,
 | 
			
		||||
                                                                                  vector<string> envs)
 | 
			
		||||
{
 | 
			
		||||
    return unique_ptr<SerialDevice>(new SerialDeviceCommand(command, args, envs, this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SerialCommunicationManagerImp::listenTo(SerialDevice *sd, function<void()> cb) {
 | 
			
		||||
    SerialDeviceImp *si = dynamic_cast<SerialDeviceImp*>(sd);
 | 
			
		||||
    if (!si) {
 | 
			
		||||
| 
						 | 
				
			
			@ -236,13 +370,13 @@ bool SerialCommunicationManagerImp::isRunning()
 | 
			
		|||
    return running_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SerialCommunicationManagerImp::opened(SerialDeviceTTY *sd) {
 | 
			
		||||
void SerialCommunicationManagerImp::opened(SerialDeviceImp *sd) {
 | 
			
		||||
    max_fd_ = max(sd->fd(), max_fd_);
 | 
			
		||||
    devices_.push_back(sd);
 | 
			
		||||
    pthread_kill(thread_, SIGUSR1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SerialCommunicationManagerImp::closed(SerialDeviceTTY *sd) {
 | 
			
		||||
void SerialCommunicationManagerImp::closed(SerialDeviceImp *sd) {
 | 
			
		||||
    auto p = find(devices_.begin(), devices_.end(), sd);
 | 
			
		||||
    if (p != devices_.end()) {
 | 
			
		||||
        devices_.erase(p);
 | 
			
		||||
| 
						 | 
				
			
			@ -270,7 +404,7 @@ void *SerialCommunicationManagerImp::eventLoop() {
 | 
			
		|||
            time_t curr = time(NULL);
 | 
			
		||||
            time_t diff = curr-start_time_;
 | 
			
		||||
            if (diff > exit_after_seconds_) {
 | 
			
		||||
                verbose("(serial) exit after %ld seconds\n", diff);
 | 
			
		||||
                verbose("(serialtty) exit after %ld seconds\n", diff);
 | 
			
		||||
                stop();
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -280,7 +414,7 @@ void *SerialCommunicationManagerImp::eventLoop() {
 | 
			
		|||
 | 
			
		||||
        if (!running_) break;
 | 
			
		||||
        if (activity < 0 && errno!=EINTR) {
 | 
			
		||||
            warning("(serial) internal error after select! errno=%s\n", strerror(errno));
 | 
			
		||||
            warning("(serialtty) internal error after select! errno=%s\n", strerror(errno));
 | 
			
		||||
        }
 | 
			
		||||
        if (activity > 0) {
 | 
			
		||||
            for (SerialDevice *d : devices_) {
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +425,7 @@ void *SerialCommunicationManagerImp::eventLoop() {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    verbose("(serial) event loop stopped!\n");
 | 
			
		||||
    verbose("(serialtty) event loop stopped!\n");
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,7 @@ struct SerialDevice {
 | 
			
		|||
 | 
			
		||||
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 void listenTo(SerialDevice *sd, function<void()> cb) = 0;
 | 
			
		||||
    virtual void stop() = 0;
 | 
			
		||||
    virtual void waitForStop() = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										79
									
								
								src/shell.cc
								
								
								
								
							
							
						
						
									
										79
									
								
								src/shell.cc
								
								
								
								
							| 
						 | 
				
			
			@ -18,6 +18,7 @@
 | 
			
		|||
#include "shell.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <memory.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -86,3 +87,81 @@ void invokeShell(string program, vector<string> args, vector<string> envs)
 | 
			
		|||
    }
 | 
			
		||||
    delete[] p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool invokeBackgroundShell(string program, vector<string> args, vector<string> envs, int *out, int *err, int *pid)
 | 
			
		||||
{
 | 
			
		||||
    int link[2];
 | 
			
		||||
    vector<const char*> argv(args.size()+2);
 | 
			
		||||
    char *p = new char[program.length()+1];
 | 
			
		||||
    strcpy(p, program.c_str());
 | 
			
		||||
    argv[0] = p;
 | 
			
		||||
    int i = 1;
 | 
			
		||||
    debug("exec background \"%s\"\n", program.c_str());
 | 
			
		||||
    for (auto &a : args) {
 | 
			
		||||
        argv[i] = a.c_str();
 | 
			
		||||
        i++;
 | 
			
		||||
        debug("arg \"%s\"\n", a.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    argv[i] = NULL;
 | 
			
		||||
 | 
			
		||||
    vector<const char*> env(envs.size()+1);
 | 
			
		||||
    env[0] = p;
 | 
			
		||||
    i = 0;
 | 
			
		||||
    for (auto &e : envs) {
 | 
			
		||||
        env[i] = e.c_str();
 | 
			
		||||
        i++;
 | 
			
		||||
        debug("env \"%s\"\n", e.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    env[i] = NULL;
 | 
			
		||||
 | 
			
		||||
    if (pipe(link) == -1) {
 | 
			
		||||
        error("Could not create pipe!\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *pid = fork();
 | 
			
		||||
    if (*pid == 0) {
 | 
			
		||||
        // I am the child!
 | 
			
		||||
        // Redirect stdout and stderr to pipe
 | 
			
		||||
        dup2 (link[1], STDOUT_FILENO);
 | 
			
		||||
        dup2 (link[1], STDERR_FILENO);
 | 
			
		||||
        // Close return pipe, not duped.
 | 
			
		||||
        close(link[0]);
 | 
			
		||||
        // Close old forward fd pipe.
 | 
			
		||||
        close(link[1]);
 | 
			
		||||
        close(0); // Close stdin
 | 
			
		||||
 | 
			
		||||
        delete[] p;
 | 
			
		||||
#if defined(__APPLE__) && defined(__MACH__)
 | 
			
		||||
        execve(program.c_str(), (char*const*)&argv[0], (char*const*)&env[0]);
 | 
			
		||||
#else
 | 
			
		||||
        execvpe(program.c_str(), (char*const*)&argv[0], (char*const*)&env[0]);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        perror("Execvp failed:");
 | 
			
		||||
        warning("Invoking shell %s failed!\n", program.c_str());
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *out = link[0];
 | 
			
		||||
    delete[] p;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void stopBackgroundShell(int pid)
 | 
			
		||||
{
 | 
			
		||||
    assert(pid > 0);
 | 
			
		||||
 | 
			
		||||
    kill(pid, SIGINT);
 | 
			
		||||
    int status;
 | 
			
		||||
    debug("waiting for child %d.\n", pid);
 | 
			
		||||
    // Wait for the child to finish!
 | 
			
		||||
    waitpid(pid, &status, 0);
 | 
			
		||||
    if (WIFEXITED(status)) {
 | 
			
		||||
        // Child exited properly.
 | 
			
		||||
        int rc = WEXITSTATUS(status);
 | 
			
		||||
        debug("bgshell: return code %d\n", rc);
 | 
			
		||||
        if (rc != 0) {
 | 
			
		||||
            warning("bgshell: exited with non-zero return code: %d\n", rc);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,3 +21,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 *err, int *pid);
 | 
			
		||||
void stopBackgroundShell(int pid);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								src/util.cc
								
								
								
								
							
							
						
						
									
										14
									
								
								src/util.cc
								
								
								
								
							| 
						 | 
				
			
			@ -100,6 +100,20 @@ bool hex2bin(string &src, vector<uchar> *target)
 | 
			
		|||
    return hex2bin(src.c_str(), target);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool hex2bin(vector<uchar> &src, vector<uchar> *target)
 | 
			
		||||
{
 | 
			
		||||
    if (src.size() % 2 == 1) return false;
 | 
			
		||||
    for (size_t i=0; i<src.size(); i+=2) {
 | 
			
		||||
        if (src[i] != ' ') {
 | 
			
		||||
            int hi = char2int(src[i]);
 | 
			
		||||
            int lo = char2int(src[i+1]);
 | 
			
		||||
            if (hi<0 || lo<0) return false;
 | 
			
		||||
            target->push_back(hi*16 + lo);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char const hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A','B','C','D','E','F'};
 | 
			
		||||
 | 
			
		||||
std::string bin2hex(vector<uchar> &target) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ typedef unsigned char uchar;
 | 
			
		|||
 | 
			
		||||
bool hex2bin(const char* src, std::vector<uchar> *target);
 | 
			
		||||
bool hex2bin(std::string &src, std::vector<uchar> *target);
 | 
			
		||||
bool hex2bin(std::vector<uchar> &src, std::vector<uchar> *target);
 | 
			
		||||
std::string bin2hex(std::vector<uchar> &target);
 | 
			
		||||
std::string bin2hex(std::vector<uchar>::iterator data, int len);
 | 
			
		||||
void strprintf(std::string &s, const char* fmt, ...);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -183,6 +183,10 @@ pair<MBusDeviceType,string> detectMBusDevice(string device, SerialCommunicationM
 | 
			
		|||
    // SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SYMLINK+="im871a",MODE="0660", GROUP="yourowngroup"
 | 
			
		||||
    // SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="amb8465",MODE="0660", GROUP="yourowngroup"
 | 
			
		||||
 | 
			
		||||
    if (device == "rtlwmbus")
 | 
			
		||||
    {
 | 
			
		||||
        return { DEVICE_RTLWMBUS, "" };
 | 
			
		||||
    }
 | 
			
		||||
    if (device == "auto")
 | 
			
		||||
    {
 | 
			
		||||
        if (detectIM871A("/dev/im871a", handler))
 | 
			
		||||
| 
						 | 
				
			
			@ -1712,6 +1716,7 @@ string formatData(int dif, int vif, int vife, string data)
 | 
			
		|||
string linkModeName(LinkMode link_mode)
 | 
			
		||||
{
 | 
			
		||||
    switch (link_mode) {
 | 
			
		||||
    case LinkModeAny: return "ANY";
 | 
			
		||||
    case LinkModeC1: return "C1";
 | 
			
		||||
    case LinkModeT1: return "T1";
 | 
			
		||||
    case UNKNOWN_LINKMODE: break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@
 | 
			
		|||
 | 
			
		||||
#include<inttypes.h>
 | 
			
		||||
 | 
			
		||||
#define LIST_OF_LINK_MODES X(LinkModeC1)X(LinkModeT1)X(UNKNOWN_LINKMODE)
 | 
			
		||||
#define LIST_OF_LINK_MODES X(LinkModeAny)X(LinkModeC1)X(LinkModeT1)X(UNKNOWN_LINKMODE)
 | 
			
		||||
 | 
			
		||||
// In link mode T1, the meter transmits a telegram every few seconds or minutes.
 | 
			
		||||
// Suitable for drive-by/walk-by collection of meter values.
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +106,7 @@ struct WMBus {
 | 
			
		|||
    virtual ~WMBus() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define LIST_OF_MBUS_DEVICES X(DEVICE_IM871A)X(DEVICE_AMB8465)X(DEVICE_SIMULATOR)X(DEVICE_UNKNOWN)
 | 
			
		||||
#define LIST_OF_MBUS_DEVICES X(DEVICE_IM871A)X(DEVICE_AMB8465)X(DEVICE_SIMULATOR)X(DEVICE_RTLWMBUS)X(DEVICE_UNKNOWN)
 | 
			
		||||
 | 
			
		||||
enum MBusDeviceType {
 | 
			
		||||
#define X(name) name,
 | 
			
		||||
| 
						 | 
				
			
			@ -121,6 +121,7 @@ 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> openSimulator(string file, SerialCommunicationManager *manager);
 | 
			
		||||
 | 
			
		||||
string manufacturer(int m_field);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,191 @@
 | 
			
		|||
/*
 | 
			
		||||
 Copyright (C) 2019 Fredrik Öhrström
 | 
			
		||||
 | 
			
		||||
 This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 it under the terms of the GNU General Public License as published by
 | 
			
		||||
 the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
 This program is distributed in the hope that it will be useful,
 | 
			
		||||
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
 You should have received a copy of the GNU General Public License
 | 
			
		||||
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include"wmbus.h"
 | 
			
		||||
#include"serial.h"
 | 
			
		||||
 | 
			
		||||
#include<assert.h>
 | 
			
		||||
#include<fcntl.h>
 | 
			
		||||
#include<pthread.h>
 | 
			
		||||
#include<semaphore.h>
 | 
			
		||||
#include<string.h>
 | 
			
		||||
#include<sys/errno.h>
 | 
			
		||||
#include<sys/types.h>
 | 
			
		||||
#include<unistd.h>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
enum FrameStatus { PartialFrame, FullFrame, ErrorInFrame };
 | 
			
		||||
 | 
			
		||||
struct WMBusRTLWMBUS : public WMBus {
 | 
			
		||||
    bool ping();
 | 
			
		||||
    uint32_t getDeviceId();
 | 
			
		||||
    LinkMode getLinkMode();
 | 
			
		||||
    void setLinkMode(LinkMode lm);
 | 
			
		||||
    void onTelegram(function<void(Telegram*)> cb);
 | 
			
		||||
 | 
			
		||||
    void processSerialData();
 | 
			
		||||
    SerialDevice *serial() { return NULL; }
 | 
			
		||||
    void simulate();
 | 
			
		||||
 | 
			
		||||
    WMBusRTLWMBUS(unique_ptr<SerialDevice> serial, SerialCommunicationManager *manager);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    unique_ptr<SerialDevice> serial_;
 | 
			
		||||
    vector<uchar> read_buffer_;
 | 
			
		||||
    vector<uchar> received_payload_;
 | 
			
		||||
    vector<function<void(Telegram*)>> telegram_listeners_;
 | 
			
		||||
 | 
			
		||||
    FrameStatus checkRTLWMBUSFrame(vector<uchar> &data,
 | 
			
		||||
                                   size_t *hex_frame_length,
 | 
			
		||||
                                   int *hex_payload_len_out,
 | 
			
		||||
                                   int *hex_payload_offset);
 | 
			
		||||
    void handleMessage(vector<uchar> &frame);
 | 
			
		||||
 | 
			
		||||
    string setup_;
 | 
			
		||||
    SerialCommunicationManager *manager_ {};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
unique_ptr<WMBus> openRTLWMBUS(string command, SerialCommunicationManager *manager)
 | 
			
		||||
{
 | 
			
		||||
    vector<string> args;
 | 
			
		||||
    vector<string> envs;
 | 
			
		||||
    args.push_back("-c");
 | 
			
		||||
    args.push_back(command);
 | 
			
		||||
    auto serial = manager->createSerialDeviceCommand("/bin/sh", args, envs);
 | 
			
		||||
    WMBusRTLWMBUS *imp = new WMBusRTLWMBUS(std::move(serial), manager);
 | 
			
		||||
    return unique_ptr<WMBus>(imp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WMBusRTLWMBUS::WMBusRTLWMBUS(unique_ptr<SerialDevice> serial, SerialCommunicationManager *manager) :
 | 
			
		||||
    serial_(std::move(serial)), manager_(manager)
 | 
			
		||||
{
 | 
			
		||||
    manager_->listenTo(serial_.get(),call(this,processSerialData));
 | 
			
		||||
    serial_->open(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool WMBusRTLWMBUS::ping() {
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t WMBusRTLWMBUS::getDeviceId() {
 | 
			
		||||
    return 0x11111111;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LinkMode WMBusRTLWMBUS::getLinkMode() {
 | 
			
		||||
 | 
			
		||||
    return LinkModeAny;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WMBusRTLWMBUS::setLinkMode(LinkMode lm)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WMBusRTLWMBUS::onTelegram(function<void(Telegram*)> cb) {
 | 
			
		||||
    telegram_listeners_.push_back(cb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WMBusRTLWMBUS::simulate()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WMBusRTLWMBUS::processSerialData()
 | 
			
		||||
{
 | 
			
		||||
    vector<uchar> data;
 | 
			
		||||
 | 
			
		||||
    // Receive and accumulated serial data until a full frame has been received.
 | 
			
		||||
    serial_->receive(&data);
 | 
			
		||||
 | 
			
		||||
    read_buffer_.insert(read_buffer_.end(), data.begin(), data.end());
 | 
			
		||||
 | 
			
		||||
    size_t frame_length;
 | 
			
		||||
    int hex_payload_len, hex_payload_offset;
 | 
			
		||||
 | 
			
		||||
    FrameStatus status = checkRTLWMBUSFrame(read_buffer_, &frame_length, &hex_payload_len, &hex_payload_offset);
 | 
			
		||||
 | 
			
		||||
    if (status == ErrorInFrame) {
 | 
			
		||||
        verbose("(rtl_wmbus) protocol error in message received!\n");
 | 
			
		||||
        string msg = bin2hex(read_buffer_);
 | 
			
		||||
        debug("(rtl_wmbus) protocol error \"%s\"\n", msg.c_str());
 | 
			
		||||
        read_buffer_.clear();
 | 
			
		||||
    } else
 | 
			
		||||
    if (status == FullFrame) {
 | 
			
		||||
        vector<uchar> payload;
 | 
			
		||||
        if (hex_payload_len > 0) {
 | 
			
		||||
            vector<uchar> hex;
 | 
			
		||||
            hex.insert(hex.end(), read_buffer_.begin()+hex_payload_offset, read_buffer_.begin()+hex_payload_offset+hex_payload_len);
 | 
			
		||||
            hex2bin(hex, &payload);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        read_buffer_.erase(read_buffer_.begin(), read_buffer_.begin()+frame_length);
 | 
			
		||||
        handleMessage(payload);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WMBusRTLWMBUS::handleMessage(vector<uchar> &frame)
 | 
			
		||||
{
 | 
			
		||||
    Telegram t;
 | 
			
		||||
    t.parse(frame);
 | 
			
		||||
    for (auto f : telegram_listeners_)
 | 
			
		||||
    {
 | 
			
		||||
        if (f) f(&t);
 | 
			
		||||
        if (isVerboseEnabled() && !t.handled) {
 | 
			
		||||
            verbose("(rtl_wmbus) telegram ignored by all configured meters!\n");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FrameStatus WMBusRTLWMBUS::checkRTLWMBUSFrame(vector<uchar> &data,
 | 
			
		||||
                                              size_t *hex_frame_length,
 | 
			
		||||
                                              int *hex_payload_len_out,
 | 
			
		||||
                                              int *hex_payload_offset)
 | 
			
		||||
{
 | 
			
		||||
    //C1;1;1;2019-02-09 07:14:18.000;117;102;94740459;0x49449344590474943508780dff5f3500827f0000f10007b06effff530100005f2c620100007f2118010000008000800080008000000000000000000e003f005500d4ff2f046d10086922
 | 
			
		||||
    if (data.size() == 0) return PartialFrame;
 | 
			
		||||
    int payload_len = 0;
 | 
			
		||||
    size_t eolp = 0;
 | 
			
		||||
    // Look for end of line.
 | 
			
		||||
    for (; eolp < data.size(); ++eolp) {
 | 
			
		||||
        if (data[eolp] == '\n') break;
 | 
			
		||||
    }
 | 
			
		||||
    if (eolp >= data.size()) return PartialFrame;
 | 
			
		||||
 | 
			
		||||
    // We got a full line, but if it is too short, then
 | 
			
		||||
    // there is something wrong. Discard the data.
 | 
			
		||||
    if (data.size() < 72) return ErrorInFrame;
 | 
			
		||||
 | 
			
		||||
    // Discard lines that are not T1 or C1 telegrams
 | 
			
		||||
    if (data[0] != 'T' && data[0] != 'C') return ErrorInFrame;
 | 
			
		||||
 | 
			
		||||
    // And the checksums should match.
 | 
			
		||||
    if (strncmp((const char*)&data[1], "1;1;1", 5)) return ErrorInFrame;
 | 
			
		||||
 | 
			
		||||
    // Look for start of telegram 0x
 | 
			
		||||
    size_t i = 0;
 | 
			
		||||
    for (; i+1 < data.size(); ++i) {
 | 
			
		||||
        if (data[i] == '0' && data[i+1] == 'x') break;
 | 
			
		||||
    }
 | 
			
		||||
    if (i+1 >= data.size()) return ErrorInFrame; // No 0x found, then discard the frame.
 | 
			
		||||
    i+=2; // Skip 0x
 | 
			
		||||
 | 
			
		||||
    payload_len = eolp-i;
 | 
			
		||||
    *hex_payload_len_out = payload_len;
 | 
			
		||||
    *hex_payload_offset = i;
 | 
			
		||||
    *hex_frame_length = eolp+1;
 | 
			
		||||
 | 
			
		||||
    return FullFrame;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -192,8 +192,3 @@ void WMBusSimulator::simulate()
 | 
			
		|||
    }
 | 
			
		||||
    manager_->stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool detectSimulator(string device, SerialCommunicationManager *manager)
 | 
			
		||||
{
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue