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