All drivers have been rewritten!

pull/708/head
Fredrik Öhrström 2022-11-28 00:03:12 +01:00
rodzic 073aafd31c
commit 9ce4328d24
26 zmienionych plików z 199 dodań i 1761 usunięć

25
CHANGES
Wyświetl plik

@ -1,14 +1,35 @@
╭─────────────────────────────────────────────────────────────────╮
│ │
│ Important change! The old style │
│ of writing drivers is now gone! BUT BUT BUT │
│ the final set of drivers to be converted were │
│ the multical 302/303/403/602/603/803 meters that │
│ were merged into a single hamheat driver. It was not │
│ possible to keep all of them backwards compatible! │
│ │
│ Note that the default fields for these meters are changed! │
│ Please use --selectfields to resolve this. │
│ │
│ I.e. It is a very good idea to check your multical heat │
│ meter values when you upgrade to wmbusmeters 2.0 ! │
│ │
╰─────────────────────────────────────────────────────────────────╯
Important change! The feature --addconversions=GJ has been removed!!! But there is a replacement!
It was used to add conversions to all energy values into gj, eg you have
total_kwh and add total_gj.
This feature was too broad and the conversion happened too late in the processing code.
You can now achieved the same thing but you have to specify each field you want to convert
The addconversion feature was too broad and the conversion happened too late in the processing code.
You can now achieve the same thing but you have to specify each field you want to convert
using the calculation feature.
E.g. --calculate_total_gj=total_kwh
ATTENTION! The multical 302/303/403/602/603/803 heat meter drivers have been merged into a single kamheat
driver. Not all values are backwards compatible! I.e. 302 is very changed, less so for the others.
The default fields have changed!
ATTENTION! The gransystems driver has been refactored to the new driver format.
The field currrent_at_phase_1_a has been renamed to current_at_phase_1_a.
The info strings in the "status" field now have underscores instead of spaces within a single string.

Wyświetl plik

@ -1,8 +1,4 @@
telegram=||33446850165706039480A20F9E28F2003018370011EA07D5070018001800160900000000000000000000000009190A22322C1215|
telegram=||294468501657060394087A4C001025A11E4EFF816C2F8D147ED0C1F959F55E0F0FF724805FCC33D19A19|
telegram=||33446850165706039480A20F9E28F2003018370011EA07D5070018001800160900000000000000000000000009190A22322C1215|
telegram=||294468501657060394087A4C001025A11E4EFF816C2F8D147ED0C1F959F55E0F0FF724805FCC33D19A19|
telegram=||33446850165706039480A20F9E28F2003018370011EA07D5070018001800160900000000000000000000000009190A22322C1215|
telegram=||294468501657060394087A4C001025A11E4EFF816C2F8D147ED0C1F959F55E0F0FF724805FCC33D19A19|
telegram=||33446850165706039480A20F9E28F2003018370011EA07D5070018001800160900000000000000000000000009190A22322C1215|
telegram=||294468501657060394087A4C001025A11E4EFF816C2F8D147ED0C1F959F55E0F0FF724805FCC33D19A19|

Wyświetl plik

