kopia lustrzana https://github.com/weetmuts/wmbusmeters
All drivers have been rewritten!
rodzic
073aafd31c
commit
9ce4328d24
25
CHANGES
25
CHANGES
|
@ -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.
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
telegram=||33446850165706039480A20F9E28F2003018370011EA07D5070018001800160900000000000000000000000009190A22322C1215|
|
||||
telegram=||294468501657060394087A4C001025A11E4EFF816C2F8D147ED0C1F959F55E0F0FF724805FCC33D19A19|
|
||||
telegram=||33446850165706039480A20F9E28F2003018370011EA07D5070018001800160900000000000000000000000009190A22322C1215|
|
||||
telegram=||294468501657060394087A4C001025A11E4EFF816C2F8D147ED0C1F959F55E0F0FF724805FCC33D19A19|
|
||||
telegram=||33446850165706039480A20F9E28F2003018370011EA07D5070018001800160900000000000000000000000009190A22322C1215|
|
||||
telegram=||294468501657060394087A4C001025A11E4EFF816C2F8D147ED0C1F959F55E0F0FF724805FCC33D19A19|
|
||||
telegram=||33446850165706039480A20F9E28F2003018370011EA07D5070018001800160900000000000000000000000009190A22322C1215|
|
||||
telegram=||294468501657060394087A4C001025A11E4EFF816C2F8D147ED0C1F959F55E0F0FF724805FCC33D19A19|
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
telegram=|2A442D2C998734761B168D2091D37CAC21576C78_02FF207100041308190000441308190000615B7F616713|
|
||||
telegram=|2E442D2C6767676730048D2039D1684020_BCDB7803062C000043060000000314630000426C7F2A022D130001FF2100|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
42
src/main.cc
42
src/main.cc
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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, ¤t_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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
151
src/meters.cc
151
src/meters.cc
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
62
src/meters.h
62
src/meters.h
|
@ -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;
|
||||
|
|
|
@ -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_ {};
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: $?"
|
||||
|
|
|
@ -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: $?"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue