Refactor q400 driver.

pull/637/head
Fredrik Öhrström 2022-10-12 17:55:47 +02:00
rodzic 79210ea5f3
commit 9152e28882
8 zmienionych plików z 143 dodań i 290 usunięć

17
CHANGES
Wyświetl plik

@ -1,4 +1,21 @@
ATTENTION! The q400 driver has been refactored to the new driver
format and some errors in the fields were discovered! Since the new driver
format cannot generate these wrong json fields, the new q400 driver
is not backwards compatible, except for the total_m3 field which is the same.
Same fields and same content: total_m3, consumption_at_set_date_m3"
Same field different content: meter_datetime now reports a full date time "2019-12-20 13:04"
before it reported only the date "2019-12-20".
New field new content: set_date is now set_datetime and reports "2019-12-01 00:00" instead of "2019-12-00"
For the Axioma version of the q400:
Broken fields: forward_flow_m3h, backward_flow_m3h, set_forward_flow_m3h, set_backward_flow_m3h
replaced with: total_forward_m3, total_backward_m3, forward_at_set_date_m3, backward_at_set_date_m3
Same fields and same content: flow_temperature_c, flow_m3h
Added the bfw 240 radio heat cost allocator.
Kajetan Krykwiński added support for the Apator E-ITN heat cost allocator. Thanks Kajetan!

Wyświetl plik

