diff --git a/.gitignore b/.gitignore index c59c3af..fb420ea 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ packaging/ testaes/ testoutput/ tests_tmp/ +3rdparty/ *~ config.log autom4te.cache/ diff --git a/Makefile b/Makefile index 49dab25..662006c 100644 --- a/Makefile +++ b/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))) diff --git a/src/cmdline.cc b/src/cmdline.cc index 7e41fcf..b27ec52 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -60,6 +60,43 @@ shared_ptr 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 parseNormalCommandLine(Configuration *c, int argc, char **argv) { int i = 1; @@ -621,13 +658,13 @@ static shared_ptr 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=\n"); } i++; - loadDriver(driver); + loadDriver(file_name, NULL); continue; } diff --git a/src/cmdline.h b/src/cmdline.h index d0654f5..b9fada2 100644 --- a/src/cmdline.h +++ b/src/cmdline.h @@ -27,5 +27,6 @@ using namespace std; shared_ptr parseCommandLine(int argc, char **argv); +void enableEarlyLoggingFromCommandLine(int argc, char **argv); #endif diff --git a/src/driver_dynamic.cc b/src/driver_dynamic.cc index 342d59c..22e3c7d 100644 --- a/src/driver_dynamic.cc +++ b/src/driver_dynamic.cc @@ -20,6 +20,8 @@ #include"driver_dynamic.h" #include"xmq.h" +#include + 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() diff --git a/src/driver_dynamic.h b/src/driver_dynamic.h index 396e6ef..83f8668 100644 --- a/src/driver_dynamic.h +++ b/src/driver_dynamic.h @@ -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); diff --git a/src/driver_iperl.cc b/src/driver_iperl.cc deleted file mode 100644 index a05231a..0000000 --- a/src/driver_iperl.cc +++ /dev/null @@ -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 . -*/ - -#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(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 diff --git a/src/driver_munia.cc b/src/driver_munia.cc index eb54df2..4c06725 100644 --- a/src/driver_munia.cc +++ b/src/driver_munia.cc @@ -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(new Driver(mi, di)); }); di.addDetection(MANUFACTURER_WEP, 0x1b, 0x02); di.addDetection(MANUFACTURER_WEP, 0x1b, 0x04); diff --git a/src/drivers.c b/src/drivers.c deleted file mode 100644 index ba5e411..0000000 --- a/src/drivers.c +++ /dev/null @@ -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 . -*/ - -#include -#include - -#include"util.h" -#include"drivers.h" -#include"meters.h" - -using namespace std; - -void loadDriversFromDir(std::string dir) -{ - if (!checkIfDirExists(dir.c_str())) return; - - vector 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); - } -} diff --git a/src/drivers.cc b/src/drivers.cc new file mode 100644 index 0000000..7189ccd --- /dev/null +++ b/src/drivers.cc @@ -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 . +*/ + +#include +#include +#include + +#include"util.h" +#include"drivers.h" +#include"meters.h" + +using namespace std; + +void loadDriversFromDir(std::string dir) +{ + if (!checkIfDirExists(dir.c_str())) return; + + vector 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 builtins_mvt_lookup_; +map 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; + } +} diff --git a/src/drivers.h b/src/drivers.h index cef3ea1..06cdc2a 100644 --- a/src/drivers.h +++ b/src/drivers.h @@ -20,6 +20,10 @@ #include +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 diff --git a/src/generated_database.cc b/src/generated_database.cc new file mode 100644 index 0000000..0756a70 --- /dev/null +++ b/src/generated_database.cc @@ -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 . +*/ + +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" }, +}; diff --git a/src/main.cc b/src/main.cc index 46e5482..8c5ce83 100644 --- a/src/main.cc +++ b/src/main.cc @@ -86,6 +86,9 @@ int main(int argc, char **argv) setVersion(VERSION); + enableEarlyLoggingFromCommandLine(argc, argv); + prepareBuiltinDrivers(); + auto config = parseCommandLine(argc, argv); if (config->version) diff --git a/src/metermanager.cc b/src/metermanager.cc index 5f1aed2..6dd782d 100644 --- a/src/metermanager.cc +++ b/src/metermanager.cc @@ -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 &input_frame, bool simulated) { + loadAllBuiltinDrivers(); Telegram t; t.about = about; diff --git a/src/meters.cc b/src/meters.cc index be4bbe6..84d196c 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -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 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 *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) diff --git a/src/meters.h b/src/meters.h index 2f2a025..056630a 100644 --- a/src/meters.h +++ b/src/meters.h @@ -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& allDrivers(); diff --git a/test.sh b/test.sh index 98f13b0..6ae314d 100755 --- a/test.sh +++ b/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 diff --git a/tests/generated_tests.xmq b/tests/generated_tests.xmq new file mode 100644 index 0000000..ea592b2 --- /dev/null +++ b/tests/generated_tests.xmq @@ -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' +} diff --git a/tests/test_bad_driver.sh b/tests/test_bad_driver.sh index 6307b09..c25602d 100755 --- a/tests/test_bad_driver.sh +++ b/tests/test_bad_driver.sh @@ -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 diff --git a/tests/test_t1_meters.sh b/tests/test_t1_meters.sh index bab7938..cc1d1d1 100755 --- a/tests/test_t1_meters.sh +++ b/tests/test_t1_meters.sh @@ -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 diff --git a/tests/testit.sh b/tests/testit.sh new file mode 100755 index 0000000..89b0f6b --- /dev/null +++ b/tests/testit.sh @@ -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