Initial work on config files and daemon mode.

pull/22/head
weetmuts 2019-02-23 13:41:17 +01:00
rodzic eceef17499
commit 05e33c32c3
18 zmienionych plików z 521 dodań i 109 usunięć

Wyświetl plik

@ -1,4 +1,12 @@
Version 0.8.4: 2019-02-23
Add config files support and daemon mode.
Version 0.8.3: 2019-02-17
Add experimental support for qcaloric.
Version 0.8.2: 2019-01-27
Properly supports short C1 frames after it has received a long frame.

Wyświetl plik

@ -44,7 +44,7 @@ endif
$(shell mkdir -p $(BUILD))
CXXFLAGS := $(DEBUG_FLAGS) -fPIC -fmessage-length=0 -std=c++11 -Wall -Wno-maybe-uninitialized -Wno-unused-function "-DWMBUSMETERS_VERSION=\"0.8.1\""
CXXFLAGS := $(DEBUG_FLAGS) -fPIC -fmessage-length=0 -std=c++11 -Wall -Wno-maybe-uninitialized -Wno-unused-function "-DWMBUSMETERS_VERSION=\"0.8.4\""
$(BUILD)/%.o: %.cc $(wildcard %.h)
$(CXX) $(CXXFLAGS) $< -c -o $@
@ -52,6 +52,7 @@ $(BUILD)/%.o: %.cc $(wildcard %.h)
METERS_OBJS:=\
$(BUILD)/aes.o \
$(BUILD)/cmdline.o \
$(BUILD)/config.o \
$(BUILD)/dvparser.o \
$(BUILD)/meters.o \
$(BUILD)/meter_multical21.o \
@ -72,6 +73,8 @@ METERS_OBJS:=\
all: $(BUILD)/wmbusmeters $(BUILD)/testinternals
@$(STRIP_BINARY)
@rm -f $(BUILD)/wmbusmetersd
@cp $(BUILD)/wmbusmeters $(BUILD)/wmbusmetersd
dist: wmbusmeters_0.8.1_$(DEBARCH).deb

Wyświetl plik