@ -145,15 +145,15 @@ telegram=|3044B4090123456713067A190020052F2F_0C1315000000046D0136A7270F050B00000
{"media":"warm water","meter":"hydrodigit","name":"HydrodigitWater2","id":"67452301","total_m3":0.015,"meter_datetime":"2021-07-07 22:01","timestamp":"1111-11-11T11:11:11Z"}
|HydrodigitWater2;67452301;0.015000;2021-07-07 22:01;1111-11-11 11:11.11
# Test Q400 water telegram (encrypted)
telegram=|2E4409077272727210077AD7102005CC2FF08D057E306D8C3078AE44AD6E3D37F8515B92FB068347783DFBB25C3C28|
{"media":"water","meter":"q400","name":"Q400Water","id":"72727272","total_m3":0,"set_date":"2019-12-01","consumption_at_set_date_m3":0,"meter_datetime":"","flow_m3h":0,"forward_flow_m3h":0,"backward_flow_m3h":0,"flow_temperature_c":0,"set_forward_flow_m3h":0,"set_backward_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|Q400Water;72727272;0.000000;1111-11-11 11:11.11
# Test Q400 water telegram
telegram=|2E4409077272727210077AD71020052F2F_046D040D742C041377000000446D0000612C4413000000002F2F2F2F2F2F|
{"media":"water","meter":"q400","name":"Q400Water","id":"72727272","meter_datetime":"2019-12-20 13:04","total_m3":0.119,"status":"TEMPORARY_ERROR","set_datetime":"2019-12-01 00:00","consumption_at_set_date_m3":0,"timestamp":"1111-11-11T11:11:11Z"}
|Q400Water;72727272;0.119;1111-11-11 11:11.11
# Test Axioma W1 telegram with additional fields compared to the older q400 meter.
telegram=|5E4409077372727210077A710050052F2F_046D0110A92704130000000004933B0000000004933C00000000023B000002592A0A446D0000A12744130000000044933B0000000044933C0000000001FD74622F2F2F2F2F2F2F2F2F2F2F2F2F2F|
{"media":"water","meter":"q400","name":"AxiomaWater","id":"72727273","total_m3":0,"set_date":"2021-07-01","consumption_at_set_date_m3":0,"meter_datetime":"2021-07-09","flow_m3h":0,"forward_flow_m3h":0,"backward_flow_m3h":0,"flow_temperature_c":26.02,"set_forward_flow_m3h":0,"set_backward_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|AxiomaWater;72727273;0.000000;1111-11-11 11:11.11
{"media":"water","meter":"q400","name":"AxiomaWater","id":"72727273","meter_datetime":"2021-07-09 16:01","total_m3":0,"total_forward_m3":0,"total_backward_m3":0,"flow_temperature_c":26.02,"volume_flow_m3h":0,"status":"OK","set_datetime":"2021-07-01 00:00","consumption_at_set_date_m3":0,"forward_at_set_date_m3":0,"backward_at_set_date_m3":0,"timestamp":"1111-11-11T11:11:11Z"}
|AxiomaWater;72727273;0;1111-11-11 11:11.11
# Test electricity meter with eBZ wMB E01.
telegram=|5B445A149922992202378C20F6900F002C25BC9E0000BF48954821BC508D72992299225A140102F6003007102F2F040330F92A0004A9FF01FF24000004A9FF026A29000004A9FF03460600000DFD11063132333435362F2F2F2F2F2F|

107
src/driver_q400.cc 100644
Wyświetl plik

@ -0,0 +1,107 @@
/*
Copyright (C) 2020-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("q400");
di.setDefaultFields("name,id,total_m3,timestamp");
di.setMeterType(MeterType::WaterMeter);
di.addLinkMode(LinkMode::T1);
di.addDetection(MANUFACTURER_AXI, 0x07, 0x10);
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
});
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields();
addOptionalFlowRelatedFields();
addStringField(
"status",
"Status and error flags.",
PrintProperty::JSON | PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS);
addStringFieldWithExtractor(
"set_datetime",
"Date and time when the previous billing period ended.",
PrintProperty::JSON,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::DateTime)
.set(StorageNr(1))
);
addNumericFieldWithExtractor(
"consumption_at_set_date",
"The total water consumption at the end of the previous billing period.",
PrintProperty::JSON,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(StorageNr(1))
);
addNumericFieldWithExtractor(
"forward_at_set_date",
"The total media volume flowing forward at the end of previous billing period.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(StorageNr(1))
.add(VIFCombinable::ForwardFlow)
);
addNumericFieldWithExtractor(
"backward_at_set_date",
"The total media volume flowing backward at the end of the previous billing period.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(StorageNr(1))
.add(VIFCombinable::BackwardFlow)
);
}
}
// Test: Q400Water q400 72727272 NOKEY
// telegram=|2E4409077272727210077AD71020052F2F_046D040D742C041377000000446D0000612C4413000000002F2F2F2F2F2F|
// {"media":"water","meter":"q400","name":"Q400Water","id":"72727272","meter_datetime":"2019-12-20 13:04","total_m3":0.119,"status":"TEMPORARY_ERROR","set_datetime":"2019-12-01 00:00","consumption_at_set_date_m3":0,"timestamp":"1111-11-11T11:11:11Z"}
// |Q400Water;72727272;0.119;1111-11-11 11:11.11
// Test: AxiomaWater q400 72727273 NOKEY
// Comment: Test Axioma W1 telegram with additional fields compared to the older q400 meter.
// telegram=|5E4409077372727210077A710050052F2F_046D0110A92704130022000004933B0000000004933C00000000023B000002592A0A446D0000A12744130000000044933B0000000044933C0000000001FD74622F2F2F2F2F2F2F2F2F2F2F2F2F2F|
// {"media":"water","meter":"q400","name":"AxiomaWater","id":"72727273","meter_datetime":"2021-07-09 16:01","total_m3":8.704,"total_forward_m3":0,"total_backward_m3":0,"flow_temperature_c":26.02,"volume_flow_m3h":0,"status":"OK","set_datetime":"2021-07-01 00:00","consumption_at_set_date_m3":0,"forward_at_set_date_m3":0,"backward_at_set_date_m3":0,"timestamp":"1111-11-11T11:11:11Z"}
// |AxiomaWater;72727273;8.704;1111-11-11 11:11.11

Wyświetl plik

@ -75,7 +75,6 @@
X(RFMAMB, MANUFACTURER_BMT, 0x1b, 0x10) \
X(RFMTX1, MANUFACTURER_BMT, 0x07, 0x05) \
X(TSD2, MANUFACTURER_TCH, 0xf0, 0x76) \
X(Q400, MANUFACTURER_AXI, 0x07, 0x10) \
X(SONTEX868, MANUFACTURER_SON, 0x08, 0x16) \
X(TOPASESKR, MANUFACTURER_AMT, 0x06, 0xf1) \
X(TOPASESKR, MANUFACTURER_AMT, 0x07, 0xf1) \

Wyświetl plik

@ -1,281 +0,0 @@
/*
Copyright (C) 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"dvparser.h"
#include"meters.h"
#include"meters_common_implementation.h"
#include"wmbus.h"
#include"wmbus_utils.h"
#include"util.h"
using namespace std;
struct MeterQ400 : public virtual MeterCommonImplementation {
MeterQ400(MeterInfo &mi);
// Total water counted through the meter
double totalWaterConsumption(Unit u);
bool hasTotalWaterConsumption();
string setDate();
double consumptionAtSetDate(Unit u);
private:
void processContent(Telegram *t);
string meter_datetime_;
double total_water_consumption_m3_ {};
string set_date_;
double consumption_at_set_date_m3_ {};
double flow_m3h_ {};
// What is flow really? The sum of forward and backward flow? Or the same as forward flow?
double forward_flow_m3h_ {};
double backward_flow_m3h_ {};
double flow_temperature_c_ {};
// Historical flow, perhaps over the last month?
double set_forward_flow_m3h_ {};
double set_backward_flow_m3h_ {};
};
shared_ptr<Meter> createQ400(MeterInfo &mi)
{
return shared_ptr<Meter>(new MeterQ400(mi));
}
MeterQ400::MeterQ400(MeterInfo &mi) :
MeterCommonImplementation(mi, "q400")
{
setMeterType(MeterType::WaterMeter);
setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV);
addLinkMode(LinkMode::T1);
addPrint("total", Quantity::Volume,
[&](Unit u){ return totalWaterConsumption(u); },
"The total water consumption recorded by this meter.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("set_date", Quantity::Text,
[&](){ return setDate(); },
"The most recent billing period date.",
PrintProperty::JSON);
addPrint("consumption_at_set_date", Quantity::Volume,
[&](Unit u){ return consumptionAtSetDate(u); },
"The total water consumption at the most recent billing period date.",
PrintProperty::JSON);
addPrint("meter_datetime", Quantity::Text,
[&](){ return meter_datetime_; },
"Meter timestamp for measurement.",
PrintProperty::JSON);
addPrint("flow", Quantity::Flow,
[&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(flow_m3h_, Unit::M3H, u); },
"Water flow?",
PrintProperty::JSON);
addPrint("forward_flow", Quantity::Flow,
[&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(forward_flow_m3h_, Unit::M3H, u); },
"Forward flow.",
PrintProperty::JSON);
addPrint("backward_flow", Quantity::Flow,
[&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(backward_flow_m3h_, Unit::M3H, u); },
"Backward flow.",
PrintProperty::JSON);
addPrint("flow_temperature", Quantity::Temperature,
[&](Unit u){ assertQuantity(u, Quantity::Temperature); return convert(flow_temperature_c_, Unit::C, u); },
"The water temperature.",
PrintProperty::JSON);
addPrint("set_forward_flow", Quantity::Flow,
[&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(set_forward_flow_m3h_, Unit::M3H, u); },
"Historical forward flow.",
PrintProperty::JSON);
addPrint("set_backward_flow", Quantity::Flow,
[&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(set_backward_flow_m3h_, Unit::M3H, u); },
"Historical backward flow.",
PrintProperty::JSON);
}
void MeterQ400::processContent(Telegram *t)
{
/*
This is the first q400 meter telegram content:
(q400) 0f: 2f2f decrypt check bytes
(q400) 11: 04 dif (32 Bit Integer/Binary Instantaneous value)
(q400) 12: 6D vif (Date and time type)
(q400) 13: 040D742C
(q400) 17: 04 dif (32 Bit Integer/Binary Instantaneous value)
(q400) 18: 13 vif (Volume l)
(q400) 19: * 00000000 total consumption (0.000000 m3)
(q400) 1d: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(q400) 1e: 6D vif (Date and time type)
(q400) 1f: * 0000612C set date (2019-12-01)
(q400) 23: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(q400) 24: 13 vif (Volume l)
(q400) 25: * 00000000 consumption at set date (0.000000 m3)
(q400) 29: 2F skip
(q400) 2a: 2F skip
(q400) 2b: 2F skip
(q400) 2c: 2F skip
(q400) 2d: 2F skip
(q400) 2e: 2F skip
*/
/* And here is the Axioma W1 meter which reports identical
version and type and manufacturer as the old q400 meter.
But it contains a lot more data.....silly,
they should have a different meter type.
(q400) 0f: 2f2f decrypt check bytes
(q400) 11: 04 dif (32 Bit Integer/Binary Instantaneous value)
(q400) 12: 6D vif (Date and time type)
(q400) 13: 0110A927
(q400) 17: 04 dif (32 Bit Integer/Binary Instantaneous value)
(q400) 18: 13 vif (Volume l)
(q400) 19: * 00000000 total consumption (0.000000 m3)
(q400) 1d: 04 dif (32 Bit Integer/Binary Instantaneous value)
(q400) 1e: 93 vif (Volume l)
(q400) 1f: 3B vife (forward flow)
(q400) 20: 00000000
(q400) 24: 04 dif (32 Bit Integer/Binary Instantaneous value)
(q400) 25: 93 vif (Volume l)
(q400) 26: 3C vife (backward flow)
(q400) 27: 00000000
(q400) 2b: 02 dif (16 Bit Integer/Binary Instantaneous value)
(q400) 2c: 3B vif (Volume flow l/h)
(q400) 2d: 0000
(q400) 2f: 02 dif (16 Bit Integer/Binary Instantaneous value)
(q400) 30: 59 vif (Flow temperature 10² °C)
(q400) 31: 2A0A
(q400) 33: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(q400) 34: 6D vif (Date and time type)
(q400) 35: * 0000A127 set date (2021-07-01)
(q400) 39: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(q400) 3a: 13 vif (Volume l)
(q400) 3b: * 00000000 consumption at set date (0.000000 m3)
(q400) 3f: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(q400) 40: 93 vif (Volume l)
(q400) 41: 3B vife (forward flow)
(q400) 42: 00000000
(q400) 46: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(q400) 47: 93 vif (Volume l)
(q400) 48: 3C vife (backward flow)
(q400) 49: 00000000
(q400) 4d: 01 dif (8 Bit Integer/Binary Instantaneous value)
(q400) 4e: FD vif (Second extension FD of VIF-codes)
(q400) 4f: 74 vife (Reserved)
(q400) 50: 62
(q400) 51: 2F skip
(q400) 52: 2F skip
(q400) 53: 2F skip
(q400) 54: 2F skip
(q400) 55: 2F skip
(q400) 56: 2F skip
(q400) 57: 2F skip
(q400) 58: 2F skip
(q400) 59: 2F skip
(q400) 5a: 2F skip
(q400) 5b: 2F skip
(q400) 5c: 2F skip
(q400) 5d: 2F skip
(q400) 5e: 2F skip
*/
int offset;
string key;
// Find keys common to both q400 and axioma.
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_m3_);
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_set_date_m3_);
t->addMoreExplanation(offset, " consumption at set date (%f m3)", consumption_at_set_date_m3_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 1, 0, &key, &t->dv_entries)) {
struct tm date;
extractDVdate(&t->dv_entries, key, &offset, &date);
set_date_ = strdate(&date);
t->addMoreExplanation(offset, " set date (%s)", set_date_.c_str());
}
// Now the axioma values:
if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 0, 0, &key, &t->dv_entries))
{
struct tm date;
extractDVdate(&t->dv_entries, key, &offset, &date);
meter_datetime_ = strdate(&date);
t->addMoreExplanation(offset, " meter datetime (%s)", meter_datetime_.c_str());
}
extractDVdouble(&t->dv_entries, "04933B", &offset, &forward_flow_m3h_);
t->addMoreExplanation(offset, " forward flow (%f m3/h)", forward_flow_m3h_);
extractDVdouble(&t->dv_entries, "04933C", &offset, &backward_flow_m3h_);
t->addMoreExplanation(offset, " backward flow (%f m3/h)", backward_flow_m3h_);
// Why does the meter send both forward flow and flow? Aren't they the same?
if(findKey(MeasurementType::Instantaneous, VIFRange::VolumeFlow, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &flow_m3h_);
t->addMoreExplanation(offset, " flow (%f m3/h)", flow_m3h_);
}
if(findKey(MeasurementType::Instantaneous, VIFRange::FlowTemperature, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &flow_temperature_c_);
t->addMoreExplanation(offset, " flow temperature (%f °C)", flow_temperature_c_);
}
extractDVdouble(&t->dv_entries, "44933B", &offset, &set_forward_flow_m3h_);
t->addMoreExplanation(offset, " set forward flow (%f m3/h)", set_forward_flow_m3h_);
extractDVdouble(&t->dv_entries, "44933C", &offset, &set_backward_flow_m3h_);
t->addMoreExplanation(offset, " set backward flow (%f m3/h)", set_backward_flow_m3h_);
}
double MeterQ400::totalWaterConsumption(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_m3_, Unit::M3, u);
}
bool MeterQ400::hasTotalWaterConsumption()
{
return true;
}
string MeterQ400::setDate()
{
return set_date_;
}
double MeterQ400::consumptionAtSetDate(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(consumption_at_set_date_m3_, Unit::M3, u);
}

