kopia lustrzana https://github.com/weetmuts/wmbusmeters
Add elster,iperl text based drivers.
rodzic
04ed4d7628
commit
fefbf507fe
|
@ -6,6 +6,7 @@ packaging/
|
|||
testaes/
|
||||
testoutput/
|
||||
tests_tmp/
|
||||
3rdparty/
|
||||
*~
|
||||
config.log
|
||||
autom4te.cache/
|
||||
|
|
17
Makefile
17
Makefile
|
@ -1,3 +1,4 @@
|
|||
|
||||
# Copyright (C) 2017-2023 Fredrik Öhrström (gpl-3.0-or-later)
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
@ -324,16 +325,16 @@ lcov:
|
|||
(cd build_debug; genhtml lcov.info)
|
||||
xdg-open build_debug/src/index.html
|
||||
|
||||
test:
|
||||
test: build/xmq
|
||||
@./test.sh build/wmbusmeters
|
||||
|
||||
testd:
|
||||
testd: build/xmq
|
||||
@./test.sh build_debug/wmbusmeters
|
||||
|
||||
testdriver:
|
||||
testdriver: build/xmq
|
||||
@./tests/test_drivers.sh build/wmbusmeters driver_${DRIVER}.cc
|
||||
|
||||
testdriverd:
|
||||
testdriverd: build/xmq
|
||||
@./tests/test_drivers.sh build_debug/wmbusmeters driver_${DRIVER}.cc
|
||||
|
||||
update_manufacturers:
|
||||
|
@ -452,6 +453,14 @@ deploy:
|
|||
collect_copyrights:
|
||||
./scripts/collect_copyrights.sh deb/copyright
|
||||
|
||||
3rdparty/xmq/build/default/release/xmq: $(wildcard 3rdparty/xmq/src/main/c/* 3rdparty/xmq/src/main/c/parts/*)
|
||||
@mkdir -p 3rdparty
|
||||
@(cd 3rdparty; git clone git@github.com:libxmq/xmq.git; cd xmq; ./configure; make; make test)
|
||||
|
||||
build/xmq: 3rdparty/xmq/build/default/release/xmq
|
||||
@cp $< $@
|
||||
@if [ "$(build/xmq --version ; echo $?)" != "0" ]; then echo "Failed to build a working xmq!"; fi
|
||||
|
||||
# Include dependency information generated by gcc in a previous compile.
|
||||
include $(wildcard $(patsubst %.o,%.d,$(PROG_OBJS) $(DRIVER_OBJS)))
|
||||
|
||||
|
|
|
@ -60,6 +60,43 @@ shared_ptr<Configuration> parseCommandLine(int argc, char **argv)
|
|||
return parseNormalCommandLine(c, argc, argv);
|
||||
}
|
||||
|
||||
void enableEarlyLoggingFromCommandLine(int argc, char **argv)
|
||||
{
|
||||
int i = 1;
|
||||
// First find all logging flags, --silent --verbose --normal --debug
|
||||
while (argv[i] && argv[i][0] == '-')
|
||||
{
|
||||
if (!strcmp(argv[i], "--silent")) {
|
||||
i++;
|
||||
silentLogging(true);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "--verbose")) {
|
||||
verboseEnabled(true);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "--normal")) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "--debug")) {
|
||||
verboseEnabled(true);
|
||||
debugEnabled(true);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "--trace")) {
|
||||
verboseEnabled(true);
|
||||
debugEnabled(true);
|
||||
traceEnabled(true);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int argc, char **argv)
|
||||
{
|
||||
int i = 1;
|
||||
|
@ -621,13 +658,13 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
|
|||
if (!strncmp(argv[i], "--driver=", 9))
|
||||
{
|
||||
size_t len = strlen(argv[i]) - 9;
|
||||
string driver = string(argv[i]+9, len);
|
||||
if (!checkFileExists(driver.c_str()))
|
||||
string file_name = string(argv[i]+9, len);
|
||||
if (!checkFileExists(file_name.c_str()))
|
||||
{
|
||||
error("You must supply a valid file to --driver=<file>\n");
|
||||
}
|
||||
i++;
|
||||
loadDriver(driver);
|
||||
loadDriver(file_name, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,5 +27,6 @@
|
|||
using namespace std;
|
||||
|
||||
shared_ptr<Configuration> parseCommandLine(int argc, char **argv);
|
||||
void enableEarlyLoggingFromCommandLine(int argc, char **argv);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include"driver_dynamic.h"
|
||||
#include"xmq.h"
|
||||
|
||||
#include<string.h>
|
||||
|
||||
string check_driver_name(const char *name, string file);
|
||||
MeterType check_meter_type(const char *meter_type_s, string file);
|
||||
string check_default_fields(const char *fields, string file);
|
||||
|
@ -38,14 +40,28 @@ void check_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynami
|
|||
|
||||
const char *line = "-------------------------------------------------------------------------------";
|
||||
|
||||
bool DriverDynamic::load(DriverInfo *di, const string &file)
|
||||
bool DriverDynamic::load(DriverInfo *di, const string &file_name, const char *content)
|
||||
{
|
||||
if (!endsWith(file, ".xmq")) return false;
|
||||
if (!checkFileExists(file.c_str())) return false;
|
||||
if (!content)
|
||||
{
|
||||
if (!endsWith(file_name, ".xmq")) return false;
|
||||
if (!checkFileExists(file_name.c_str())) return false;
|
||||
}
|
||||
|
||||
string file = file_name;
|
||||
XMQDoc *doc = xmqNewDoc();
|
||||
|
||||
bool ok = xmqParseFile(doc, file.c_str(), NULL);
|
||||
bool ok = false;
|
||||
|
||||
if (!content)
|
||||
{
|
||||
ok = xmqParseFile(doc, file.c_str(), NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
file = "builtin";
|
||||
ok = xmqParseBuffer(doc, content, content+strlen(content), NULL);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
warning("(driver) error loading wmbusmeters driver file %s\n%s\n%s\n",
|
||||
|
@ -67,7 +83,10 @@ bool DriverDynamic::load(DriverInfo *di, const string &file)
|
|||
string default_fields = check_default_fields(xmqGetString(doc, NULL, "/driver/default_fields"), file);
|
||||
di->setDefaultFields(default_fields);
|
||||
|
||||
verbose("(driver) loading driver %s from file %s\n", name.c_str(), file.c_str());
|
||||
if (!content)
|
||||
{
|
||||
verbose("(driver) loading driver %s from file %s\n", name.c_str(), file.c_str());
|
||||
}
|
||||
|
||||
di->setDynamic(file, doc);
|
||||
|
||||
|
@ -89,6 +108,7 @@ DriverDynamic::DriverDynamic(MeterInfo &mi, DriverInfo &di) :
|
|||
MeterCommonImplementation(mi, di), file_name_(di.getDynamicFileName())
|
||||
{
|
||||
XMQDoc *doc = di.getDynamicDriver();
|
||||
assert(doc);
|
||||
|
||||
verbose("(driver) constructing driver %s from already loaded file %s\n",
|
||||
di.name().str().c_str(),
|
||||
|
@ -101,8 +121,6 @@ DriverDynamic::DriverDynamic(MeterInfo &mi, DriverInfo &di) :
|
|||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
xmqFreeDoc(doc);
|
||||
}
|
||||
|
||||
DriverDynamic::~DriverDynamic()
|
||||
|
|
|
@ -24,7 +24,7 @@ struct DriverDynamic : public virtual MeterCommonImplementation
|
|||
{
|
||||
DriverDynamic(MeterInfo &mi, DriverInfo &di);
|
||||
~DriverDynamic();
|
||||
static bool load(DriverInfo *di, const string &name);
|
||||
static bool load(DriverInfo *di, const string &name, const char *content);
|
||||
static XMQProceed add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *di);
|
||||
static XMQProceed add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *dd);
|
||||
static XMQProceed add_match(XMQDoc *doc, XMQNode *match, DriverDynamic *dd);
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2017-2022 Fredrik Öhrström (gpl-3.0-or-later)
|
||||
Copyright (C) 2018 David Mallon (gpl-3.0-or-later)
|
||||
|
||||
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"meters_common_implementation.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Driver : public virtual MeterCommonImplementation
|
||||
{
|
||||
Driver(MeterInfo &mi, DriverInfo &di);
|
||||
};
|
||||
|
||||
static bool ok = registerDriver([](DriverInfo&di)
|
||||
{
|
||||
di.setName("iperl");
|
||||
di.setDefaultFields("name,id,total_m3,max_flow_m3h,timestamp");
|
||||
di.setMeterType(MeterType::WaterMeter);
|
||||
di.addLinkMode(LinkMode::T1);
|
||||
di.addDetection(MANUFACTURER_SEN, 0x06, 0x68);
|
||||
di.addDetection(MANUFACTURER_SEN, 0x07, 0x68);
|
||||
di.addDetection(MANUFACTURER_SEN, 0x07, 0x7c);
|
||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||
});
|
||||
|
||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||
{
|
||||
addNumericFieldWithExtractor(
|
||||
"total",
|
||||
"The total water consumption recorded by this meter.",
|
||||
DEFAULT_PRINT_PROPERTIES,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Volume)
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"max_flow",
|
||||
"The maximum flow recorded during previous period.",
|
||||
DEFAULT_PRINT_PROPERTIES,
|
||||
Quantity::Flow,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::VolumeFlow));
|
||||
}
|
||||
}
|
||||
|
||||
// Test: MoreWater iperl 12345699 NOKEY
|
||||
// Comment: Test iPerl T1 telegram, that after decryption, has 2f2f markers.
|
||||
// telegram=|1E44AE4C9956341268077A36001000_2F2F0413181E0000023B00002F2F2F2F|
|
||||
// {"media":"water","meter":"iperl","name":"MoreWater","id":"12345699","total_m3":7.704,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
// |MoreWater;12345699;7.704;0;1111-11-11 11:11.11
|
||||
|
||||
// Test: WaterWater iperl 33225544 NOKEY
|
||||
// Comment: Test iPerl T1 telegram not encrypted, which has no 2f2f markers.
|
||||
// telegram=|1844AE4C4455223368077A55000000_041389E20100023B0000|
|
||||
// {"media":"water","meter":"iperl","name":"WaterWater","id":"33225544","total_m3":123.529,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
// |WaterWater;33225544;123.529;0;1111-11-11 11:11.11
|
|
@ -29,7 +29,6 @@ namespace
|
|||
di.setName("munia");
|
||||
di.setDefaultFields("name,id,current_temperature_c,current_relative_humidity_rh,timestamp");
|
||||
di.setMeterType(MeterType::TempHygroMeter);
|
||||
di.addLinkMode(LinkMode::MBUS);
|
||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||
di.addDetection(MANUFACTURER_WEP, 0x1b, 0x02);
|
||||
di.addDetection(MANUFACTURER_WEP, 0x1b, 0x04);
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||
|
||||
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<vector>
|
||||
#include<string>
|
||||
|
||||
#include"util.h"
|
||||
#include"drivers.h"
|
||||
#include"meters.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void loadDriversFromDir(std::string dir)
|
||||
{
|
||||
if (!checkIfDirExists(dir.c_str())) return;
|
||||
|
||||
vector<string> drivers;
|
||||
listFiles(dir, &drivers);
|
||||
|
||||
verbose("(drivers) scanning dir %s\n", dir.c_str());
|
||||
|
||||
for (string &file : drivers)
|
||||
{
|
||||
string filename = dir+"/"+file;
|
||||
string s = loadDriver(filename);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
Copyright (C) 2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||
|
||||
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<vector>
|
||||
#include<string>
|
||||
#include<map>
|
||||
|
||||
#include"util.h"
|
||||
#include"drivers.h"
|
||||
#include"meters.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void loadDriversFromDir(std::string dir)
|
||||
{
|
||||
if (!checkIfDirExists(dir.c_str())) return;
|
||||
|
||||
vector<string> drivers;
|
||||
listFiles(dir, &drivers);
|
||||
|
||||
verbose("(drivers) scanning dir %s\n", dir.c_str());
|
||||
|
||||
for (string &file : drivers)
|
||||
{
|
||||
string file_name = dir+"/"+file;
|
||||
string s = loadDriver(file_name, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
struct BuiltinDriver
|
||||
{
|
||||
const char *name;
|
||||
const char *content;
|
||||
bool loaded;
|
||||
};
|
||||
|
||||
struct MVT
|
||||
{
|
||||
uint16_t mfct;
|
||||
uchar version;
|
||||
uchar type;
|
||||
};
|
||||
|
||||
struct MapToDriver {
|
||||
MVT mvt;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
#include"generated_database.cc"
|
||||
|
||||
map<uint32_t,const char*> builtins_mvt_lookup_;
|
||||
map<string,BuiltinDriver*> builtins_name_lookup_;
|
||||
|
||||
bool loadBuiltinDriver(string driver_name)
|
||||
{
|
||||
// Check that there is such a builtin driver.
|
||||
if (builtins_name_lookup_.count(driver_name) == 0) return false;
|
||||
|
||||
BuiltinDriver *driver = builtins_name_lookup_[driver_name];
|
||||
if (driver->loaded) return true;
|
||||
|
||||
string name = loadDriver("", driver->content);
|
||||
driver->loaded = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void loadAllBuiltinDrivers()
|
||||
{
|
||||
for (auto &p : builtins_name_lookup_)
|
||||
{
|
||||
if (!p.second->loaded)
|
||||
{
|
||||
loadBuiltinDriver(p.first);
|
||||
p.second->loaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *findBuiltinDriver(uint16_t mfct, uchar ver, uchar type)
|
||||
{
|
||||
uint32_t key = mfct << 16 | ver << 8 | type;
|
||||
if (builtins_mvt_lookup_.count(key) == 0) return NULL;
|
||||
return builtins_mvt_lookup_[key];
|
||||
}
|
||||
|
||||
void prepareBuiltinDrivers()
|
||||
{
|
||||
size_t num_mvts = sizeof(builtins_mvts_) / sizeof(MapToDriver);
|
||||
size_t num_drivers = sizeof(builtins_) / sizeof(BuiltinDriver);
|
||||
|
||||
for (size_t i = 0; i < num_drivers; ++i)
|
||||
{
|
||||
builtins_name_lookup_[builtins_[i].name] = &builtins_[i];
|
||||
debug("(drivers) added builtin driver %s\n", builtins_[i].name);
|
||||
}
|
||||
for (size_t i = 0; i < num_mvts; ++i)
|
||||
{
|
||||
uint32_t key = builtins_mvts_[i].mvt.mfct << 16 |
|
||||
builtins_mvts_[i].mvt.version << 8 |
|
||||
builtins_mvts_[i].mvt.type;
|
||||
builtins_mvt_lookup_[key] = builtins_mvts_[i].name;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,10 @@
|
|||
|
||||
#include<string>
|
||||
|
||||
void prepareBuiltinDrivers();
|
||||
void loadDriversFromDir(std::string dir);
|
||||
bool loadBuiltinDriver(std::string driver_name);
|
||||
void loadAllBuiltinDrivers();
|
||||
const char *findBuiltinDriver(uint16_t mfct, uchar ver, uchar type);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright (C) 2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
BuiltinDriver builtins_[] =
|
||||
{
|
||||
{ "elster", "driver{name=elster meter_type=GasMeter default_fields=name,id,total_m3,timestamp detect{mvt=ELS,81,03}field{name=total quantity=Volume match{measurement_type=Instantaneous vif_range=Volume}}test{args='Gas elster 05105025 NOKEY'telegram=3644A511640010253837722550100593158103E70020052F2F_0374E602000C137034220302FD74EE0F2F2F2F2F2F2F2F2F2F2F2F2F2F2F json='{\"media\":\"gas\",\"meter\":\"elster\",\"name\":\"Gas\",\"id\":\"05105025\",\"total_m3\":3223.47,\"timestamp\":\"1111-11-11T11:11:11Z\"}'fields='Gas;05105025;3223.47;1111-11-11 11:11.11'}}", false },
|
||||
{ "iperl", "driver{name=iperl meter_type=WaterMeter default_fields=name,id,total_m3,max_flow_m3h,timestamp detect{mvt=SEN,68,06 mvt=SEN,68,07 mvt=SEN,7c,07}field{name=total quantity=Volume match{measurement_type=Instantaneous vif_range=Volume}about{de='Der Gesamtwasserverbrauch.'en='The total water consumption.'fr='''La consommation totale d'eau.'''sv='Den totala vattenförbrukningen.'}}field{name=max_flow quantity=Flow match{measurement_type=Instantaneous vif_range=VolumeFlow}about{en='The maximum flow recorded during previous period.'}}test{args='MoreWater iperl 12345699 NOKEY'coment='Test iPerl T1 telegram, that after decryption, has 2f2f markers.'telegram=1E44AE4C9956341268077A36001000_2F2F0413181E0000023B00002F2F2F2F json='{\"media\":\"water\",\"meter\":\"iperl\",\"name\":\"MoreWater\",\"id\":\"12345699\",\"total_m3\":7.704,\"max_flow_m3h\":0,\"timestamp\":\"1111-11-11T11:11:11Z\"}'fields='MoreWater;12345699;7.704;0;1111-11-11 11:11.11'}test{args='WaterWater iperl 33225544 NOKEY'comment='Test iPerl T1 telegram not encrypted, which has no 2f2f markers.'telegram=1844AE4C4455223368077A55000000_041389E20100023B0000 json='{\"media\":\"water\",\"meter\":\"iperl\",\"name\":\"WaterWater\",\"id\":\"33225544\",\"total_m3\":123.529,\"max_flow_m3h\":0,\"timestamp\":\"1111-11-11T11:11:11Z\"}'fields='WaterWater;33225544;123.529;0;1111-11-11 11:11.11'}}", false },
|
||||
};
|
||||
|
||||
MapToDriver builtins_mvts_[] =
|
||||
{
|
||||
{ { MANUFACTURER_ELS,0x81,0x03 }, "elster" },
|
||||
{ { MANUFACTURER_SEN,0x68,0x06 }, "iperl" },
|
||||
{ { MANUFACTURER_SEN,0x68,0x07 }, "iperl" },
|
||||
{ { MANUFACTURER_SEN,0x7c,0x07 }, "iperl" },
|
||||
};
|
|
@ -86,6 +86,9 @@ int main(int argc, char **argv)
|
|||
|
||||
setVersion(VERSION);
|
||||
|
||||
enableEarlyLoggingFromCommandLine(argc, argv);
|
||||
prepareBuiltinDrivers();
|
||||
|
||||
auto config = parseCommandLine(argc, argv);
|
||||
|
||||
if (config->version)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2017-2022 Fredrik Öhrström (gpl-3.0-or-later)
|
||||
Copyright (C) 2017-2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||
|
||||
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
|
||||
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include"bus.h"
|
||||
#include"config.h"
|
||||
#include"drivers.h"
|
||||
#include"meters.h"
|
||||
#include"meters_common_implementation.h"
|
||||
#include"units.h"
|
||||
|
@ -378,6 +379,7 @@ public:
|
|||
|
||||
void analyzeTelegram(AboutTelegram &about, vector<uchar> &input_frame, bool simulated)
|
||||
{
|
||||
loadAllBuiltinDrivers();
|
||||
Telegram t;
|
||||
t.about = about;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include"bus.h"
|
||||
#include"config.h"
|
||||
#include"drivers.h"
|
||||
#include"driver_dynamic.h"
|
||||
#include"meters.h"
|
||||
#include"meters_common_implementation.h"
|
||||
|
@ -125,6 +126,10 @@ bool DriverInfo::isValidMedia(uchar type)
|
|||
return false;
|
||||
}
|
||||
|
||||
DriverInfo::~DriverInfo()
|
||||
{
|
||||
}
|
||||
|
||||
bool DriverInfo::isCloseEnoughMedia(uchar type)
|
||||
{
|
||||
for (auto &dd : detect_)
|
||||
|
@ -198,11 +203,11 @@ bool registerDriver(function<void(DriverInfo&)> setup)
|
|||
return true;
|
||||
}
|
||||
|
||||
string loadDriver(const string &file)
|
||||
string loadDriver(const string &file, const char *content)
|
||||
{
|
||||
DriverInfo di;
|
||||
|
||||
bool ok = DriverDynamic::load(&di, file);
|
||||
bool ok = DriverDynamic::load(&di, file, content);
|
||||
if (!ok)
|
||||
{
|
||||
error("Failed to load driver from file: %s\n", file.c_str());
|
||||
|
@ -269,32 +274,47 @@ string loadDriver(const string &file)
|
|||
|
||||
bool lookupDriverInfo(const string& driver_name, DriverInfo *out_di)
|
||||
{
|
||||
// Lookup an already loaded driver, it might be compiled in as well.
|
||||
DriverInfo *di = lookupDriver(driver_name);
|
||||
if (di == NULL)
|
||||
if (di)
|
||||
{
|
||||
if (!endsWith(driver_name, ".xmq") || !checkFileExists(driver_name.c_str()))
|
||||
if (out_di) *out_di = *di;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Lookup a dynamic text driver that can be loaded from memory.
|
||||
if (loadBuiltinDriver(driver_name))
|
||||
{
|
||||
// It is loaded, lets fetch the DriverInfo.
|
||||
di = lookupDriver(driver_name);
|
||||
if (di)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ok, not built in, but it ends with .xmq and the file exists!
|
||||
string new_name = loadDriver(driver_name);
|
||||
|
||||
// Check again if it was registered.
|
||||
di = lookupDriver(new_name);
|
||||
|
||||
if (di == NULL)
|
||||
{
|
||||
return false;
|
||||
if (out_di) *out_di = *di;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_di != NULL)
|
||||
// Is this a dynamic text driver file?
|
||||
if (!endsWith(driver_name, ".xmq") || !checkFileExists(driver_name.c_str()))
|
||||
{
|
||||
*out_di = *di;
|
||||
// Nope, give up.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
string file_name = driver_name;
|
||||
|
||||
// Load the driver from the file.
|
||||
string new_name = loadDriver(file_name, NULL);
|
||||
|
||||
// Check again if it was registered.
|
||||
di = lookupDriver(new_name);
|
||||
if (di)
|
||||
{
|
||||
if (out_di) *out_di = *di;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi,
|
||||
|
@ -1980,13 +2000,20 @@ ELLSecurityMode MeterCommonImplementation::expectedELLSecurityMode()
|
|||
|
||||
void detectMeterDrivers(int manufacturer, int media, int version, vector<string> *drivers)
|
||||
{
|
||||
bool found = false;
|
||||
for (DriverInfo *p : allDrivers())
|
||||
{
|
||||
if (p->detect(manufacturer, media, version))
|
||||
{
|
||||
drivers->push_back(p->name().str());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
const char *name = findBuiltinDriver(manufacturer, version, media);
|
||||
if (name) drivers->push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
bool isMeterDriverValid(DriverName driver_name, int manufacturer, int media, int version)
|
||||
|
|
|
@ -192,6 +192,7 @@ private:
|
|||
string dynamic_file_name_; // Name of actual loaded driver file.
|
||||
|
||||
public:
|
||||
~DriverInfo();
|
||||
DriverInfo() {};
|
||||
void setName(std::string n) { name_ = n; }
|
||||
void addNameAlias(std::string n) { name_aliases_.push_back(n); }
|
||||
|
@ -236,7 +237,7 @@ DriverInfo pickMeterDriver(Telegram *t);
|
|||
// Return true for mbus and S2/C2/T2 drivers.
|
||||
bool driverNeedsPolling(DriverName& dn);
|
||||
|
||||
string loadDriver(const string &file);
|
||||
string loadDriver(const string &file, const char *content);
|
||||
|
||||
vector<DriverInfo*>& allDrivers();
|
||||
|
||||
|
|
2
test.sh
2
test.sh
|
@ -169,6 +169,8 @@ if [ "$?" != "0" ]; then RC="1"; fi
|
|||
./tests/test_rtlwmbus_timestamps.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
./build/xmq tests/generated_tests.xmq for-each /test --shell='./tests/testit.sh '$PROG' "${args}" "${telegram}" "${json}" "${fields}"'
|
||||
|
||||
./tests/test_drivers.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
//2024-02-11_21:26
|
||||
test {
|
||||
args = 'Gas elster 05105025 NOKEY'
|
||||
telegram = 3644A511640010253837722550100593158103E70020052F2F_0374E602000C137034220302FD74EE0F2F2F2F2F2F2F2F2F2F2F2F2F2F2F
|
||||
json = '{"media":"gas","meter":"elster","name":"Gas","id":"05105025","total_m3":3223.47,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||
fields = 'Gas;05105025;3223.47;1111-11-11 11:11.11'
|
||||
}
|
||||
test {
|
||||
args = 'WaterWater iperl 33225544 NOKEY'
|
||||
comment = 'Test iPerl T1 telegram not encrypted, which has no 2f2f markers.'
|
||||
telegram = 1844AE4C4455223368077A55000000_041389E20100023B0000
|
||||
json = '{"media":"water","meter":"iperl","name":"WaterWater","id":"33225544","total_m3":123.529,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||
fields = 'WaterWater;33225544;123.529;0;1111-11-11 11:11.11'
|
||||
}
|
||||
test {
|
||||
args = 'MoreWater iperl 12345699 NOKEY'
|
||||
coment = 'Test iPerl T1 telegram, that after decryption, has 2f2f markers.'
|
||||
telegram = 1E44AE4C9956341268077A36001000_2F2F0413181E0000023B00002F2F2F2F
|
||||
json = '{"media":"water","meter":"iperl","name":"MoreWater","id":"12345699","total_m3":7.704,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||
fields = 'MoreWater;12345699;7.704;0;1111-11-11 11:11.11'
|
||||
}
|
|
@ -7,7 +7,7 @@ mkdir -p $TEST
|
|||
performCheck() {
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_response.txt
|
||||
cat $TEST/test_output.txt | grep -v == | grep -v \# | grep -v Indirect | grep -v Direct | grep -v -e "^$" | grep -v SUMMARY | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_response.txt
|
||||
diff $TEST/test_expected.txt $TEST/test_response.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
|
|
|
@ -70,7 +70,7 @@ METERS="MyWarmWater supercom587 12345678 NOKEY
|
|||
|
||||
|
||||
cat simulations/simulation_t1.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt
|
||||
$PROG --format=json simulations/simulation_t1.txt $METERS 2> $TEST/test_stderr.txt | jq --sort-keys . > $TEST/test_output.txt
|
||||
$PROG --format=json simulations/simulation_t1.txt $METERS | jq --sort-keys . > $TEST/test_output.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||
|
@ -91,11 +91,10 @@ then
|
|||
else
|
||||
echo "wmbusmeters returned error code: $?"
|
||||
cat $TEST/test_output.txt
|
||||
cat $TEST/test_stderr.txt
|
||||
fi
|
||||
|
||||
cat simulations/simulation_t1.txt | grep '^|' | sed 's/^|//' > $TEST/test_expected.txt
|
||||
$PROG --format=fields simulations/simulation_t1.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt
|
||||
$PROG --format=fields simulations/simulation_t1.txt $METERS > $TEST/test_output.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
cat $TEST/test_output.txt | sed 's/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9].[0-9][0-9]$/1111-11-11 11:11.11/' > $TEST/test_responses.txt
|
||||
|
@ -116,7 +115,6 @@ then
|
|||
else
|
||||
echo "wmbusmeters returned error code: $?"
|
||||
cat $TEST/test_output.txt
|
||||
cat $TEST/test_stderr.txt
|
||||
fi
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash
|
||||
|
||||
TEST=build/test
|
||||
mkdir -p $TEST
|
||||
|
||||
PROG="$1"
|
||||
ARGS="$2"
|
||||
HEX="$3"
|
||||
JSON="$4"
|
||||
FIELDS="$5"
|
||||
|
||||
OK=true
|
||||
|
||||
rm -f $TEST/test_output.txt $TEST/test_expected.txt
|
||||
|
||||
$PROG --format=json $HEX $ARGS \
|
||||
| jq . --sort-keys \
|
||||
| sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' \
|
||||
> $TEST/test_output.txt
|
||||
|
||||
echo "$JSON" | jq . --sort-keys > $TEST/test_expected.txt
|
||||
|
||||
if ! diff $TEST/test_expected.txt $TEST/test_output.txt
|
||||
then
|
||||
if [ "$USE_MELD" = "true" ]
|
||||
then
|
||||
meld $TEST/test_expected.txt $TEST/test_output.txt
|
||||
fi
|
||||
OK=false
|
||||
fi
|
||||
|
||||
rm -f $TEST/test_output.txt $TEST/test_expected.txt
|
||||
|
||||
$PROG --format=fields $HEX $ARGS \
|
||||
| sed 's/....-..-.. ..:..:../1111-11-11 11:11.11/' \
|
||||
> $TEST/test_output.txt
|
||||
|
||||
echo "$FIELDS" > $TEST/test_expected.txt
|
||||
|
||||
if ! diff $TEST/test_expected.txt $TEST/test_output.txt
|
||||
then
|
||||
if [ "$USE_MELD" = "true" ]
|
||||
then
|
||||
meld $TEST/test_expected.txt $TEST/test_output.txt
|
||||
fi
|
||||
OK=false
|
||||
fi
|
||||
|
||||
if [ "$OK" = "true" ]
|
||||
then
|
||||
echo "OK: $ARGS"
|
||||
else
|
||||
echo "ERROR: $ARGS $TELEGRAM"
|
||||
fi
|
Ładowanie…
Reference in New Issue