@ -19,6 +19,8 @@
#include"meters.h"
#include"util.h"
#include<string>
using namespace std;
unique_ptr<CommandLine> parseCommandLine(int argc, char **argv) {
@ -26,6 +28,14 @@ unique_ptr<CommandLine> parseCommandLine(int argc, char **argv) {
CommandLine * c = new CommandLine;
int i=1;
const char *filename = strrchr(argv[0], '/')+1;
if (!strcmp(filename, "wmbusmetersd")) {
c->daemon = true;
if (argc > 1) {
error("Usage error: wmbusmetersd does not accept any arguments.\n");
}
return unique_ptr<CommandLine>(c);
}
if (argc < 2) {
c->need_help = true;
return unique_ptr<CommandLine>(c);
@ -73,6 +83,13 @@ unique_ptr<CommandLine> parseCommandLine(int argc, char **argv) {
i++;
continue;
}
if (!strcmp(argv[i], "--useconfig")) {
c->useconfig = true;
if (i > 1 || argc > 2) {
error("Usage error: --useconfig implies no other arguments on the command line.\n");
}
return unique_ptr<CommandLine>(c);
}
if (!strncmp(argv[i], "--robot", 7)) {
if (strlen(argv[i]) == 7 ||
(strlen(argv[i]) == 12 &&
@ -109,7 +126,7 @@ unique_ptr<CommandLine> parseCommandLine(int argc, char **argv) {
if (strlen(argv[i]) > 12 && argv[i][12] == '=') {
size_t len = strlen(argv[i])-13;
if (len > 0) {
c->meterfiles_dir = argv[i]+13;
c->meterfiles_dir = string(argv[i]+13, len);
} else {
c->meterfiles_dir = "/tmp";
}
@ -117,7 +134,7 @@ unique_ptr<CommandLine> parseCommandLine(int argc, char **argv) {
c->meterfiles_dir = "/tmp";
}
if (!checkIfDirExists(c->meterfiles_dir.c_str())) {
error("Directory does not exist \"%s\"\n", c->meterfiles_dir.c_str());
error("Cannot write meter files into dir \"%s\"\n", c->meterfiles_dir);
}
i++;
continue;
@ -158,7 +175,7 @@ unique_ptr<CommandLine> parseCommandLine(int argc, char **argv) {
c->usb_device = argv[i];
i++;
if (!c->usb_device) error("You must supply the usb device to which the wmbus dongle is connected.\n");
if (c->usb_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");
@ -166,16 +183,16 @@ unique_ptr<CommandLine> parseCommandLine(int argc, char **argv) {
int num_meters = (argc-i)/4;
for (int m=0; m<num_meters; ++m) {
char *name = argv[m*4+i+0];
char *type = argv[m*4+i+1];
char *id = argv[m*4+i+2];
char *key = argv[m*4+i+3];
string name = argv[m*4+i+0];
string type = argv[m*4+i+1];
string id = argv[m*4+i+2];
string key = argv[m*4+i+3];
MeterType mt = toMeterType(type);
if (mt == UNKNOWN_METER) error("Not a valid meter type \"%s\"\n", type);
if (!isValidId(id)) error("Not a valid meter id \"%s\"\n", id);
if (!isValidKey(key)) error("Not a valid meter key \"%s\"\n", key);
if (mt == UNKNOWN_METER) error("Not a valid meter type \"%s\"\n", type.c_str());
if (!isValidId(id)) error("Not a valid meter id \"%s\"\n", id.c_str());
if (!isValidKey(key)) error("Not a valid meter key \"%s\"\n", key.c_str());
c->meters.push_back(MeterInfo(name,type,id,key));
}

Wyświetl plik

@ -18,6 +18,7 @@
#ifndef CMDLINE_H
#define CMDLINE_H
#include"config.h"
#include"meters.h"
#include<memory>
#include<string.h>
@ -25,44 +26,6 @@
using namespace std;
struct MeterInfo {
char *name;
char *type;
char *id;
char *key;
MeterInfo(char *n, char *t, char *i, char *k) {
name = n;
type = t;
id = i;
key = k;
}
};
struct CommandLine {
bool need_help {};
bool silence {};
bool verbose {};
bool debug {};
bool logtelegrams {};
bool meterfiles {};
string meterfiles_dir;
bool json {};
bool fields {};
char separator { ';' };
vector<string> shells;
bool list_shell_envs {};
bool oneshot {};
int exitafter {}; // Seconds to exit.
char *usb_device {};
LinkMode link_mode {};
bool link_mode_set {};
bool no_init {};
vector<MeterInfo> meters;
~CommandLine() = default;
};
unique_ptr<CommandLine> parseCommandLine(int argc, char **argv);
#endif

122
config.cc 100644
Wyświetl plik

@ -0,0 +1,122 @@
/*
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"config.h"
#include"meters.h"
#include<vector>
#include<string>
using namespace std;
pair<string,string> getNextKeyValue(vector<char> &buf, vector<char>::iterator &i)
{
bool eof, err;
string key, value;
key = eatToSkipWhitespace(buf, i, '=', 64, &eof, &err);
if (eof || err) goto nomore;
value = eatToSkipWhitespace(buf, i, '\n', 4096, &eof, &err);
if (err) goto nomore;
return { key, value };
nomore:
return { "", "" };
}
void parseMeterConfig(CommandLine *c, vector<char> &buf, string file)
{
auto i = buf.begin();
string name;
string type;
string id;
string key;
for (;;) {
auto p = getNextKeyValue(buf, i);
if (p.first == "") break;
if (p.first == "name") name = p.second;
if (p.first == "type") type = p.second;
if (p.first == "id") id = p.second;
if (p.first == "key") key = p.second;
}
MeterType mt = toMeterType(type);
if (mt == UNKNOWN_METER) error("Not a valid meter type \"%s\"\n", type.c_str());
if (!isValidId(id)) error("Not a valid meter id \"%s\"\n", id.c_str());
if (!isValidKey(key)) error("Not a valid meter key \"%s\"\n", key.c_str());
c->meters.push_back(MeterInfo(name, type, id, key));
return;
}
unique_ptr<CommandLine> loadConfiguration()
{
CommandLine *c = new CommandLine;
vector<char> global_conf;
loadFile("/etc/wmbusmeters.conf", &global_conf);
auto i = global_conf.begin();
string loglevel;
string device;
for (;;) {
auto p = getNextKeyValue(global_conf, i);
if (p.first == "") break;
if (p.first == "loglevel") loglevel = p.second;
if (p.first == "device") device = p.second;
}
if (loglevel == "verbose") {
c->verbose = true;
} else
if (loglevel == "debug") {
c->debug = true;
} else
if (loglevel == "silent") {
c->silence = true;
} else
if (loglevel == "normal") {
} else
{
warning("No such log level: \"%s\"\n", loglevel.c_str());
}
// cmdline->logtelegrams
c->usb_device = device;
vector<string> meter_files;
listFiles("/etc/wmbusmeters.d", &meter_files);
for (auto& f : meter_files)
{
vector<char> meter_conf;
string file = string("/etc/wmbusmeters.d/")+f;
loadFile(file.c_str(), &meter_conf);
parseMeterConfig(c, meter_conf, file);
}
return unique_ptr<CommandLine>(c);
}

69
config.h 100644
Wyświetl plik

@ -0,0 +1,69 @@
/*
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/>.
*/
#ifndef CONFIG_H
#define CONFIG_H
#include"util.h"
#include"wmbus.h"
#include<vector>
using namespace std;
struct MeterInfo {
string name;
string type;
string id;
string key;
MeterInfo(string& n, string& t, string& i, string& k) {
name = n;
type = t;
id = i;
key = k;
}
};
struct CommandLine {
bool daemon {};
bool useconfig {};
bool need_help {};
bool silence {};
bool verbose {};
bool debug {};
bool logtelegrams {};
bool meterfiles {};
std::string meterfiles_dir;
bool json {};
bool fields {};
char separator { ';' };
std::vector<std::string> shells;
bool list_shell_envs {};
bool oneshot {};
int exitafter {}; // Seconds to exit.
string usb_device;
LinkMode link_mode {};
bool link_mode_set {};
bool no_init {};
vector<MeterInfo> meters;
~CommandLine() = default;
};
unique_ptr<CommandLine> loadConfiguration();
#endif

88
main.cc
Wyświetl plik

@ -16,6 +16,7 @@
*/
#include"cmdline.h"
#include"config.h"
#include"meters.h"
#include"printer.h"
#include"serial.h"
@ -24,9 +25,22 @@
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
using namespace std;
void oneshotCheck(CommandLine *cmdline, SerialCommunicationManager *manager, Meter *meter, vector<unique_ptr<Meter>> &meters);
void startUsingCommandline(CommandLine *cmdline);
void startUsingConfigFiles();
void startDaemon(); // Will use config files.
int main(int argc, char **argv)
{
@ -47,7 +61,9 @@ int main(int argc, char **argv)
printf(" --shellenvs list the env variables available for the meter.\n");
printf(" --oneshot wait for an update from each meter, then quit.\n\n");
printf(" --exitafter=20h program exits after running for twenty hoursh\n"
" or 10m for ten minutes or 5s for five seconds.\n\n");
" or 10m for ten minutes or 5s for five seconds.\n");
printf(" --useconfig read from /etc/wmbusmeters.conf and /etc/wmbusmeters.d\n");
printf(" check the man page for how to write the config files.\n\n");
printf("Specifying auto as the device will automatically look for usb\n");
printf("wmbus dongles on /dev/im871a and /dev/amb8465\n\n");
printf("The meter types: multical21,flowiq3100,supercom587,iperl (water meters) are supported.\n"
@ -56,9 +72,25 @@ int main(int argc, char **argv)
exit(0);
}
if (cmdline->daemon) {
startDaemon();
exit(0);
}
if (cmdline->useconfig) {
startUsingConfigFiles();
exit(0);
}
// We want the data visible in the log file asap!
setbuf(stdout, NULL);
startUsingCommandline(cmdline.get());
exit(0);
}
void startUsingCommandline(CommandLine *cmdline)
{
warningSilenced(cmdline->silence);
verboseEnabled(cmdline->verbose);
logTelegramsEnabled(cmdline->logtelegrams);
@ -71,7 +103,7 @@ int main(int argc, char **argv)
if (cmdline->meterfiles) {
verbose("(cmdline) store meter files in: \"%s\"\n", cmdline->meterfiles_dir.c_str());
}
verbose("(cmdline) using usb device: %s\n", cmdline->usb_device);
verbose("(cmdline) using usb device: %s\n", cmdline->usb_device.c_str());
verbose("(cmdline) number of meters: %d\n", cmdline->meters.size());
auto manager = createSerialCommunicationManager(cmdline->exitafter);
@ -172,7 +204,7 @@ int main(int argc, char **argv)
&ignore2, cmdline->separator,
&ignore3,
&envs);
printf("Environment variables provided to shell for meter %s:\n", m.type);
printf("Environment variables provided to shell for meter %s:\n", m.type.c_str());
for (auto &e : envs) {
int p = e.find('=');
string key = e.substr(0,p);
@ -181,7 +213,7 @@ int main(int argc, char **argv)
exit(0);
}
meters.back()->onUpdate(calll(output.get(),print,Meter*));
meters.back()->onUpdate([&](Meter*meter) { oneshotCheck(cmdline.get(), manager.get(), meter, meters); });
meters.back()->onUpdate([&](Meter*meter) { oneshotCheck(cmdline, manager.get(), meter, meters); });
}
} else {
printf("No meters configured. Printing id:s of all telegrams heard!\n\n");
@ -206,3 +238,51 @@ void oneshotCheck(CommandLine *cmdline, SerialCommunicationManager *manager, Met
// All meters have received at least one update! Stop!
manager->stop();
}
void startDaemon()
{
pid_t pid = fork();
if (pid < 0)
{
error("Could not fork.\n");
}
if (pid > 0)
{
// Parent returns to exit nicely.
return;
}
// Change the file mode mask
umask(0);
setlogmask(LOG_UPTO (LOG_NOTICE));
openlog("wmbusmetersd", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
syslog(LOG_NOTICE, "wmbusmeters started by User %d", getuid ());
enableSyslog();
// Create a new SID for the daemon
pid_t sid = setsid();
if (sid < 0) {
// log
exit(-1);
}
if ((chdir("/")) < 0) {
error("Could not change to root as current working directory.");
}
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
startUsingConfigFiles();
}
void startUsingConfigFiles()
{
unique_ptr<CommandLine> cmdline = loadConfiguration();
startUsingCommandline(cmdline.get());
}

Wyświetl plik

@ -34,7 +34,7 @@
using namespace std;
struct MeterIperl : public virtual WaterMeter, public virtual MeterCommonImplementation {
MeterIperl(WMBus *bus, const char *name, const char *id, const char *key);
MeterIperl(WMBus *bus, string& name, string& id, string& key);
// Total water counted through the meter
double totalWaterConsumption();
@ -68,7 +68,7 @@ private:
double total_water_consumption_ {};
};
MeterIperl::MeterIperl(WMBus *bus, const char *name, const char *id, const char *key) :
MeterIperl::MeterIperl(WMBus *bus, string& name, string& id, string& key) :
MeterCommonImplementation(bus, name, id, key, IPERL_METER, MANUFACTURER_SEN, 0x16, LinkModeT1)
{
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
@ -80,7 +80,7 @@ double MeterIperl::totalWaterConsumption()
return total_water_consumption_;
}
unique_ptr<WaterMeter> createIperl(WMBus *bus, const char *name, const char *id, const char *key)
unique_ptr<WaterMeter> createIperl(WMBus *bus, string& name, string& id, string& key)
{
return unique_ptr<WaterMeter>(new MeterIperl(bus,name,id,key));
}

Wyświetl plik

@ -45,7 +45,7 @@ using namespace std;
#define INFO_CODE_BURST_SHIFT (4+9)
struct MeterMultical21 : public virtual WaterMeter, public virtual MeterCommonImplementation {
MeterMultical21(WMBus *bus, const char *name, const char *id, const char *key, MeterType mt);
MeterMultical21(WMBus *bus, string& name, string& id, string& key, MeterType mt);
// Total water counted through the meter
double totalWaterConsumption();
@ -103,7 +103,7 @@ private:
int expected_version_ {}; // 0x1b for Multical21 and 0x1d for FlowIQ3100
};
MeterMultical21::MeterMultical21(WMBus *bus, const char *name, const char *id, const char *key, MeterType mt) :
MeterMultical21::MeterMultical21(WMBus *bus, string& name, string& id, string& key, MeterType mt) :
MeterCommonImplementation(bus, name, id, key, mt, MANUFACTURER_KAM, 0x16, LinkModeC1)
{
if (type() == MULTICAL21_METER) {
@ -169,7 +169,7 @@ bool MeterMultical21::hasExternalTemperature()
return has_external_temperature_;
}
unique_ptr<WaterMeter> createMultical21(WMBus *bus, const char *name, const char *id, const char *key, MeterType mt)
unique_ptr<WaterMeter> createMultical21(WMBus *bus, string& name, string& id, string& key, MeterType mt)
{
if (mt != MULTICAL21_METER && mt != FLOWIQ3100_METER) {
error("Internal error! Not a proper meter type when creating a multical21 style meter.\n");

Wyświetl plik

@ -28,7 +28,7 @@
#include<vector>
struct MeterMultical302 : public virtual HeatMeter, public virtual MeterCommonImplementation {
MeterMultical302(WMBus *bus, const char *name, const char *id, const char *key);
MeterMultical302(WMBus *bus, string& name, string& id, string& key);
double totalEnergyConsumption();
double currentPowerConsumption();
@ -49,7 +49,7 @@ private:
double total_volume_ {};
};
MeterMultical302::MeterMultical302(WMBus *bus, const char *name, const char *id, const char *key) :
MeterMultical302::MeterMultical302(WMBus *bus, string& name, string& id, string& key) :
MeterCommonImplementation(bus, name, id, key, MULTICAL302_METER, MANUFACTURER_KAM, 0x04, LinkModeC1)
{
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
@ -178,7 +178,7 @@ void MeterMultical302::processContent(Telegram *t) {
}
}
unique_ptr<HeatMeter> createMultical302(WMBus *bus, const char *name, const char *id, const char *key) {
unique_ptr<HeatMeter> createMultical302(WMBus *bus, string& name, string& id, string& key) {
return unique_ptr<HeatMeter>(new MeterMultical302(bus,name,id,key));
}

Wyświetl plik

@ -30,7 +30,7 @@
#include<vector>
struct MeterOmnipower : public virtual ElectricityMeter, public virtual MeterCommonImplementation {
MeterOmnipower(WMBus *bus, const char *name, const char *id, const char *key);
MeterOmnipower(WMBus *bus, string& name, string& id, string& key);
double totalEnergyConsumption();
double currentPowerConsumption();
@ -48,7 +48,7 @@ private:
double current_power_ {};
};
MeterOmnipower::MeterOmnipower(WMBus *bus, const char *name, const char *id, const char *key) :
MeterOmnipower::MeterOmnipower(WMBus *bus, string& name, string& id, string& key) :
MeterCommonImplementation(bus, name, id, key, OMNIPOWER_METER, MANUFACTURER_KAM, 0x04, LinkModeC1)
{
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
@ -118,7 +118,7 @@ void MeterOmnipower::processContent(Telegram *t)
t->addMoreExplanation(offset, " total power (%f kwh)", total_energy_);
}
unique_ptr<ElectricityMeter> createOmnipower(WMBus *bus, const char *name, const char *id, const char *key)
unique_ptr<ElectricityMeter> createOmnipower(WMBus *bus, string& name, string& id, string& key)
{
return unique_ptr<ElectricityMeter>(new MeterOmnipower(bus,name,id,key));
}

Wyświetl plik

@ -30,7 +30,7 @@
#include<vector>
struct MeterQCaloric : public virtual HeatCostMeter, public virtual MeterCommonImplementation {
MeterQCaloric(WMBus *bus, const char *name, const char *id, const char *key);
MeterQCaloric(WMBus *bus, string& name, string& id, string& key);
double totalEnergyConsumption();
@ -46,7 +46,7 @@ private:
double total_energy_ {};
};
MeterQCaloric::MeterQCaloric(WMBus *bus, const char *name, const char *id, const char *key) :
MeterQCaloric::MeterQCaloric(WMBus *bus, string& name, string& id, string& key) :
MeterCommonImplementation(bus, name, id, key, QCALORIC_METER, MANUFACTURER_QDS, 0x08, LinkModeC1)
{
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
@ -101,7 +101,7 @@ void MeterQCaloric::processContent(Telegram *t)
parseDV(t, t->content, t->content.begin(), t->content.size(), &values);
}
unique_ptr<HeatCostMeter> createQCaloric(WMBus *bus, const char *name, const char *id, const char *key)
unique_ptr<HeatCostMeter> createQCaloric(WMBus *bus, string& name, string& id, string& key)
{
return unique_ptr<HeatCostMeter>(new MeterQCaloric(bus,name,id,key));
}

Wyświetl plik

@ -33,7 +33,7 @@
using namespace std;
struct MeterSupercom587 : public virtual WaterMeter, public virtual MeterCommonImplementation {
MeterSupercom587(WMBus *bus, const char *name, const char *id, const char *key);
MeterSupercom587(WMBus *bus, string& name, string& id, string& key);
// Total water counted through the meter
double totalWaterConsumption();
@ -67,7 +67,7 @@ private:
double total_water_consumption_ {};
};
MeterSupercom587::MeterSupercom587(WMBus *bus, const char *name, const char *id, const char *key) :
MeterSupercom587::MeterSupercom587(WMBus *bus, string& name, string& id, string& key) :
MeterCommonImplementation(bus, name, id, key, SUPERCOM587_METER, MANUFACTURER_SON, 0x16, LinkModeT1)
{
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
@ -79,7 +79,7 @@ double MeterSupercom587::totalWaterConsumption()
return total_water_consumption_;
}
unique_ptr<WaterMeter> createSupercom587(WMBus *bus, const char *name, const char *id, const char *key)
unique_ptr<WaterMeter> createSupercom587(WMBus *bus, string& name, string& id, string& key)
{
return unique_ptr<WaterMeter>(new MeterSupercom587(bus,name,id,key));
}

Wyświetl plik

@ -20,7 +20,7 @@
#include<memory.h>
MeterCommonImplementation::MeterCommonImplementation(WMBus *bus, const char *name, const char *id, const char *key,
MeterCommonImplementation::MeterCommonImplementation(WMBus *bus, string& name, string& id, string& key,
MeterType type, int manufacturer, int media,
LinkMode required_link_mode) :
type_(type), manufacturer_(manufacturer), media_(media), name_(name), bus_(bus),
@ -28,7 +28,7 @@ MeterCommonImplementation::MeterCommonImplementation(WMBus *bus, const char *nam
{
use_aes_ = true;
hex2bin(id, &id_);
if (strlen(key) == 0) {
if (key.length() == 0) {
use_aes_ = false;
} else {
hex2bin(key, &key_);
@ -97,26 +97,26 @@ string MeterCommonImplementation::datetimeOfUpdateRobot()
return string(datetime);
}
MeterType toMeterType(const char *type)
MeterType toMeterType(string& type)
{
if (!strcmp(type, "multical21")) return MULTICAL21_METER;
if (!strcmp(type, "flowiq3100")) return FLOWIQ3100_METER;
if (!strcmp(type, "multical302")) return MULTICAL302_METER;
if (!strcmp(type, "omnipower")) return OMNIPOWER_METER;
if (!strcmp(type, "supercom587")) return SUPERCOM587_METER;
if (!strcmp(type, "iperl")) return IPERL_METER;
if (!strcmp(type, "qcaloric")) return QCALORIC_METER;
if (type == "multical21") return MULTICAL21_METER;
if (type == "flowiq3100") return FLOWIQ3100_METER;
if (type == "multical302") return MULTICAL302_METER;
if (type == "omnipower") return OMNIPOWER_METER;
if (type == "supercom587") return SUPERCOM587_METER;
if (type == "iperl") return IPERL_METER;
if (type == "qcaloric") return QCALORIC_METER;
return UNKNOWN_METER;
}
LinkMode toMeterLinkMode(const char *type)
LinkMode toMeterLinkMode(string& type)
{
if (!strcmp(type, "multical21")) return LinkModeC1;
if (!strcmp(type, "flowiq3100")) return LinkModeC1;
if (!strcmp(type, "multical302")) return LinkModeC1;
if (!strcmp(type, "omnipower")) return LinkModeC1;
if (!strcmp(type, "supercom587")) return LinkModeT1;
if (!strcmp(type, "iperl")) return LinkModeT1;
if (type == "multical21") return LinkModeC1;
if (type == "flowiq3100") return LinkModeC1;
if (type == "multical302") return LinkModeC1;
if (type == "omnipower") return LinkModeC1;
if (type == "supercom587") return LinkModeT1;
if (type == "iperl") return LinkModeT1;
return UNKNOWN_LINKMODE;
}

Wyświetl plik

@ -106,14 +106,14 @@ struct HeatCostMeter : public virtual Meter {
struct GenericMeter : public virtual Meter {
};
MeterType toMeterType(const char *type);
LinkMode toMeterLinkMode(const char *type);
unique_ptr<WaterMeter> createMultical21(WMBus *bus, const char *name, const char *id, const char *key, MeterType mt);
unique_ptr<HeatMeter> createMultical302(WMBus *bus, const char *name, const char *id, const char *key);
unique_ptr<ElectricityMeter> createOmnipower(WMBus *bus, const char *name, const char *id, const char *key);
unique_ptr<WaterMeter> createSupercom587(WMBus *bus, const char *name, const char *id, const char *key);
unique_ptr<WaterMeter> createIperl(WMBus *bus, const char *name, const char *id, const char *key);
unique_ptr<HeatCostMeter> createQCaloric(WMBus *bus, const char *name, const char *id, const char *key);
GenericMeter *createGeneric(WMBus *bus, const char *name, const char *id, const char *key);
MeterType toMeterType(string& type);
LinkMode toMeterLinkMode(string& type);
unique_ptr<WaterMeter> createMultical21(WMBus *bus, string& name, string& id, string& key, MeterType mt);
unique_ptr<HeatMeter> createMultical302(WMBus *bus, string& name, string& id, string& key);
unique_ptr<ElectricityMeter> createOmnipower(WMBus *bus, string& name, string& id, string& key);
unique_ptr<WaterMeter> createSupercom587(WMBus *bus, string& name, string& id, string& key);
unique_ptr<WaterMeter> createIperl(WMBus *bus, string& name, string& id, string& key);
unique_ptr<HeatCostMeter> createQCaloric(WMBus *bus, string& name, string& id, string& key);
GenericMeter *createGeneric(WMBus *bus, string& name, string& id, string& key);
#endif

Wyświetl plik

@ -46,7 +46,7 @@ struct MeterCommonImplementation : public virtual Meter
double getRecordAsDouble(std::string record);
uint16_t getRecordAsUInt16(std::string record);
MeterCommonImplementation(WMBus *bus, const char *name, const char *id, const char *key,
MeterCommonImplementation(WMBus *bus, string& name, string& id, string& key,
MeterType type, int manufacturer, int media,
LinkMode required_link_mode);

152
util.cc
Wyświetl plik

@ -16,6 +16,7 @@
*/
#include"util.h"
#include<dirent.h>
#include<functional>
#include<signal.h>
#include<stdarg.h>
@ -23,6 +24,10 @@
#include<string.h>
#include<string>
#include<sys/stat.h>
#include<syslog.h>
#include<unistd.h>
#include<sys/types.h>
#include<fcntl.h>
using namespace std;
@ -141,6 +146,7 @@ void error(const char* fmt, ...) {
exit(1);
}
bool syslog_enabled_ = false;
bool warning_enabled_ = true;
bool verbose_enabled_ = false;
bool debug_enabled_ = false;
@ -150,6 +156,10 @@ void warningSilenced(bool b) {
warning_enabled_ = !b;
}
void enableSyslog() {
syslog_enabled_ = true;
}
void verboseEnabled(bool b) {
verbose_enabled_ = b;
}
@ -185,7 +195,11 @@ void warning(const char* fmt, ...) {
if (warning_enabled_) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
if (syslog_enabled_) {
vsyslog(LOG_WARNING, fmt, args);
} else {
vprintf(fmt, args);
}
va_end(args);
}
}
@ -194,7 +208,11 @@ void verbose(const char* fmt, ...) {
if (verbose_enabled_) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
if (syslog_enabled_) {
vsyslog(LOG_INFO, fmt, args);
} else {
vprintf(fmt, args);
}
va_end(args);
}
}
@ -203,25 +221,28 @@ void debug(const char* fmt, ...) {
if (debug_enabled_) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
if (syslog_enabled_) {
vsyslog(LOG_INFO, fmt, args);
} else {
vprintf(fmt, args);
}
va_end(args);
}
}
bool isValidId(char *id)
bool isValidId(string& id)
{
if (strlen(id) == 0) return true;
if (strlen(id) != 8) return false;
if (id.length() != 8) return false;
for (int i=0; i<8; ++i) {
if (id[i]<'0' || id[i]>'9') return false;
}
return true;
}
bool isValidKey(char *key)
bool isValidKey(string& key)
{
if (strlen(key) == 0) return true;
if (strlen(key) != 32) return false;
if (key.length() == 0) return true;
if (key.length() != 32) return false;
vector<uchar> tmp;
return hex2bin(key, &tmp);
}
@ -438,3 +459,116 @@ bool crc16_CCITT_check(uchar *data, uint16_t length)
uint16_t crc = ~crc16_CCITT(data, length);
return crc == CRC16_GOOD_VALUE;
}
bool listFiles(const char *dir, vector<string> *files)
{
DIR *dp = NULL;
struct dirent *dptr = NULL;
if (NULL == (dp = opendir(dir)))
{
return false;
}
while(NULL != (dptr = ::readdir(dp)))
{
if (!strcmp(dptr->d_name,".") ||
!strcmp(dptr->d_name,".."))
{
// Ignore . .. dirs.
continue;
}
files->push_back(string(dptr->d_name));
}
closedir(dp);
return true;
}
bool loadFile(const char *file, vector<char> *buf)
{
int blocksize = 1024;
char block[blocksize];
int fd = open(file, O_RDONLY);
if (fd == -1) {
return false;
}
while (true) {
ssize_t n = read(fd, block, sizeof(block));
if (n == -1) {
if (errno == EINTR) {
continue;
}
warning("Could not read file %s errno=%d\n", file, errno);
close(fd);
return false;
}
buf->insert(buf->end(), block, block+n);
if (n < (ssize_t)sizeof(block)) {
break;
}
}
close(fd);
return true;
}
string eatToSkipWhitespace(vector<char> &v, vector<char>::iterator &i, int c, size_t max, bool *eof, bool *err)
{
eatWhitespace(v, i, eof);
if (*eof) {
if (c != -1) {
*err = true;
}
return "";
}
string s = eatTo(v,i,c,max,eof,err);
trimWhitespace(&s);
return s;
}
string eatTo(vector<char> &v, vector<char>::iterator &i, int c, size_t max, bool *eof, bool *err)
{
string s;
*eof = false;
*err = false;
while (max > 0 && i != v.end() && (c == -1 || *i != c))
{
s += *i;
i++;
max--;
}
if (c != -1 && (i == v.end() || *i != c))
{
*err = true;
}
if (i != v.end())
{
i++;
}
if (i == v.end()) {
*eof = true;
}
return s;
}
void eatWhitespace(vector<char> &v, vector<char>::iterator &i, bool *eof)
{
*eof = false;
while (i != v.end() && (*i == ' ' || *i == '\t'))
{
i++;
}
if (i == v.end()) {
*eof = true;
}
}
void trimWhitespace(string *s)
{
const char *ws = " \t";
s->erase(0, s->find_first_not_of(ws));
s->erase(s->find_last_not_of(ws) + 1);
}

20
util.h
Wyświetl plik

@ -20,6 +20,7 @@
#include<signal.h>
#include<stdint.h>
#include<string>
#include<functional>
#include<vector>
@ -38,6 +39,7 @@ void strprintf(std::string &s, const char* fmt, ...);
void xorit(uchar *srca, uchar *srcb, uchar *dest, int len);
void enableSyslog();
void error(const char* fmt, ...);
void verbose(const char* fmt, ...);
void debug(const char* fmt, ...);
@ -55,14 +57,16 @@ bool isLogTelegramsEnabled();
void debugPayload(std::string intro, std::vector<uchar> &payload);
void logTelegram(std::string intro, std::vector<uchar> &header, std::vector<uchar> &content);
bool isValidId(char *id);
bool isValidKey(char *key);
bool isValidId(std::string& id);
bool isValidKey(std::string& key);
void incrementIV(uchar *iv, size_t len);
bool checkCharacterDeviceExists(const char *tty, bool fail_if_not);
bool checkIfSimulationFile(const char *file);
bool checkIfDirExists(const char *dir);
bool listFiles(const char *dir, std::vector<std::string> *files);
bool loadFile(const char *file, std::vector<char> *buf);
std::string eatTo(std::vector<uchar> &v, std::vector<uchar>::iterator &i, int c, size_t max, bool *eof, bool *err);
@ -76,4 +80,16 @@ uint16_t crc16_EN13757(uchar *data, size_t len);
uint16_t crc16_CCITT(uchar *data, uint16_t length);
bool crc16_CCITT_check(uchar *data, uint16_t length);
// Eat characters from the vector v, iterating using i, until the end char c is found.
// If end char == -1, then do not expect any end char, get all until eof.
// If the end char is not found, return error.
// If the maximum length is reached without finding the end char, return error.
std::string eatTo(std::vector<char> &v, std::vector<char>::iterator &i, int c, size_t max, bool *eof, bool *err);
// Eat whitespace (space and tab, not end of lines).
void eatWhitespace(std::vector<char> &v, std::vector<char>::iterator &i, bool *eof);
// First eat whitespace, then start eating until c is found or eof. The found string is trimmed from beginning and ending whitespace.
std::string eatToSkipWhitespace(std::vector<char> &v, std::vector<char>::iterator &i, int c, size_t max, bool *eof, bool *err);
// Remove leading and trailing white space
void trimWhitespace(std::string *s);
#endif