Wyświetl plik

@ -3075,6 +3075,18 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
.set(VIFRange::Volume)
);
addNumericFieldWithExtractor(
"total_forward",
"The total media volume flowing forward.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.add(VIFCombinable::ForwardFlow)
);
addNumericFieldWithExtractor(
"total_backward",
"The total media volume flowing backward.",

Wyświetl plik

@ -83,7 +83,6 @@ LIST_OF_METER_TYPES
X(rfmamb, T1_bit, TempHygroMeter, RFMAMB, RfmAmb) \
X(rfmtx1, T1_bit, WaterMeter, RFMTX1, RfmTX1) \
X(tsd2, T1_bit, SmokeDetector, TSD2, TSD2) \
X(q400, T1_bit, WaterMeter, Q400, Q400) \
X(sontex868, T1_bit, HeatCostAllocationMeter, SONTEX868, Sontex868) \
X(topaseskr, T1_bit, WaterMeter, TOPASESKR, TopasEsKr) \
X(vario451, T1_bit, HeatMeter, VARIO451, Vario451) \

Wyświetl plik

@ -41,7 +41,7 @@ METERS="MyWarmWater supercom587 12345678 NOKEY
HydrusIzarRS auto 60897379 NOKEY
HydrodigitWater hydrodigit 86868686 NOKEY
HydrodigitWater2 hydrodigit 67452301 NOKEY
Q400Water q400 72727272 AAA896100FED12DD614DD5D46369ACDD
Q400Water q400 72727272 NOKEY
AxiomaWater q400 72727273 NOKEY
Elen1 ebzwmbe 22992299 NOKEY
Elen2 esyswm 77997799 NOKEY