Change nan:s to null:s in fields. Fix problem with irrelevant fields listed for --listfields for some meters. Refactor unismart driver.

pull/645/head^2
Fredrik Öhrström 2022-11-12 16:30:10 +01:00
rodzic b999952fb5
commit 5675716286
26 zmienionych plików z 573 dodań i 528 usunięć

12
CHANGES
Wyświetl plik

@ -1,3 +1,15 @@
ATTENTION! When values are missing in the fields output, they were previously
reported as "nan" but they are now reported as "null".
ATTENTION! The unismart driver has been refactored to the new driver format.
Several fields were unknown before and are still unknown and their content
have slightly changed: status and version.
The field "other_counter" is replaced with just "other" and its content changed.
The field "suppler_info" is replaced with "supplier_info".
The field "device_date_time" is replaced with "device_timestamp" and now contains seconds
and a timezone.
ATTENTION! A bug was fixed where the on_time_h and operating_time_h
(and related error times) were wrong.

Wyświetl plik

@ -29,14 +29,14 @@ telegram=|1844AE4C4455223368077A55000000_041389E20100023B0000|
# Test amiplus/apator electricity meter
telegram=|4E4401061010101002027A00004005_2F2F0E035040691500000B2B300300066D00790C7423400C78371204860BABC8FC100000000E833C8074000000000BAB3C0000000AFDC9FC0136022F2F2F2F2F|
{"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.05,"current_power_consumption_kw":0.33,"total_energy_production_kwh":7.48,"current_power_production_kw":0,"voltage_at_phase_1_v":236,"voltage_at_phase_2_v":null,"voltage_at_phase_3_v":null,"device_date_time":"2019-03-20 12:57","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
|MyElectricity1;10101010;15694.050000;0.330000;7.480000;0.000000;236.000000;nan;nan;nan;nan;nan;nan;nan;nan;1111-11-11 11:11.11
{"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.05,"current_power_consumption_kw":0.33,"total_energy_production_kwh":7.48,"current_power_production_kw":0,"voltage_at_phase_1_v":236,"voltage_at_phase_2_v":null,"voltage_at_phase_3_v":null,"device_date_time":"2019-03-20 12:57:00","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
|MyElectricity1;10101010;15694.05;0.33;7.48;0;236;null;null;null;null;null;null;null;null;1111-11-11 11:11.11
# Test amiplus/apator electricity meter with three phase voltages
telegram=|5E44B6105843250000027A2A005005_2F2F0C7835221400066D404708AC2A400E032022650900000E833C0000000000001B2B9647000B2B5510000BAB3C0000000AFDC9FC0135020AFDC9FC0245020AFDC9FC0339020BABC8FC100000002F2F|
{"media":"electricity","meter":"amiplus","name":"MyElectricity2","id":"00254358","total_energy_consumption_kwh":9652.22,"current_power_consumption_kw":1.055,"total_energy_production_kwh":0,"current_power_production_kw":0,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":245,"voltage_at_phase_3_v":239,"device_date_time":"2021-10-12 08:07","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
|MyElectricity2;00254358;9652.220000;1.055000;0.000000;0.000000;235.000000;245.000000;239.000000;nan;nan;nan;nan;nan;nan;1111-11-11 11:11.11
{"media":"electricity","meter":"amiplus","name":"MyElectricity2","id":"00254358","total_energy_consumption_kwh":9652.22,"current_power_consumption_kw":1.055,"total_energy_production_kwh":0,"current_power_production_kw":0,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":245,"voltage_at_phase_3_v":239,"device_date_time":"2021-10-12 08:07:00","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
|MyElectricity2;00254358;9652.22;1.055;0;0;235;245;239;null;null;null;null;null;null;1111-11-11 11:11.11
# Test MKRadio3 T1 telegrams
@ -294,8 +294,8 @@ telegram=76442104710007612507727100076121042507B5006005E2E95A3C2A1279A5415E67326
# Test Unismart Gas Meter
telegram=|6044B8059430040001037A1D005085E2B670BCF1A5C87E0C1A51DA18924EF984613DA2A9CD39D8F4C7208326C76D42DBEADF80D574192B71BD7C4F56A7F1513151768A9DB804883B28CB085CA2D0F7438C361CB9E2734712ED9BFBB2A14EF55208|
{"media":"gas","meter":"unismart","name":"GasMeter","id":"00043094","fabrication_no":"3162296","total_date_time":"2021-09-15 13:18","total_m3":917,"target_date_time":"2021-09-01 06:00","target_m3":911.32,"version":"UGG4","device_date_time":"2021-09-15 13:18","suppler_info":"00","status":"F00C","parameter_set":"02","other_counter":20,"timestamp":"1111-11-11T11:11:11Z"}
|GasMeter;00043094;917.000000;911.320000;1111-11-11 11:11.11
{"media":"gas","meter":"unismart","name":"GasMeter","id":"00043094","fabrication_no":"03162296","status":"STATUS_FLAGS_CF0","other":"OTHER_FLAGS_14","total_date_time":"2021-09-15 13:18","total_m3":917,"target_date_time":"2021-09-01 06:00","target_m3":911.32,"version":" 4GGU","supplier_info":"00","parameter_set":"02","meter_timestamp":"2021-09-15 13:18:30","timestamp":"1111-11-11T11:11:11Z"}
|GasMeter;00043094;917;911.32;1111-11-11 11:11.11
# Test Hydrocal M3 heat/cooling meter
telegram=|8E44B409747372710B0D7A798080052F2F_0C0E59600100046D1D36B9290C13679947000C0E000000000C13590000000C13000000000C13000000000A5A18020A5E11020F823D06003D06003D06003D0600140600620500480400E402001601000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002F2F|

Wyświetl plik

@ -27,6 +27,7 @@ namespace
static bool ok = registerDriver([](DriverInfo&di)
{
di.setName("amiplus");
di.setDefaultFields("name,id,total_energy_consumption_kwh,current_power_consumption_kw,total_energy_production_kwh,current_power_production_kw,voltage_at_phase_1_v,voltage_at_phase_2_v,voltage_at_phase_3_v,total_energy_consumption_tariff_1_kwh,total_energy_consumption_tariff_2_kwh,total_energy_consumption_tariff_3_kwh,total_energy_production_tariff_1_kwh,total_energy_production_tariff_2_kwh,total_energy_production_tariff_3_kwh,timestamp");
di.setMeterType(MeterType::ElectricityMeter);
di.addLinkMode(LinkMode::T1);
di.addDetection(MANUFACTURER_APA, 0x02, 0x02);
@ -188,19 +189,19 @@ namespace
// Test: MyElectricity1 amiplus 10101010 NOKEY
// telegram=|4E4401061010101002027A00004005_2F2F0E035040691500000B2B300300066D00790C7423400C78371204860BABC8FC100000000E833C8074000000000BAB3C0000000AFDC9FC0136022F2F2F2F2F|
// {"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.05,"current_power_consumption_kw":0.33,"total_energy_production_kwh":7.48,"current_power_production_kw":0,"voltage_at_phase_1_v":236,"voltage_at_phase_2_v":null,"voltage_at_phase_3_v":null,"device_date_time":"2019-03-20 12:57","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
// |MyElectricity1;10101010;15694.050000;0.330000;7.480000;0.000000;236.000000;nan;nan;nan;nan;nan;nan;nan;nan;1111-11-11 11:11.11
// {"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.05,"current_power_consumption_kw":0.33,"total_energy_production_kwh":7.48,"current_power_production_kw":0,"voltage_at_phase_1_v":236,"voltage_at_phase_2_v":null,"voltage_at_phase_3_v":null,"device_date_time":"2019-03-20 12:57:00","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
// |MyElectricity1;10101010;15694.05;0.33;7.48;0;236;null;null;null;null;null;null;null;null;1111-11-11 11:11.11
// Test: MyElectricity2 amiplus 00254358 NOKEY
// amiplus/apator electricity meter with three phase voltages
// telegram=|5E44B6105843250000027A2A005005_2F2F0C7835221400066D404708AC2A400E032022650900000E833C0000000000001B2B9647000B2B5510000BAB3C0000000AFDC9FC0135020AFDC9FC0245020AFDC9FC0339020BABC8FC100000002F2F|
// {"media":"electricity","meter":"amiplus","name":"MyElectricity2","id":"00254358","total_energy_consumption_kwh":9652.22,"current_power_consumption_kw":1.055,"total_energy_production_kwh":0,"current_power_production_kw":0,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":245,"voltage_at_phase_3_v":239,"device_date_time":"2021-10-12 08:07","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
// |MyElectricity2;00254358;9652.220000;1.055000;0.000000;0.000000;235.000000;245.000000;239.000000;nan;nan;nan;nan;nan;nan;1111-11-11 11:11.11
// {"media":"electricity","meter":"amiplus","name":"MyElectricity2","id":"00254358","total_energy_consumption_kwh":9652.22,"current_power_consumption_kw":1.055,"total_energy_production_kwh":0,"current_power_production_kw":0,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":245,"voltage_at_phase_3_v":239,"device_date_time":"2021-10-12 08:07:00","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
// |MyElectricity2;00254358;9652.22;1.055;0;0;235;245;239;null;null;null;null;null;null;1111-11-11 11:11.11
// Test: MyElectricity3 amiplus 86064864 NOKEY
// amiplus/apator electricity meter with three phase voltages and 2 tariffs
// telegram=|804401066448068602027A000070052F2F_066D1E5C11DA21400C78644806868E10036110012500008E20038106531800008E10833C9949000000008E20833C8606000000001B2B5228020B2B3217000BAB3C0000000AFDC9FC0131020AFDC9FC0225020AFDC9FC0331020BABC8FC100000002F2F2F2F2F2F2F2F2F2F2F2F2FDE47|
// {"media":"electricity","meter":"amiplus","name":"MyElectricity3","id":"86064864","total_energy_consumption_kwh":null,"current_power_consumption_kw":1.732,"total_energy_production_kwh":null,"current_power_production_kw":0,"voltage_at_phase_1_v":231,"voltage_at_phase_2_v":225,"voltage_at_phase_3_v":231,"device_date_time":"2022-01-26 17:28","total_energy_consumption_tariff_1_kwh":25011.061,"total_energy_consumption_tariff_2_kwh":18530.681,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":4.999,"total_energy_production_tariff_2_kwh":0.686,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
// |MyElectricity3;86064864;nan;1.732000;nan;0.000000;231.000000;225.000000;231.000000;25011.061000;18530.681000;nan;4.999000;0.686000;nan;1111-11-11 11:11.11
// {"media":"electricity","meter":"amiplus","name":"MyElectricity3","id":"86064864","total_energy_consumption_kwh":null,"current_power_consumption_kw":1.732,"total_energy_production_kwh":null,"current_power_production_kw":0,"voltage_at_phase_1_v":231,"voltage_at_phase_2_v":225,"voltage_at_phase_3_v":231,"device_date_time":"2022-01-26 17:28:30","total_energy_consumption_tariff_1_kwh":25011.061,"total_energy_consumption_tariff_2_kwh":18530.681,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":4.999,"total_energy_production_tariff_2_kwh":0.686,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
// |MyElectricity3;86064864;null;1.732;null;0;231;225;231;25011.061;18530.681;null;4.999;0.686;null;1111-11-11 11:11.11

Wyświetl plik

@ -39,7 +39,7 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields();
addOptionalCommonFields("software_version");
addStringField(
"status",

Wyświetl plik

@ -41,7 +41,7 @@ namespace
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT |
PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS);
addOptionalCommonFields();
addOptionalCommonFields("on_time_h");
addNumericFieldWithExtractor(
"total_energy_consumption",

Wyświetl plik

@ -237,7 +237,7 @@ namespace
.set(StorageNr(1))
);
addOptionalCommonFields();
addOptionalCommonFields("operating_time_h,on_time_h,meter_datetime");
}
}

Wyświetl plik

@ -37,7 +37,7 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields();
addOptionalCommonFields("fabrication_no,enhanced_id,location");
addStringFieldWithExtractor(
"location_hex",

Wyświetl plik

@ -43,8 +43,8 @@ namespace
addLinkMode(LinkMode::T1);
addOptionalCommonFields();
addOptionalFlowRelatedFields();
addOptionalCommonFields("enhanced_id,meter_datetime");
addOptionalFlowRelatedFields("total_m3,total_backward_m3,volume_flow_m3h");
addStringFieldWithExtractorAndLookup(
"status",
@ -136,10 +136,10 @@ namespace
// Test: SomeWater itron 12345698 NOKEY
// Comment: Test ITRON T1 telegram not encrypted, which has no 2f2f markers.
// telegram=|384497269856341203077AD90000A0#0413FD110000066D2C1AA1D521004413300F0000426CBF2C047F0000060C027F862A0E79678372082100|
// {"media":"water","meter":"itron","name":"SomeWater","id":"12345698","enhanced_id":"002108728367","meter_datetime":"2022-01-21 01:26","total_m3":4.605,"status":"OK","target_m3":3.888,"target_date":"2021-12-31","unknown_a":"WOOTA_C060000","unknown_b":"WOOTB_2A86","timestamp":"1111-11-11T11:11:11Z"}
// {"media":"water","meter":"itron","name":"SomeWater","id":"12345698","enhanced_id":"002108728367","meter_datetime":"2022-01-21 01:26:44","total_m3":4.605,"status":"OK","target_m3":3.888,"target_date":"2021-12-31","unknown_a":"WOOTA_C060000","unknown_b":"WOOTB_2A86","timestamp":"1111-11-11T11:11:11Z"}
// |SomeWater;12345698;4.605;3.888;1111-11-11 11:11.11
// Test: MoreWater itron 18000056 NOKEY
// telegram=|46449726560000183307725600001897263307AF0030052F2F_066D0E1015C82A000C13771252000C933C000000000B3B0400004C1361045200426CC12A03FD971C0000002F2F2F|
// {"media":"water","meter":"itron","name":"MoreWater","id":"18000056","meter_datetime":"2022-10-08 21:16","total_m3":521.277,"total_backward_m3":0,"volume_flow_m3h":0.004,"status":"OK","target_m3":520.461,"target_date":"2022-10-01","timestamp":"1111-11-11T11:11:11Z"}
// {"media":"water","meter":"itron","name":"MoreWater","id":"18000056","meter_datetime":"2022-10-08 21:16:14","total_m3":521.277,"total_backward_m3":0,"volume_flow_m3h":0.004,"status":"OK","target_m3":520.461,"target_date":"2022-10-01","timestamp":"1111-11-11T11:11:11Z"}
// |MoreWater;18000056;521.277;520.461;1111-11-11 11:11.11

Wyświetl plik

@ -37,8 +37,8 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields();
addOptionalFlowRelatedFields();
addOptionalCommonFields("meter_date,fabrication_no,operating_time_h,on_time_h,on_time_at_error_h,meter_datetime");
addOptionalFlowRelatedFields("total_m3,total_backward_m3,volume_flow_m3h");
/* If the meter is recently commissioned, the target water consumption value is bogus.
The bits store 0xffffffff. Should we deal with this? Now a very large value is printed in the json.

Wyświetl plik

@ -39,7 +39,7 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields();
addOptionalCommonFields("on_time_h");
// Technical Description Multical 603 page 116 section 7.7.2 Information code types on serial communication.
addStringFieldWithExtractorAndLookup(

Wyświetl plik

@ -36,8 +36,6 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields();
addStringFieldWithExtractorAndLookup(
"status",
"Meter status. Reports OK if neither tpl sts nor error flags have bits set.",
@ -59,11 +57,6 @@ namespace
},
}));
/*
.set(MeasurementType::Instantaneous)
.set(VIFRange::ErrorFlags)
.add(VIFCombinable::StandardConformantDataContent)
*/
addNumericFieldWithExtractor(
"current_temperature",
"The current temperature.",

Wyświetl plik

@ -36,7 +36,7 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields();
addOptionalCommonFields("fabrication_no,software_version");
addStringField(
"status",

Wyświetl plik

@ -36,8 +36,8 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields();
addOptionalFlowRelatedFields();
addOptionalCommonFields("meter_datetime");
addOptionalFlowRelatedFields("total_m3,total_forward_m3,total_backward_m3,flow_temperature_c,volume_flow_m3h");
addStringField(
"status",

Wyświetl plik

@ -35,8 +35,8 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields();
addOptionalFlowRelatedFields();
addOptionalCommonFields("fabrication_no,operating_time_h,on_time_h,meter_datetime,meter_datetime_at_error");
addOptionalFlowRelatedFields("total_m3,flow_temperature_c,return_temperature_c,flow_return_temperature_difference_c,volume_flow_m3h");
addStringFieldWithExtractorAndLookup(
"status",

Wyświetl plik

@ -0,0 +1,168 @@
/*
Copyright (C) 2021-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("unismart");
di.setDefaultFields("name,id,total_m3,target_m3,timestamp");
di.setMeterType(MeterType::GasMeter);
di.addLinkMode(LinkMode::T1);
di.addDetection(MANUFACTURER_AMX, 0x03, 0x01);
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
});
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addOptionalCommonFields("fabrication_no");
addStringFieldWithExtractorAndLookup(
"status",
"Status of meter?",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | PrintProperty::STATUS,
FieldMatcher::build()
.set(DifVifKey("02FD74")),
{
{
{
"STATUS_FLAGS",
Translate::Type::BitToString,
0xffff,
"OK",
{
}
},
},
});
addStringFieldWithExtractorAndLookup(
"other",
"Other status of meter?",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | PrintProperty::STATUS,
FieldMatcher::build()
.set(DifVifKey("017F")),
{
{
{
"OTHER_FLAGS",
Translate::Type::BitToString,
0xff,
"",
{
}
},
},
});
addStringFieldWithExtractor(
"total_date_time",
"Timestamp for this total measurement.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::DateTime)
.set(IndexNr(1))
);
addNumericFieldWithExtractor(
"total",
"The total gas consumption recorded by this meter.",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.add(VIFCombinable::UncorrectedMeterUnit)
);
addStringFieldWithExtractor(
"target_date_time",
"Timestamp for gas consumption recorded at the beginning of this month.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::DateTime)
.set(StorageNr(1))
);
addNumericFieldWithExtractor(
"target",
"The total gas consumption recorded by this meter at the beginning of this month.",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(StorageNr(1))
.add(VIFCombinable::UncorrectedMeterUnit)
);
addStringFieldWithExtractor(
"version",
"Model version.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ModelVersion)
);
addStringFieldWithExtractor(
"supplier_info",
"Supplier info?",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::SpecialSupplierInformation)
);
addStringFieldWithExtractor(
"parameter_set",
"Meter configued with this parameter set?",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ParameterSet)
);
addStringFieldWithExtractor(
"meter_timestamp",
"Timestamp when this measurement was sent.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::DateTime)
.set(IndexNr(2))
);
}
}
// Test: GasMeter unismart 00043094 00000000000000000000000000000000
// telegram=|6044B8059430040001037A1D005085E2B670BCF1A5C87E0C1A51DA18924EF984613DA2A9CD39D8F4C7208326C76D42DBEADF80D574192B71BD7C4F56A7F1513151768A9DB804883B28CB085CA2D0F7438C361CB9E2734712ED9BFBB2A14EF55208|
// {"media":"gas","meter":"unismart","name":"GasMeter","id":"00043094","fabrication_no":"03162296","status":"STATUS_FLAGS_CF0","other":"OTHER_FLAGS_14","total_date_time":"2021-09-15 13:18","total_m3":917,"target_date_time":"2021-09-01 06:00","target_m3":911.32,"version":" 4GGU","supplier_info":"00","parameter_set":"02","meter_timestamp":"2021-09-15 13:18:30","timestamp":"1111-11-11T11:11:11Z"}
// |GasMeter;00043094;917;911.32;1111-11-11 11:11.11

Wyświetl plik

@ -1168,7 +1168,10 @@ bool DVEntry::extractDate(struct tm *out)
// ..ss ssss
int sec = (0x3f) & v[0];
out->tm_sec = sec;
// some daylight saving time decoding needed here....
// There are also bits for day of week, week of year.
// A bit for if daylight saving is in use or not and its offset.
// A bit if it is a leap year.
// I am unsure how to deal with this here..... TODO
}
return ok;

Wyświetl plik

@ -62,6 +62,7 @@
X(Current,0x7D50,0x7D5F, Quantity::Current, Unit::Ampere) \
X(ResetCounter,0x7D60,0x7D60, Quantity::Counter, Unit::COUNTER) \
X(CumulationCounter,0x7D61,0x7D61, Quantity::Counter, Unit::COUNTER) \
X(SpecialSupplierInformation,0x7D67,0x7D67, Quantity::Text, Unit::TXT) \
X(RemainingBattery,0x7D74,0x7D74, Quantity::Time, Unit::Day) \
X(DurationSinceReadout,0x7DAC,0x7DAC, Quantity::Time, Unit::Hour) \
X(AnyVolumeVIF,0x00,0x00, Quantity::Volume, Unit::Unknown) \
@ -280,6 +281,7 @@ struct IndexNr
IndexNr(int n) : nr_(n) {}
int intValue() { return nr_; }
bool operator==(IndexNr s) { return nr_ == s.nr_; }
bool operator!=(IndexNr s) { return nr_ != s.nr_; }
private:
int nr_;
@ -381,8 +383,9 @@ struct FieldMatcher
SubUnitNr subunit_nr_from { 0 };
SubUnitNr subunit_nr_to { 0 };
// If the telegram has multiple identical difvif entries, use entry with this index nr.
// First entry has nr 1, which is the default value.
// If the telegram has multiple identical difvif entries matching this field
// and you want to catch the second matching entry, then set the index nr to 2.
// The default is 1.
IndexNr index_nr { 1 };
FieldMatcher() : active(false) { }

Wyświetl plik

@ -75,7 +75,6 @@
X(SONTEX868, MANUFACTURER_SON, 0x08, 0x16) \
X(TOPASESKR, MANUFACTURER_AMT, 0x06, 0xf1) \
X(TOPASESKR, MANUFACTURER_AMT, 0x07, 0xf1) \
X(UNISMART, MANUFACTURER_AMX, 0x03, 0x01) \

Wyświetl plik

@ -1,272 +0,0 @@
/*
Copyright (C) 2021 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"
#include<algorithm>
using namespace std;
struct MeterUnismart : public virtual MeterCommonImplementation {
MeterUnismart(MeterInfo &mi);
// Total gas counted through the meter
double totalGasConsumption(Unit u);
bool hasTotalGasConsumption();
// Consumption at the beginning of this month.
double targetGasConsumption(Unit u);
private:
void processContent(Telegram *t);
string fabrication_no_;
string total_date_time_;
double total_gas_consumption_m3_ {};
string target_date_time_;
double target_gas_consumption_m3_ {};
string version_;
string device_date_time_;
string supplier_info_;
string status_;
string parameter_set_;
uint8_t other_;
};
shared_ptr<Meter> createUnismart(MeterInfo &mi)
{
return shared_ptr<Meter>(new MeterUnismart(mi));
}
MeterUnismart::MeterUnismart(MeterInfo &mi) :
MeterCommonImplementation(mi, "unismart")
{
setMeterType(MeterType::GasMeter);
setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV);
addLinkMode(LinkMode::T1);
addPrint("fabrication_no", Quantity::Text,
[&](){ return fabrication_no_; },
"Static fabrication no information.",
PrintProperty::JSON);
addPrint("total_date_time", Quantity::Text,
[&](){ return total_date_time_; },
"Timestamp for this total measurement.",
PrintProperty::JSON);
addPrint("total", Quantity::Volume,
[&](Unit u){ return totalGasConsumption(u); },
"The total gas consumption recorded by this meter.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("target_date_time", Quantity::Text,
[&](){ return target_date_time_; },
"Timestamp for gas consumption recorded at the beginning of this month.",
PrintProperty::JSON);
addPrint("target", Quantity::Volume,
[&](Unit u){ return targetGasConsumption(u); },
"The total gas consumption recorded by this meter at the beginning of this month.",
PrintProperty::FIELD | PrintProperty::JSON);
addPrint("version", Quantity::Text,
[&](){ return version_; },
"Model/version a reported by meter.",
PrintProperty::JSON);
addPrint("device_date_time", Quantity::Text,
[&](){ return device_date_time_; },
"Device date time? Seems to be the same as total date time.",
PrintProperty::JSON);
addPrint("suppler_info", Quantity::Text,
[&](){ return supplier_info_; },
"?",
PrintProperty::JSON);
addPrint("status", Quantity::Text,
[&](){ return status_; },
"?",
PrintProperty::JSON);
addPrint("parameter_set", Quantity::Text,
[&](){ return parameter_set_; },
"?",
PrintProperty::JSON);
addPrint("other", Quantity::Counter,
[&](Unit u){ return other_; },
"?",
PrintProperty::JSON);
}
void MeterUnismart::processContent(Telegram *t)
{
/*
(unismart) 11: 0C dif (8 digit BCD Instantaneous value)
(unismart) 12: 78 vif (Fabrication no)
(unismart) 13: 96221603
(unismart) 17: 04 dif (32 Bit Integer/Binary Instantaneous value)
(unismart) 18: 6D vif (Date and time type)
(unismart) 19: 122DAF29
(unismart) 1d: 0C dif (8 digit BCD Instantaneous value)
(unismart) 1e: 94 vif (Volume 10² m³)
(unismart) 1f: 3A vife (uncorrected meter unit)
(unismart) 20: * 00170900 total consumption (917.000000 m3)
(unismart) 24: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(unismart) 25: 6D vif (Date and time type)
(unismart) 26: 0026A129
(unismart) 2a: 4C dif (8 digit BCD Instantaneous value storagenr=1)
(unismart) 2b: 94 vif (Volume 10² m³)
(unismart) 2c: 3A vife (uncorrected meter unit)
(unismart) 2d: 32110900
(unismart) 31: 01 dif (8 Bit Integer/Binary Instantaneous value)
(unismart) 32: FD vif (Second extension FD of VIF-codes)
(unismart) 33: 67 vife (Special supplier information)
(unismart) 34: 00
(unismart) 35: 02 dif (16 Bit Integer/Binary Instantaneous value)
(unismart) 36: FD vif (Second extension FD of VIF-codes)
(unismart) 37: 74 vife (Reserved)
(unismart) 38: F00C
(unismart) 3a: 0D dif (variable length Instantaneous value)
(unismart) 3b: FD vif (Second extension FD of VIF-codes)
(unismart) 3c: 0C vife (Model/Version)
(unismart) 3d: 06 varlen=6
(unismart) 3e: 554747342020
(unismart) 44: 01 dif (8 Bit Integer/Binary Instantaneous value)
(unismart) 45: FD vif (Second extension FD of VIF-codes)
(unismart) 46: 0B vife (Parameter set identification)
(unismart) 47: 02
(unismart) 48: 01 dif (8 Bit Integer/Binary Instantaneous value)
(unismart) 49: 7F vif (Manufacturer specific)
(unismart) 4a: 14
(unismart) 4b: 06 dif (48 Bit Integer/Binary Instantaneous value)
(unismart) 4c: 6D vif (Date and time type)
(unismart) 4d: 1E120DAF296D
(unismart) 53: 2F skip
(unismart) 54: 2F skip
(unismart) 55: 2F skip
(unismart) 56: 2F skip
(unismart) 57: 2F skip
(unismart) 58: 2F skip
(unismart) 59: 2F skip
(unismart) 5a: 2F skip
(unismart) 5b: 2F skip
(unismart) 5c: 2F skip
(unismart) 5d: 2F skip
(unismart) 5e: 2F skip
*/
int offset;
string key;
uint64_t v {};
if (extractDVlong(&t->dv_entries, "0C78", &offset, &v))
{
fabrication_no_ = to_string(v);
t->addMoreExplanation(offset, " fabrication no (%zu)", v);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 0, 0, &key, &t->dv_entries)) {
struct tm datetime;
extractDVdate(&t->dv_entries, key, &offset, &datetime);
total_date_time_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " total datetime (%s)", total_date_time_.c_str());
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries))
{
extractDVdouble(&t->dv_entries, key, &offset, &total_gas_consumption_m3_);
t->addMoreExplanation(offset, " total consumption (%f m3)", total_gas_consumption_m3_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 1, 0, &key, &t->dv_entries)) {
struct tm datetime;
extractDVdate(&t->dv_entries, key, &offset, &datetime);
target_date_time_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " target datetime (%s)", target_date_time_.c_str());
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 0, &key, &t->dv_entries))
{
extractDVdouble(&t->dv_entries, key, &offset, &target_gas_consumption_m3_);
t->addMoreExplanation(offset, " target consumption (%f m3)", target_gas_consumption_m3_);
}
string tmp;
if (extractDVHexString(&t->dv_entries, "0DFD0C", &offset, &tmp))
{
vector<uchar> bin;
hex2bin(tmp, &bin);
version_ = safeString(bin);
trimWhitespace(&version_);
t->addMoreExplanation(offset, " version (%s)", version_.c_str());
}
struct tm datetime;
if (extractDVdate(&t->dv_entries, "066D", &offset, &datetime))
{
device_date_time_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " device datetime (%s)", device_date_time_.c_str());
}
if (extractDVHexString(&t->dv_entries, "01FD67", &offset, &supplier_info_))
{
t->addMoreExplanation(offset, " suppler info (%s)", supplier_info_.c_str());
}
if (extractDVHexString(&t->dv_entries, "02FD74", &offset, &status_))
{
t->addMoreExplanation(offset, " status (%s)", status_.c_str());
}
if (extractDVHexString(&t->dv_entries, "01FD0B", &offset, &parameter_set_))
{
t->addMoreExplanation(offset, " parameter set (%s)", parameter_set_.c_str());
}
if (extractDVuint8(&t->dv_entries, "017F", &offset, &other_))
{
t->addMoreExplanation(offset, " status2 (%d)", other_);
}
}
double MeterUnismart::totalGasConsumption(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_gas_consumption_m3_, Unit::M3, u);
}
bool MeterUnismart::hasTotalGasConsumption()
{
return true;
}
double MeterUnismart::targetGasConsumption(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(target_gas_consumption_m3_, Unit::M3, u);
}

Wyświetl plik

@ -1873,6 +1873,8 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
{
map<FieldInfo*,DVEntry*> found;
// Sort the dv_entries based on their offset in the telegram.
// I.e. restore the ordering that was implicit in the telegram.
vector<DVEntry*> sorted_entries;
for (auto &p : t->dv_entries)
@ -1882,30 +1884,32 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
sort(sorted_entries.begin(), sorted_entries.end(),
[](const DVEntry* a, const DVEntry *b) -> bool { return a->offset < b->offset; });
// Iterate through the data content (dv_entries) in the telegram.
for (DVEntry *dve : sorted_entries)
// Now go through each field_info defined by the driver.
for (FieldInfo &fi : field_infos_)
{
// We have telegram content, a dif-vif-value entry.
// Now check for a field info that wants to handle this telegram content entry.
for (FieldInfo &fi : field_infos_)
int current_match_nr = 0;
// This field_info has not been matched to a dv_entry before!
debug("(meters) trying field info %s(%s)[%d]...\n",
fi.vname().c_str(),
toString(fi.xuantity()),
fi.index());
// Iterate through dv_entries in the telegram in the same order the telegram presented them.
for (DVEntry *dve : sorted_entries)
{
if (fi.hasMatcher() && fi.matches(dve))
{
if (found.count(&fi) != 0)
{
DVEntry *old = found[&fi];
current_match_nr++;
verbose("(meter) while processing field extractors ignoring dventry %s at offset %d matching since "
"field %s was already matched against dventry %s at offset %d !\n",
dve->dif_vif_key.str().c_str(),
dve->offset,
fi.vname().c_str(),
old->dif_vif_key.str().c_str(),
old->offset);
}
else
if (fi.matcher().index_nr != IndexNr(current_match_nr))
{
// We have field that wants to handle this entry!
// This field info did match, but requires another index nr!
// Increment the current index nr and look for the next match.
}
else if (found.count(&fi) == 0)
{
// This field_info has not been matched to a dv_entry before!
debug("(meters) using field info %s(%s)[%d] to extract %s at offset %d\n",
fi.vname().c_str(),
toString(fi.xuantity()),
@ -1917,6 +1921,18 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
fi.performExtraction(this, t, dve);
found[&fi] = dve;
}
else
{
DVEntry *old = found[&fi];
verbose("(meter) while processing field extractors ignoring dventry %s at offset %d matching since "
"field %s was already matched against dventry %s at offset %d !\n",
dve->dif_vif_key.str().c_str(),
dve->offset,
fi.vname().c_str(),
old->dif_vif_key.str().c_str(),
old->offset);
}
}
}
}
@ -2912,7 +2928,17 @@ bool FieldInfo::extractString(Meter *m, Telegram *t, DVEntry *dve)
{
struct tm datetime;
dve->extractDate(&datetime);
string extracted_device_date_time = strdatetime(&datetime);
string extracted_device_date_time;
if (dve->value.size() == 12)
{
// A long date time sec + timezone field. TODO add timezone data.
extracted_device_date_time = strdatetimesec(&datetime);
}
else
{
extracted_device_date_time = strdatetime(&datetime);
}
m->setStringValue(this, extracted_device_date_time);
t->addMoreExplanation(dve->offset, renderJsonText(m));
found = true;
@ -2926,13 +2952,14 @@ bool FieldInfo::extractString(Meter *m, Telegram *t, DVEntry *dve)
t->addMoreExplanation(dve->offset, renderJsonText(m));
found = true;
}
else if (matcher_.vif_range == VIFRange::EnhancedIdentification ||
else if (matcher_.vif_range == VIFRange::Any ||
matcher_.vif_range == VIFRange::EnhancedIdentification ||
matcher_.vif_range == VIFRange::FabricationNo ||
matcher_.vif_range == VIFRange::ModelVersion ||
matcher_.vif_range == VIFRange::SoftwareVersion ||
matcher_.vif_range == VIFRange::Customer ||
matcher_.vif_range == VIFRange::Location ||
matcher_.vif_range == VIFRange::Any ||
matcher_.vif_range == VIFRange::SpecialSupplierInformation ||
matcher_.vif_range == VIFRange::ParameterSet)
{
string extracted_id;
@ -3030,204 +3057,302 @@ bool Address::parse(string &s)
return true;
}
void MeterCommonImplementation::addOptionalCommonFields()
bool checkIf(set<string> &fields, const char *s)
{
addStringFieldWithExtractor(
"fabrication_no",
"Fabrication number.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::FabricationNo)
);
addStringFieldWithExtractor(
"enhanced_id",
"Enhanced identification number.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::EnhancedIdentification)
);
addStringFieldWithExtractor(
"software_version",
"Software version.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::SoftwareVersion)
);
addStringFieldWithExtractor(
"customer",
"Customer name.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Customer)
);
addStringFieldWithExtractor(
"location",
"Meter installed at this customer location.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Location)
);
addNumericFieldWithExtractor(
"operating_time",
"How long the meter has been collecting data.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::OperatingTime)
);
addNumericFieldWithExtractor(
"on_time",
"How long the meter has been powered up.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::OnTime)
);
addNumericFieldWithExtractor(
"on_time_at_error",
"How long the meter has been in an error state while powered up.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::AtError)
.set(VIFRange::OnTime)
);
addStringFieldWithExtractor(
"meter_date",
"Date when the meter sent the telegram.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Date)
);
addStringFieldWithExtractor(
"meter_date_at_error",
"Date when the meter was in error.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::AtError)
.set(VIFRange::Date)
);
addStringFieldWithExtractor(
"meter_datetime",
"Date and time when the meter sent the telegram.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::DateTime)
);
addStringFieldWithExtractor(
"meter_datetime_at_error",
"Date and time when the meter was in error.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::AtError)
.set(VIFRange::DateTime)
);
if (fields.count(s) > 0)
{
fields.erase(s);
return true;
}
return false;
}
void MeterCommonImplementation::addOptionalFlowRelatedFields()
void checkFieldsEmpty(set<string> &fields, string name)
{
addNumericFieldWithExtractor(
"total",
"The total media volume consumption recorded by this meter.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
);
if (fields.size() > 0)
{
string info;
for (auto &s : fields) { info += s+" "; }
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)
);
warning("(meter) when adding common fields to driver %s, these fields were not found: %s\n",
name.c_str(),
info.c_str());
}
}
addNumericFieldWithExtractor(
"total_backward",
"The total media volume flowing backward.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.add(VIFCombinable::BackwardFlow)
);
void MeterCommonImplementation::addOptionalCommonFields(string field_names)
{
set<string> fields = splitStringIntoSet(field_names, ',');
addNumericFieldWithExtractor(
"flow_temperature",
"Forward media temperature.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::FlowTemperature)
);
if (checkIf(fields, "fabrication_no"))
{
addStringFieldWithExtractor(
"fabrication_no",
"Fabrication number.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::FabricationNo)
);
}
addNumericFieldWithExtractor(
"return_temperature",
"Return media temperature.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ReturnTemperature)
);
if (checkIf(fields,"enhanced_id"))
{
addStringFieldWithExtractor(
"enhanced_id",
"Enhanced identification number.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::EnhancedIdentification)
);
}
addNumericFieldWithExtractor(
"flow_return_temperature_difference",
"The difference between flow and return media temperatures.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::AutoSigned,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::TemperatureDifference)
);
if (checkIf(fields,"software_version"))
{
addStringFieldWithExtractor(
"software_version",
"Software version.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::SoftwareVersion)
);
}
addNumericFieldWithExtractor(
"volume_flow",
"Media volume flow.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Flow,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::VolumeFlow)
);
if (checkIf(fields,"model_version"))
{
addStringFieldWithExtractor(
"model_version",
"Meter model version.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ModelVersion)
);
}
if (checkIf(fields,"customer"))
{
addStringFieldWithExtractor(
"customer",
"Customer name.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Customer)
);
}
if (checkIf(fields,"location"))
{
addStringFieldWithExtractor(
"location",
"Meter installed at this customer location.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Location)
);
}
if (checkIf(fields,"operating_time_h"))
{
addNumericFieldWithExtractor(
"operating_time",
"How long the meter has been collecting data.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::OperatingTime)
);
}
if (checkIf(fields,"on_time_h"))
{
addNumericFieldWithExtractor(
"on_time",
"How long the meter has been powered up.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::OnTime)
);
}
if (checkIf(fields,"on_time_at_error_h"))
{
addNumericFieldWithExtractor(
"on_time_at_error",
"How long the meter has been in an error state while powered up.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::AtError)
.set(VIFRange::OnTime)
);
}
if (checkIf(fields,"meter_date"))
{
addStringFieldWithExtractor(
"meter_date",
"Date when the meter sent the telegram.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Date)
);
}
if (checkIf(fields,"meter_date_at_error"))
{
addStringFieldWithExtractor(
"meter_date_at_error",
"Date when the meter was in error.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::AtError)
.set(VIFRange::Date)
);
}
if (checkIf(fields,"meter_datetime"))
{
addStringFieldWithExtractor(
"meter_datetime",
"Date and time when the meter sent the telegram.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::DateTime)
);
}
if (checkIf(fields,"meter_datetime_at_error"))
{
addStringFieldWithExtractor(
"meter_datetime_at_error",
"Date and time when the meter was in error.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::AtError)
.set(VIFRange::DateTime)
);
}
checkFieldsEmpty(fields, name());
}
void MeterCommonImplementation::addOptionalFlowRelatedFields(string field_names)
{
set<string> fields = splitStringIntoSet(field_names, ',');
if (checkIf(fields,"total_m3"))
{
addNumericFieldWithExtractor(
"total",
"The total media volume consumption recorded by this meter.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
);
}
if (checkIf(fields,"total_forward_m3"))
{
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)
);
}
if (checkIf(fields,"total_backward_m3"))
{
addNumericFieldWithExtractor(
"total_backward",
"The total media volume flowing backward.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.add(VIFCombinable::BackwardFlow)
);
}
if (checkIf(fields,"flow_temperature_c"))
{
addNumericFieldWithExtractor(
"flow_temperature",
"Forward media temperature.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::FlowTemperature)
);
}
if (checkIf(fields,"return_temperature_c"))
{
addNumericFieldWithExtractor(
"return_temperature",
"Return media temperature.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ReturnTemperature)
);
}
if (checkIf(fields,"flow_return_temperature_difference_c"))
{
addNumericFieldWithExtractor(
"flow_return_temperature_difference",
"The difference between flow and return media temperatures.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::AutoSigned,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::TemperatureDifference)
);
}
if (checkIf(fields,"volume_flow_m3h"))
{
addNumericFieldWithExtractor(
"volume_flow",
"Media volume flow.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Flow,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::VolumeFlow)
);
}
}

Wyświetl plik

@ -83,7 +83,6 @@ LIST_OF_METER_TYPES
X(sontex868, T1_bit, HeatCostAllocationMeter, SONTEX868, Sontex868) \
X(topaseskr, T1_bit, WaterMeter, TOPASESKR, TopasEsKr) \
X(lse_08, S1_bit|C1_bit, HeatCostAllocationMeter, LSE_08, LSE_08) \
X(unismart, T1_bit, GasMeter, UNISMART, Unismart) \
enum class MeterDriver {
@ -292,7 +291,8 @@ enum PrintProperty
DEPRECATED = 32, // This field is about to be removed or changed in a newer driver, which will have a new name.
STATUS = 64, // This is >the< status field and it should read OK of not error flags are set.
JOIN_TPL_STATUS = 128, // This text field also includes the tpl status decoding. multiple OK:s collapse to a single OK.
JOIN_INTO_STATUS = 256 // This text field is injected into the already defined status field. multiple OK:s collapse.
JOIN_INTO_STATUS = 256, // This text field is injected into the already defined status field. multiple OK:s collapse.
OFFICIAL = 512 // This field is listed as an official field for the driver.
};
struct PrintProperties

Wyświetl plik

@ -103,7 +103,6 @@ protected:
void setMeterType(MeterType mt);
void addLinkMode(LinkMode lm);
void addMfctTPLStatusBits(Translate::Lookup lookup);
void setDefaultFields(string f);
// Print with the default unit for this quantity.
void addPrint(string vname, Quantity vquantity,
@ -257,8 +256,8 @@ protected:
std::string decodeTPLStatusByte(uchar sts);
void addOptionalCommonFields();
void addOptionalFlowRelatedFields();
void addOptionalCommonFields(string fields);
void addOptionalFlowRelatedFields(string fields);
vector<string> &selectedFields() { return selected_fields_; }
void setSelectedFields(vector<string> &f) { selected_fields_ = f; }

Wyświetl plik

@ -1884,6 +1884,13 @@ vector<string> splitString(const string &s, char c)
return v;
}
set<string> splitStringIntoSet(const string &s, char c)
{
vector<string> v = splitString(s, c);
set<string> words(v.begin(), v.end());
return words;
}
vector<string> splitDeviceString(const string& ds)
{
string s = ds;

Wyświetl plik

@ -23,6 +23,7 @@
#include<string>
#include<functional>
#include<map>
#include<set>
#include<vector>
void onExit(std::function<void()> cb);
@ -152,6 +153,8 @@ bool isNumber(const std::string& fq);
std::vector<std::string> splitMatchExpressions(const std::string& mes);
// Split s into strings separated by c.
std::vector<std::string> splitString(const std::string &s, char c);
// Split s into strings separated by c and store inte set.
std::set<std::string> splitStringIntoSet(const std::string &s, char c);
// Split device string cul:c1:CMD(bar 1:2) into cul c1 CMD(bar 1:2)
// I.e. the : colon inside CMD is not used for splitting.
std::vector<std::string> splitDeviceString(const std::string &s);

Wyświetl plik

@ -13,11 +13,7 @@ cat > $TEST/test_expected.txt <<EOF
(dvparser) warning: unexpected end of data
(dvparser) warning: unexpected end of data
(dvparser) found new format "046D0406041301FD17426C4406840106840206840306840406840506840606840706840806840906C1337F47A64E0C062364" with hash b934, remembering!
(meter) while processing field extractors ignoring dventry 0C06 at offset 129 matching since field total was already matched against dventry 0406 at offset 25 !
(dvparser) found new format "046D0406041301FD17426C4406840106840206840306840406840506840606840706840806840906585D65E6958F6B5E93DBA60CD99D06EB27D97106000000840F060003620501" with hash 6c76, remembering!
(meter) while processing field extractors ignoring dventry 0000 at offset 146 matching since field total was already matched against dventry 0406 at offset 25 !
(meter) while processing field extractors ignoring dventry 0600 at offset 151 matching since field total was already matched against dventry 0406 at offset 25 !
(meter) while processing field extractors ignoring dventry 0501 at offset 166 matching since field total was already matched against dventry 0406 at offset 25 !
EOF
$PROG --format=fields --selectfields=id,current_consumption_hca,device_date_time --debug simulations/simulation_broken.txt \
@ -35,5 +31,11 @@ then
fi
else
echo "wmbusmeters returned error code: $?"
echo "EXPECTED----------------------------------------------"
cat $TEST/test_expected.txt
echo "----------------------------------------------"
echo "GOT----------------------------------------------"
cat $TEST/test_output.txt
echo "----------------------------------------------"
fi

Wyświetl plik

@ -41,6 +41,8 @@ do
echo OK json: $TESTNAME
TESTRESULT="OK"
else
jq --sort-keys . $TEST/test_expected_json.txt
jq --sort-keys . $TEST/test_response_json.txt
TESTRESULT="ERROR"
fi
else