@ -19,24 +19,6 @@ telegram=|21442D2C776655441B168D2079CC8C3A20_F4307912C40DFF00002F4E00003D010203|
telegram=|4D44372C525252523A168D203894DF7920F93278_04FF23000000000413AEAC0000441364A80000426C812A023B000092013BEF01A2013B000006FF1B067000097000A1015B0C91015B14A1016713|
{"media":"cold water","meter":"flowiq2200","name":"MyWater","id":"52525252","status":"OK","total_m3":44.206,"target_m3":43.108,"target_date":"2020-10-01","flow_m3h":0,"min_flow_temperature_c":12,"max_flow_temperature_c":20,"min_external_temperature_c":19,"max_flow_m3h":0.495,"min_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
# Test Multical302 C1 telegrams
# full telegram
telegram=|2E442D2C6767676730048D2039D1684020_BCDB7803062C000043060000000314630000426C7F2A022D130001FF2100|
{"media":"heat","meter":"multical302","name":"MyHeater","id":"67676767","total_energy_consumption_kwh":44,"current_power_consumption_kw":1.9,"total_volume_m3":0.99,"at_date":"2019-10-31 00:00","total_energy_consumption_at_date_kwh":0,"current_status":"","timestamp":"1111-11-11T11:11:11Z"}
# compressed telegram
telegram=|25442D2C6767676730048D203AD2684020_D81579E7F1D5902C00000000006300007F2A130000|
{"media":"heat","meter":"multical302","name":"MyHeater","id":"67676767","total_energy_consumption_kwh":44,"current_power_consumption_kw":1.9,"total_volume_m3":0.99,"at_date":"2019-10-31 00:00","total_energy_consumption_at_date_kwh":0,"current_status":"","timestamp":"1111-11-11T11:11:11Z"}
# Mj full telegram
telegram=|2E442D2C46464646300C8D207A70EA6021B1C178_030FC51000430F9210000314072B05426CBE2B022D0C0001FF2100|
{"media":"heat volume at inlet","meter":"multical302","name":"MyHeaterMj","id":"46464646","total_energy_consumption_kwh":11925,"current_power_consumption_kw":1.2,"total_volume_m3":3386.95,"at_date":"2021-11-30 00:00","total_energy_consumption_at_date_kwh":11783.333333,"current_status":"","timestamp":"1111-11-11T11:11:11Z"}
# Mj compressed telegram
telegram=|25442D2C46464646300C8D20D3E2EB60212B6D79E26DCD65_C51000921000152B05BE2B0C0000|
{"media":"heat volume at inlet","meter":"multical302","name":"MyHeaterMj","id":"46464646","total_energy_consumption_kwh":11925,"current_power_consumption_kw":1.2,"total_volume_m3":3387.09,"at_date":"2021-11-30 00:00","total_energy_consumption_at_date_kwh":11783.333333,"current_status":"","timestamp":"1111-11-11T11:11:11Z"}
# Test QCaloric C1 telegrams
telegram=|314493441234567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422|
@ -54,34 +36,6 @@ telegram=|314493449392919034087a520000200b6e9700004b6e700200426c9f2ccb086e970000
telegram=|2744961566666666201B7AF9000020_2F2F02651E094265180902FD1B30030DFD0F05302E302E340F|
{"media":"room sensor","meter":"cma12w","name":"Rum","id":"66666666","software_version":"4.0.0","status":"OK","current_temperature_c":23.34,"average_temperature_1h_c":23.28,"battery":"BATTERY_330","timestamp":"1111-11-11T11:11:11Z"}
# Test Multical403 C1 telegrams, with cooling configuration
# full telegram
telegram=|88442D2C02017878340A8D208D529C132037FC78_040E2D0A000004FF07F8FF000004FF08401801000413C1900500844014000000008480401400000000043BED0000000259BC06025DCD07142DE7FFFFFF84100E0000000084200E0000000004FF2200000000026C9228440E5F0300004413960D0200C4401400000000C480401400000000426C8128|
{"media":"cooling load volume at outlet","meter":"multical403","name":"My403Cooling","id":"78780102","total_energy_consumption_kwh":723.611111,"total_volume_m3":364.737,"volume_flow_m3h":0.237,"t1_temperature_c":17.24,"t2_temperature_c":19.97,"at_date":"2020-08-18 00:00","current_status":"","timestamp":"1111-11-11T11:11:11Z"}
# compressed telegram
telegram=|5B442D2C02017878340A8D2096809C1320EF2B7934147ED7_2D0A0000FAFF000043180100CE9005000000000000000000EE000000BA06CB07E7FFFFFF00000000000000000000000092285F030000960D020000000000000000008128|
{"media":"cooling load volume at outlet","meter":"multical403","name":"My403Cooling","id":"78780102","total_energy_consumption_kwh":723.611111,"total_volume_m3":364.75,"volume_flow_m3h":0.238,"t1_temperature_c":17.22,"t2_temperature_c":19.95,"at_date":"2020-08-18 00:00","current_status":"","timestamp":"1111-11-11T11:11:11Z"}
# Test Multical602 C1 telegram full
telegram=|7A442D2C012815781C048D206450E76322344678_02F9FF1511130406690B010004EEFF07C1BC020004EEFF0890D401000414A925040084401400000000848040140000000002FD170000026CB929426CBF284406100A01004414D81A0400C4401400000000C480401400000000043B3900000002592A17025D2912|
{"media":"heat","meter":"multical602","name":"Heato","id":"78152801","total_energy_consumption_kwh":68457,"total_volume_m3":2717.85,"volume_flow_m3h":0.057,"t1_temperature_c":59.3,"t2_temperature_c":46.49,"at_date":"2021-09-25 00:00","current_status":"","energy_forward_kwh":0,"energy_returned_kwh":0,"timestamp":"1111-11-11T11:11:11Z"}
# Test Multical602 C1 telegram compressed
telegram=|4F442D2C012815781C048D206551E76322BE767900843005_1113690B0100C1BC020090D40100A925040000000000000000000000B929BF28100A0100D81A04000000000000000000390000002A172912|
{"media":"heat","meter":"multical602","name":"Heato","id":"78152801","total_energy_consumption_kwh":68457,"total_volume_m3":2717.85,"volume_flow_m3h":0.057,"t1_temperature_c":59.3,"t2_temperature_c":46.49,"at_date":"2021-09-25 00:00","current_status":"","energy_forward_kwh":0,"energy_returned_kwh":0,"timestamp":"1111-11-11T11:11:11Z"}
# Test Multical603 C1 telegram
telegram=|42442D2C3636363635048D20E18025B62087D078_0406A500000004FF072B01000004FF089C000000041421020000043B120000000259D014025D000904FF2200000000|
{"media":"heat","meter":"multical603","name":"Heat","id":"36363636","status":"OK","total_energy_consumption_kwh":165,"total_volume_m3":5.45,"volume_flow_m3h":0.018,"t1_temperature_c":53.28,"t2_temperature_c":23.04,"current_status":"","forward_energy_m3c":299,"return_energy_m3c":156,"energy_forward_kwh":299,"energy_returned_kwh":156,"timestamp":"1111-11-11T11:11:11Z"}
# Test Multical803 C1 telegram
telegram=|88442D2C8180808039048D208640513220EA7978_040FA000000004FF070200000004FF08090000000414FF000000844014000000008480401400000000043B0000000002590000025D0000142D0000000084100F0000000084200F0000000004FF2260000100026C892B440F00000000441400000000C4401400000000C480401400000000426C812B|
{"media":"heat","meter":"multical803","name":"Heater","id":"80808081","total_energy_consumption_kwh":444.444444,"total_volume_m3":2.55,"volume_flow_m3h":0,"t1_temperature_c":0,"t2_temperature_c":0,"at_date":"2020-11-09 00:00","current_status":"SENSOR_T1_BELOW_MEASURING_RANGE SENSOR_T2_BELOW_MEASURING_RANGE","energy_forward_kwh":0.555556,"energy_returned_kwh":2.5,"timestamp":"1111-11-11T11:11:11Z"}
# Test Kamstrup OmniPower C1 tlg
# Full tlg
telegram=|2D442D2C5768663230028D20E4E2C81C20878C78_04041A03000004843C00000000042B0300000004AB3C00000000|

Wyświetl plik

@ -0,0 +1,2 @@
telegram=|2A442D2C998734761B168D2091D37CAC21576C78_02FF207100041308190000441308190000615B7F616713|
telegram=|2E442D2C6767676730048D2039D1684020_BCDB7803062C000043060000000314630000426C7F2A022D130001FF2100|

Wyświetl plik

@ -152,8 +152,7 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
MeterInfo mi;
mi.parse("x", s, "00000000", "");
if (mi.driver == MeterDriver::UNKNOWN &&
mi.driver_name.str() == "")
if (mi.driver_name.str() == "")
{
error("Not a valid meter driver \"%s\"\n", s.c_str());
}
@ -634,14 +633,14 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
mi.parse(name, driver, id, key);
mi.poll_interval = c->pollinterval;
if (mi.driver == MeterDriver::UNKNOWN &&
mi.driver_name.str() == "")
if (mi.driver_name.str() == "")
{
error("Not a valid meter driver \"%s\"\n", driver.c_str());
}
LinkModeSet default_modes = toMeterLinkModeSet(mi.driver);
//LinkModeSet default_modes = toMeterLinkModeSet(mi.driver);
/*
if (default_modes.has(LinkMode::MBUS))
{
// MBus primary address 0-250
@ -653,6 +652,7 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
if (!isValidMatchExpressions(id, true)) error("Not a valid id nor a valid meter match expression \"%s\"\n", id.c_str());
}
if (!isValidKey(key, mi)) error("Not a valid meter key \"%s\"\n", key.c_str());
*/
c->meters.push_back(mi);

Wyświetl plik

@ -187,11 +187,6 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
debug("(cmdline) setting link modes to %s for meter %s\n",
mi.link_modes.hr().c_str(), name.c_str());
*/
MeterDriver mt = mi.driver;
if (mt == MeterDriver::UNKNOWN) {
warning("Not a valid meter driver \"%s\"\n", driver.c_str());
use = false;
}
if (!isValidMatchExpressions(id, true)) {
warning("Not a valid meter id nor a valid meter match expression \"%s\"\n", id.c_str());
use = false;
@ -816,7 +811,7 @@ LinkModeCalculationResult calculateLinkModes(Configuration *config, BusDevice *d
{
meters_union.unionLinkModeSet(m.link_modes);
string meter = m.link_modes.hr();
debug("(config) meter %s link mode(s): %s\n", toString(m.driver).c_str(), meter.c_str());
debug("(config) meter %s link mode(s): %s\n", m.driverName().str().c_str(), meter.c_str());
}
string metersu = meters_union.hr();
debug("(config) all possible link modes that the meters might transmit on: %s\n", metersu.c_str());

Wyświetl plik

@ -246,7 +246,7 @@ namespace
// |MyTapWaterg;27202020;15.992;1111-11-11 11:11.11
// Test: NewAndOld apator172 00148686 NOKEY
// Test: NewAndOld apator162 00148686 NOKEY
// Comment: New apator162 telegram which can be decoded.
// telegram=4E4401068686140005077A350040852F2F_0F005B599600000010AA55000041545A42850BD800437D037301C5500000564B00009E4600006A410000A01778EC03FFFFFFFFFFFFFFFFFFFFFFFFFFE393
// {"media":"water","meter":"apator162","name":"NewAndOld","id":"00148686","total_m3":21.93,"timestamp":"1111-11-11T11:11:11Z"}

Wyświetl plik

@ -27,13 +27,30 @@ namespace
static bool ok = registerDriver([](DriverInfo&di)
{
di.setName("multical603");
di.setDefaultFields("name,id,total_energy_consumption_kwh,total_volume_m3,volume_flow_m3h,t1_temperature_c,t2_temperature_c,current_status,timestamp");
di.setName("kamheat");
di.addNameAlias("multical302");
di.addNameAlias("multical303");
di.addNameAlias("multical403");
di.addNameAlias("multical602");
di.addNameAlias("multical603");
di.addNameAlias("multical803");
di.setDefaultFields("name,id,total_energy_consumption_kwh,total_volume_m3,status,timestamp");
di.setMeterType(MeterType::HeatMeter);
di.addLinkMode(LinkMode::C1);
di.addLinkMode(LinkMode::T1);
di.addDetection(MANUFACTURER_KAM, 0x04, 0x35);
di.addDetection(MANUFACTURER_KAM, 0x0c, 0x35);
di.addDetection(MANUFACTURER_KAM, 0x04, 0x30); // 302
di.addDetection(MANUFACTURER_KAM, 0x0d, 0x30); // 302
di.addDetection(MANUFACTURER_KAM, 0x0c, 0x30); // 302
di.addDetection(MANUFACTURER_KAM, 0x04, 0x40); // 303
di.addDetection(MANUFACTURER_KAM, 0x0a, 0x34); // 403
di.addDetection(MANUFACTURER_KAM, 0x0b, 0x34); // 403
di.addDetection(MANUFACTURER_KAM, 0x0c, 0x34); // 403
di.addDetection(MANUFACTURER_KAM, 0x0d, 0x34); // 403
di.addDetection(MANUFACTURER_KAM, 0x04, 0x1c); // 602
di.addDetection(MANUFACTURER_KAM, 0x04, 0x35); // 603
di.addDetection(MANUFACTURER_KAM, 0x0c, 0x35); // 603
di.addDetection(MANUFACTURER_KAM, 0x04, 0x39); // 803
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
});
@ -45,8 +62,7 @@ namespace
addStringFieldWithExtractorAndLookup(
"status",
"Status and error flags.",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT |
PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS,
PrintProperty::JSON | PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS,
FieldMatcher::build()
.set(DifVifKey("04FF22")),
Translate::Lookup(
@ -98,7 +114,7 @@ namespace
addNumericFieldWithExtractor(
"total_energy_consumption",
"The total energy consumption recorded by this meter.",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Energy,
VifScaling::Auto,
FieldMatcher::build()
@ -172,7 +188,6 @@ namespace
.set(VIFRange::ReturnTemperature)
);
addNumericFieldWithExtractor(
"max_flow",
"The maximum flow of water that passed through this meter.",
@ -184,59 +199,6 @@ namespace
.set(VIFRange::VolumeFlow)
);
// Backwards compatible current_status to be removed.
addStringFieldWithExtractorAndLookup(
"current_status",
"Status and error flags (9/369/ Info Bits).",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::DEPRECATED,
FieldMatcher::build()
.set(DifVifKey("04FF22")),
Translate::Lookup(
{
{
{
"ERROR_FLAGS",
Translate::Type::BitToString,
AlwaysTrigger, MaskBits(0xffffffff),
"",
{
{ 0x00000001, "VOLTAGE_INTERRUPTED" },
{ 0x00000002, "LOW_BATTERY_LEVEL" },
{ 0x00000004, "SENSOR_ERROR" },
{ 0x00000008, "SENSOR_T1_ABOVE_MEASURING_RANGE" },
{ 0x00000010, "SENSOR_T2_ABOVE_MEASURING_RANGE" },
{ 0x00000020, "SENSOR_T1_BELOW_MEASURING_RANGE" },
{ 0x00000040, "SENSOR_T2_BELOW_MEASURING_RANGE" },
{ 0x00000080, "TEMP_DIFF_WRONG_POLARITY" },
{ 0x00000100, "FLOW_SENSOR_WEAK_OR_AIR" },
{ 0x00000200, "WRONG_FLOW_DIRECTION" },
{ 0x00000400, "RESERVED_BIT_10" },
{ 0x00000800, "FLOW_INCREASED" },
{ 0x00001000, "IN_A1_LEAKAGE_IN_THE_SYSTEM" },
{ 0x00002000, "IN_B1_LEAKAGE_IN_THE_SYSTEM" },
{ 0x00004000, "IN-A1_A2_EXTERNAL_ALARM" },
{ 0x00008000, "IN-B1_B2_EXTERNAL_ALARM" },
{ 0x00010000, "V1_COMMUNICATION_ERROR" },
{ 0x00020000, "V1_WRONG_PULSE_FIGURE" },
{ 0x00040000, "IN_A2_LEAKAGE_IN_THE_SYSTEM" },
{ 0x00080000, "IN_B2_LEAKAGE_IN_THE_SYSTEM" },
{ 0x00100000, "T3_ABOVE_MEASURING_RANGE_OR_SWITCHED_OFF" },
{ 0x00200000, "T3_BELOW_MEASURING_RANGE_OR_SHORT_CIRCUITED" },
{ 0x00400000, "V2_COMMUNICATION_ERROR" },
{ 0x00800000, "V2_WRONG_PULSE_FIGURE" },
{ 0x01000000, "V2_AIR" },
{ 0x02000000, "V2_WRONG_FLOW_DIRECTION" },
{ 0x04000000, "RESERVED_BIT_26" },
{ 0x08000000, "V2_INCREASED_FLOW" },
{ 0x10000000, "V1_V2_BURST_WATER_LOSS" },
{ 0x20000000, "V1_V2_BURST_WATER_PENETRATION" },
{ 0x40000000, "V1_V2_LEAKAGE_WATER_LOSS" },
{ 0x80000000, "V1_V2_LEAKAGE_WATER_PENETRATION" }
}
},
},
}));
addNumericFieldWithExtractor(
"forward_energy",
"The forward energy of the water (4/97/Energy E8).",
@ -257,28 +219,6 @@ namespace
.set(DifVifKey("04FF08")),
Unit::M3C);
/* Deprecated kwh version where unit should be m3c. */
addNumericFieldWithExtractor(
"energy_forward",
"Deprecated! The forward energy of the water but in wrong unit! Should be m3c!",
PrintProperty::JSON | PrintProperty::OPTIONAL | PrintProperty::DEPRECATED,
Quantity::Energy,
VifScaling::None,
FieldMatcher::build()
.set(DifVifKey("04FF07")),
Unit::KWH);
/* Deprecated kwh version where unit should be m3c. */
addNumericFieldWithExtractor(
"energy_returned",
"Deprecated! The return energy of the water but in wrong unit! Should be m3c!",
PrintProperty::JSON | PrintProperty::OPTIONAL | PrintProperty::DEPRECATED,
Quantity::Energy,
VifScaling::None,
FieldMatcher::build()
.set(DifVifKey("04FF08")),
Unit::KWH);
addStringFieldWithExtractor(
"meter_date",
"The date and time (10/348/Date and time).",
@ -291,7 +231,7 @@ namespace
addNumericFieldWithExtractor(
"target_energy",
"The energy consumption recorded by this meter at the set date (11/60/Heat energy E1/026C).",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::OPTIONAL,
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Energy,
VifScaling::Auto,
FieldMatcher::build()
@ -303,7 +243,7 @@ namespace
addNumericFieldWithExtractor(
"target_volume",
"The amount of water that had passed through this meter at the set date (13/68/Volume V1).",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::OPTIONAL,
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
@ -315,7 +255,7 @@ namespace
addStringFieldWithExtractor(
"target_date",
"The most recent billing period date and time (14/348/Date and Time logged).",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::OPTIONAL,
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Date)
@ -323,13 +263,57 @@ namespace
);
}
}
// Test: MyHeater multical302 67676767 NOKEY
// Comment: Using vif kwh
// telegram=|2E442D2C6767676730048D2039D1684020_BCDB7803062C000043060000000314630000426C7F2A022D130001FF2100|
// {"id": "67676767","media": "heat","meter": "kamheat","name": "MyHeater","power_kw": 1.9,"status": "OK","target_date": "2019-10-31","target_energy_kwh": 0,"timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 44,"total_volume_m3": 0.99}
// |MyHeater;67676767;44;0.99;OK;1111-11-11 11:11.11
// telegram=|25442D2C6767676730048D203AD2684020_D81579E7F1D5902C00000000006300007F2A130000|
// {"id": "67676767","media": "heat","meter": "kamheat","name": "MyHeater","power_kw": 1.9,"status": "OK","target_date": "2019-10-31","target_energy_kwh": 0,"timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 44,"total_volume_m3": 0.99}
// |MyHeater;67676767;44;0.99;OK;1111-11-11 11:11.11
// Test: MyHeaterMj multical302 46464646 NOKEY
// Comment: Using mj kwh
// telegram=|2E442D2C46464646300C8D207A70EA6021B1C178_030FC51000430F9210000314072B05426CBE2B022D0C0001FF2100|
// {"id": "46464646","media": "heat volume at inlet","meter": "kamheat","name": "MyHeaterMj","power_kw": 1.2,"status": "OK","target_date": "2021-11-30","target_energy_kwh": 11783.333333,"timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 11925,"total_volume_m3": 3386.95}
// |MyHeaterMj;46464646;11925;3386.95;OK;1111-11-11 11:11.11
// telegram=|25442D2C46464646300C8D20D3E2EB60212B6D79E26DCD65_C51000921000152B05BE2B0C0000|
// {"id": "46464646","media": "heat volume at inlet","meter": "kamheat","name": "MyHeaterMj","power_kw": 1.2,"status": "OK","target_date": "2021-11-30","target_energy_kwh": 11783.333333,"timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 11925,"total_volume_m3": 3387.09}
// |MyHeaterMj;46464646;11925;3387.09;OK;1111-11-11 11:11.11
// Test: Heat multical603 36363636 NOKEY
// Comment:
// telegram=|42442D2C3636363635048D20E18025B62087D078_0406A500000004FF072B01000004FF089C000000041421020000043B120000000259D014025D000904FF2200000000|
// {"media":"heat","meter":"multical603","name":"Heat","id":"36363636","status":"OK","total_energy_consumption_kwh":165,"total_volume_m3":5.45,"volume_flow_m3h":0.018,"t1_temperature_c":53.28,"t2_temperature_c":23.04,"current_status":"","forward_energy_m3c":299,"return_energy_m3c":156,"energy_forward_kwh":299,"energy_returned_kwh":156,"timestamp":"1111-11-11T11:11:11Z"}
// |Heat;36363636;165;5.45;0.018;53.28;23.04;;1111-11-11 11:11.11
// {"media":"heat","meter":"kamheat","name":"Heat","id":"36363636","status":"OK","total_energy_consumption_kwh":165,"total_volume_m3":5.45,"volume_flow_m3h":0.018,"t1_temperature_c":53.28,"t2_temperature_c":23.04,"forward_energy_m3c":299,"return_energy_m3c":156,"timestamp":"1111-11-11T11:11:11Z"}
// |Heat;36363636;165;5.45;OK;1111-11-11 11:11.11
// Test: HeatInlet multical603 66666666 NOKEY
// telegram=|5A442D2C66666666350C8D2066D0E16420C6A178_0406051C000004FF07393D000004FF08AE2400000414F7680000043B47000000042D1600000002596D14025DFD0804FF22000000000422E61A0000143B8C010000142D7C000000|
// {"media":"heat volume at inlet","meter":"multical603","name":"HeatInlet","id":"66666666","on_time_h":6886,"status":"OK","total_energy_consumption_kwh":7173,"total_volume_m3":268.71,"volume_flow_m3h":0.071,"power_kw":2.2,"max_power_kw":12.4,"t1_temperature_c":52.29,"t2_temperature_c":23.01,"max_flow_m3h":0.396,"current_status":"","forward_energy_m3c":15673,"return_energy_m3c":9390,"energy_forward_kwh":15673,"energy_returned_kwh":9390,"timestamp":"1111-11-11T11:11:11Z"}
// |HeatInlet;66666666;7173;268.71;0.071;52.29;23.01;;1111-11-11 11:11.11
// {"media":"heat volume at inlet","meter":"kamheat","name":"HeatInlet","id":"66666666","on_time_h":6886,"status":"OK","total_energy_consumption_kwh":7173,"total_volume_m3":268.71,"volume_flow_m3h":0.071,"power_kw":2.2,"max_power_kw":12.4,"t1_temperature_c":52.29,"t2_temperature_c":23.01,"max_flow_m3h":0.396,"forward_energy_m3c":15673,"return_energy_m3c":9390,"timestamp":"1111-11-11T11:11:11Z"}
// |HeatInlet;66666666;7173;268.71;OK;1111-11-11 11:11.11
// Test: My403Cooling multical403 78780102 NOKEY
// telegram=|88442D2C02017878340A8D208D529C132037FC78_040E2D0A000004FF07F8FF000004FF08401801000413C1900500844014000000008480401400000000043BED0000000259BC06025DCD07142DE7FFFFFF84100E0000000084200E0000000004FF2200000000026C9228440E5F0300004413960D0200C4401400000000C480401400000000426C8128|
// {"forward_energy_m3c": 65528,"id": "78780102","max_power_kw": 429496727.1,"media": "cooling load volume at outlet","meter": "kamheat","meter_date": "2020-08-18","name": "My403Cooling","return_energy_m3c": 71744,"status": "OK","t1_temperature_c": 17.24,"t2_temperature_c": 19.97,"target_date": "2020-08-01","target_energy_kwh": 239.722222,"target_volume_m3": 134.55,"timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 723.611111,"total_volume_m3": 364.737,"volume_flow_m3h": 0.237}
// |My403Cooling;78780102;723.611111;364.737;OK;1111-11-11 11:11.11
// telegram=|5B442D2C02017878340A8D2096809C1320EF2B7934147ED7_2D0A0000FAFF000043180100CE9005000000000000000000EE000000BA06CB07E7FFFFFF00000000000000000000000092285F030000960D020000000000000000008128|
// {"forward_energy_m3c": 65530,"id": "78780102","max_power_kw": 429496727.1,"media": "cooling load volume at outlet","meter": "kamheat","meter_date": "2020-08-18","name": "My403Cooling","return_energy_m3c": 71747,"status": "OK","t1_temperature_c": 17.22,"t2_temperature_c": 19.95,"target_date": "2020-08-01","target_energy_kwh": 239.722222,"target_volume_m3": 134.55,"timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 723.611111,"total_volume_m3": 364.75,"volume_flow_m3h": 0.238}
// |My403Cooling;78780102;723.611111;364.75;OK;1111-11-11 11:11.11
// Test: Heato multical602 78152801 NOKEY
// telegram=|7A442D2C012815781C048D206450E76322344678_02F9FF1511130406690B010004EEFF07C1BC020004EEFF0890D401000414A925040084401400000000848040140000000002FD170000026CB929426CBF284406100A01004414D81A0400C4401400000000C480401400000000043B3900000002592A17025D2912|
// {"id": "78152801","media": "heat","meter": "kamheat","meter_date": "2021-09-25","name": "Heato","status": "OK","t1_temperature_c": 59.3,"t2_temperature_c": 46.49,"target_date": "2021-08-31","target_energy_kwh": 68112,"target_volume_m3": 2690.16,"timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 68457,"total_volume_m3": 2717.85,"volume_flow_m3h": 0.057}
// |Heato;78152801;68457;2717.85;OK;1111-11-11 11:11.11
// telegram=|4F442D2C012815781C048D206551E76322BE767900843005_1113690B0100C1BC020090D40100A925040000000000000000000000B929BF28100A0100D81A04000000000000000000390000002A172912|
// {"id": "78152801","media": "heat","meter": "kamheat","meter_date": "2021-09-25","name": "Heato","status": "OK","t1_temperature_c": 59.3,"t2_temperature_c": 46.49,"target_date": "2021-08-31","target_energy_kwh": 68112,"target_volume_m3": 2690.16,"timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 68457,"total_volume_m3": 2717.85,"volume_flow_m3h": 0.057}
// |Heato;78152801;68457;2717.85;OK;1111-11-11 11:11.11
// Test: Heater multical803 80808081 NOKEY
// telegram=|88442D2C8180808039048D208640513220EA7978_040FA000000004FF070200000004FF08090000000414FF000000844014000000008480401400000000043B0000000002590000025D0000142D0000000084100F0000000084200F0000000004FF2260000100026C892B440F00000000441400000000C4401400000000C480401400000000426C812B|
// {"forward_energy_m3c": 2,"id": "80808081","max_power_kw": 0,"media": "heat","meter": "kamheat","meter_date": "2020-11-09","name": "Heater","return_energy_m3c": 9,"status": "SENSOR_T1_BELOW_MEASURING_RANGE SENSOR_T2_BELOW_MEASURING_RANGE V1_COMMUNICATION_ERROR","t1_temperature_c": 0,"t2_temperature_c": 0,"target_date": "2020-11-01","target_energy_kwh": 0,"target_volume_m3": 0,"timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 444.444444,"total_volume_m3": 2.55,"volume_flow_m3h": 0}
// |Heater;80808081;444.444444;2.55;SENSOR_T1_BELOW_MEASURING_RANGE SENSOR_T2_BELOW_MEASURING_RANGE V1_COMMUNICATION_ERROR;1111-11-11 11:11.11

Wyświetl plik

@ -126,7 +126,7 @@ namespace
}
}
// Test: HCA2 qcaloric 04998541 NOKEY
// Test: HCA2 lse_08 04998541 NOKEY
// telegram=|294465324185990401087a0080000082046c7f018b046e210300046d1a0e6f0202fdac7e012301fd0c01|
// {"media":"heat cost allocation","meter":"lse_08","name":"HCA2","id":"04998541","status":"TPL_MFCT_80","set_date":"2003-01-31","consumption_at_set_date_hca":321,"device_date_time":"2003-02-15 14:26","duration_since_readout_h":2.489167,"model_version":"01","timestamp":"1111-11-11T11:11:11Z"}
// |HCA2;04998541;2003-01-31;321;1111-11-11 11:11.11

Wyświetl plik

@ -1,197 +0,0 @@
/*
Copyright (C) 2020-2022 thecem (gpl-3.0-or-later)
Copyright (C) 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 <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("multical303");
di.setDefaultFields("name,id,status,total_energy_kwh,target_energy_kwh,timestamp");
di.setMeterType(MeterType::HeatMeter);
di.addLinkMode(LinkMode::C1);
di.addLinkMode(LinkMode::T1);
di.addDetection(MANUFACTURER_KAM, 0x04, 0x40);
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_energy",
"The total energy consumption recorded by this meter.",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
Quantity::Energy,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::AnyEnergyVIF)
);
addNumericFieldWithExtractor(
"total_volume",
"The volume of water (3/68/Volume V1).",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
);
addNumericFieldWithExtractor(
"forward_energy",
"The forward energy of the water (4/97/Energy E8).",
PrintProperty::JSON,
Quantity::Energy,
VifScaling::None,
FieldMatcher::build()
.set(DifVifKey("04FF07")),
Unit::M3C);
addNumericFieldWithExtractor(
"return_energy",
"The return energy of the water (5/110/Energy E9).",
PrintProperty::JSON,
Quantity::Energy,
VifScaling::None,
FieldMatcher::build()
.set(DifVifKey("04FF08")),
Unit::M3C);
addNumericFieldWithExtractor(
"forward",
"The forward temperature of the water (6/86/t2 actual 2 decimals).",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::FlowTemperature)
);
addNumericFieldWithExtractor(
"return",
"The return temperature of the water (7/87/t2 actual 2 decimals).",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ReturnTemperature)
);
addNumericFieldWithExtractor(
"actual_flow",
"The actual amount of water that pass through this meter (8/74/Flow V1 actual).",
PrintProperty::JSON,
Quantity::Flow,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::VolumeFlow)
);
addStringFieldWithExtractorAndLookup(
"status",
"Status and error flags (9/369/ Info Bits).",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT |
PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS,
FieldMatcher::build()
.set(DifVifKey("02FF22")),
Translate::Lookup(
{
{
{
"ERROR_FLAGS",
Translate::Type::BitToString,
AlwaysTrigger, MaskBits(0xffff),
"OK",
{
{ 0x0000, "OK" },
{ 0x0001, "VOLTAGE_INTERRUPTED" },
{ 0x0002, "LOW_BATTERY_LEVEL" },
{ 0x0004, "SENSOR_ERROR" },
{ 0x0008, "SENSOR_T1_ABOVE_MEASURING_RANGE" },
{ 0x0010, "SENSOR_T2_ABOVE_MEASURING_RANGE" },
{ 0x0020, "SENSOR_T1_BELOW_MEASURING_RANGE" },
{ 0x0040, "SENSOR_T2_BELOW_MEASURING_RANGE" },
{ 0x0080, "TEMP_DIFF_WRONG_POLARITY" },
{ 0x0100, "FLOW_SENSOR_WEAK_OR_AIR" },
{ 0x0200, "WRONG_FLOW_DIRECTION" },
{ 0x0400, "UNKNOWN" },
{ 0x0800, "FLOW_INCREASED" },
}
},
},
}));
addStringFieldWithExtractor(
"meter_date",
"The date and time (10/348/Date and time).",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Date)
);
addNumericFieldWithExtractor(
"target_energy",
"The energy consumption recorded by this meter at the set date (11/60/Heat energy E1/026C).",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
Quantity::Energy,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::AnyEnergyVIF)
.set(StorageNr(1))
);
addNumericFieldWithExtractor(
"target_volume",
"The amount of water that had passed through this meter at the set date (13/68/Volume V1).",
PrintProperty::JSON | PrintProperty::FIELD,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(StorageNr(1))
);
addStringFieldWithExtractor(
"target_date",
"The most recent billing period date and time (14/348/Date and Time logged).",
PrintProperty::JSON | PrintProperty::FIELD,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Date)
.set(StorageNr(1))
);
}
}
// Test: Heat multical303 82788281 75EDE0CBBB6E126764898645AA366568
// Comment:
// telegram=|_5E442D2C8182788240047A83005025186E9C6D9815EBFC04CBE8E4B8C8A6B9949C9DAA629CD96D920F321CFBEE7AE104DD8532C5C0EE79B4CFACCFA75D3A5EB6D4493DFAFE91B15C3A3DCFCE899138B8EA02CDB609D31CF019F9E4FD04559E|
// {"media":"heat","meter":"multical303","name":"Heat","id":"82788281","total_energy_kwh":0,"total_volume_m3":2.38,"forward_energy_m3c":61,"return_energy_m3c":61,"forward_c":26.07,"return_c":26.22,"actual_flow_m3h":0,"status":"OK","meter_date":"2022-08-18","target_energy_kwh":0,"target_volume_m3":0,"target_date":"2022-08-01","timestamp":"1111-11-11T11:11:11Z"}
// |Heat;82788281;OK;0;0;1111-11-11 11:11.11

Wyświetl plik

@ -197,20 +197,12 @@ void list_shell_envs(Configuration *config, string meter_driver)
shared_ptr<Meter> meter;
DriverInfo di;
mi.driver = toMeterDriver(meter_driver);
if (mi.driver != MeterDriver::UNKNOWN)
mi.driver_name = meter_driver;
if (!lookupDriverInfo(meter_driver, &di))
{
meter = createMeter(&mi);
}
else
{
mi.driver_name = meter_driver;
if (!lookupDriverInfo(meter_driver, &di))
{
error("No such driver %s\n", meter_driver.c_str());
}
meter = di.construct(mi);
error("No such driver %s\n", meter_driver.c_str());
}
meter = di.construct(mi);
printf("METER_DEVICE\n"
"METER_ID\n"
@ -254,20 +246,12 @@ void list_fields(Configuration *config, string meter_driver)
shared_ptr<Meter> meter;
DriverInfo di;
mi.driver = toMeterDriver(meter_driver);
if (mi.driver != MeterDriver::UNKNOWN)
mi.driver_name = meter_driver;
if (!lookupDriverInfo(meter_driver, &di))
{
meter = createMeter(&mi);
}
else
{
mi.driver_name = meter_driver;
if (!lookupDriverInfo(meter_driver, &di))
{
error("No such driver %s\n", meter_driver.c_str());
}
meter = di.construct(mi);
error("No such driver %s\n", meter_driver.c_str());
}
meter = di.construct(mi);
int width = 13; // Width of timestamp_utc
for (FieldInfo &fi : meter->fieldInfos())
@ -321,14 +305,6 @@ void list_fields(Configuration *config, string meter_driver)
void list_meters(Configuration *config)
{
#define X(mname,link,info,type,cname) \
if (config->list_meters_search == "" || \
stringFoundCaseIgnored(#info, config->list_meters_search) || \
stringFoundCaseIgnored(#mname, config->list_meters_search)) \
printf("%-14s %s\n", #mname, #info);
LIST_OF_METERS
#undef X
for (DriverInfo *di : allDrivers())
{
string mname = di->name().str();
@ -485,7 +461,7 @@ void setup_meters(Configuration *config, MeterManager *manager)
config->extra_calculated_fields.begin(),
config->extra_calculated_fields.end());
if (m.usesPolling() || driverNeedsPolling(m.driver, m.driver_name))
if (m.usesPolling() || driverNeedsPolling(m.driver_name))
{
// A polling meter must be defined from the start.
auto meter = createMeter(&m);

Wyświetl plik

@ -1,42 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// This is the old style meter detection. Drivers are succesively rewritten
// from meter_xyz.cc to driver_xyz.cc only old style drivers are listed here.
// The new driver_xyz.cc file format is selfcontained so eventually this
// file be empty and go away.
//
// This file is only included into meters.cc
//
// List of numbers that can be used to detect the meter driver from a telegram.
//
// meter driver, manufacturer, media, version
//
#define METER_DETECTION \
X(MULTICAL302,MANUFACTURER_KAM, 0x04, 0x30) \
X(MULTICAL302,MANUFACTURER_KAM, 0x0d, 0x30) \
X(MULTICAL302,MANUFACTURER_KAM, 0x0c, 0x30) \
X(MULTICAL403,MANUFACTURER_KAM, 0x0a, 0x34) \
X(MULTICAL403,MANUFACTURER_KAM, 0x0b, 0x34) \
X(MULTICAL403,MANUFACTURER_KAM, 0x0c, 0x34) \
X(MULTICAL403,MANUFACTURER_KAM, 0x0d, 0x34) \
X(MULTICAL602,MANUFACTURER_KAM, 0x04, 0x1c) \
X(MULTICAL803,MANUFACTURER_KAM, 0x04, 0x39) \
// End of list

Wyświetl plik

@ -1,208 +0,0 @@
/*
Copyright (C) 2018-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 <http://www.gnu.org/licenses/>.
*/
#include"meters.h"
#include"meters_common_implementation.h"
#include"dvparser.h"
#include"wmbus.h"
#include"wmbus_utils.h"
#include"util.h"
#define INFO_CODE_VOLTAGE_INTERRUPTED 1
#define INFO_CODE_WRONG_FLOW_DIRECTION 2
#define INFO_CODE_SENSOR_T2_OUT_OF_RANGE 4
#define INFO_CODE_SENSOR_T1_OUT_OF_RANGE 8
#define INFO_CODE_FLOW_SENSOR_WEAK_OR_AIR 16
#define INFO_CODE_TEMP_DIFF_WRONG_POLARITY 32
#define INFO_CODE_VOLTAGE_TOO_LOW 128
struct MeterMultical302 : public virtual MeterCommonImplementation {
MeterMultical302(MeterInfo &mi);
double totalEnergyConsumption(Unit u);
double targetEnergyConsumption(Unit u);
double currentPowerConsumption(Unit u);
string status();
double totalVolume(Unit u);
double targetVolume(Unit u);
private:
void processContent(Telegram *t);
uchar info_codes_ {};
double total_energy_kwh_ {};
double target_energy_kwh_ {};
double current_power_kw_ {};
double total_volume_m3_ {};
string target_date_ {};
};
MeterMultical302::MeterMultical302(MeterInfo &mi) :
MeterCommonImplementation(mi, "multical302")
{
setMeterType(MeterType::HeatMeter);
setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR);
addLinkMode(LinkMode::C1);
addPrint("total_energy_consumption", Quantity::Energy,
[&](Unit u){ return totalEnergyConsumption(u); },
"The total energy consumption recorded by this meter.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("current_power_consumption", Quantity::Power,
[&](Unit u){ return currentPowerConsumption(u); },
"Current power consumption.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("total_volume", Quantity::Volume,
[&](Unit u){ return totalVolume(u); },
"Total volume of heat media.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("at_date", Quantity::Text,
[&](){ return target_date_; },
"Date when total energy consumption was recorded.",
PrintProperty::JSON);
addPrint("total_energy_consumption_at_date", Quantity::Energy,
[&](Unit u){ return targetEnergyConsumption(u); },
"The total energy consumption recorded at the target date.",
PrintProperty::JSON);
addPrint("current_status", Quantity::Text,
[&](){ return status(); },
"Status of meter.",
PrintProperty::FIELD | PrintProperty::JSON);
}
shared_ptr<Meter> createMultical302(MeterInfo &mi) {
return shared_ptr<Meter>(new MeterMultical302(mi));
}
double MeterMultical302::totalEnergyConsumption(Unit u)
{
assertQuantity(u, Quantity::Energy);
return convert(total_energy_kwh_, Unit::KWH, u);
}
double MeterMultical302::targetEnergyConsumption(Unit u)
{
assertQuantity(u, Quantity::Energy);
return convert(target_energy_kwh_, Unit::KWH, u);
}
double MeterMultical302::totalVolume(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_volume_m3_, Unit::M3, u);
}
double MeterMultical302::currentPowerConsumption(Unit u)
{
assertQuantity(u, Quantity::Power);
return convert(current_power_kw_, Unit::KW, u);
}
void MeterMultical302::processContent(Telegram *t)
{
/*
(multical302) 11: bcdb payload crc
(multical302) 13: 78 frame type (long frame)
(multical302) 14: 03 dif (24 Bit Integer/Binary Instantaneous value)
(multical302) 15: 06 vif (Energy kWh)
(multical302) 16: * 2C0000 total energy consumption (44.000000 kWh)
(multical302) 19: 43 dif (24 Bit Integer/Binary Instantaneous value storagenr=1)
(multical302) 1a: 06 vif (Energy kWh)
(multical302) 1b: * 000000 target energy consumption (0.000000 kWh)
(multical302) 1e: 03 dif (24 Bit Integer/Binary Instantaneous value)
(multical302) 1f: 14 vif (Volume 10² m³)
(multical302) 20: * 630000 total volume (0.990000 m3)
(multical302) 23: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
(multical302) 24: 6C vif (Date type G)
(multical302) 25: * 7F2A target date (2019-10-31 00:00)
(multical302) 27: 02 dif (16 Bit Integer/Binary Instantaneous value)
(multical302) 28: 2D vif (Power 10² W)
(multical302) 29: * 1300 current power consumption (1.900000 kW)
(multical302) 2b: 01 dif (8 Bit Integer/Binary Instantaneous value)
(multical302) 2c: FF vif (Vendor extension)
(multical302) 2d: 21 vife (per minute)
(multical302) 2e: * 00 info codes (00)
*/
int offset;
string key;
extractDVuint8(&t->dv_entries, "01FF21", &offset, &info_codes_);
t->addMoreExplanation(offset, " info codes (%s)", status().c_str());
if(findKey(MeasurementType::Instantaneous, VIFRange::EnergyWh, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_energy_kwh_);
t->addMoreExplanation(offset, " total energy consumption (%f kWh)", total_energy_kwh_);
} else if (findKey(MeasurementType::Instantaneous, VIFRange::EnergyMJ, 0, 0, &key, &t->dv_entries)) {
double mj;
extractDVdouble(&t->dv_entries, key, &offset, &mj);
total_energy_kwh_ = convert(mj, Unit::MJ, Unit::KWH);
t->addMoreExplanation(offset, " total energy consumption (%f kWh)", total_energy_kwh_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_volume_m3_);
t->addMoreExplanation(offset, " total volume (%f m3)", total_volume_m3_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::EnergyWh, 1, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &target_energy_kwh_);
t->addMoreExplanation(offset, " target energy consumption (%f kWh)", target_energy_kwh_);
} else if(findKey(MeasurementType::Instantaneous, VIFRange::EnergyMJ, 1, 0, &key, &t->dv_entries)){
double mj;
extractDVdouble(&t->dv_entries, key, &offset, &mj);
target_energy_kwh_ = convert(mj, Unit::MJ, Unit::KWH);
t->addMoreExplanation(offset, " target energy consumption (%f kWh)", target_energy_kwh_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::PowerW, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &current_power_kw_);
t->addMoreExplanation(offset, " current power consumption (%f kW)", current_power_kw_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 1, 0, &key, &t->dv_entries)) {
struct tm datetime;
extractDVdate(&t->dv_entries, key, &offset, &datetime);
target_date_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " target date (%s)", target_date_.c_str());
}
}
string MeterMultical302::status()
{
string s;
if (info_codes_ & INFO_CODE_VOLTAGE_INTERRUPTED) s.append("VOLTAGE_INTERRUPTED ");
if (info_codes_ & INFO_CODE_WRONG_FLOW_DIRECTION) s.append("WRONG_FLOW_DIRECTION ");
if (info_codes_ & INFO_CODE_SENSOR_T2_OUT_OF_RANGE) s.append("SENSOR_T2_OUT_OF_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T1_OUT_OF_RANGE) s.append("SENSOR_T1_OUT_OF_RANGE ");
if (info_codes_ & INFO_CODE_FLOW_SENSOR_WEAK_OR_AIR) s.append("FLOW_SENSOR_WEAK_OR_AIR ");
if (info_codes_ & INFO_CODE_TEMP_DIFF_WRONG_POLARITY) s.append("TEMP_DIFF_WRONG_POLARITY ");
if (info_codes_ & 64) s.append("UNKNOWN_64 ");
if (info_codes_ & INFO_CODE_VOLTAGE_TOO_LOW) s.append("VOLTAGE_TOO_LOW ");
if (s.length() > 0) {
s.pop_back(); // Remove final space
return s;
}
return s;
}

Wyświetl plik

@ -1,209 +0,0 @@
/*
Copyright (C) 2018-2020 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2020 Eric Bus (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.h"
#include"meters_common_implementation.h"
#include"dvparser.h"
#include"wmbus.h"
#include"wmbus_utils.h"
#include"util.h"
#define INFO_CODE_VOLTAGE_INTERRUPTED 1
#define INFO_CODE_LOW_BATTERY_LEVEL 2
#define INFO_CODE_EXTERNAL_ALARM 4
#define INFO_CODE_SENSOR_T1_ABOVE_MEASURING_RANGE 8
#define INFO_CODE_SENSOR_T2_ABOVE_MEASURING_RANGE 16
#define INFO_CODE_SENSOR_T1_BELOW_MEASURING_RANGE 32
#define INFO_CODE_SENSOR_T2_BELOW_MEASURING_RANGE 64
#define INFO_CODE_TEMP_DIFF_WRONG_POLARITY 128
struct MeterMultical403 : public virtual MeterCommonImplementation {
MeterMultical403(MeterInfo &mi);
double totalEnergyConsumption(Unit u);
string status();
double totalVolume(Unit u);
double volumeFlow(Unit u);
// Water temperatures
double t1Temperature(Unit u);
bool hasT1Temperature();
double t2Temperature(Unit u);
bool hasT2Temperature();
private:
void processContent(Telegram *t);
uchar info_codes_ {};
double total_energy_mj_ {};
double total_volume_m3_ {};
double volume_flow_m3h_ {};
double t1_temperature_c_ { 127 };
bool has_t1_temperature_ {};
double t2_temperature_c_ { 127 };
bool has_t2_temperature_ {};
string target_date_ {};
};
MeterMultical403::MeterMultical403(MeterInfo &mi) :
MeterCommonImplementation(mi, "multical403")
{
setMeterType(MeterType::HeatMeter);
setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR);
addLinkMode(LinkMode::C1);
addPrint("total_energy_consumption", Quantity::Energy,
[&](Unit u){ return totalEnergyConsumption(u); },
"The total energy consumption recorded by this meter.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("total_volume", Quantity::Volume,
[&](Unit u){ return totalVolume(u); },
"Total volume of media.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("volume_flow", Quantity::Flow,
[&](Unit u){ return volumeFlow(u); },
"The current flow.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("t1_temperature", Quantity::Temperature,
[&](Unit u){ return t1Temperature(u); },
"The T1 temperature.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("t2_temperature", Quantity::Temperature,
[&](Unit u){ return t2Temperature(u); },
"The T2 temperature.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("at_date", Quantity::Text,
[&](){ return target_date_; },
"Date when total energy consumption was recorded.",
PrintProperty::JSON);
addPrint("current_status", Quantity::Text,
[&](){ return status(); },
"Status of meter.",
PrintProperty::FIELD | PrintProperty::JSON);
}
shared_ptr<Meter> createMultical403(MeterInfo &mi) {
return shared_ptr<Meter>(new MeterMultical403(mi));
}
double MeterMultical403::totalEnergyConsumption(Unit u)
{
assertQuantity(u, Quantity::Energy);
return convert(total_energy_mj_, Unit::MJ, u);
}
double MeterMultical403::totalVolume(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_volume_m3_, Unit::M3, u);
}
double MeterMultical403::t1Temperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(t1_temperature_c_, Unit::C, u);
}
bool MeterMultical403::hasT1Temperature()
{
return has_t1_temperature_;
}
double MeterMultical403::t2Temperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(t2_temperature_c_, Unit::C, u);
}
bool MeterMultical403::hasT2Temperature()
{
return has_t2_temperature_;
}
double MeterMultical403::volumeFlow(Unit u)
{
assertQuantity(u, Quantity::Flow);
return convert(volume_flow_m3h_, Unit::M3H, u);
}
void MeterMultical403::processContent(Telegram *t)
{
int offset;
string key;
extractDVuint8(&t->dv_entries, "04FF22", &offset, &info_codes_);
t->addMoreExplanation(offset, " info codes (%s)", status().c_str());
if(findKey(MeasurementType::Instantaneous, VIFRange::EnergyMJ, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_energy_mj_);
t->addMoreExplanation(offset, " total energy consumption (%f MJ)", total_energy_mj_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_volume_m3_);
t->addMoreExplanation(offset, " total volume (%f m3)", total_volume_m3_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::VolumeFlow, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &volume_flow_m3h_);
t->addMoreExplanation(offset, " volume flow (%f m3/h)", volume_flow_m3h_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::FlowTemperature, 0, 0, &key, &t->dv_entries)) {
has_t1_temperature_ = extractDVdouble(&t->dv_entries, key, &offset, &t1_temperature_c_);
t->addMoreExplanation(offset, " T1 flow temperature (%f °C)", t1_temperature_c_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::ReturnTemperature, 0, 0, &key, &t->dv_entries)) {
has_t2_temperature_ = extractDVdouble(&t->dv_entries, key, &offset, &t2_temperature_c_);
t->addMoreExplanation(offset, " T2 flow temperature (%f °C)", t2_temperature_c_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 0, 0, &key, &t->dv_entries)) {
struct tm datetime;
extractDVdate(&t->dv_entries, key, &offset, &datetime);
target_date_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " target date (%s)", target_date_.c_str());
}
}
string MeterMultical403::status()
{
string s;
if (info_codes_ & INFO_CODE_VOLTAGE_INTERRUPTED) s.append("VOLTAGE_INTERRUPTED ");
if (info_codes_ & INFO_CODE_LOW_BATTERY_LEVEL) s.append("LOW_BATTERY_LEVEL ");
if (info_codes_ & INFO_CODE_EXTERNAL_ALARM) s.append("EXTERNAL_ALARM ");
if (info_codes_ & INFO_CODE_SENSOR_T1_ABOVE_MEASURING_RANGE) s.append("SENSOR_T1_ABOVE_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T2_ABOVE_MEASURING_RANGE) s.append("SENSOR_T2_ABOVE_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T1_BELOW_MEASURING_RANGE) s.append("SENSOR_T1_BELOW_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T2_BELOW_MEASURING_RANGE) s.append("SENSOR_T2_BELOW_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_TEMP_DIFF_WRONG_POLARITY) s.append("TEMP_DIFF_WRONG_POLARITY ");
if (s.length() > 0) {
s.pop_back(); // Remove final space
return s;
}
return s;
}

Wyświetl plik

@ -1,285 +0,0 @@
/*
Copyright (C) 2018-2021 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2020 Eric Bus (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.h"
#include"meters_common_implementation.h"
#include"dvparser.h"
#include"wmbus.h"
#include"wmbus_utils.h"
#include"util.h"
#define INFO_CODE_VOLTAGE_INTERRUPTED 1
#define INFO_CODE_LOW_BATTERY_LEVEL 2
#define INFO_CODE_EXTERNAL_ALARM 4
#define INFO_CODE_SENSOR_T1_ABOVE_MEASURING_RANGE 8
#define INFO_CODE_SENSOR_T2_ABOVE_MEASURING_RANGE 16
#define INFO_CODE_SENSOR_T1_BELOW_MEASURING_RANGE 32
#define INFO_CODE_SENSOR_T2_BELOW_MEASURING_RANGE 64
#define INFO_CODE_TEMP_DIFF_WRONG_POLARITY 128
struct MeterMultical602 : public virtual MeterCommonImplementation {
MeterMultical602(MeterInfo &mi);
double totalEnergyConsumption(Unit u);
string status();
double totalVolume(Unit u);
double volumeFlow(Unit u);
// Water temperatures
double t1Temperature(Unit u);
bool hasT1Temperature();
double t2Temperature(Unit u);
bool hasT2Temperature();
private:
void processContent(Telegram *t);
uchar info_codes_ {};
double total_energy_kwh_ {};
double total_volume_m3_ {};
double volume_flow_m3h_ {};
double t1_temperature_c_ { 127 };
bool has_t1_temperature_ {};
double t2_temperature_c_ { 127 };
bool has_t2_temperature_ {};
string target_date_ {};
uint32_t energy_forward_kwh_ {};
uint32_t energy_returned_kwh_ {};
};
MeterMultical602::MeterMultical602(MeterInfo &mi) :
MeterCommonImplementation(mi, "multical602")
{
setMeterType(MeterType::HeatMeter);
setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR);
addLinkMode(LinkMode::C1);
addPrint("total_energy_consumption", Quantity::Energy,
[&](Unit u){ return totalEnergyConsumption(u); },
"The total energy consumption recorded by this meter.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("total_volume", Quantity::Volume,
[&](Unit u){ return totalVolume(u); },
"Total volume of media.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("volume_flow", Quantity::Flow,
[&](Unit u){ return volumeFlow(u); },
"The current flow.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("t1_temperature", Quantity::Temperature,
[&](Unit u){ return t1Temperature(u); },
"The T1 temperature.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("t2_temperature", Quantity::Temperature,
[&](Unit u){ return t2Temperature(u); },
"The T2 temperature.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("at_date", Quantity::Text,
[&](){ return target_date_; },
"Date when total energy consumption was recorded.",
PrintProperty::JSON);
addPrint("current_status", Quantity::Text,
[&](){ return status(); },
"Status of meter.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("energy_forward", Quantity::Energy,
[&](Unit u){ assertQuantity(u, Quantity::Energy); return convert(energy_forward_kwh_, Unit::KWH, u); },
"Energy forward.",
PrintProperty::JSON);
addPrint("energy_returned", Quantity::Energy,
[&](Unit u){ assertQuantity(u, Quantity::Energy); return convert(energy_returned_kwh_, Unit::KWH, u); },
"Energy returned.",
PrintProperty::JSON);
}
shared_ptr<Meter> createMultical602(MeterInfo &mi) {
return shared_ptr<Meter>(new MeterMultical602(mi));
}
double MeterMultical602::totalEnergyConsumption(Unit u)
{
assertQuantity(u, Quantity::Energy);
return convert(total_energy_kwh_, Unit::KWH, u);
}
double MeterMultical602::totalVolume(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_volume_m3_, Unit::M3, u);
}
double MeterMultical602::t1Temperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(t1_temperature_c_, Unit::C, u);
}
bool MeterMultical602::hasT1Temperature()
{
return has_t1_temperature_;
}
double MeterMultical602::t2Temperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(t2_temperature_c_, Unit::C, u);
}
bool MeterMultical602::hasT2Temperature()
{
return has_t2_temperature_;
}
double MeterMultical602::volumeFlow(Unit u)
{
assertQuantity(u, Quantity::Flow);
return convert(volume_flow_m3h_, Unit::M3H, u);
}
void MeterMultical602::processContent(Telegram *t)
{
/*
(multical602) 14: 02 dif (16 Bit Integer/Binary Instantaneous value)
(multical602) 15: F9 vif (Enhanced identification)
(multical602) 16: FF vife (additive correction constant: unit of VIF * 10^0)
(multical602) 17: 15 vife (?)
(multical602) 18: 1113
(multical602) 1a: 04 dif (32 Bit Integer/Binary Instantaneous value)
(multical602) 1b: 06 vif (Energy kWh)
(multical602) 1c: * 690B0100 total energy consumption (68457.000000 kWh)
(multical602) 20: 04 dif (32 Bit Integer/Binary Instantaneous value)
(multical602) 21: EE vif (Units for H.C.A.)
(multical602) 22: FF vife (additive correction constant: unit of VIF * 10^0)
(multical602) 23: 07 vife (?)
(multical602) 24: C1BC0200
(multical602) 28: 04 dif (32 Bit Integer/Binary Instantaneous value)
(multical602) 29: EE vif (Units for H.C.A.)
(multical602) 2a: FF vife (additive correction constant: unit of VIF * 10^0)
(multical602) 2b: 08 vife (?)
(multical602) 2c: 90D40100
(multical602) 30: 04 dif (32 Bit Integer/Binary Instantaneous value)
(multical602) 31: 14 vif (Volume 10² m³)
(multical602) 32: * A9250400 total volume (2717.850000 m3)
(multical602) 36: 84 dif (32 Bit Integer/Binary Instantaneous value)
(multical602) 37: 40 dife (subunit=1 tariff=0 storagenr=0)
(multical602) 38: 14 vif (Volume 10² m³)
(multical602) 39: 00000000
(multical602) 3d: 84 dif (32 Bit Integer/Binary Instantaneous value)
(multical602) 3e: 80 dife (subunit=0 tariff=0 storagenr=0)
(multical602) 3f: 40 dife (subunit=2 tariff=0 storagenr=0)
(multical602) 40: 14 vif (Volume 10² m³)
(multical602) 41: 00000000
(multical602) 45: 02 dif (16 Bit Integer/Binary Instantaneous value)
(multical602) 46: FD vif (Second extension FD of VIF-codes)
(multical602) 47: 17 vife (Error flags (binary))
(multical602) 48: 0000
(multical602) 4a: 02 dif (16 Bit Integer/Binary Instantaneous value)
(multical602) 4b: 6C vif (Date type G)
(multical602) 4c: * B929 target date (2021-09-25 00:00)
(multical602) 4e: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
(multical602) 4f: 6C vif (Date type G)
(multical602) 50: BF28
(multical602) 52: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(multical602) 53: 06 vif (Energy kWh)
(multical602) 54: 100A0100
(multical602) 58: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(multical602) 59: 14 vif (Volume 10² m³)
(multical602) 5a: D81A0400
(multical602) 5e: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(multical602) 5f: 40 dife (subunit=1 tariff=0 storagenr=1)
(multical602) 60: 14 vif (Volume 10² m³)
(multical602) 61: 00000000
(multical602) 65: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(multical602) 66: 80 dife (subunit=0 tariff=0 storagenr=1)
(multical602) 67: 40 dife (subunit=2 tariff=0 storagenr=1)
(multical602) 68: 14 vif (Volume 10² m³)
(multical602) 69: 00000000
(multical602) 6d: 04 dif (32 Bit Integer/Binary Instantaneous value)
(multical602) 6e: 3B vif (Volume flow l/h)
(multical602) 6f: * 39000000 volume flow (0.057000 m3/h)
(multical602) 73: 02 dif (16 Bit Integer/Binary Instantaneous value)
(multical602) 74: 59 vif (Flow temperature 10² °C)
(multical602) 75: * 2A17 T1 flow temperature (59.300000 °C)
(multical602) 77: 02 dif (16 Bit Integer/Binary Instantaneous value)
(multical602) 78: 5D vif (Return temperature 10² °C)
(multical602) 79: * 2912 T2 flow temperature (46.490000 °C)
*/
int offset;
string key;
if(findKey(MeasurementType::Instantaneous, VIFRange::EnergyWh, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_energy_kwh_);
t->addMoreExplanation(offset, " total energy consumption (%f kWh)", total_energy_kwh_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_volume_m3_);
t->addMoreExplanation(offset, " total volume (%f m3)", total_volume_m3_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::VolumeFlow, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &volume_flow_m3h_);
t->addMoreExplanation(offset, " volume flow (%f m3/h)", volume_flow_m3h_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::FlowTemperature, 0, 0, &key, &t->dv_entries)) {
has_t1_temperature_ = extractDVdouble(&t->dv_entries, key, &offset, &t1_temperature_c_);
t->addMoreExplanation(offset, " T1 flow temperature (%f °C)", t1_temperature_c_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::ReturnTemperature, 0, 0, &key, &t->dv_entries)) {
has_t2_temperature_ = extractDVdouble(&t->dv_entries, key, &offset, &t2_temperature_c_);
t->addMoreExplanation(offset, " T2 flow temperature (%f °C)", t2_temperature_c_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 0, 0, &key, &t->dv_entries)) {
struct tm datetime;
extractDVdate(&t->dv_entries, key, &offset, &datetime);
target_date_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " target date (%s)", target_date_.c_str());
}
}
string MeterMultical602::status()
{
string s;
if (info_codes_ & INFO_CODE_VOLTAGE_INTERRUPTED) s.append("VOLTAGE_INTERRUPTED ");
if (info_codes_ & INFO_CODE_LOW_BATTERY_LEVEL) s.append("LOW_BATTERY_LEVEL ");
if (info_codes_ & INFO_CODE_EXTERNAL_ALARM) s.append("EXTERNAL_ALARM ");
if (info_codes_ & INFO_CODE_SENSOR_T1_ABOVE_MEASURING_RANGE) s.append("SENSOR_T1_ABOVE_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T2_ABOVE_MEASURING_RANGE) s.append("SENSOR_T2_ABOVE_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T1_BELOW_MEASURING_RANGE) s.append("SENSOR_T1_BELOW_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T2_BELOW_MEASURING_RANGE) s.append("SENSOR_T2_BELOW_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_TEMP_DIFF_WRONG_POLARITY) s.append("TEMP_DIFF_WRONG_POLARITY ");
if (s.length() > 0) {
s.pop_back(); // Remove final space
return s;
}
return s;
}

