From 26db03ffaaa71f556b19c6c32cc93846366c2279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Thu, 29 Dec 2022 23:48:58 +0100 Subject: [PATCH] Fix bugs in sharky774 and add cooling version of meter. --- CHANGES | 13 +- simulations/simulation_t1.txt | 4 +- src/driver_sharky774.cc | 336 ++++++++++++++++++---------------- src/meters.cc | 6 +- src/util.cc | 13 +- 5 files changed, 200 insertions(+), 172 deletions(-) diff --git a/CHANGES b/CHANGES index d00840c..55e4d0d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,16 @@ -A bug in the hydrocalm3 driver was found. When total_cooling_kwh +ATTENTION! The sharky774 driver had a bug that triggered when the meter used Joules instead of kWh. +Also the calculated temperature difference had to be removed. You can re-add the temperature difference +by using --calculate_temperature_difference_c=flow_temperature_c-return_temperature_c + +Since a field disappeared the default fields were disrupted and a shorter list of fields +have now been picked for the default fields. Please use --selectedfields=... to recreate +your previous fields. + +Also the field operating_time_h now prints the proper operating time and +the new field operating_time_in_error_h prints the time when in error. + +ATTENTION! A bug in the hydrocalm3 driver was found. When total_cooling_kwh and other similar values were used as text fields (not a json fields) they were replaced with dates instead. This was due to a bug in the new code handling Quantity::PointInTime. diff --git a/simulations/simulation_t1.txt b/simulations/simulation_t1.txt index 5192207..12d7b0a 100644 --- a/simulations/simulation_t1.txt +++ b/simulations/simulation_t1.txt @@ -250,8 +250,8 @@ telegram=||9e44731e17011020010278046d0813bc2104030000000084100300000000842003000 # Test Hydrometer/Diehl Metering Sharky 774 heat meter telegram=|5E44A5112751617241047A8B0050052F2F0C0E000000000C13010000000B3B0000000C2B000000000A5A26020A5E18020B260321000AA6180000C2026CBE2BCC020E00000000CC021301000000DB023B000000DC022B000000002F2F2F2F2F| -{"media":"heat","meter":"sharky774","name":"Sharky774","id":"72615127","total_energy_consumption_kwh":0,"total_volume_m3":0.001,"volume_flow_m3h":0,"power_kw":0,"flow_temperature_c":22.6,"return_temperature_c":21.8,"temperature_difference_c":0.8,"operating_time_h":0,"energy_at_set_date_kwh":0,"set_date":"","timestamp":"1111-11-11T11:11:11Z"} -|Sharky774;72615127;0.000000;0.001000;0.000000;0.000000;22.600000;21.800000;0.800000;0.000000;1111-11-11 11:11.11 +{"media":"heat","meter":"sharky774","name":"Sharky774","id":"72615127","total_energy_consumption_kwh":0,"total_volume_m3":0.001,"volume_flow_m3h":0,"power_kw":0,"flow_temperature_c":22.6,"return_temperature_c":21.8,"operating_time_h":2103,"operating_time_in_error_h":0,"timestamp":"1111-11-11T11:11:11Z"} +|Sharky774;72615127;0;null;null;1111-11-11 11:11.11 # Test Hydrometer/Diehl Metering Sharky 775 heat meter telegram=|534424232004256092687A370045752235854DEEEA5939FAD81C25FEEF5A23C38FB9168493C563F08DB10BAF87F660FBA91296BA2397E8F4220B86D3A192FB51E0BFCF24DCE72118E0C75A9E89F43BDFE370824B| diff --git a/src/driver_sharky774.cc b/src/driver_sharky774.cc index 9132b71..bf921b5 100644 --- a/src/driver_sharky774.cc +++ b/src/driver_sharky774.cc @@ -18,190 +18,202 @@ #include"meters_common_implementation.h" -struct MeterSharky774 : public virtual MeterCommonImplementation +namespace { - MeterSharky774(MeterInfo &mi, DriverInfo &di); + struct Driver : public virtual MeterCommonImplementation + { + Driver(MeterInfo &mi, DriverInfo &di); + }; -private: + static bool ok = registerDriver([](DriverInfo&di) + { + di.setName("sharky774"); + di.setDefaultFields("name,id," + "total_energy_consumption_kwh," + "energy_at_set_date_kwh," + "set_date," + "timestamp"); + di.setMeterType(MeterType::HeatMeter); + di.addLinkMode(LinkMode::T1); + di.addDetection(MANUFACTURER_DME, 0x04, 0x41); + di.addDetection(MANUFACTURER_DME, 0x0d, 0x41); + di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); + }); - double total_energy_consumption_kwh_ {}; - double total_volume_m3_ {}; - double volume_flow_m3h_ {}; - double power_kw_ {}; - double flow_temperature_c_ {}; - double return_temperature_c_ {}; - double energy_at_set_date_kwh_ {}; - double operating_time_h_ {}; - string set_date_txt_; -}; + Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) + { + addNumericFieldWithExtractor( + "total_energy_consumption", + "The total energy consumption recorded by this meter.", + PrintProperty::JSON | PrintProperty::IMPORTANT, + Quantity::Energy, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::AnyEnergyVIF) + ); -static bool ok = registerDriver([](DriverInfo&di) -{ - di.setName("sharky774"); - di.setMeterType(MeterType::HeatMeter); - di.addLinkMode(LinkMode::T1); - di.addDetection(MANUFACTURER_DME, 0x04, 0x41); - di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new MeterSharky774(mi, di)); }); -}); + addNumericFieldWithExtractor( + "total_cooling_consumption", + "The total cooling energy consumption recorded by this meter.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Energy, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::AnyEnergyVIF) + .set(TariffNr(1)) + ); -MeterSharky774::MeterSharky774(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) -{ - addNumericFieldWithExtractor( - "total_energy_consumption", - Quantity::Energy, - NoDifVifKey, - VifScaling::Auto, - MeasurementType::Instantaneous, - VIFRange::AnyEnergyVIF, - StorageNr(0), - TariffNr(0), - IndexNr(1), - PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT, - "The total energy consumption recorded by this meter.", - SET_FUNC(total_energy_consumption_kwh_, Unit::KWH), - GET_FUNC(total_energy_consumption_kwh_, Unit::MJ)); + addNumericFieldWithExtractor( + "total_volume", + "The total volume recorded by this meter.", + PrintProperty::JSON, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::AnyVolumeVIF) + ); - addNumericFieldWithExtractor( - "total_volume", - Quantity::Volume, - NoDifVifKey, - VifScaling::Auto, - MeasurementType::Instantaneous, - VIFRange::AnyVolumeVIF, - StorageNr(0), - TariffNr(0), - IndexNr(1), - PrintProperty::JSON | PrintProperty::FIELD, - "The total volume recorded by this meter.", - SET_FUNC(total_volume_m3_, Unit::M3), - GET_FUNC(total_volume_m3_, Unit::M3)); + addNumericFieldWithExtractor( + "total_cooling_volume", + "The total cooling volume recorded by this meter.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::AnyVolumeVIF) + .set(TariffNr(2)) + ); - addNumericFieldWithExtractor( - "volume_flow", - Quantity::Flow, - NoDifVifKey, - VifScaling::Auto, - MeasurementType::Instantaneous, - VIFRange::VolumeFlow, - StorageNr(0), - TariffNr(0), - IndexNr(1), - PrintProperty::JSON | PrintProperty::FIELD, - "The current flow.", - SET_FUNC(volume_flow_m3h_, Unit::M3H), - GET_FUNC(volume_flow_m3h_, Unit::M3H)); + addNumericFieldWithExtractor( + "volume_flow", + "The current flow.", + PrintProperty::JSON, + Quantity::Flow, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::VolumeFlow) + ); - addNumericFieldWithExtractor( - "power", - Quantity::Power, - NoDifVifKey, - VifScaling::AutoSigned, - MeasurementType::Instantaneous, - VIFRange::AnyPowerVIF, - StorageNr(0), - TariffNr(0), - IndexNr(1), - PrintProperty::JSON | PrintProperty::FIELD, - "The power.", - SET_FUNC(power_kw_, Unit::KW), - GET_FUNC(power_kw_, Unit::KW)); + addNumericFieldWithExtractor( + "power", + "The power.", + PrintProperty::JSON, + Quantity::Power, + VifScaling::AutoSigned, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::AnyPowerVIF) + ); - addNumericFieldWithExtractor( - "flow_temperature", - Quantity::Temperature, - NoDifVifKey, - VifScaling::Auto, - MeasurementType::Instantaneous, - VIFRange::FlowTemperature, - StorageNr(0), - TariffNr(0), - IndexNr(1), - PrintProperty::JSON | PrintProperty::FIELD, - "The flow temperature.", - SET_FUNC(flow_temperature_c_, Unit::C), - GET_FUNC(flow_temperature_c_, Unit::C)); + addNumericFieldWithExtractor( + "flow_temperature", + "The flow temperature.", + PrintProperty::JSON, + Quantity::Temperature, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::FlowTemperature) + ); - addNumericFieldWithExtractor( - "return_temperature", - Quantity::Temperature, - NoDifVifKey, - VifScaling::Auto, - MeasurementType::Instantaneous, - VIFRange::ReturnTemperature, - StorageNr(0), - TariffNr(0), - IndexNr(1), - PrintProperty::JSON | PrintProperty::FIELD, - "The return temperature.", - SET_FUNC(return_temperature_c_, Unit::C), - GET_FUNC(return_temperature_c_, Unit::C)); + addNumericFieldWithExtractor( + "return_temperature", + "The return temperature.", + PrintProperty::JSON, + Quantity::Temperature, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::ReturnTemperature) + ); - addNumericField("temperature_difference", - Quantity::Temperature, - PrintProperty::JSON | PrintProperty::FIELD, - "The temperature difference.", - [](Unit u, double v) {}, - [this](Unit u) { return flow_temperature_c_ - return_temperature_c_; }); + addNumericFieldWithExtractor( + "operating_time", + "How long the meter has been collecting data.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Time, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::OperatingTime) + ); - addNumericFieldWithExtractor( - "operating_time", - Quantity::Time, - DifVifKey("0AA618"), - VifScaling::Auto, - MeasurementType::Instantaneous, - VIFRange::None, - StorageNr(0), - TariffNr(0), - IndexNr(1), - PrintProperty::JSON, - "The operating time.", - SET_FUNC(operating_time_h_, Unit::Hour), - GET_FUNC(operating_time_h_, Unit::Hour)); + addNumericFieldWithExtractor( + "operating_time_in_error", + "How long the meter has been in an error state and not collected data.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Time, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::OperatingTime) + .add(VIFCombinable::RecordErrorCodeMeterToController) + ); - addNumericFieldWithExtractor( - "energy_at_set_date", - Quantity::Energy, - NoDifVifKey, - VifScaling::Auto, - MeasurementType::Instantaneous, - VIFRange::AnyEnergyVIF, - StorageNr(1), - TariffNr(0), - IndexNr(1), - PrintProperty::JSON | PrintProperty::FIELD, - "The total energy consumption recorded by this meter at the set date.", - SET_FUNC(energy_at_set_date_kwh_, Unit::KWH), - GET_FUNC(energy_at_set_date_kwh_, Unit::KWH)); + addNumericFieldWithExtractor( + "energy_at_set_date", + "The total energy consumption recorded by this meter at the set date.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Energy, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::AnyEnergyVIF) + .set(StorageNr(1)) + ); - addStringFieldWithExtractor( - "set_date", - Quantity::Text, - NoDifVifKey, - MeasurementType::Instantaneous, - VIFRange::Date, - StorageNr(1), - TariffNr(0), - IndexNr(1), - PrintProperty::JSON, - "The last billing set date.", - SET_STRING_FUNC(set_date_txt_), - GET_STRING_FUNC(set_date_txt_)); + addNumericFieldWithExtractor( + "cooling_at_set_date", + "The total cooling energy consumption recorded by this meter at the set date.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Energy, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::AnyEnergyVIF) + .set(StorageNr(1)) + .set(TariffNr(1)) + ); + addNumericFieldWithExtractor( + "set", + "The last billing set date.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::PointInTime, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Date) + .set(StorageNr(1)), + Unit::DateLT + ); + } } // Test: Heato sharky774 58496405 NOKEY -// telegram=3E44A5110564495841047A700030052F2F#0C06846800000C13195364000B3B0400000C2B110100000A5A17050A5E76020AA61800004C0647630000426CBF25 -// {"media":"heat","meter":"sharky774","name":"Heato","id":"58496405","total_energy_consumption_kwh":1912.222222,"total_volume_m3":645.319,"volume_flow_m3h":0.004,"power_kw":0.111,"flow_temperature_c":51.7,"return_temperature_c":27.6,"temperature_difference_c":24.1,"operating_time_h":0,"energy_at_set_date_kwh":6347,"set_date":"2021-05-31","timestamp":"1111-11-11T11:11:11Z"} -// |Heato;58496405;1912.222222;645.319000;0.004000;0.111000;51.700000;27.600000;24.100000;6347.000000;1111-11-11 11:11.11 +// telegram=3E44A5110564495841047A700030052F2F_0C06846800000C13195364000B3B0400000C2B110100000A5A17050A5E76020AA61800004C0647630000426CBF25 +// {"media":"heat","meter":"sharky774","name":"Heato","id":"58496405","total_energy_consumption_kwh":6884,"total_volume_m3":645.319,"volume_flow_m3h":0.004,"power_kw":0.111,"flow_temperature_c":51.7,"return_temperature_c":27.6,"operating_time_in_error_h":0,"energy_at_set_date_kwh":6347,"set_date":"2021-05-31","timestamp":"1111-11-11T11:11:11Z"} +// |Heato;58496405;6884;6347;2021-05-31;1111-11-11 11:11.11 // This test telegram has more historical data! // Test: Heatoo sharky774 72615127 NOKEY // telegram=|5E44A5112751617241047A8B0050052F2F0C0E000000000C13010000000B3B0000000C2B000000000A5A26020A5E18020B260321000AA6180000C2026CBE2BCC020E00000000CC021301000000DB023B000000DC022B000000002F2F2F2F2F| -// {"media":"heat","meter":"sharky774","name":"Heatoo","id":"72615127","total_energy_consumption_kwh":0,"total_volume_m3":0.001,"volume_flow_m3h":0,"power_kw":0,"flow_temperature_c":22.6,"return_temperature_c":21.8,"temperature_difference_c":0.8,"operating_time_h":0,"energy_at_set_date_kwh":0,"set_date":"","timestamp":"1111-11-11T11:11:11Z"} -// |Heatoo;72615127;0.000000;0.001000;0.000000;0.000000;22.600000;21.800000;0.800000;0.000000;1111-11-11 11:11.11 +// {"media":"heat","meter":"sharky774","name":"Heatoo","id":"72615127","total_energy_consumption_kwh":0,"total_volume_m3":0.001,"volume_flow_m3h":0,"power_kw":0,"flow_temperature_c":22.6,"return_temperature_c":21.8,"operating_time_h":2103,"operating_time_in_error_h":0,"timestamp":"1111-11-11T11:11:11Z"} +// |Heatoo;72615127;0;null;null;1111-11-11 11:11.11 // This telegram contains a negative power value encoded in bcd. // Test: Heatooo sharky774 61243590 NOKEY -// telegram=3E44A5119035246141047A1A0030052F2F#0C06026301000C13688609040B3B0802000C2B220000F00A5A71020A5E72020AA61800004C0636370100426CBF25 -// {"media":"heat","meter":"sharky774","name":"Heatooo","id":"61243590","total_energy_consumption_kwh":4528.333333,"total_volume_m3":4098.668,"volume_flow_m3h":0.208,"power_kw":-0.022,"flow_temperature_c":27.1,"return_temperature_c":27.2,"temperature_difference_c":-0.1,"operating_time_h":0,"energy_at_set_date_kwh":13736,"set_date":"2021-05-31","timestamp":"1111-11-11T11:11:11Z"} -// |Heatooo;61243590;4528.333333;4098.668000;0.208000;-0.022000;27.100000;27.200000;-0.100000;13736.000000;1111-11-11 11:11.11 +// telegram=3E44A5119035246141047A1A0030052F2F_0C06026301000C13688609040B3B0802000C2B220000F00A5A71020A5E72020AA61800004C0636370100426CBF25 +// {"media":"heat","meter":"sharky774","name":"Heatooo","id":"61243590","total_energy_consumption_kwh":16302,"total_volume_m3":4098.668,"volume_flow_m3h":0.208,"power_kw":-0.022,"flow_temperature_c":27.1,"return_temperature_c":27.2,"operating_time_in_error_h":0,"energy_at_set_date_kwh":13736,"set_date":"2021-05-31","timestamp":"1111-11-11T11:11:11Z"} +// |Heatooo;61243590;16302;13736;2021-05-31;1111-11-11 11:11.11 + +// This telegram contains cooling data as well. +// Test: Coolo sharky774 71942539 NOKEY +// telegram=5E44A51139259471410D7A720050052F2F_0C06742400008C1006000000000C13823522008C2013494400000B3B0000000C2B000000000A5A22030A5E91020AA61800004C0619130000CC100600000000426CDF252F2F2F2F2F2F2F2F2F2F2F +// {"cooling_at_set_date_kwh": 0,"energy_at_set_date_kwh": 1319,"flow_temperature_c": 32.2,"id": "71942539","media": "heat/cooling load","meter": "sharky774","name": "Coolo","operating_time_in_error_h": 0,"power_kw": 0,"return_temperature_c": 29.1,"set_date": "2022-05-31","timestamp": "1111-11-11T11:11:11Z","total_cooling_consumption_kwh": 0,"total_cooling_volume_m3": 4.449,"total_energy_consumption_kwh": 2474,"total_volume_m3": 223.582,"volume_flow_m3h": 0} +// |Coolo;71942539;2474;1319;2022-05-31;1111-11-11 11:11.11 diff --git a/src/meters.cc b/src/meters.cc index 4b7e025..e36b07b 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -1172,13 +1172,15 @@ bool checkPrintableField(string *buf, string desired_field_name, Meter *m, Teleg // We have the correc field. if (fi.displayUnit() == Unit::DateLT) { - *buf += strdate(m->getNumericValue(&fi, Unit::DateLT)); + double d = m->getNumericValue(&fi, Unit::DateLT); + *buf += strdate(d); *buf += c; return true; } else if (fi.displayUnit() == Unit::DateTimeLT) { - *buf += strdatetime(m->getNumericValue(&fi, Unit::DateTimeLT)); + double d = m->getNumericValue(&fi, Unit::DateTimeLT); + *buf += strdatetime(d); *buf += c; return true; } diff --git a/src/util.cc b/src/util.cc index f22ca39..6ee2896 100644 --- a/src/util.cc +++ b/src/util.cc @@ -22,23 +22,24 @@ #include #include #include +#include +#include #include #include #include +#include #include #include #include #include #include #include -#include -#include #include #include -#include -#include #include -#include +#include +#include +#include #if defined(__APPLE__) && defined(__MACH__) #include @@ -1364,6 +1365,7 @@ string strdate(struct tm *date) string strdate(double v) { + if (isnan(v)) return "null"; struct tm date; time_t t = v; localtime_r(&t, &date); @@ -1379,6 +1381,7 @@ string strdatetime(struct tm *datetime) string strdatetime(double v) { + if (isnan(v)) return "null"; struct tm datetime; time_t t = v; localtime_r(&t, &datetime);