diff --git a/simulations/simulation_t1.txt b/simulations/simulation_t1.txt index b25b9a1..3a140f9 100644 --- a/simulations/simulation_t1.txt +++ b/simulations/simulation_t1.txt @@ -237,11 +237,6 @@ telegram=|37446850336633663943a2_10672c866100181c01000480794435d5000000000000000 {"media":"heat","meter":"compact5","name":"Heating2","id":"66336633","total_kwh":25250,"current_kwh":284,"previous_kwh":24966,"timestamp":"1111-11-11T11:11:11Z"} |Heating2;66336633;25250;284;24966;1111-11-11 11:11.11 -# Test Maddalena EVO 868 wmbus module on water meter -telegram=|aa4424347677787950077ac10000202f2f041306070000046d1e31b12104fd17000000000e787880048120004413c9040000426c9f2c840113c904000082016c9f2cd3013b9a0200c4016d0534a7218104fd280182046c9f2c840413c9040000c404131b00000084051300000000c405130000000084061300000000c406130000000084071300000000c407130000000084081300000000c408130000000084091300000000c4091300000000ffff| -{"media":"water","meter":"evo868","name":"Votchka","id":"79787776","total_m3":1.798,"device_date_time":"2021-01-17 17:30","current_status":"OK","fabrication_no":"002081048078","consumption_at_set_date_m3":1.225,"set_date":"2020-12-31","consumption_at_set_date_2_m3":1.225,"set_date_2":"2020-12-31","max_flow_since_datetime_m3h":0.666,"max_flow_datetime":"2021-01-07 20:05","consumption_at_history_1_m3":1.225,"history_1_date":"2020-12-31","consumption_at_history_2_m3":0.027,"history_2_date":"2020-11-30","consumption_at_history_3_m3":0,"history_3_date":"2020-10-31","consumption_at_history_4_m3":0,"history_4_date":"2020-09-30","consumption_at_history_5_m3":0,"history_5_date":"2020-08-31","consumption_at_history_6_m3":0,"history_6_date":"2020-07-31","consumption_at_history_7_m3":0,"history_7_date":"2020-06-30","consumption_at_history_8_m3":0,"history_8_date":"2020-05-31","consumption_at_history_9_m3":0,"history_9_date":"2020-04-30","consumption_at_history_10_m3":0,"history_10_date":"2020-03-31","consumption_at_history_11_m3":0,"history_11_date":"2020-02-29","consumption_at_history_12_m3":0,"history_12_date":"2020-01-31","timestamp":"1111-11-11T11:11:11Z"} -|Votchka;79787776;1.798000;OK;1.225000;2020-12-31;1111-11-11 11:11.11 - # Test Gran-System-S electricity meter 101 telegram=||7844731e78610418010278046d0f13bc21040394030000841003690300008420032b00000084300300000000848010030000000084016d0000bc2184010394030000841103690300008421032b00000084310300000000848110030000000004fd482e09000004fd5b0000000002fb2d861304fd1700000201| {"media":"electricity","meter":"gransystems","name":"Gran101","id":"18046178","total_energy_consumption_kwh":0.916,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":0,"voltage_at_phase_3_v":0,"currrent_at_phase_1_a":0,"currrent_at_phase_2_a":0,"currrent_at_phase_3_a":0,"frequency_hz":49.98,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} diff --git a/src/driver_evo868.cc b/src/driver_evo868.cc new file mode 100644 index 0000000..f6f74c4 --- /dev/null +++ b/src/driver_evo868.cc @@ -0,0 +1,151 @@ +/* + Copyright (C) 2017-2022 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"meters_common_implementation.h" + +namespace +{ + struct Driver : public virtual MeterCommonImplementation + { + Driver(MeterInfo &mi, DriverInfo &di); + }; + + static bool ok = registerDriver([](DriverInfo&di) + { + di.setName("evo868"); + di.setDefaultFields("name,id,total_m3,set_date,consumption_at_set_date_m3,timestamp"); + di.setMeterType(MeterType::WaterMeter); + di.addLinkMode(LinkMode::T1); + di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); + di.addDetection(MANUFACTURER_MAD, 0x07, 0x50); + + }); + + Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) + { + addStringFieldWithExtractorAndLookup( + "current_status", + "Status of meter.", + PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT + | PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::ErrorFlags), + { + { + { + "ERROR_FLAGS", + Translate::Type::BitToString, + 0xffff, + "OK", + { + // { 0x01 , "WOOT" }, + } + }, + }, + }); + + addOptionalCommonFields("fabrication_no,meter_datetime"); + addOptionalFlowRelatedFields("total_m3"); + + addNumericFieldWithExtractor( + "consumption_at_set_date", + "The total water consumption at the most recent billing period date.", + PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(StorageNr(1)) + ); + + addStringFieldWithExtractor( + "set_date", + "The most recent billing period date.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Date) + .set(StorageNr(1)) + ); + + addNumericFieldWithExtractor( + "consumption_at_set_date_2", + "The total water consumption at the second most recent billing period date.", + PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(StorageNr(2)) + ); + + addStringFieldWithExtractor( + "set_date_2", + "The second most recent billing period date.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Date) + .set(StorageNr(2)) + ); + + addNumericFieldWithExtractor( + "max_flow_since_datetime", + "Maximum water flow since date time.", + PrintProperty::JSON | PrintProperty::FIELD, + Quantity::Flow, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Maximum) + .set(VIFRange::VolumeFlow) + .set(StorageNr(3)) + ); + + addStringFieldWithExtractor( + "max_flow_datetime", + "The datetime to which maximum flow is measured.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::DateTime) + .set(StorageNr(3)) + ); + + addNumericFieldWithExtractor( + "consumption_at_history_{storage_counter-7counter}", + "The total water consumption at the historic date.", + PrintProperty::JSON, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(StorageNr(8),StorageNr(19)) + ); + + } + +} + +// est: Votchka evo868 79787776 NOKEY +// omment: Test Maddalena EVO 868 wmbus module on water meter +// elegram=|aa4424347677787950077ac10000202f2f041306070000046d1e31b12104fd17000000000e787880048120004413c9040000426c9f2c840113c904000082016c9f2cd3013b9a0200c4016d0534a7218104fd280182046c9f2c840413c9040000c404131b00000084051300000000c405130000000084061300000000c406130000000084071300000000c407130000000084081300000000c408130000000084091300000000c4091300000000ffff| +// "media":"water","meter":"evo868","name":"Votchka","id":"79787776","total_m3":1.798,"device_date_time":"2021-01-17 17:30","current_status":"OK","fabrication_no":"002081048078","consumption_at_set_date_m3":1.225,"set_date":"2020-12-31","consumption_at_set_date_2_m3":1.225,"set_date_2":"2020-12-31","max_flow_since_datetime_m3h":0.666,"max_flow_datetime":"2021-01-07 20:05","consumption_at_history_1_m3":1.225,"history_1_date":"2020-12-31","consumption_at_history_2_m3":0.027,"history_2_date":"2020-11-30","consumption_at_history_3_m3":0,"history_3_date":"2020-10-31","consumption_at_history_4_m3":0,"history_4_date":"2020-09-30","consumption_at_history_5_m3":0,"history_5_date":"2020-08-31","consumption_at_history_6_m3":0,"history_6_date":"2020-07-31","consumption_at_history_7_m3":0,"history_7_date":"2020-06-30","consumption_at_history_8_m3":0,"history_8_date":"2020-05-31","consumption_at_history_9_m3":0,"history_9_date":"2020-04-30","consumption_at_history_10_m3":0,"history_10_date":"2020-03-31","consumption_at_history_11_m3":0,"history_11_date":"2020-02-29","consumption_at_history_12_m3":0,"history_12_date":"2020-01-31","timestamp":"1111-11-11T11:11:11Z"} +// Votchka;79787776;1.798;OK;1.225;2020-12-31;1111-11-11 11:11.11 diff --git a/src/dvparser.h b/src/dvparser.h index 571e041..e616e89 100644 --- a/src/dvparser.h +++ b/src/dvparser.h @@ -282,6 +282,7 @@ struct SubUnitNr SubUnitNr(int n) : nr_(n) {} int intValue() { return nr_; } bool operator==(SubUnitNr s) { return nr_ == s.nr_; } + bool operator!=(SubUnitNr s) { return nr_ != s.nr_; } bool operator>=(SubUnitNr s) { return nr_ >= s.nr_; } bool operator<=(SubUnitNr s) { return nr_ <= s.nr_; } @@ -450,6 +451,16 @@ struct FieldMatcher FieldMatcher &set(IndexNr i) { index_nr = i; return *this; } bool matches(DVEntry &dv_entry); + + // Returns true of there is any range for storage, tariff, subunit nrs. + // I.e. this matcher is expected to match against multiple dv entries! + bool expectedToMatchAgainstMultipleEntries() + { + return (match_storage_nr && storage_nr_from != storage_nr_to) + || (match_tariff_nr && tariff_nr_from != tariff_nr_to) + || (match_subunit_nr && subunit_nr_from != subunit_nr_to); + } + std::string str(); }; diff --git a/src/formula.cc b/src/formula.cc index 7f85f55..91d84b3 100644 --- a/src/formula.cc +++ b/src/formula.cc @@ -1083,7 +1083,14 @@ string StringInterpolatorImplementation::apply(DVEntry *dve) if (f < formulas_.size()) { - result += tostrprintf("%g", formulas_[f]->calculate(Unit::COUNTER, dve)); + if (dve != NULL) + { + result += tostrprintf("%g", formulas_[f]->calculate(Unit::COUNTER, dve)); + } + else + { + result += "{NULL}"; + } f++; } } diff --git a/src/meter_detection.h b/src/meter_detection.h index 26260f5..4ed7561 100644 --- a/src/meter_detection.h +++ b/src/meter_detection.h @@ -29,7 +29,6 @@ #define METER_DETECTION \ X(CCx01, MANUFACTURER_GSS, 0x02, 0x01) \ X(EURISII, MANUFACTURER_INE, 0x08, 0x55) \ - X(EVO868, MANUFACTURER_MAD, 0x07, 0x50) \ X(HYDROCALM3,MANUFACTURER_BMT, 0x0d, 0x0b) \ X(HYDRODIGIT,MANUFACTURER_BMT, 0x06, 0x13) \ X(HYDRODIGIT,MANUFACTURER_BMT, 0x07, 0x13) \ diff --git a/src/meter_evo868.cc b/src/meter_evo868.cc deleted file mode 100644 index c3b775d..0000000 --- a/src/meter_evo868.cc +++ /dev/null @@ -1,371 +0,0 @@ -/* - Copyright (C) 2017-2020 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"dvparser.h" -#include"meters.h" -#include"meters_common_implementation.h" -#include"wmbus.h" -#include"wmbus_utils.h" -#include"util.h" - -#include - -using namespace std; - -struct MeterEvo868 : public virtual MeterCommonImplementation { - MeterEvo868(MeterInfo &mi); - - // Total water counted through the meter - double totalWaterConsumption(Unit u); - bool hasTotalWaterConsumption(); - uint32_t error_flags_ {}; - string fabrication_no_; - double consumption_at_set_date_m3_ {}; - string set_date_; - double consumption_at_set_date_2_m3_ {}; - string set_date_2_; - double max_flow_since_datetime_m3h_ {}; - string max_flow_datetime_; - - double consumption_at_history_date_m3_[12]; - string history_date_[12]; - - string status(); - -private: - void processContent(Telegram *t); - - double total_water_consumption_m3_ {}; - string device_date_time_; -}; - -shared_ptr createEVO868(MeterInfo &mi) -{ - return shared_ptr(new MeterEvo868(mi)); -} - -MeterEvo868::MeterEvo868(MeterInfo &mi) : - MeterCommonImplementation(mi, "evo868") -{ - setMeterType(MeterType::WaterMeter); - - setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); - - addLinkMode(LinkMode::T1); - - addPrint("total", Quantity::Volume, - [&](Unit u){ return totalWaterConsumption(u); }, - "The total water consumption recorded by this meter.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("device_date_time", Quantity::Text, - [&](){ return device_date_time_; }, - "Device date time.", - PrintProperty::JSON); - - addPrint("current_status", Quantity::Text, - [&](){ return status(); }, - "Status of meter.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("fabrication_no", Quantity::Text, - [&](){ return fabrication_no_; }, - "Fabrication number.", - PrintProperty::JSON); - - addPrint("consumption_at_set_date", Quantity::Volume, - [&](Unit u){ assertQuantity(u, Quantity::Volume); return convert(consumption_at_set_date_m3_, Unit::M3, u); }, - "The total water consumption at the most recent billing period date.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("set_date", Quantity::Text, - [&](){ return set_date_; }, - "The most recent billing period date.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("consumption_at_set_date_2", Quantity::Volume, - [&](Unit u){ assertQuantity(u, Quantity::Volume); return convert(consumption_at_set_date_2_m3_, Unit::M3, u); }, - "The total water consumption at the second recent billing period date.", - PrintProperty::JSON); - - addPrint("set_date_2", Quantity::Text, - [&](){ return set_date_2_; }, - "The second recent billing period date.", - PrintProperty::JSON); - - addPrint("max_flow_since_datetime", Quantity::Flow, - [&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(max_flow_since_datetime_m3h_, Unit::M3H, u); }, - "Maximum water flow since date time.", - PrintProperty::JSON); - - addPrint("max_flow_datetime", Quantity::Text, - [&](){ return max_flow_datetime_; }, - "The datetime to which maximum flow is measured.", - PrintProperty::JSON); - - for (int i=1; i<=12; ++i) - { - string key = tostrprintf("consumption_at_history_%d", i); - string epl = tostrprintf("The total water consumption at the history date %d.", i); - - addPrint(key, Quantity::Volume, - [this,i](Unit u){ assertQuantity(u, Quantity::Volume); return convert(consumption_at_history_date_m3_[i-1], Unit::M3, u); }, - epl, - PrintProperty::JSON); - key = tostrprintf("history_%d_date", i); - epl = tostrprintf("The history date %d.", i); - - addPrint(key, Quantity::Text, - [this,i](){ return history_date_[i-1]; }, - epl, - PrintProperty::JSON); - } - -} - -void MeterEvo868::processContent(Telegram *t) -{ - /* -(evo868) 11: 04 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) 12: 13 vif (Volume l) -(evo868) 13: * 06070000 total consumption (1.798000 m3) - -(evo868) 17: 04 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) 18: 6D vif (Date and time type) -(evo868) 19: 1E31B121 - -(evo868) 1d: 04 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) 1e: FD vif (Second extension of VIF-codes) -(evo868) 1f: 17 vife (Error flags (binary)) -(evo868) 20: 00000000 - -(evo868) 24: 0E dif (12 digit BCD Instantaneous value) -(evo868) 25: 78 vif (Fabrication no) -(evo868) 26: 788004812000 - -(evo868) 2c: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) -(evo868) 2d: 13 vif (Volume l) -(evo868) 2e: C9040000 - -(evo868) 32: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1) -(evo868) 33: 6C vif (Date type G) -(evo868) 34: 9F2C - -(evo868) 36: 84 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) 37: 01 dife (subunit=0 tariff=0 storagenr=2) -(evo868) 38: 13 vif (Volume l) -(evo868) 39: C9040000 - -(evo868) 3d: 82 dif (16 Bit Integer/Binary Instantaneous value) -(evo868) 3e: 01 dife (subunit=0 tariff=0 storagenr=2) -(evo868) 3f: 6C vif (Date type G) -(evo868) 40: 9F2C - -(evo868) 42: D3 dif (24 Bit Integer/Binary Maximum value storagenr=1) -(evo868) 43: 01 dife (subunit=0 tariff=0 storagenr=3) -(evo868) 44: 3B vif (Volume flow l/h) -(evo868) 45: 9A0200 - -(evo868) 48: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) -(evo868) 49: 01 dife (subunit=0 tariff=0 storagenr=3) -(evo868) 4a: 6D vif (Date and time type) -(evo868) 4b: 0534A721 - -(evo868) 4f: 81 dif (8 Bit Integer/Binary Instantaneous value) -(evo868) 50: 04 dife (subunit=0 tariff=0 storagenr=8) -(evo868) 51: FD vif (Second extension of VIF-codes) -(evo868) 52: 28 vife (Storage interval month(s)) -(evo868) 53: 01 - -(evo868) 54: 82 dif (16 Bit Integer/Binary Instantaneous value) -(evo868) 55: 04 dife (subunit=0 tariff=0 storagenr=8) -(evo868) 56: 6C vif (Date type G) -(evo868) 57: 9F2C - -(evo868) 59: 84 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) 5a: 04 dife (subunit=0 tariff=0 storagenr=8) -(evo868) 5b: 13 vif (Volume l) -(evo868) 5c: C9040000 - -(evo868) 60: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) -(evo868) 61: 04 dife (subunit=0 tariff=0 storagenr=9) -(evo868) 62: 13 vif (Volume l) -(evo868) 63: 1B000000 - -(evo868) 67: 84 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) 68: 05 dife (subunit=0 tariff=0 storagenr=10) -(evo868) 69: 13 vif (Volume l) -(evo868) 6a: 00000000 - -(evo868) 6e: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) -(evo868) 6f: 05 dife (subunit=0 tariff=0 storagenr=11) -(evo868) 70: 13 vif (Volume l) -(evo868) 71: 00000000 - -(evo868) 75: 84 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) 76: 06 dife (subunit=0 tariff=0 storagenr=12) -(evo868) 77: 13 vif (Volume l) -(evo868) 78: 00000000 - -(evo868) 7c: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) -(evo868) 7d: 06 dife (subunit=0 tariff=0 storagenr=13) -(evo868) 7e: 13 vif (Volume l) -(evo868) 7f: 00000000 - -(evo868) 83: 84 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) 84: 07 dife (subunit=0 tariff=0 storagenr=14) -(evo868) 85: 13 vif (Volume l) -(evo868) 86: 00000000 - -(evo868) 8a: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) -(evo868) 8b: 07 dife (subunit=0 tariff=0 storagenr=15) -(evo868) 8c: 13 vif (Volume l) -(evo868) 8d: 00000000 - -(evo868) 91: 84 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) 92: 08 dife (subunit=0 tariff=0 storagenr=16) -(evo868) 93: 13 vif (Volume l) -(evo868) 94: 00000000 - -(evo868) 98: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) -(evo868) 99: 08 dife (subunit=0 tariff=0 storagenr=17) -(evo868) 9a: 13 vif (Volume l) -(evo868) 9b: 00000000 - -(evo868) 9f: 84 dif (32 Bit Integer/Binary Instantaneous value) -(evo868) a0: 09 dife (subunit=0 tariff=0 storagenr=18) -(evo868) a1: 13 vif (Volume l) -(evo868) a2: 00000000 - -(evo868) a6: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) -(evo868) a7: 09 dife (subunit=0 tariff=0 storagenr=19) -(evo868) a8: 13 vif (Volume l) -(evo868) a9: 00000000 - */ - int offset; - string key; - - if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_m3_); - t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 0, 0, &key, &t->dv_entries)) { - struct tm datetime; - extractDVdate(&t->dv_entries, key, &offset, &datetime); - device_date_time_ = strdatetime(&datetime); - t->addMoreExplanation(offset, " device datetime (%s)", device_date_time_.c_str()); - } - - extractDVuint32(&t->dv_entries, "04FD17", &offset, &error_flags_); - t->addMoreExplanation(offset, " error flags (%s)", status().c_str()); - - extractDVReadableString(&t->dv_entries, "0E78", &offset, &fabrication_no_); - t->addMoreExplanation(offset, " fabrication no (%s)", fabrication_no_.c_str()); - - if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_set_date_m3_); - t->addMoreExplanation(offset, " consumption at set date (%f m3)", consumption_at_set_date_m3_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 1, 0, &key, &t->dv_entries)) { - struct tm date; - extractDVdate(&t->dv_entries, key, &offset, &date); - set_date_ = strdate(&date); - t->addMoreExplanation(offset, " set date (%s)", set_date_.c_str()); - } - - if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 2, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_set_date_2_m3_); - t->addMoreExplanation(offset, " consumption at set date 2 (%f m3)", consumption_at_set_date_2_m3_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 2, 0, &key, &t->dv_entries)) { - struct tm date; - extractDVdate(&t->dv_entries, key, &offset, &date); - set_date_2_ = strdate(&date); - t->addMoreExplanation(offset, " set date 2 (%s)", set_date_2_.c_str()); - } - - if(findKey(MeasurementType::Maximum, VIFRange::VolumeFlow, 3, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &max_flow_since_datetime_m3h_); - t->addMoreExplanation(offset, " max flow (%f m3/h)", max_flow_since_datetime_m3h_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 3, 0, &key, &t->dv_entries)) { - struct tm datetime; - extractDVdate(&t->dv_entries, key, &offset, &datetime); - max_flow_datetime_ = strdatetime(&datetime); - t->addMoreExplanation(offset, " max flow since datetime (%s)", max_flow_datetime_.c_str()); - } - - uint8_t month_increment = 0; - extractDVuint8(&t->dv_entries, "8104FD28", &offset, &month_increment); - t->addMoreExplanation(offset, " month increment (%d)", month_increment); - - struct tm date; - if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 8, 0, &key, &t->dv_entries)) { - extractDVdate(&t->dv_entries, key, &offset, &date); - string start = strdate(&date); - t->addMoreExplanation(offset, " history starts with date (%s)", start.c_str()); - } - - // 12 months of historical data, starting in storage 8. - for (int i=1; i<=12; ++i) - { - if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, i+7, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_history_date_m3_[i-1]); - t->addMoreExplanation(offset, " consumption at history %d (%f m3)", i, consumption_at_history_date_m3_[i-1]); - struct tm d = date; - if (i>1) addMonths(&d, 1-i); - history_date_[i-1] = strdate(&d); - } - } -} - -double MeterEvo868::totalWaterConsumption(Unit u) -{ - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_m3_, Unit::M3, u); -} - -bool MeterEvo868::hasTotalWaterConsumption() -{ - return true; -} - -string MeterEvo868::status() -{ - if (error_flags_ == 0) - { - return "OK"; - } - - /* Possible errors according to datasheet: - overflow (threshold configurable, must be activated) - backflow (threshold set, configurable) - leak - meter blocked - non-used (days threshold set, configurable) - magnetic and mechanical tampering (removal) - */ - - // How do we decode these? - string info; - strprintf(&info, "ERROR bits %08x", error_flags_); - return info; -} diff --git a/src/meters.cc b/src/meters.cc index d332a3f..f806b1c 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -1871,7 +1871,8 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector found; + // Multiple dventries can be matched against a single wildcard FieldInfo. + map> founds; // Sort the dv_entries based on their offset in the telegram. // I.e. restore the ordering that was implicit in the telegram. @@ -1902,14 +1903,14 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t) { current_match_nr++; - if (fi.matcher().index_nr != IndexNr(current_match_nr)) + if (fi.matcher().index_nr != IndexNr(current_match_nr) && + !fi.matcher().expectedToMatchAgainstMultipleEntries()) { // This field info did match, but requires another index nr! // Increment the current index nr and look for the next match. } - else if (found.count(&fi) == 0) + else if (founds[&fi].count(dve) == 0 || fi.matcher().expectedToMatchAgainstMultipleEntries()) { - // This field_info has not been matched to a dv_entry before! debug("(meters) using field info %s(%s)[%d] to extract %s at offset %d\n", fi.vname().c_str(), toString(fi.xuantity()), @@ -1919,19 +1920,27 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t) dve->addFieldInfo(&fi); fi.performExtraction(this, t, dve); - found[&fi] = dve; + founds[&fi].insert(dve); } else { - DVEntry *old = found[&fi]; + if (isVerboseEnabled()) + { + set old = founds[&fi]; + string olds; + for (DVEntry *dve : old) + { + olds += to_string(dve->offset)+","; + } + olds.pop_back(); - verbose("(meter) while processing field extractors ignoring dventry %s at offset %d matching since " - "field %s was already matched against dventry %s at offset %d !\n", - dve->dif_vif_key.str().c_str(), - dve->offset, - fi.vname().c_str(), - old->dif_vif_key.str().c_str(), - old->offset); + verbose("(meter) while processing field extractors ignoring dventry %s at offset %d matching since " + "field %s was already matched against offsets %s !\n", + dve->dif_vif_key.str().c_str(), + dve->offset, + fi.vname().c_str(), + olds.c_str()); + } } } } @@ -1945,7 +1954,7 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t) { fi.performExtraction(this, t, NULL); } - else if (found.count(&fi) == 0 && fi.printProperties().hasJOINTPLSTATUS()) + else if (founds.count(&fi) == 0 && fi.printProperties().hasJOINTPLSTATUS()) { // This is a status field and it joins the tpl status but it also // has a potential dve match, which did not trigger. Now @@ -2144,15 +2153,22 @@ FieldInfo::FieldInfo(int index, string FieldInfo::renderJsonOnlyDefaultUnit(Meter *m) { - return renderJson(m, NULL); + return renderJson(m, NULL, NULL); } string FieldInfo::renderJsonText(Meter *m) { - return renderJson(m, NULL); + return renderJson(m, NULL, NULL); } -string FieldInfo::generateFieldName(DVEntry *dve) +string FieldInfo::generateFieldNameNoUnit(DVEntry *dve) +{ + if (!valid_field_name_) return "bad_field_name"; + + return field_name_->apply(dve); +} + +string FieldInfo::generateFieldNameWithUnit(DVEntry *dve) { if (!valid_field_name_) return "bad_field_name"; @@ -2167,12 +2183,14 @@ string FieldInfo::generateFieldName(DVEntry *dve) return var+"_"+default_unit; } -string FieldInfo::renderJson(Meter *m, vector *conversions) + +string FieldInfo::renderJson(Meter *m, DVEntry *dve, vector *conversions) { string s; string default_unit = unitToStringLowerCase(defaultUnit()); - string var = vname(); + string field_name = generateFieldNameNoUnit(dve); + if (xuantity() == Quantity::Text) { string v = m->getStringValue(this); @@ -2182,18 +2200,18 @@ string FieldInfo::renderJson(Meter *m, vector *conversions) // be translated into "something":null in the json, indicating that there is no value. // This should not be a problem for now. Lets deal with it when a meter decides to send "null" // as its version string for example. - s += "\""+var+"\":null"; + s += "\""+field_name+"\":null"; } else { // Normally the string values are quoted in json. TODO quote the value properly. // A well crafted meter could send a version string with " and break the json format. - s += "\""+var+"\":\""+v+"\""; + s += "\""+field_name+"\":\""+v+"\""; } } else { - s += "\""+var+"_"+default_unit+"\":"+valueToString(m->getNumericValue(this, defaultUnit()), defaultUnit()); + s += "\""+field_name+"_"+default_unit+"\":"+valueToString(m->getNumericValue(this, defaultUnit()), defaultUnit()); if (conversions != NULL) { @@ -2202,7 +2220,7 @@ string FieldInfo::renderJson(Meter *m, vector *conversions) { string unit = unitToStringLowerCase(u); // Appending extra conversion unit. - s += ",\""+var+"_"+unit+"\":"+valueToString(m->getNumericValue(this, u), u); + s += ",\""+field_name+"_"+unit+"\":"+valueToString(m->getNumericValue(this, u), u); } } } @@ -2259,7 +2277,7 @@ void MeterCommonImplementation::printMeter(Telegram *t, } // Iterate over the meter field infos... - map found; // Found from the telegram + map> founds; // Multiple dventries can match to a single field info. set found_vnames; for (FieldInfo& fi : field_infos_) @@ -2274,10 +2292,11 @@ void MeterCommonImplementation::printMeter(Telegram *t, // Has the entry been matches to this field, then print it as json. if (dve->hasFieldInfo(&fi)) { - assert(found.count(&fi) == 0); + assert(founds[&fi].count(dve) == 0); - found[&fi] = dve; - found_vnames.insert(fi.vname()); + founds[&fi].insert(dve); + string field_name = fi.generateFieldNameNoUnit(dve); + found_vnames.insert(field_name); } } } @@ -2287,17 +2306,20 @@ void MeterCommonImplementation::printMeter(Telegram *t, { if (fi.printProperties().hasJSON()) { - if (found.count(&fi) != 0) + if (founds.count(&fi) != 0) { - DVEntry *dve = found[&fi]; - debug("(meters) render field %s(%s %s)[%d] with dventry @%d key %s data %s\n", - fi.vname().c_str(), toString(fi.xuantity()), unitToStringLowerCase(fi.defaultUnit()).c_str(), fi.index(), - dve->offset, - dve->dif_vif_key.str().c_str(), - dve->value.c_str()); - string out = fi.renderJson(this, &conversions()); - debug("(meters) %s\n", out.c_str()); - s += indent+out+","+newline; + // This field info has matched against some dventries. + for (DVEntry *dve : founds[&fi]) + { + debug("(meters) render field %s(%s %s)[%d] with dventry @%d key %s data %s\n", + fi.vname().c_str(), toString(fi.xuantity()), unitToStringLowerCase(fi.defaultUnit()).c_str(), fi.index(), + dve->offset, + dve->dif_vif_key.str().c_str(), + dve->value.c_str()); + string out = fi.renderJson(this, dve, &conversions()); + debug("(meters) %s\n", out.c_str()); + s += indent+out+","+newline; + } } else { @@ -2314,7 +2336,7 @@ void MeterCommonImplementation::printMeter(Telegram *t, // Or if no value has been received, null. debug("(meters) render field %s(%s)[%d] without dventry\n", fi.vname().c_str(), toString(fi.xuantity()), fi.index()); - string out = fi.renderJson(this, &conversions()); + string out = fi.renderJson(this, NULL, &conversions()); debug("(meters) %s\n", out.c_str()); s += indent+out+","+newline; } @@ -2813,8 +2835,11 @@ bool FieldInfo::extractNumeric(Meter *m, Telegram *t, DVEntry *dve) assert(dve != NULL); assert(key == "" || dve->dif_vif_key.str() == key); - // Generate the json field name: - string field_name = generateFieldName(dve); + string field_name; + if (isDebugEnabled()) + { + field_name = generateFieldNameWithUnit(dve); + } double extracted_double_value = NAN; if (dve->extractDouble(&extracted_double_value, @@ -2848,7 +2873,7 @@ bool FieldInfo::extractNumeric(Meter *m, Telegram *t, DVEntry *dve) extracted_double_value); } m->setNumericValue(this, default_unit_, convert(extracted_double_value, decoded_unit, default_unit_)); - t->addMoreExplanation(dve->offset, renderJson(m, &m->conversions())); + t->addMoreExplanation(dve->offset, renderJson(m, dve, &m->conversions())); found = true; } return found; @@ -2939,7 +2964,7 @@ bool FieldInfo::extractString(Meter *m, Telegram *t, DVEntry *dve) assert(key == "" || dve->dif_vif_key.str() == key); // Generate the json field name: - string field_name = generateFieldName(dve); + string field_name = generateFieldNameNoUnit(dve); uint64_t extracted_bits {}; if (lookup_.hasLookups() || (print_properties_.hasJOINTPLSTATUS())) diff --git a/src/meters.h b/src/meters.h index 914f8b2..3dd7214 100644 --- a/src/meters.h +++ b/src/meters.h @@ -62,7 +62,6 @@ LIST_OF_METER_TYPES X(auto, 0, AutoMeter, AUTO, Auto) \ X(unknown, 0, UnknownMeter, UNKNOWN, Unknown) \ X(eurisii, T1_bit, HeatCostAllocationMeter, EURISII, EurisII) \ - X(evo868, T1_bit, WaterMeter, EVO868, EVO868) \ X(gransystems,T1_bit, ElectricityMeter, CCx01, CCx01) \ X(hydrocalm3, T1_bit, HeatMeter, HYDROCALM3, HydrocalM3) \ X(hydrodigit, T1_bit, WaterMeter, HYDRODIGIT, Hydrodigit) \ @@ -350,13 +349,14 @@ struct FieldInfo void performCalculation(Meter *m); string renderJsonOnlyDefaultUnit(Meter *m); - string renderJson(Meter *m, vector *additional_conversions); + string renderJson(Meter *m, DVEntry *dve, vector *additional_conversions); string renderJsonText(Meter *m); // Render the field name based on the actual field from the telegram. // A FieldInfo can be declared to handle any number of storage fields of a certain range. // The vname is then a pattern total_at_month_{storage_counter} that gets translated into // total_at_month_2 (for the dventry with storage nr 2.) - string generateFieldName(DVEntry *dve); + string generateFieldNameWithUnit(DVEntry *dve); + string generateFieldNameNoUnit(DVEntry *dve); // Check if the meter object stores a value for this field. bool hasValue(Meter *m); diff --git a/src/testinternals.cc b/src/testinternals.cc index 0ed6a79..8bb51e5 100644 --- a/src/testinternals.cc +++ b/src/testinternals.cc @@ -1790,6 +1790,9 @@ LIST_OF_QUANTITIES test_si_convert(1.0, 60.0, Unit::Minute, "min", Unit::Second, "s", Quantity::Time, &from_set, &to_set); // 1 day is 1/365.2425 year test_si_convert(1.0, 1.0/365.2425, Unit::Day, "d", Unit::Year, "y", Quantity::Time, &from_set, &to_set); + // 1 month is 30.437 days + test_si_convert(2.0, 30.437*2, Unit::Month, "month", Unit::Day, "d", Quantity::Time, &from_set, &to_set); + test_si_convert(30.437*2, 2.0, Unit::Day, "d", Unit::Month, "month", Quantity::Time, &from_set, &to_set); // 100 hours is 100/24 days. test_si_convert(100.0, 100.0/24.0, Unit::Hour, "h", Unit::Day, "d", Quantity::Time, &from_set, &to_set); // 1 year is 365.2425 days. diff --git a/src/units.cc b/src/units.cc index 750d6ad..7c81377 100644 --- a/src/units.cc +++ b/src/units.cc @@ -93,6 +93,7 @@ using namespace std; X(Minute, 60.0, SIExp().s(1)) \ X(Hour, 3600.0, SIExp().s(1)) \ X(Day, 3600.0*24, SIExp().s(1)) \ + X(Month, 3600.0*24*30.437, SIExp().s(1)) \ X(Year, 3600.0*24*365.2425, SIExp().s(1)) \ X(DateTimeUT, 1.0, SIExp().s(1)) \ X(DateTimeUTC, 1.0, SIExp().s(1)) \ diff --git a/src/units.h b/src/units.h index 2768faf..1c8a61b 100644 --- a/src/units.h +++ b/src/units.h @@ -102,6 +102,7 @@ LIST_OF_QUANTITIES X(Minute,min,"min",Time,"minute") \ X(Hour,h,"h",Time,"hour") \ X(Day,d,"d",Time,"day") \ + X(Month,month,"month",Time,"month") \ X(Year,y,"y",Time,"year") \ X(DateTimeUT,ut,"ut",PointInTime,"unix timestamp") \ X(DateTimeUTC,utc,"utc",PointInTime,"coordinated universal time") \ diff --git a/test.sh b/test.sh index eb83096..61c3463 100755 --- a/test.sh +++ b/test.sh @@ -9,6 +9,12 @@ then exit 1 fi +if ! command -v jq > /dev/null +then + echo "You have to install jq !" + exit 1 +fi + $TESTINTERNAL if [ "$?" = "0" ]; then echo OK: test internals diff --git a/tests/test_anyid.sh b/tests/test_anyid.sh index 670d5be..88c2891 100755 --- a/tests/test_anyid.sh +++ b/tests/test_anyid.sh @@ -8,15 +8,15 @@ TEST=testoutput TESTNAME="Test ANYID" TESTRESULT="ERROR" -cat > $TEST/test_expected.txt < $TEST/test_expected.txt {"media":"cold water","meter":"multical21","name":"Vatten","id":"76348799","status":"DRY","total_m3":6.408,"target_m3":6.408,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"} EOF $PROG --format=json 2A442D2C998734761B168D2091D37CAC21576C7802FF207100041308190000441308190000615B7F616713 \ - Vatten auto ANYID NOKEY > $TEST/test_output.txt 2>&1 + Vatten auto ANYID NOKEY 2>&1 | 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 + cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt diff $TEST/test_expected.txt $TEST/test_responses.txt if [ "$?" = "0" ] then diff --git a/tests/test_c1_meters.sh b/tests/test_c1_meters.sh index ace6536..39624eb 100755 --- a/tests/test_c1_meters.sh +++ b/tests/test_c1_meters.sh @@ -9,7 +9,7 @@ TEST=testoutput TESTNAME="Test C1 meters" TESTRESULT="ERROR" -cat simulations/simulation_c1.txt | grep '^{' > $TEST/test_expected.txt +cat simulations/simulation_c1.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt $PROG --format=json simulations/simulation_c1.txt \ MyHeater multical302 67676767 NOKEY \ MyHeaterMj multical302 46464646 NOKEY \ @@ -27,11 +27,11 @@ $PROG --format=json simulations/simulation_c1.txt \ Smokey ei6500 00012811 NOKEY \ Vatten weh_07 86868686 NOKEY \ Mino minomess 55036410 NOKEY \ - > $TEST/test_output.txt 2> $TEST/test_stderr.txt + | jq --sort-keys . > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then - cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt + cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt diff $TEST/test_expected.txt $TEST/test_responses.txt if [ "$?" = "0" ] then diff --git a/tests/test_drivers.sh b/tests/test_drivers.sh index 068b36a..7d21617 100755 --- a/tests/test_drivers.sh +++ b/tests/test_drivers.sh @@ -41,8 +41,6 @@ do echo OK json: $TESTNAME TESTRESULT="OK" else - jq --sort-keys . $TEST/test_expected_json.txt - jq --sort-keys . $TEST/test_response_json.txt TESTRESULT="ERROR" fi else diff --git a/tests/test_list_envs.sh b/tests/test_list_envs.sh index 559eca8..a8d4bba 100755 --- a/tests/test_list_envs.sh +++ b/tests/test_list_envs.sh @@ -8,9 +8,9 @@ TEST=testoutput TESTNAME="Test list-envs and list-fields" TESTRESULT="ERROR" -$PROG --listenvs=amiplus > $TEST/test_output.txt 2>&1 +$PROG --listenvs=amiplus | sort > $TEST/test_output.txt 2>&1 -cat < $TEST/test_expected.txt +cat < $TEST/test_expected.txt METER_JSON METER_ID METER_NAME @@ -54,9 +54,9 @@ then exit 1 fi -$PROG --listfields=amiplus > $TEST/test_output.txt 2>&1 +$PROG --listfields=amiplus | sort > $TEST/test_output.txt 2>&1 -cat < $TEST/test_expected.txt +cat < $TEST/test_expected.txt id The meter id number. name Your name for the meter. media What does the meter measure? diff --git a/tests/test_listen_to_all.sh b/tests/test_listen_to_all.sh index 3ad532c..12cc13d 100755 --- a/tests/test_listen_to_all.sh +++ b/tests/test_listen_to_all.sh @@ -255,11 +255,6 @@ Received telegram from: 66336633 type: Heat meter (0x43) ver: 0x39 driver: compact5 -Received telegram from: 79787776 - manufacturer: (MAD) Maddalena, Italy (0x3424) - type: Water meter (0x07) - ver: 0x50 - driver: evo868 Received telegram from: 18046178 manufacturer: (GSS) R D Gran System S, Belarus (0x1e73) type: Electricity meter (0x02) diff --git a/tests/test_mbus.sh b/tests/test_mbus.sh index 7a954f2..d2bd7a4 100755 --- a/tests/test_mbus.sh +++ b/tests/test_mbus.sh @@ -9,15 +9,15 @@ TEST=testoutput TESTNAME="Test mbus" TESTRESULT="ERROR" -cat simulations/simulation_mbus.txt | grep '^{' > $TEST/test_expected.txt +cat simulations/simulation_mbus.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt $PROG --format=json simulations/simulation_mbus.txt \ MyUltra ultraheat 70444600 NOKEY \ MySenso sensostar 10484075 NOKEY \ - > $TEST/test_output.txt 2> $TEST/test_stderr.txt + 2> $TEST/test_stderr.txt | 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 + cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt diff $TEST/test_expected.txt $TEST/test_responses.txt if [ "$?" = "0" ] then diff --git a/tests/test_s1_meters.sh b/tests/test_s1_meters.sh index 5aa719f..d375917 100755 --- a/tests/test_s1_meters.sh +++ b/tests/test_s1_meters.sh @@ -14,11 +14,11 @@ METERS="HCA auto 91835132 NOKEY \ QWW auto 11121314 NOKEY \ QWWW auto 12417708 NOKEY" -cat simulations/simulation_s1.txt | grep '^{' > $TEST/test_expected.txt -$PROG --format=json simulations/simulation_s1.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt +cat simulations/simulation_s1.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt +$PROG --format=json simulations/simulation_s1.txt $METERS 2> $TEST/test_stderr.txt | 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 + cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt diff $TEST/test_expected.txt $TEST/test_responses.txt if [ "$?" = "0" ] then diff --git a/tests/test_t1_meters.sh b/tests/test_t1_meters.sh index 55483b0..a771f27 100755 --- a/tests/test_t1_meters.sh +++ b/tests/test_t1_meters.sh @@ -24,7 +24,6 @@ METERS="MyWarmWater supercom587 12345678 NOKEY Gran101 gransystems 18046178 NOKEY Gran301 gransystems 20100117 NOKEY HeatMeter eurisii 88018801 NOKEY - Votchka evo868 79787776 NOKEY Smokeo lansensm 00010204 NOKEY Tempoo lansenth 00010203 NOKEY Dooro lansendw 00010205 NOKEY @@ -70,11 +69,11 @@ METERS="MyWarmWater supercom587 12345678 NOKEY QSmokep qsmoke 48128850 NOKEY" -cat simulations/simulation_t1.txt | grep '^{' > $TEST/test_expected.txt -$PROG --format=json simulations/simulation_t1.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt +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 if [ "$?" = "0" ] then - cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt + cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt diff $TEST/test_expected.txt $TEST/test_responses.txt if [ "$?" = "0" ] then