Wyświetl plik

@ -1,298 +0,0 @@
/*
Copyright (C) 2018-2020 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2020 Eric Bus (gpl-3.0-or-later)
Copyright (C) 2020 Nikodem Jędrzejczak (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.h"
#include"meters_common_implementation.h"
#include"dvparser.h"
#include"wmbus.h"
#include"wmbus_utils.h"
#include"util.h"
#define INFO_CODE_VOLTAGE_INTERRUPTED 1
#define INFO_CODE_LOW_BATTERY_LEVEL 2
#define INFO_CODE_EXTERNAL_ALARM 4
#define INFO_CODE_SENSOR_T1_ABOVE_MEASURING_RANGE 8
#define INFO_CODE_SENSOR_T2_ABOVE_MEASURING_RANGE 16
#define INFO_CODE_SENSOR_T1_BELOW_MEASURING_RANGE 32
#define INFO_CODE_SENSOR_T2_BELOW_MEASURING_RANGE 64
#define INFO_CODE_TEMP_DIFF_WRONG_POLARITY 128
struct MeterMultical803 : public virtual MeterCommonImplementation {
MeterMultical803(MeterInfo &mi);
double totalEnergyConsumption(Unit u);
string status();
double totalVolume(Unit u);
double volumeFlow(Unit u);
// Water temperatures
double t1Temperature(Unit u);
bool hasT1Temperature();
double t2Temperature(Unit u);
bool hasT2Temperature();
private:
void processContent(Telegram *t);
uchar info_codes_ {};
double total_energy_mj_ {};
double total_volume_m3_ {};
double volume_flow_m3h_ {};
double t1_temperature_c_ { 127 };
bool has_t1_temperature_ {};
double t2_temperature_c_ { 127 };
bool has_t2_temperature_ {};
string target_date_ {};
uint32_t energy_forward_mj_ {};
uint32_t energy_returned_mj_ {};
};
MeterMultical803::MeterMultical803(MeterInfo &mi) :
MeterCommonImplementation(mi, "multical803")
{
setMeterType(MeterType::HeatMeter);
setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR);
addLinkMode(LinkMode::C1);
addPrint("total_energy_consumption", Quantity::Energy,
[&](Unit u){ return totalEnergyConsumption(u); },
"The total energy consumption recorded by this meter.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("total_volume", Quantity::Volume,
[&](Unit u){ return totalVolume(u); },
"Total volume of media.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("volume_flow", Quantity::Flow,
[&](Unit u){ return volumeFlow(u); },
"The current flow.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("t1_temperature", Quantity::Temperature,
[&](Unit u){ return t1Temperature(u); },
"The T1 temperature.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("t2_temperature", Quantity::Temperature,
[&](Unit u){ return t2Temperature(u); },
"The T2 temperature.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("at_date", Quantity::Text,
[&](){ return target_date_; },
"Date when total energy consumption was recorded.",
PrintProperty::JSON);
addPrint("current_status", Quantity::Text,
[&](){ return status(); },
"Status of meter.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("energy_forward", Quantity::Energy,
[&](Unit u){ assertQuantity(u, Quantity::Energy); return convert(energy_forward_mj_, Unit::MJ, u); },
"Energy forward.",
PrintProperty::JSON);
addPrint("energy_returned", Quantity::Energy,
[&](Unit u){ assertQuantity(u, Quantity::Energy); return convert(energy_returned_mj_, Unit::MJ, u); },
"Energy returned.",
PrintProperty::JSON);
}
shared_ptr<Meter> createMultical803(MeterInfo &mi) {
return shared_ptr<Meter>(new MeterMultical803(mi));
}
double MeterMultical803::totalEnergyConsumption(Unit u)
{
assertQuantity(u, Quantity::Energy);
return convert(total_energy_mj_, Unit::MJ, u);
}
double MeterMultical803::totalVolume(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_volume_m3_, Unit::M3, u);
}
double MeterMultical803::t1Temperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(t1_temperature_c_, Unit::C, u);
}
bool MeterMultical803::hasT1Temperature()
{
return has_t1_temperature_;
}
double MeterMultical803::t2Temperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(t2_temperature_c_, Unit::C, u);
}
bool MeterMultical803::hasT2Temperature()
{
return has_t2_temperature_;
}
double MeterMultical803::volumeFlow(Unit u)
{
assertQuantity(u, Quantity::Flow);
return convert(volume_flow_m3h_, Unit::M3H, u);
}
void MeterMultical803::processContent(Telegram *t)
{
/*
(wmbus) 14: 04 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 15: 0F vif (Energy 10 J)
(wmbus) 16: 00000000
(wmbus) 1a: 04 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 1b: FF vif (Vendor extension)
(wmbus) 1c: 07 vife (?)
(wmbus) 1d: 00000000
(wmbus) 21: 04 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 22: FF vif (Vendor extension)
(wmbus) 23: 08 vife (?)
(wmbus) 24: 00000000
(wmbus) 28: 04 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 29: 14 vif (Volume 10² m³)
(wmbus) 2a: 00000000
(wmbus) 2e: 84 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 2f: 40 dife (subunit=1 tariff=0 storagenr=0)
(wmbus) 30: 14 vif (Volume 10² m³)
(wmbus) 31: 00000000
(wmbus) 35: 84 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 36: 80 dife (subunit=0 tariff=0 storagenr=0)
(wmbus) 37: 40 dife (subunit=2 tariff=0 storagenr=0)
(wmbus) 38: 14 vif (Volume 10² m³)
(wmbus) 39: 00000000
(wmbus) 3d: 04 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 3e: 3B vif (Volume flow l/h)
(wmbus) 3f: 00000000
(wmbus) 43: 02 dif (16 Bit Integer/Binary Instantaneous value)
(wmbus) 44: 59 vif (Flow temperature 10² °C)
(wmbus) 45: 0000
(wmbus) 47: 02 dif (16 Bit Integer/Binary Instantaneous value)
(wmbus) 48: 5D vif (Return temperature 10² °C)
(wmbus) 49: 0000
(wmbus) 4b: 14 dif (32 Bit Integer/Binary Maximum value)
(wmbus) 4c: 2D vif (Power 10² W)
(wmbus) 4d: 00000000
(wmbus) 51: 84 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 52: 10 dife (subunit=0 tariff=1 storagenr=0)
(wmbus) 53: 0F vif (Energy 10 J)
(wmbus) 54: 00000000
(wmbus) 58: 84 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 59: 20 dife (subunit=0 tariff=2 storagenr=0)
(wmbus) 5a: 0F vif (Energy 10 J)
(wmbus) 5b: 00000000
(wmbus) 5f: 04 dif (32 Bit Integer/Binary Instantaneous value)
(wmbus) 60: FF vif (Vendor extension)
(wmbus) 61: 22 vife (per hour)
(wmbus) 62: 60000100
(wmbus) 66: 02 dif (16 Bit Integer/Binary Instantaneous value)
(wmbus) 67: 6C vif (Date type G)
(wmbus) 68: 892B
(wmbus) 6a: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(wmbus) 6b: 0F vif (Energy 10 J)
(wmbus) 6c: 00000000
(wmbus) 70: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(wmbus) 71: 14 vif (Volume 10² m³)
(wmbus) 72: 00000000
(wmbus) 76: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(wmbus) 77: 40 dife (subunit=1 tariff=0 storagenr=1)
(wmbus) 78: 14 vif (Volume 10² m³)
(wmbus) 79: 00000000
(wmbus) 7d: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(wmbus) 7e: 80 dife (subunit=0 tariff=0 storagenr=1)
(wmbus) 7f: 40 dife (subunit=2 tariff=0 storagenr=1)
(wmbus) 80: 14 vif (Volume 10² m³)
(wmbus) 81: 00000000
(wmbus) 85: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
(wmbus) 86: 6C vif (Date type G)
(wmbus) 87: 812B
*/
int offset;
string key;
extractDVuint8(&t->dv_entries, "04FF22", &offset, &info_codes_);
t->addMoreExplanation(offset, " info codes (%s)", status().c_str());
extractDVuint32(&t->dv_entries, "04FF07", &offset, &energy_forward_mj_);
t->addMoreExplanation(offset, " energy forward mj (%zu)", energy_forward_mj_);
extractDVuint32(&t->dv_entries, "04FF08", &offset, &energy_returned_mj_);
t->addMoreExplanation(offset, " energy returned mj (%zu)", energy_returned_mj_);
if(findKey(MeasurementType::Instantaneous, VIFRange::EnergyMJ, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_energy_mj_);
t->addMoreExplanation(offset, " total energy consumption (%f MJ)", total_energy_mj_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_volume_m3_);
t->addMoreExplanation(offset, " total volume (%f m3)", total_volume_m3_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::VolumeFlow, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &volume_flow_m3h_);
t->addMoreExplanation(offset, " volume flow (%f m3/h)", volume_flow_m3h_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::FlowTemperature, 0, 0, &key, &t->dv_entries)) {
has_t1_temperature_ = extractDVdouble(&t->dv_entries, key, &offset, &t1_temperature_c_);
t->addMoreExplanation(offset, " T1 flow temperature (%f °C)", t1_temperature_c_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::ReturnTemperature, 0, 0, &key, &t->dv_entries)) {
has_t2_temperature_ = extractDVdouble(&t->dv_entries, key, &offset, &t2_temperature_c_);
t->addMoreExplanation(offset, " T2 flow temperature (%f °C)", t2_temperature_c_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 0, 0, &key, &t->dv_entries)) {
struct tm datetime;
extractDVdate(&t->dv_entries, key, &offset, &datetime);
target_date_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " target date (%s)", target_date_.c_str());
}
}
string MeterMultical803::status()
{
string s;
if (info_codes_ & INFO_CODE_VOLTAGE_INTERRUPTED) s.append("VOLTAGE_INTERRUPTED ");
if (info_codes_ & INFO_CODE_LOW_BATTERY_LEVEL) s.append("LOW_BATTERY_LEVEL ");
if (info_codes_ & INFO_CODE_EXTERNAL_ALARM) s.append("EXTERNAL_ALARM ");
if (info_codes_ & INFO_CODE_SENSOR_T1_ABOVE_MEASURING_RANGE) s.append("SENSOR_T1_ABOVE_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T2_ABOVE_MEASURING_RANGE) s.append("SENSOR_T2_ABOVE_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T1_BELOW_MEASURING_RANGE) s.append("SENSOR_T1_BELOW_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_SENSOR_T2_BELOW_MEASURING_RANGE) s.append("SENSOR_T2_BELOW_MEASURING_RANGE ");
if (info_codes_ & INFO_CODE_TEMP_DIFF_WRONG_POLARITY) s.append("TEMP_DIFF_WRONG_POLARITY ");
if (s.length() > 0) {
s.pop_back(); // Remove final space
return s;
}
return s;
}

Wyświetl plik

@ -18,7 +18,6 @@
#include"bus.h"
#include"config.h"
#include"meters.h"
#include"meter_detection.h"
#include"meters_common_implementation.h"
#include"units.h"
#include"wmbus.h"
@ -174,11 +173,11 @@ public:
meter_info.ids = tmp_ids;
meter_info.idsc = t.ids.back();
if (meter_info.driver == MeterDriver::AUTO)
if (meter_info.driverName().str() == "auto")
{
// Look up the proper meter driver!
DriverInfo di = pickMeterDriver(&t);
if (di.driver() == MeterDriver::UNKNOWN && di.name().str() == "")
if (di.name().str() == "")
{
if (should_analyze_ == false)
{
@ -188,7 +187,6 @@ public:
}
else
{
meter_info.driver = di.driver();
meter_info.driver_name = di.name();
}
}
@ -199,7 +197,7 @@ public:
verbose("(meter) used meter template %s %s %s to match %s\n",
mi.name.c_str(),
mi.idsc.c_str(),
toString(mi.driver).c_str(),
mi.driverName().str().c_str(),
idsc.c_str());
if (is_daemon_)
@ -208,15 +206,15 @@ public:
meter->index(),
mi.name.c_str(),
meter_info.idsc.c_str(),
toString(mi.driver).c_str());
mi.driverName().str().c_str());
}
else
{
verbose("(meter) started meter %d (%s %s %s)\n",
meter->index(),
mi.name.c_str(),
meter_info.idsc.c_str(),
toString(mi.driver).c_str());
meter->index(),
mi.name.c_str(),
meter_info.idsc.c_str(),
mi.driverName().str().c_str());
}
bool match = false;
@ -286,79 +284,6 @@ public:
analyze_verbose_ = verbose;
}
string findBestOldStyleDriver(MeterInfo &mi,
int *best_length,
int *best_understood,
Telegram &t,
AboutTelegram &about,
vector<uchar> &input_frame,
bool simulated,
string force)
{
vector<MeterDriver> old_drivers;
#define X(mname,linkmode,info,type,cname) old_drivers.push_back(MeterDriver::type);
LIST_OF_METERS
#undef X
string best_driver = "";
for (MeterDriver odr : old_drivers)
{
if (odr == MeterDriver::AUTO) continue;
if (odr == MeterDriver::UNKNOWN) continue;
string driver_name = toString(odr);
if (force != "")
{
if (driver_name != force) continue;
return driver_name;
}
if (force == "" &&
!isMeterDriverReasonableForMedia(odr, "", t.dll_type) &&
!isMeterDriverReasonableForMedia(odr, "", t.tpl_type))
{
// Sanity check, skip this driver since it is not relevant for this media.
continue;
}
debug("Testing old style driver %s...\n", driver_name.c_str());
mi.driver = odr;
mi.driver_name = DriverName("");
auto meter = createMeter(&mi);
bool match = false;
string id;
bool h = meter->handleTelegram(about, input_frame, simulated, &id, &match, &t);
if (!match)
{
debug("no match!\n");
}
else if (!h)
{
// Oups, we added a new meter object tailored for this telegram
// but it still did not handle it! This can happen if the wrong
// decryption key was used. But it is ok if analyzing....
debug("Newly created meter (%s %s %s) did not handle telegram!\n",
meter->name().c_str(), meter->idsc().c_str(), meter->driverName().str().c_str());
}
else
{
int l = 0;
int u = 0;
t.analyzeParse(OutputFormat::NONE, &l, &u);
if (analyze_verbose_ && force == "") printf("(verbose) old %02d/%02d %s\n", u, l, driver_name.c_str());
if (u > *best_understood)
{
*best_understood = u;
*best_length = l;
best_driver = driver_name;
if (analyze_verbose_ && force == "") printf("(verbose) old best so far: %s %02d/%02d\n", best_driver.c_str(), u, l);
}
}
}
return best_driver;
}
string findBestNewStyleDriver(MeterInfo &mi,
int *best_length,
int *best_understood,
@ -380,15 +305,14 @@ LIST_OF_METERS
}
if (only == "" &&
!isMeterDriverReasonableForMedia(MeterDriver::AUTO, driver_name, t.dll_type) &&
!isMeterDriverReasonableForMedia(MeterDriver::AUTO, driver_name, t.tpl_type))
!isMeterDriverReasonableForMedia(driver_name, t.dll_type) &&
!isMeterDriverReasonableForMedia(driver_name, t.tpl_type))
{
// Sanity check, skip this driver since it is not relevant for this media.
continue;
}
debug("Testing new style driver %s...\n", driver_name.c_str());
mi.driver = MeterDriver::UNKNOWN;
debug("Testing driver %s...\n", driver_name.c_str());
mi.driver_name = driver_name;
auto meter = createMeter(&mi);
@ -465,32 +389,15 @@ LIST_OF_METERS
int using_understood = 0;
// Driver that understands most of the telegram content.
string best_driver = "";
int best_length = 0;
int best_understood = 0;
string best_driver = findBestNewStyleDriver(mi, &best_length, &best_understood, t, about, input_frame, simulated, "");
int old_best_length = 0;
int old_best_understood = 0;
string best_old_driver = findBestOldStyleDriver(mi, &old_best_length, &old_best_understood, t, about, input_frame, simulated, "");
int new_best_length = 0;
int new_best_understood = 0;
string best_new_driver = findBestNewStyleDriver(mi, &new_best_length, &new_best_understood, t, about, input_frame, simulated, "");
mi.driver = MeterDriver::UNKNOWN;
mi.driver_name = DriverName("");
// Use the existing mapping from mfct/media/version to driver.
DriverInfo auto_di = pickMeterDriver(&t);
string auto_driver = auto_di.name().str();
if (auto_driver == "")
{
auto_driver = toString(auto_di.driver());
if (auto_driver == "unknown")
{
auto_driver = "";
}
}
// Will be non-empty if an explicit driver has been selected.
string force_driver = analyze_driver_;
@ -505,55 +412,14 @@ LIST_OF_METERS
if (force_driver != "")
{
using_driver = findBestOldStyleDriver(mi, &force_length, &force_understood, t, about, input_frame, simulated,
using_driver = findBestNewStyleDriver(mi, &force_length, &force_understood, t, about, input_frame, simulated,
force_driver);
mi.driver_name = using_driver;
if (using_driver != "")
{
mi.driver = toMeterDriver(using_driver);
mi.driver_name = DriverName("");
using_driver += "(driver should be upgraded)";
}
else
{
using_driver = findBestNewStyleDriver(mi, &force_length, &force_understood, t, about, input_frame, simulated,
force_driver);
mi.driver_name = using_driver;
mi.driver = MeterDriver::UNKNOWN;
}
using_length = force_length;
using_understood = force_understood;
}
if (old_best_understood > new_best_understood)
{
best_length = old_best_length;
best_understood = old_best_understood;
best_driver = best_old_driver+"(driver should be upgraded)";
if (using_driver == "")
{
mi.driver = toMeterDriver(best_old_driver);
mi.driver_name = DriverName("");
using_driver = best_driver;
using_length = best_length;
using_understood = best_understood;
}
}
else if (new_best_understood >= old_best_understood)
{
best_length = new_best_length;
best_understood = new_best_understood;
best_driver = best_new_driver;
if (using_driver == "")
{
mi.driver_name = best_new_driver;
mi.driver = MeterDriver::UNKNOWN;
using_driver = best_new_driver;
using_length = best_length;
using_understood = best_understood;
}
}
auto meter = createMeter(&mi);
bool match = false;

Wyświetl plik

@ -18,7 +18,6 @@
#include"bus.h"
#include"config.h"
#include"meters.h"
#include"meter_detection.h"
#include"meters_common_implementation.h"
#include"units.h"
#include"wmbus.h"
@ -167,7 +166,7 @@ bool lookupDriverInfo(const string& driver, DriverInfo *out_di)
MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi,
string driver) :
driver_(driver), bus_(mi.bus), name_(mi.name), waiting_for_poll_response_sem_("waiting_for_poll_response")
driver_name_(driver), bus_(mi.bus), name_(mi.name), waiting_for_poll_response_sem_("waiting_for_poll_response")
{
ids_ = mi.ids;
idsc_ = toIdsCommaSeparated(ids_);
@ -190,7 +189,6 @@ MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi,
MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi,
DriverInfo &di) :
type_(di.type()),
driver_(di.name().str()),
driver_name_(di.name()),
bus_(mi.bus),
name_(mi.name),
@ -273,17 +271,8 @@ vector<string> &MeterCommonImplementation::meterExtraConstantFields()
return extra_constant_fields_;
}
MeterDriver MeterCommonImplementation::driver()
{
return toMeterDriver(driver_);
}
DriverName MeterCommonImplementation::driverName()
{
if (driver_name_.str() == "")
{
return DriverName(toString(driver()));
}
return driver_name_;
}
@ -899,16 +888,8 @@ bool MeterCommonImplementation::usesPolling()
link_modes_.has(LinkMode::S2);
}
bool driverNeedsPolling(MeterDriver d, DriverName& dn)
bool driverNeedsPolling(DriverName& dn)
{
if (d != MeterDriver::UNKNOWN && d != MeterDriver::AUTO)
{
#define X(mname,linkmodes,info,driver,cname) if (d == MeterDriver::driver && 0 != ((linkmodes) & MBUS_bit)) return true;
LIST_OF_METERS
#undef X
return false;
}
DriverInfo *di = lookupDriver(dn.str());
if (di == NULL) return false;
@ -928,51 +909,17 @@ LIST_OF_METER_TYPES
return "unknown";
}
string toString(MeterDriver mt)
{
#define X(mname,link,info,type,cname) if (mt == MeterDriver::type) return #mname;
LIST_OF_METERS
#undef X
return "unknown";
}
string toString(DriverInfo &di)
{
if (di.driver() != MeterDriver::UNKNOWN &&
di.driver() != MeterDriver::AUTO) return toString(di.driver());
return di.name().str();
}
MeterDriver toMeterDriver(const string& t)
{
#define X(mname,linkmodes,info,type,cname) if (t == #mname) return MeterDriver::type;
LIST_OF_METERS
#undef X
return MeterDriver::UNKNOWN;
}
LinkModeSet toMeterLinkModeSet(const string& t)
{
#define X(mname,linkmodes,info,type,cname) if (t == #mname) return LinkModeSet(linkmodes);
LIST_OF_METERS
#undef X
return LinkModeSet();
}
LinkModeSet toMeterLinkModeSet(MeterDriver d)
{
#define X(mname,linkmodes,info,driver,cname) if (d == MeterDriver::driver) return LinkModeSet(linkmodes);
LIST_OF_METERS
#undef X
return LinkModeSet();
}
bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, MeterInfo *mi)
{
string name;
vector<string> ids;
string idsc;
MeterDriver driver;
string driver_name;
assert((meter && !mi) ||
(!meter && mi));
@ -982,14 +929,14 @@ bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, Me
name = meter->name();
ids = meter->ids();
idsc = meter->idsc();
driver = meter->driver();
driver_name = meter->driverName().str();
}
else
{
name = mi->name;
ids = mi->ids;
idsc = mi->idsc;
driver = mi->driver;
driver_name = mi->driver_name.str();
}
debug("(meter) %s: for me? %s in %s\n", name.c_str(), t->idsc.c_str(), idsc.c_str());
@ -1003,13 +950,13 @@ bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, Me
return false;
}
bool valid_driver = isMeterDriverValid(driver, t->dll_mfct, t->dll_type, t->dll_version);
bool valid_driver = isMeterDriverValid(driver_name, t->dll_mfct, t->dll_type, t->dll_version);
if (!valid_driver && t->tpl_id_found)
{
valid_driver = isMeterDriverValid(driver, t->tpl_mfct, t->tpl_type, t->tpl_version);
valid_driver = isMeterDriverValid(driver_name, t->tpl_mfct, t->tpl_type, t->tpl_version);
}
if (!valid_driver && driver != MeterDriver::AUTO)
if (!valid_driver)
{
// Are we using the right driver? Perhaps not since
// this particular driver, mfct, media, version combo
@ -1020,8 +967,8 @@ bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, Me
// The match for the id was not exact, thus the user is listening using a wildcard
// to many meters and some received matched meter telegrams are not from the right meter type,
// ie their driver does not match. Lets just ignore telegrams that probably cannot be decoded properly.
verbose("(meter) ignoring telegram from %s since it matched a wildcard id rule but driver does not match.\n",
t->idsc.c_str());
verbose("(meter) ignoring telegram from %s since it matched a wildcard id rule but driver (%s) does not match.\n",
t->idsc.c_str(), driver_name.c_str());
return false;
}
@ -1036,7 +983,7 @@ bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, Me
warning("(meter) %s: meter detection did not match the selected driver %s! correct driver is: %s\n"
"(meter) Not printing this warning again for id: %02x%02x%02x%02x mfct: (%s) %s (0x%02x) type: %s (0x%02x) ver: 0x%02x\n",
name.c_str(),
toString(driver).c_str(),
driver_name.c_str(),
possible_drivers.c_str(),
t->dll_id_b[3], t->dll_id_b[2], t->dll_id_b[1], t->dll_id_b[0],
manufacturerFlag(t->dll_mfct).c_str(),
@ -1313,7 +1260,7 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<ucha
}
*id_match = true;
verbose("(meter) %s(%d) %s handling telegram from %s\n", name().c_str(), index(), meterDriver().c_str(), t.ids.back().c_str());
verbose("(meter) %s(%d) %s handling telegram from %s\n", name().c_str(), index(), driverName().str().c_str(), t.ids.back().c_str());
if (isDebugEnabled())
{
@ -1336,7 +1283,7 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<ucha
}
char log_prefix[256];
snprintf(log_prefix, 255, "(%s) log", meterDriver().c_str());
snprintf(log_prefix, 255, "(%s) log", driverName().str().c_str());
logTelegram(t.original, t.frame, t.header_size, t.suffix_size);
if (usesPolling())
@ -1355,7 +1302,7 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<ucha
if (isDebugEnabled())
{
char log_prefix[256];
snprintf(log_prefix, 255, "(%s)", meterDriver().c_str());
snprintf(log_prefix, 255, "(%s)", driverName().str().c_str());
t.explainParse(log_prefix, 0);
}
@ -1855,7 +1802,7 @@ void MeterCommonImplementation::printMeter(Telegram *t,
string s;
s += "{"+newline;
s += indent+"\"media\":\""+media+"\","+newline;
s += indent+"\"meter\":\""+meterDriver()+"\","+newline;
s += indent+"\"meter\":\""+driverName().str()+"\","+newline;
s += indent+"\"name\":\""+name()+"\","+newline;
if (t->ids.size() > 0)
{
@ -1967,7 +1914,7 @@ void MeterCommonImplementation::printMeter(Telegram *t,
}
envs->push_back(string("METER_NAME=")+name());
envs->push_back(string("METER_MEDIA=")+media);
envs->push_back(string("METER_TYPE=")+meterDriver());
envs->push_back(string("METER_TYPE=")+driverName().str());
envs->push_back(string("METER_TIMESTAMP=")+datetimeOfUpdateRobot());
envs->push_back(string("METER_TIMESTAMP_UTC=")+datetimeOfUpdateRobot());
envs->push_back(string("METER_TIMESTAMP_UT=")+unixTimestampOfUpdate());
@ -2032,10 +1979,6 @@ ELLSecurityMode MeterCommonImplementation::expectedELLSecurityMode()
void detectMeterDrivers(int manufacturer, int media, int version, vector<string> *drivers)
{
#define X(TY,MA,ME,VE) { if (manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { drivers->push_back(toString(MeterDriver::TY)); }}
METER_DETECTION
#undef X
for (DriverInfo *p : allDrivers())
{
if (p->detect(manufacturer, media, version))
@ -2045,32 +1988,22 @@ METER_DETECTION
}
}
bool isMeterDriverValid(MeterDriver type, int manufacturer, int media, int version)
bool isMeterDriverValid(DriverName driver_name, int manufacturer, int media, int version)
{
#define X(TY,MA,ME,VE) { if (type == MeterDriver::TY && manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { return true; }}
METER_DETECTION
#undef X
for (DriverInfo *p : allDrivers())
{
if (p->detect(manufacturer, media, version))
{
return true;
if (p->hasDriverName(driver_name)) return true;
}
}
return false;
}
bool isMeterDriverReasonableForMedia(MeterDriver type, string driver_name, int media)
bool isMeterDriverReasonableForMedia(string driver_name, int media)
{
if (media == 0x37) return false; // Skip converter meter side since they do not give any useful information.
if (driver_name == "")
{
#define X(TY,MA,ME,VE) { if (type == MeterDriver::TY && isCloseEnough(media, ME)) { return true; }}
METER_DETECTION
#undef X
}
for (DriverInfo *p : allDrivers())
{
@ -2083,6 +2016,8 @@ METER_DETECTION
return false;
}
DriverInfo driver_unknown_;
DriverInfo pickMeterDriver(Telegram *t)
{
int manufacturer = t->dll_mfct;
@ -2096,10 +2031,6 @@ DriverInfo pickMeterDriver(Telegram *t)
version = t->tpl_version;
}
#define X(TY,MA,ME,VE) { if (manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { return MeterDriver::TY; }}
METER_DETECTION
#undef X
for (DriverInfo *p : allDrivers())
{
if (p->detect(manufacturer, media, version))
@ -2108,7 +2039,7 @@ METER_DETECTION
}
}
return MeterDriver::UNKNOWN;
return driver_unknown_;
}
shared_ptr<Meter> createMeter(MeterInfo *mi)
@ -2143,25 +2074,10 @@ shared_ptr<Meter> createMeter(MeterInfo *mi)
return newm;
}
switch (mi->driver)
{
#define X(mname,link,info,driver,cname) \
case MeterDriver::driver: \
{ \
newm = create##cname(*mi); \
newm->setPollInterval(mi->poll_interval); \
verbose("(meter) created %s " #mname " %s %s\n", \
mi->name.c_str(), mi->idsc.c_str(), keymsg); \
return newm; \
} \
break;
LIST_OF_METERS
#undef X
}
return newm;
}
bool is_driver_and_extras(const string& t, MeterDriver *out_driver, DriverName *out_driver_name, string *out_extras)
bool is_driver_and_extras(const string& t, DriverName *out_driver_name, string *out_extras)
{
// piigth(jump=foo)
// multical21
@ -2179,13 +2095,9 @@ bool is_driver_and_extras(const string& t, MeterDriver *out_driver, DriverName *
{
*out_driver_name = di.name();
// We found a registered driver.
*out_driver = MeterDriver::AUTO; // To go away!
*out_extras = "";
return true;
}
MeterDriver md = toMeterDriver(t);
if (md == MeterDriver::UNKNOWN) return false;
*out_driver = md;
*out_extras = "";
return true;
}
@ -2198,16 +2110,9 @@ bool is_driver_and_extras(const string& t, MeterDriver *out_driver, DriverName *
bool found = lookupDriverInfo(type, &di);
MeterDriver md = toMeterDriver(type);
if (found)
{
*out_driver_name = di.name();
*out_driver = MeterDriver::AUTO; // To go away!
}
else
{
if (md == MeterDriver::UNKNOWN) return false;
*out_driver = md;
}
string extras = t.substr(ps+1, pe-ps-1);
@ -2219,7 +2124,7 @@ bool is_driver_and_extras(const string& t, MeterDriver *out_driver, DriverName *
string MeterInfo::str()
{
string r;
r += toString(driver);
r += driver_name.str();
if (extras != "")
{
r += "("+extras+")";
@ -2256,7 +2161,7 @@ bool MeterInfo::parse(string n, string d, string i, string k)
for (auto& p : parts)
{
if (!driverextras_checked && is_driver_and_extras(p, &driver, &driver_name, &extras))
if (!driverextras_checked && is_driver_and_extras(p, &driver_name, &extras))
{
driverextras_checked = true;
}
@ -2292,7 +2197,7 @@ bool MeterInfo::parse(string n, string d, string i, string k)
{
// No explicit link mode set, set to the default link modes
// that the meter can transmit on.
link_modes = toMeterLinkModeSet(driver);
// link_modes = toMeterLinkModeSet(driver);
}
return true;
@ -2386,10 +2291,6 @@ string FieldInfo::str()
DriverName MeterInfo::driverName()
{
if (driver_name.str() == "")
{
return DriverName(toString(driver));
}
return driver_name;
}

Wyświetl plik

@ -53,52 +53,25 @@ LIST_OF_METER_TYPES
#undef X
};
// This is the old style meter list. Drivers are succesively rewritten
// from meter_xyz.cc to driver_xyz.cc only old style drivers are listed here.
// The new driver_xyz.cc file format is selfcontained so eventually this
// macro LIST_OF_METERS will be empty and go away.
#define LIST_OF_METERS \
X(auto, 0, AutoMeter, AUTO, Auto) \
X(unknown, 0, UnknownMeter, UNKNOWN, Unknown) \
X(multical302,C1_bit|T1_bit, HeatMeter, MULTICAL302, Multical302) \
X(multical403,C1_bit, HeatMeter, MULTICAL403, Multical403) \
X(multical602,C1_bit, HeatMeter, MULTICAL602, Multical602) \
X(multical803,C1_bit, HeatMeter, MULTICAL803, Multical803) \
enum class MeterDriver {
#define X(mname,linkmode,info,type,cname) type,
LIST_OF_METERS
#undef X
};
struct DriverName
{
DriverName() {};
DriverName(string s) : name_(s) {};
string str() { return name_; }
const string &str() const { return name_; }
bool operator==(const DriverName &dn) const { return name_ == dn.name_; }
private:
string name_;
};
struct MeterMatch
{
MeterDriver driver;
int manufacturer;
int media;
int version;
};
// Return a list of matching drivers, like: multical21
void detectMeterDrivers(int manufacturer, int media, int version, std::vector<std::string> *drivers);
// When entering the driver, check that the telegram is indeed known to be
// compatible with the driver(type), if not then print a warning.
bool isMeterDriverValid(MeterDriver type, int manufacturer, int media, int version);
bool isMeterDriverValid(DriverName driver_name, int manufacturer, int media, int version);
// For an unknown telegram, when analyzing check if the media type is reasonable in relation to the driver.
// Ie. do not try to decode a door sensor telegram with a water meter driver.
bool isMeterDriverReasonableForMedia(MeterDriver type, string driver_name, int media);
bool isMeterDriverReasonableForMedia(string driver_name, int media);
struct MeterInfo;
bool isValidKey(const string& key, MeterInfo &mt);
@ -128,7 +101,6 @@ struct MeterInfo
// A bus can be an mbus or a wmbus dongle.
// The bus can be the empty string, which means that it will fallback to the first defined bus.
string name; // User specified name of this (group of) meters.
MeterDriver driver {}; // Requested driver for decoding telegrams from this meter.
DriverName driver_name; // Will replace MeterDriver.
string extras; // Extra driver specific settings.
vector<string> ids; // Match expressions for ids.
@ -151,11 +123,10 @@ struct MeterInfo
string str();
DriverName driverName();
MeterInfo(string b, string n, MeterDriver d, string e, vector<string> i, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &j, vector<string> &calcfs)
MeterInfo(string b, string n, string e, vector<string> i, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &j, vector<string> &calcfs)
{
bus = b;
name = n;
driver = d;
extras = e,
ids = i;
idsc = toIdsCommaSeparated(ids);
@ -171,7 +142,6 @@ struct MeterInfo
{
bus = "";
name = "";
driver = MeterDriver::UNKNOWN;
ids.clear();
idsc = "";
key = "";
@ -203,7 +173,6 @@ struct DriverInfo
{
private:
MeterDriver driver_ {}; // Old driver enum, to go away.
DriverName name_; // auto, unknown, amiplus, lse_07_17, multical21 etc
vector<DriverName> name_aliases_; // Secondary names that will map to this driver.
LinkModeSet linkmodes_; // C1, T1, S1 or combinations thereof.
@ -216,7 +185,6 @@ private:
public:
DriverInfo() {};
DriverInfo(MeterDriver mt) : driver_(mt) {};
void setName(std::string n) { name_ = n; }
void addNameAlias(std::string n) { name_aliases_.push_back(n); }
void setMeterType(MeterType t) { type_ = t; }
@ -228,9 +196,14 @@ public:
void addDetection(uint16_t mfct, uchar type, uchar ver) { detect_.push_back({ mfct, type, ver }); }
vector<DriverDetect> &detect() { return detect_; }
MeterDriver driver() { return driver_; }
DriverName name() { return name_; }
vector<DriverName>& nameAliases() { return name_aliases_; }
bool hasDriverName(DriverName dn) {
if (name_ == dn) return true;
for (auto &i : name_aliases_) if (i == dn) return true;
return false;
}
MeterType type() { return type_; }
vector<string>& defaultFields() { return default_fields_; }
LinkModeSet linkModes() { return linkmodes_; }
@ -247,7 +220,7 @@ bool lookupDriverInfo(const string& driver, DriverInfo *di = NULL);
// Return the best driver match for a telegram.
DriverInfo pickMeterDriver(Telegram *t);
// Return true for mbus and S2/C2/T2 drivers.
bool driverNeedsPolling(MeterDriver driver, DriverName& dn);
bool driverNeedsPolling(DriverName& dn);
vector<DriverInfo*>& allDrivers();
@ -410,10 +383,8 @@ struct Meter
// Either the default fields specified in the driver, or override fields in the meter configuration file.
virtual vector<string> &selectedFields() = 0;
virtual void setSelectedFields(vector<string> &f) = 0;
virtual string meterDriver() = 0;
virtual string name() = 0;
virtual MeterDriver driver() = 0;
virtual DriverName driverName() = 0;
virtual DriverName driverName() = 0;
virtual string datetimeOfUpdateHumanReadable() = 0;
virtual string datetimeOfUpdateRobot() = 0;
@ -485,15 +456,8 @@ struct MeterManager
shared_ptr<MeterManager> createMeterManager(bool daemon);
const char *toString(MeterType type);
string toString(MeterDriver driver);
string toString(DriverInfo &driver);
MeterDriver toMeterDriver(const string& driver);
LinkModeSet toMeterLinkModeSet(const string& driver);
LinkModeSet toMeterLinkModeSet(MeterDriver driver);
#define X(mname,linkmode,info,type,cname) shared_ptr<Meter> create##cname(MeterInfo &m);
LIST_OF_METERS
#undef X
struct Configuration;
struct MeterInfo;

Wyświetl plik

@ -62,7 +62,6 @@ struct MeterCommonImplementation : public virtual Meter
vector<FieldInfo> &fieldInfos();
vector<string> &extraConstantFields();
string name();
MeterDriver driver();
DriverName driverName();
ELLSecurityMode expectedELLSecurityMode();
@ -88,8 +87,6 @@ struct MeterCommonImplementation : public virtual Meter
~MeterCommonImplementation() = default;
string meterDriver() { return driver_; }
protected:
void triggerUpdate(Telegram *t);
@ -287,7 +284,6 @@ private:
int index_ {};
MeterType type_ {};
string driver_ {};
DriverName driver_name_;
string bus_ {};
MeterKeys meter_keys_ {};

Wyświetl plik

@ -920,7 +920,7 @@ void testm(string arg, bool xok,
}
if (ok == false) return;
bool driver_ok = toString(mi.driver) == xdriver || mi.driverName().str() == xdriver;
bool driver_ok = mi.driverName().str() == xdriver;
bool extras_ok = mi.extras == xextras;
bool bus_ok = mi.bus == xbus;
bool bps_ok = to_string(mi.bps) == xbps;
@ -929,13 +929,13 @@ void testm(string arg, bool xok,
if (!driver_ok || !extras_ok || !bus_ok || !bps_ok || !link_modes_ok)
{
printf("ERROR in meterc parsing parts \"%s\" got\n"
"driver: \"%s\"/\"%s\", extras: \"%s\", bus: \"%s\", bbps: \"%s\", linkmodes: \"%s\"\n"
"driver: \"%s\", extras: \"%s\", bus: \"%s\", bbps: \"%s\", linkmodes: \"%s\"\n"
"but expected\n"
"driver: \"%s\", extras: \"%s\", bus: \"%s\", bbps: \"%s\", linkmodes: \"%s\"\n",
arg.c_str(),
toString(mi.driver).c_str(), mi.driverName().str().c_str(),
mi.driverName().str().c_str(),
mi.extras.c_str(),
mi.bus.c_str(),
to_string(mi.bps).c_str(),
@ -965,20 +965,20 @@ void testc(string file, string file_content,
mi = c.meters.back();
if ((toString(mi.driver) != xdriver && mi.driverName().str() != xdriver) ||
if (mi.driverName().str() != xdriver ||
mi.extras != xextras ||
mi.bus != xbus ||
to_string(mi.bps) != xbps ||
mi.link_modes.hr() != xlm)
{
printf("ERROR in meterc parsing parts \"%s\" got\n"
"driver: \"%s\"/\"%s\", extras: \"%s\", bus: \"%s\", bbps: \"%s\", linkmodes: \"%s\"\n"
"driver: \"%s\", extras: \"%s\", bus: \"%s\", bbps: \"%s\", linkmodes: \"%s\"\n"
"but expected\n"
"driver: \"%s\", extras: \"%s\", bus: \"%s\", bbps: \"%s\", linkmodes: \"%s\"\n",
file.c_str(),
toString(mi.driver).c_str(), mi.driverName().str().c_str(),
mi.driverName().str().c_str(),
mi.extras.c_str(),
mi.bus.c_str(),
to_string(mi.bps).c_str(),

Wyświetl plik

@ -13,7 +13,7 @@ cat <<EOF | jq --sort-keys . > $TEST/test_expected.txt
EOF
$PROG --format=json 2A442D2C998734761B168D2091D37CAC21576C7802FF207100041308190000441308190000615B7F616713 \
Vatten auto ANYID NOKEY 2>&1 | jq --sort-keys . > $TEST/test_output.txt
Vatten multical21 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
@ -27,6 +27,17 @@ then
fi
fi
# Now test that anyid and auto does not work...
$PROG --verbose 2A442D2C998734761B168D2091D37CAC21576C7802FF207100041308190000441308190000615B7F616713 \
Vatten auto ANYID NOKEY > $TEST/test_output.txt 2>&1
if ! grep -o "ignoring telegram from 76348799" $TEST/test_output.txt > /dev/null
then
echo "Expected telegram to be ignored with auto + ANYID is used!"
TESTRESULT="ERROR"
else
echo "OK: Ignoring auto + ANYID"
fi
if [ "$TESTRESULT" = "ERROR" ]
then

Wyświetl plik

@ -37,6 +37,11 @@ then
then
echo OK: $TESTNAME
TESTRESULT="OK"
else
if [ "$USE_MELD" = "true" ]
then
meld $TEST/test_expected.txt $TEST/test_responses.txt
fi
fi
else
echo "wmbusmeters returned error code: $?"

Wyświetl plik

@ -65,6 +65,10 @@ do
TESTRESULT="OK"
else
TESTRESULT="ERROR"
if [ "$USE_MELD" = "true" ]
then
meld $TEST/test_expected_fields.txt $TEST/test_response_fields.txt
fi
fi
else
echo "wmbusmeters returned error code: $?"

Wyświetl plik

@ -13,6 +13,7 @@ $PROG --format=json simulations/simulation_bad_keys.txt room fhkvdataiv 03065716
cat > $TEST/expected_err.txt <<EOF
(wmbus) WARNING! no key to decrypt payload! Permanently ignoring telegrams from id: 03065716 mfct: (TCH) Techem Service (0x5068) type: Heat Cost Allocator (0x08) ver: 0x94
(meter) newly created meter (room 03065716 fhkvdataiv) did not handle telegram!
EOF
diff $TEST/test_stderr.txt $TEST/expected_err.txt
@ -27,6 +28,7 @@ $PROG --format=json simulations/simulation_bad_keys.txt room fhkvdataiv 03065716
cat > $TEST/expected_err.txt <<EOF
(wmbus) WARNING!! decrypted content failed check, did you use the correct decryption key? Permanently ignoring telegrams from id: 03065716 mfct: (TCH) Techem Service (0x5068) type: Heat Cost Allocator (0x08) ver: 0x94
(meter) newly created meter (room 03065716 fhkvdataiv) did not handle telegram!
EOF
diff $TEST/test_stderr.txt $TEST/expected_err.txt

Wyświetl plik

@ -6,7 +6,7 @@ rm -rf testoutput
mkdir -p testoutput
TEST=testoutput
SIM=simulations/simulation_c1.txt
SIM=simulations/simulation_oneshot.txt
$PROG --oneshot --verbose $SIM MyHeater multical302 67676767 NOKEY MyTapWater multical21 76348799 NOKEY > $TEST/test_output.txt 2> $TEST/test_stderr.txt