kopia lustrzana https://github.com/weetmuts/wmbusmeters
Preparing tests for sorted json output keys.
rodzic
53a2d650e5
commit
f650577ea2
|
@ -237,11 +237,6 @@ telegram=|37446850336633663943a2_10672c866100181c01000480794435d5000000000000000
|
|||
{"media":"heat","meter":"compact5","name":"Heating2","id":"66336633","total_kwh":25250,"current_kwh":284,"previous_kwh":24966,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
|Heating2;66336633;25250;284;24966;1111-11-11 11:11.11
|
||||
|
||||
# Test Maddalena EVO 868 wmbus module on water meter
|
||||
telegram=|aa4424347677787950077ac10000202f2f041306070000046d1e31b12104fd17000000000e787880048120004413c9040000426c9f2c840113c904000082016c9f2cd3013b9a0200c4016d0534a7218104fd280182046c9f2c840413c9040000c404131b00000084051300000000c405130000000084061300000000c406130000000084071300000000c407130000000084081300000000c408130000000084091300000000c4091300000000ffff|
|
||||
{"media":"water","meter":"evo868","name":"Votchka","id":"79787776","total_m3":1.798,"device_date_time":"2021-01-17 17:30","current_status":"OK","fabrication_no":"002081048078","consumption_at_set_date_m3":1.225,"set_date":"2020-12-31","consumption_at_set_date_2_m3":1.225,"set_date_2":"2020-12-31","max_flow_since_datetime_m3h":0.666,"max_flow_datetime":"2021-01-07 20:05","consumption_at_history_1_m3":1.225,"history_1_date":"2020-12-31","consumption_at_history_2_m3":0.027,"history_2_date":"2020-11-30","consumption_at_history_3_m3":0,"history_3_date":"2020-10-31","consumption_at_history_4_m3":0,"history_4_date":"2020-09-30","consumption_at_history_5_m3":0,"history_5_date":"2020-08-31","consumption_at_history_6_m3":0,"history_6_date":"2020-07-31","consumption_at_history_7_m3":0,"history_7_date":"2020-06-30","consumption_at_history_8_m3":0,"history_8_date":"2020-05-31","consumption_at_history_9_m3":0,"history_9_date":"2020-04-30","consumption_at_history_10_m3":0,"history_10_date":"2020-03-31","consumption_at_history_11_m3":0,"history_11_date":"2020-02-29","consumption_at_history_12_m3":0,"history_12_date":"2020-01-31","timestamp":"1111-11-11T11:11:11Z"}
|
||||
|Votchka;79787776;1.798000;OK;1.225000;2020-12-31;1111-11-11 11:11.11
|
||||
|
||||
# Test Gran-System-S electricity meter 101
|
||||
telegram=||7844731e78610418010278046d0f13bc21040394030000841003690300008420032b00000084300300000000848010030000000084016d0000bc2184010394030000841103690300008421032b00000084310300000000848110030000000004fd482e09000004fd5b0000000002fb2d861304fd1700000201|
|
||||
{"media":"electricity","meter":"gransystems","name":"Gran101","id":"18046178","total_energy_consumption_kwh":0.916,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":0,"voltage_at_phase_3_v":0,"currrent_at_phase_1_a":0,"currrent_at_phase_2_a":0,"currrent_at_phase_3_a":0,"frequency_hz":49.98,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include"meters_common_implementation.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Driver : public virtual MeterCommonImplementation
|
||||
{
|
||||
Driver(MeterInfo &mi, DriverInfo &di);
|
||||
};
|
||||
|
||||
static bool ok = registerDriver([](DriverInfo&di)
|
||||
{
|
||||
di.setName("evo868");
|
||||
di.setDefaultFields("name,id,total_m3,set_date,consumption_at_set_date_m3,timestamp");
|
||||
di.setMeterType(MeterType::WaterMeter);
|
||||
di.addLinkMode(LinkMode::T1);
|
||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||
di.addDetection(MANUFACTURER_MAD, 0x07, 0x50);
|
||||
|
||||
});
|
||||
|
||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||
{
|
||||
addStringFieldWithExtractorAndLookup(
|
||||
"current_status",
|
||||
"Status of meter.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT
|
||||
| PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::ErrorFlags),
|
||||
{
|
||||
{
|
||||
{
|
||||
"ERROR_FLAGS",
|
||||
Translate::Type::BitToString,
|
||||
0xffff,
|
||||
"OK",
|
||||
{
|
||||
// { 0x01 , "WOOT" },
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
addOptionalCommonFields("fabrication_no,meter_datetime");
|
||||
addOptionalFlowRelatedFields("total_m3");
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"consumption_at_set_date",
|
||||
"The total water consumption at the most recent billing period date.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Volume)
|
||||
.set(StorageNr(1))
|
||||
);
|
||||
|
||||
addStringFieldWithExtractor(
|
||||
"set_date",
|
||||
"The most recent billing period date.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Date)
|
||||
.set(StorageNr(1))
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"consumption_at_set_date_2",
|
||||
"The total water consumption at the second most recent billing period date.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Volume)
|
||||
.set(StorageNr(2))
|
||||
);
|
||||
|
||||
addStringFieldWithExtractor(
|
||||
"set_date_2",
|
||||
"The second most recent billing period date.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Date)
|
||||
.set(StorageNr(2))
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"max_flow_since_datetime",
|
||||
"Maximum water flow since date time.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD,
|
||||
Quantity::Flow,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Maximum)
|
||||
.set(VIFRange::VolumeFlow)
|
||||
.set(StorageNr(3))
|
||||
);
|
||||
|
||||
addStringFieldWithExtractor(
|
||||
"max_flow_datetime",
|
||||
"The datetime to which maximum flow is measured.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::DateTime)
|
||||
.set(StorageNr(3))
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"consumption_at_history_{storage_counter-7counter}",
|
||||
"The total water consumption at the historic date.",
|
||||
PrintProperty::JSON,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Volume)
|
||||
.set(StorageNr(8),StorageNr(19))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// est: Votchka evo868 79787776 NOKEY
|
||||
// omment: Test Maddalena EVO 868 wmbus module on water meter
|
||||
// elegram=|aa4424347677787950077ac10000202f2f041306070000046d1e31b12104fd17000000000e787880048120004413c9040000426c9f2c840113c904000082016c9f2cd3013b9a0200c4016d0534a7218104fd280182046c9f2c840413c9040000c404131b00000084051300000000c405130000000084061300000000c406130000000084071300000000c407130000000084081300000000c408130000000084091300000000c4091300000000ffff|
|
||||
// "media":"water","meter":"evo868","name":"Votchka","id":"79787776","total_m3":1.798,"device_date_time":"2021-01-17 17:30","current_status":"OK","fabrication_no":"002081048078","consumption_at_set_date_m3":1.225,"set_date":"2020-12-31","consumption_at_set_date_2_m3":1.225,"set_date_2":"2020-12-31","max_flow_since_datetime_m3h":0.666,"max_flow_datetime":"2021-01-07 20:05","consumption_at_history_1_m3":1.225,"history_1_date":"2020-12-31","consumption_at_history_2_m3":0.027,"history_2_date":"2020-11-30","consumption_at_history_3_m3":0,"history_3_date":"2020-10-31","consumption_at_history_4_m3":0,"history_4_date":"2020-09-30","consumption_at_history_5_m3":0,"history_5_date":"2020-08-31","consumption_at_history_6_m3":0,"history_6_date":"2020-07-31","consumption_at_history_7_m3":0,"history_7_date":"2020-06-30","consumption_at_history_8_m3":0,"history_8_date":"2020-05-31","consumption_at_history_9_m3":0,"history_9_date":"2020-04-30","consumption_at_history_10_m3":0,"history_10_date":"2020-03-31","consumption_at_history_11_m3":0,"history_11_date":"2020-02-29","consumption_at_history_12_m3":0,"history_12_date":"2020-01-31","timestamp":"1111-11-11T11:11:11Z"}
|
||||
// Votchka;79787776;1.798;OK;1.225;2020-12-31;1111-11-11 11:11.11
|
|
@ -282,6 +282,7 @@ struct SubUnitNr
|
|||
SubUnitNr(int n) : nr_(n) {}
|
||||
int intValue() { return nr_; }
|
||||
bool operator==(SubUnitNr s) { return nr_ == s.nr_; }
|
||||
bool operator!=(SubUnitNr s) { return nr_ != s.nr_; }
|
||||
bool operator>=(SubUnitNr s) { return nr_ >= s.nr_; }
|
||||
bool operator<=(SubUnitNr s) { return nr_ <= s.nr_; }
|
||||
|
||||
|
@ -450,6 +451,16 @@ struct FieldMatcher
|
|||
FieldMatcher &set(IndexNr i) { index_nr = i; return *this; }
|
||||
|
||||
bool matches(DVEntry &dv_entry);
|
||||
|
||||
// Returns true of there is any range for storage, tariff, subunit nrs.
|
||||
// I.e. this matcher is expected to match against multiple dv entries!
|
||||
bool expectedToMatchAgainstMultipleEntries()
|
||||
{
|
||||
return (match_storage_nr && storage_nr_from != storage_nr_to)
|
||||
|| (match_tariff_nr && tariff_nr_from != tariff_nr_to)
|
||||
|| (match_subunit_nr && subunit_nr_from != subunit_nr_to);
|
||||
}
|
||||
|
||||
std::string str();
|
||||
};
|
||||
|
||||
|
|
|
@ -1083,7 +1083,14 @@ string StringInterpolatorImplementation::apply(DVEntry *dve)
|
|||
|
||||
if (f < formulas_.size())
|
||||
{
|
||||
result += tostrprintf("%g", formulas_[f]->calculate(Unit::COUNTER, dve));
|
||||
if (dve != NULL)
|
||||
{
|
||||
result += tostrprintf("%g", formulas_[f]->calculate(Unit::COUNTER, dve));
|
||||
}
|
||||
else
|
||||
{
|
||||
result += "{NULL}";
|
||||
}
|
||||
f++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#define METER_DETECTION \
|
||||
X(CCx01, MANUFACTURER_GSS, 0x02, 0x01) \
|
||||
X(EURISII, MANUFACTURER_INE, 0x08, 0x55) \
|
||||
X(EVO868, MANUFACTURER_MAD, 0x07, 0x50) \
|
||||
X(HYDROCALM3,MANUFACTURER_BMT, 0x0d, 0x0b) \
|
||||
X(HYDRODIGIT,MANUFACTURER_BMT, 0x06, 0x13) \
|
||||
X(HYDRODIGIT,MANUFACTURER_BMT, 0x07, 0x13) \
|
||||
|
|
|
@ -1,371 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2017-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"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct MeterEvo868 : public virtual MeterCommonImplementation {
|
||||
MeterEvo868(MeterInfo &mi);
|
||||
|
||||
// Total water counted through the meter
|
||||
double totalWaterConsumption(Unit u);
|
||||
bool hasTotalWaterConsumption();
|
||||
uint32_t error_flags_ {};
|
||||
string fabrication_no_;
|
||||
double consumption_at_set_date_m3_ {};
|
||||
string set_date_;
|
||||
double consumption_at_set_date_2_m3_ {};
|
||||
string set_date_2_;
|
||||
double max_flow_since_datetime_m3h_ {};
|
||||
string max_flow_datetime_;
|
||||
|
||||
double consumption_at_history_date_m3_[12];
|
||||
string history_date_[12];
|
||||
|
||||
string status();
|
||||
|
||||
private:
|
||||
void processContent(Telegram *t);
|
||||
|
||||
double total_water_consumption_m3_ {};
|
||||
string device_date_time_;
|
||||
};
|
||||
|
||||
shared_ptr<Meter> createEVO868(MeterInfo &mi)
|
||||
{
|
||||
return shared_ptr<Meter>(new MeterEvo868(mi));
|
||||
}
|
||||
|
||||
MeterEvo868::MeterEvo868(MeterInfo &mi) :
|
||||
MeterCommonImplementation(mi, "evo868")
|
||||
{
|
||||
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("device_date_time", Quantity::Text,
|
||||
[&](){ return device_date_time_; },
|
||||
"Device date time.",
|
||||
PrintProperty::JSON);
|
||||
|
||||
addPrint("current_status", Quantity::Text,
|
||||
[&](){ return status(); },
|
||||
"Status of meter.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("fabrication_no", Quantity::Text,
|
||||
[&](){ return fabrication_no_; },
|
||||
"Fabrication number.",
|
||||
PrintProperty::JSON);
|
||||
|
||||
addPrint("consumption_at_set_date", Quantity::Volume,
|
||||
[&](Unit u){ assertQuantity(u, Quantity::Volume); return convert(consumption_at_set_date_m3_, Unit::M3, u); },
|
||||
"The total water consumption at the most recent billing period date.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("set_date", Quantity::Text,
|
||||
[&](){ return set_date_; },
|
||||
"The most recent billing period date.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("consumption_at_set_date_2", Quantity::Volume,
|
||||
[&](Unit u){ assertQuantity(u, Quantity::Volume); return convert(consumption_at_set_date_2_m3_, Unit::M3, u); },
|
||||
"The total water consumption at the second recent billing period date.",
|
||||
PrintProperty::JSON);
|
||||
|
||||
addPrint("set_date_2", Quantity::Text,
|
||||
[&](){ return set_date_2_; },
|
||||
"The second recent billing period date.",
|
||||
PrintProperty::JSON);
|
||||
|
||||
addPrint("max_flow_since_datetime", Quantity::Flow,
|
||||
[&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(max_flow_since_datetime_m3h_, Unit::M3H, u); },
|
||||
"Maximum water flow since date time.",
|
||||
PrintProperty::JSON);
|
||||
|
||||
addPrint("max_flow_datetime", Quantity::Text,
|
||||
[&](){ return max_flow_datetime_; },
|
||||
"The datetime to which maximum flow is measured.",
|
||||
PrintProperty::JSON);
|
||||
|
||||
for (int i=1; i<=12; ++i)
|
||||
{
|
||||
string key = tostrprintf("consumption_at_history_%d", i);
|
||||
string epl = tostrprintf("The total water consumption at the history date %d.", i);
|
||||
|
||||
addPrint(key, Quantity::Volume,
|
||||
[this,i](Unit u){ assertQuantity(u, Quantity::Volume); return convert(consumption_at_history_date_m3_[i-1], Unit::M3, u); },
|
||||
epl,
|
||||
PrintProperty::JSON);
|
||||
key = tostrprintf("history_%d_date", i);
|
||||
epl = tostrprintf("The history date %d.", i);
|
||||
|
||||
addPrint(key, Quantity::Text,
|
||||
[this,i](){ return history_date_[i-1]; },
|
||||
epl,
|
||||
PrintProperty::JSON);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MeterEvo868::processContent(Telegram *t)
|
||||
{
|
||||
/*
|
||||
(evo868) 11: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 12: 13 vif (Volume l)
|
||||
(evo868) 13: * 06070000 total consumption (1.798000 m3)
|
||||
|
||||
(evo868) 17: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 18: 6D vif (Date and time type)
|
||||
(evo868) 19: 1E31B121
|
||||
|
||||
(evo868) 1d: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 1e: FD vif (Second extension of VIF-codes)
|
||||
(evo868) 1f: 17 vife (Error flags (binary))
|
||||
(evo868) 20: 00000000
|
||||
|
||||
(evo868) 24: 0E dif (12 digit BCD Instantaneous value)
|
||||
(evo868) 25: 78 vif (Fabrication no)
|
||||
(evo868) 26: 788004812000
|
||||
|
||||
(evo868) 2c: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
(evo868) 2d: 13 vif (Volume l)
|
||||
(evo868) 2e: C9040000
|
||||
|
||||
(evo868) 32: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
(evo868) 33: 6C vif (Date type G)
|
||||
(evo868) 34: 9F2C
|
||||
|
||||
(evo868) 36: 84 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 37: 01 dife (subunit=0 tariff=0 storagenr=2)
|
||||
(evo868) 38: 13 vif (Volume l)
|
||||
(evo868) 39: C9040000
|
||||
|
||||
(evo868) 3d: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 3e: 01 dife (subunit=0 tariff=0 storagenr=2)
|
||||
(evo868) 3f: 6C vif (Date type G)
|
||||
(evo868) 40: 9F2C
|
||||
|
||||
(evo868) 42: D3 dif (24 Bit Integer/Binary Maximum value storagenr=1)
|
||||
(evo868) 43: 01 dife (subunit=0 tariff=0 storagenr=3)
|
||||
(evo868) 44: 3B vif (Volume flow l/h)
|
||||
(evo868) 45: 9A0200
|
||||
|
||||
(evo868) 48: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
(evo868) 49: 01 dife (subunit=0 tariff=0 storagenr=3)
|
||||
(evo868) 4a: 6D vif (Date and time type)
|
||||
(evo868) 4b: 0534A721
|
||||
|
||||
(evo868) 4f: 81 dif (8 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 50: 04 dife (subunit=0 tariff=0 storagenr=8)
|
||||
(evo868) 51: FD vif (Second extension of VIF-codes)
|
||||
(evo868) 52: 28 vife (Storage interval month(s))
|
||||
(evo868) 53: 01
|
||||
|
||||
(evo868) 54: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 55: 04 dife (subunit=0 tariff=0 storagenr=8)
|
||||
(evo868) 56: 6C vif (Date type G)
|
||||
(evo868) 57: 9F2C
|
||||
|
||||
(evo868) 59: 84 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 5a: 04 dife (subunit=0 tariff=0 storagenr=8)
|
||||
(evo868) 5b: 13 vif (Volume l)
|
||||
(evo868) 5c: C9040000
|
||||
|
||||
(evo868) 60: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
(evo868) 61: 04 dife (subunit=0 tariff=0 storagenr=9)
|
||||
(evo868) 62: 13 vif (Volume l)
|
||||
(evo868) 63: 1B000000
|
||||
|
||||
(evo868) 67: 84 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 68: 05 dife (subunit=0 tariff=0 storagenr=10)
|
||||
(evo868) 69: 13 vif (Volume l)
|
||||
(evo868) 6a: 00000000
|
||||
|
||||
(evo868) 6e: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
(evo868) 6f: 05 dife (subunit=0 tariff=0 storagenr=11)
|
||||
(evo868) 70: 13 vif (Volume l)
|
||||
(evo868) 71: 00000000
|
||||
|
||||
(evo868) 75: 84 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 76: 06 dife (subunit=0 tariff=0 storagenr=12)
|
||||
(evo868) 77: 13 vif (Volume l)
|
||||
(evo868) 78: 00000000
|
||||
|
||||
(evo868) 7c: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
(evo868) 7d: 06 dife (subunit=0 tariff=0 storagenr=13)
|
||||
(evo868) 7e: 13 vif (Volume l)
|
||||
(evo868) 7f: 00000000
|
||||
|
||||
(evo868) 83: 84 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 84: 07 dife (subunit=0 tariff=0 storagenr=14)
|
||||
(evo868) 85: 13 vif (Volume l)
|
||||
(evo868) 86: 00000000
|
||||
|
||||
(evo868) 8a: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
(evo868) 8b: 07 dife (subunit=0 tariff=0 storagenr=15)
|
||||
(evo868) 8c: 13 vif (Volume l)
|
||||
(evo868) 8d: 00000000
|
||||
|
||||
(evo868) 91: 84 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) 92: 08 dife (subunit=0 tariff=0 storagenr=16)
|
||||
(evo868) 93: 13 vif (Volume l)
|
||||
(evo868) 94: 00000000
|
||||
|
||||
(evo868) 98: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
(evo868) 99: 08 dife (subunit=0 tariff=0 storagenr=17)
|
||||
(evo868) 9a: 13 vif (Volume l)
|
||||
(evo868) 9b: 00000000
|
||||
|
||||
(evo868) 9f: 84 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(evo868) a0: 09 dife (subunit=0 tariff=0 storagenr=18)
|
||||
(evo868) a1: 13 vif (Volume l)
|
||||
(evo868) a2: 00000000
|
||||
|
||||
(evo868) a6: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
(evo868) a7: 09 dife (subunit=0 tariff=0 storagenr=19)
|
||||
(evo868) a8: 13 vif (Volume l)
|
||||
(evo868) a9: 00000000
|
||||
*/
|
||||
int offset;
|
||||
string key;
|
||||
|
||||
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::DateTime, 0, 0, &key, &t->dv_entries)) {
|
||||
struct tm datetime;
|
||||
extractDVdate(&t->dv_entries, key, &offset, &datetime);
|
||||
device_date_time_ = strdatetime(&datetime);
|
||||
t->addMoreExplanation(offset, " device datetime (%s)", device_date_time_.c_str());
|
||||
}
|
||||
|
||||
extractDVuint32(&t->dv_entries, "04FD17", &offset, &error_flags_);
|
||||
t->addMoreExplanation(offset, " error flags (%s)", status().c_str());
|
||||
|
||||
extractDVReadableString(&t->dv_entries, "0E78", &offset, &fabrication_no_);
|
||||
t->addMoreExplanation(offset, " fabrication no (%s)", fabrication_no_.c_str());
|
||||
|
||||
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::Date, 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());
|
||||
}
|
||||
|
||||
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 2, 0, &key, &t->dv_entries)) {
|
||||
extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_set_date_2_m3_);
|
||||
t->addMoreExplanation(offset, " consumption at set date 2 (%f m3)", consumption_at_set_date_2_m3_);
|
||||
}
|
||||
|
||||
if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 2, 0, &key, &t->dv_entries)) {
|
||||
struct tm date;
|
||||
extractDVdate(&t->dv_entries, key, &offset, &date);
|
||||
set_date_2_ = strdate(&date);
|
||||
t->addMoreExplanation(offset, " set date 2 (%s)", set_date_2_.c_str());
|
||||
}
|
||||
|
||||
if(findKey(MeasurementType::Maximum, VIFRange::VolumeFlow, 3, 0, &key, &t->dv_entries)) {
|
||||
extractDVdouble(&t->dv_entries, key, &offset, &max_flow_since_datetime_m3h_);
|
||||
t->addMoreExplanation(offset, " max flow (%f m3/h)", max_flow_since_datetime_m3h_);
|
||||
}
|
||||
|
||||
if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 3, 0, &key, &t->dv_entries)) {
|
||||
struct tm datetime;
|
||||
extractDVdate(&t->dv_entries, key, &offset, &datetime);
|
||||
max_flow_datetime_ = strdatetime(&datetime);
|
||||
t->addMoreExplanation(offset, " max flow since datetime (%s)", max_flow_datetime_.c_str());
|
||||
}
|
||||
|
||||
uint8_t month_increment = 0;
|
||||
extractDVuint8(&t->dv_entries, "8104FD28", &offset, &month_increment);
|
||||
t->addMoreExplanation(offset, " month increment (%d)", month_increment);
|
||||
|
||||
struct tm date;
|
||||
if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 8, 0, &key, &t->dv_entries)) {
|
||||
extractDVdate(&t->dv_entries, key, &offset, &date);
|
||||
string start = strdate(&date);
|
||||
t->addMoreExplanation(offset, " history starts with date (%s)", start.c_str());
|
||||
}
|
||||
|
||||
// 12 months of historical data, starting in storage 8.
|
||||
for (int i=1; i<=12; ++i)
|
||||
{
|
||||
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, i+7, 0, &key, &t->dv_entries)) {
|
||||
extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_history_date_m3_[i-1]);
|
||||
t->addMoreExplanation(offset, " consumption at history %d (%f m3)", i, consumption_at_history_date_m3_[i-1]);
|
||||
struct tm d = date;
|
||||
if (i>1) addMonths(&d, 1-i);
|
||||
history_date_[i-1] = strdate(&d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double MeterEvo868::totalWaterConsumption(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Volume);
|
||||
return convert(total_water_consumption_m3_, Unit::M3, u);
|
||||
}
|
||||
|
||||
bool MeterEvo868::hasTotalWaterConsumption()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string MeterEvo868::status()
|
||||
{
|
||||
if (error_flags_ == 0)
|
||||
{
|
||||
return "OK";
|
||||
}
|
||||
|
||||
/* Possible errors according to datasheet:
|
||||
overflow (threshold configurable, must be activated)
|
||||
backflow (threshold set, configurable)
|
||||
leak
|
||||
meter blocked
|
||||
non-used (days threshold set, configurable)
|
||||
magnetic and mechanical tampering (removal)
|
||||
*/
|
||||
|
||||
// How do we decode these?
|
||||
string info;
|
||||
strprintf(&info, "ERROR bits %08x", error_flags_);
|
||||
return info;
|
||||
}
|
109
src/meters.cc
109
src/meters.cc
|
@ -1871,7 +1871,8 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<ucha
|
|||
|
||||
void MeterCommonImplementation::processFieldExtractors(Telegram *t)
|
||||
{
|
||||
map<FieldInfo*,DVEntry*> found;
|
||||
// Multiple dventries can be matched against a single wildcard FieldInfo.
|
||||
map<FieldInfo*,set<DVEntry*>> founds;
|
||||
|
||||
// Sort the dv_entries based on their offset in the telegram.
|
||||
// I.e. restore the ordering that was implicit in the telegram.
|
||||
|
@ -1902,14 +1903,14 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
|
|||
{
|
||||
current_match_nr++;
|
||||
|
||||
if (fi.matcher().index_nr != IndexNr(current_match_nr))
|
||||
if (fi.matcher().index_nr != IndexNr(current_match_nr) &&
|
||||
!fi.matcher().expectedToMatchAgainstMultipleEntries())
|
||||
{
|
||||
// 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)
|
||||
else if (founds[&fi].count(dve) == 0 || fi.matcher().expectedToMatchAgainstMultipleEntries())
|
||||
{
|
||||
// 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()),
|
||||
|
@ -1919,19 +1920,27 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
|
|||
|
||||
dve->addFieldInfo(&fi);
|
||||
fi.performExtraction(this, t, dve);
|
||||
found[&fi] = dve;
|
||||
founds[&fi].insert(dve);
|
||||
}
|
||||
else
|
||||
{
|
||||
DVEntry *old = found[&fi];
|
||||
if (isVerboseEnabled())
|
||||
{
|
||||
set<DVEntry*> old = founds[&fi];
|
||||
string olds;
|
||||
for (DVEntry *dve : old)
|
||||
{
|
||||
olds += to_string(dve->offset)+",";
|
||||
}
|
||||
olds.pop_back();
|
||||
|
||||
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);
|
||||
verbose("(meter) while processing field extractors ignoring dventry %s at offset %d matching since "
|
||||
"field %s was already matched against offsets %s !\n",
|
||||
dve->dif_vif_key.str().c_str(),
|
||||
dve->offset,
|
||||
fi.vname().c_str(),
|
||||
olds.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1945,7 +1954,7 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
|
|||
{
|
||||
fi.performExtraction(this, t, NULL);
|
||||
}
|
||||
else if (found.count(&fi) == 0 && fi.printProperties().hasJOINTPLSTATUS())
|
||||
else if (founds.count(&fi) == 0 && fi.printProperties().hasJOINTPLSTATUS())
|
||||
{
|
||||
// This is a status field and it joins the tpl status but it also
|
||||
// has a potential dve match, which did not trigger. Now
|
||||
|
@ -2144,15 +2153,22 @@ FieldInfo::FieldInfo(int index,
|
|||
|
||||
string FieldInfo::renderJsonOnlyDefaultUnit(Meter *m)
|
||||
{
|
||||
return renderJson(m, NULL);
|
||||
return renderJson(m, NULL, NULL);
|
||||
}
|
||||
|
||||
string FieldInfo::renderJsonText(Meter *m)
|
||||
{
|
||||
return renderJson(m, NULL);
|
||||
return renderJson(m, NULL, NULL);
|
||||
}
|
||||
|
||||
string FieldInfo::generateFieldName(DVEntry *dve)
|
||||
string FieldInfo::generateFieldNameNoUnit(DVEntry *dve)
|
||||
{
|
||||
if (!valid_field_name_) return "bad_field_name";
|
||||
|
||||
return field_name_->apply(dve);
|
||||
}
|
||||
|
||||
string FieldInfo::generateFieldNameWithUnit(DVEntry *dve)
|
||||
{
|
||||
if (!valid_field_name_) return "bad_field_name";
|
||||
|
||||
|
@ -2167,12 +2183,14 @@ string FieldInfo::generateFieldName(DVEntry *dve)
|
|||
return var+"_"+default_unit;
|
||||
}
|
||||
|
||||
string FieldInfo::renderJson(Meter *m, vector<Unit> *conversions)
|
||||
|
||||
string FieldInfo::renderJson(Meter *m, DVEntry *dve, vector<Unit> *conversions)
|
||||
{
|
||||
string s;
|
||||
|
||||
string default_unit = unitToStringLowerCase(defaultUnit());
|
||||
string var = vname();
|
||||
string field_name = generateFieldNameNoUnit(dve);
|
||||
|
||||
if (xuantity() == Quantity::Text)
|
||||
{
|
||||
string v = m->getStringValue(this);
|
||||
|
@ -2182,18 +2200,18 @@ string FieldInfo::renderJson(Meter *m, vector<Unit> *conversions)
|
|||
// be translated into "something":null in the json, indicating that there is no value.
|
||||
// This should not be a problem for now. Lets deal with it when a meter decides to send "null"
|
||||
// as its version string for example.
|
||||
s += "\""+var+"\":null";
|
||||
s += "\""+field_name+"\":null";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normally the string values are quoted in json. TODO quote the value properly.
|
||||
// A well crafted meter could send a version string with " and break the json format.
|
||||
s += "\""+var+"\":\""+v+"\"";
|
||||
s += "\""+field_name+"\":\""+v+"\"";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s += "\""+var+"_"+default_unit+"\":"+valueToString(m->getNumericValue(this, defaultUnit()), defaultUnit());
|
||||
s += "\""+field_name+"_"+default_unit+"\":"+valueToString(m->getNumericValue(this, defaultUnit()), defaultUnit());
|
||||
|
||||
if (conversions != NULL)
|
||||
{
|
||||
|
@ -2202,7 +2220,7 @@ string FieldInfo::renderJson(Meter *m, vector<Unit> *conversions)
|
|||
{
|
||||
string unit = unitToStringLowerCase(u);
|
||||
// Appending extra conversion unit.
|
||||
s += ",\""+var+"_"+unit+"\":"+valueToString(m->getNumericValue(this, u), u);
|
||||
s += ",\""+field_name+"_"+unit+"\":"+valueToString(m->getNumericValue(this, u), u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2259,7 +2277,7 @@ void MeterCommonImplementation::printMeter(Telegram *t,
|
|||
}
|
||||
|
||||
// Iterate over the meter field infos...
|
||||
map<FieldInfo*,DVEntry*> found; // Found from the telegram
|
||||
map<FieldInfo*,set<DVEntry*>> founds; // Multiple dventries can match to a single field info.
|
||||
set<string> found_vnames;
|
||||
|
||||
for (FieldInfo& fi : field_infos_)
|
||||
|
@ -2274,10 +2292,11 @@ void MeterCommonImplementation::printMeter(Telegram *t,
|
|||
// Has the entry been matches to this field, then print it as json.
|
||||
if (dve->hasFieldInfo(&fi))
|
||||
{
|
||||
assert(found.count(&fi) == 0);
|
||||
assert(founds[&fi].count(dve) == 0);
|
||||
|
||||
found[&fi] = dve;
|
||||
found_vnames.insert(fi.vname());
|
||||
founds[&fi].insert(dve);
|
||||
string field_name = fi.generateFieldNameNoUnit(dve);
|
||||
found_vnames.insert(field_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2287,17 +2306,20 @@ void MeterCommonImplementation::printMeter(Telegram *t,
|
|||
{
|
||||
if (fi.printProperties().hasJSON())
|
||||
{
|
||||
if (found.count(&fi) != 0)
|
||||
if (founds.count(&fi) != 0)
|
||||
{
|
||||
DVEntry *dve = found[&fi];
|
||||
debug("(meters) render field %s(%s %s)[%d] with dventry @%d key %s data %s\n",
|
||||
fi.vname().c_str(), toString(fi.xuantity()), unitToStringLowerCase(fi.defaultUnit()).c_str(), fi.index(),
|
||||
dve->offset,
|
||||
dve->dif_vif_key.str().c_str(),
|
||||
dve->value.c_str());
|
||||
string out = fi.renderJson(this, &conversions());
|
||||
debug("(meters) %s\n", out.c_str());
|
||||
s += indent+out+","+newline;
|
||||
// This field info has matched against some dventries.
|
||||
for (DVEntry *dve : founds[&fi])
|
||||
{
|
||||
debug("(meters) render field %s(%s %s)[%d] with dventry @%d key %s data %s\n",
|
||||
fi.vname().c_str(), toString(fi.xuantity()), unitToStringLowerCase(fi.defaultUnit()).c_str(), fi.index(),
|
||||
dve->offset,
|
||||
dve->dif_vif_key.str().c_str(),
|
||||
dve->value.c_str());
|
||||
string out = fi.renderJson(this, dve, &conversions());
|
||||
debug("(meters) %s\n", out.c_str());
|
||||
s += indent+out+","+newline;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2314,7 +2336,7 @@ void MeterCommonImplementation::printMeter(Telegram *t,
|
|||
// Or if no value has been received, null.
|
||||
debug("(meters) render field %s(%s)[%d] without dventry\n",
|
||||
fi.vname().c_str(), toString(fi.xuantity()), fi.index());
|
||||
string out = fi.renderJson(this, &conversions());
|
||||
string out = fi.renderJson(this, NULL, &conversions());
|
||||
debug("(meters) %s\n", out.c_str());
|
||||
s += indent+out+","+newline;
|
||||
}
|
||||
|
@ -2813,8 +2835,11 @@ bool FieldInfo::extractNumeric(Meter *m, Telegram *t, DVEntry *dve)
|
|||
assert(dve != NULL);
|
||||
assert(key == "" || dve->dif_vif_key.str() == key);
|
||||
|
||||
// Generate the json field name:
|
||||
string field_name = generateFieldName(dve);
|
||||
string field_name;
|
||||
if (isDebugEnabled())
|
||||
{
|
||||
field_name = generateFieldNameWithUnit(dve);
|
||||
}
|
||||
|
||||
double extracted_double_value = NAN;
|
||||
if (dve->extractDouble(&extracted_double_value,
|
||||
|
@ -2848,7 +2873,7 @@ bool FieldInfo::extractNumeric(Meter *m, Telegram *t, DVEntry *dve)
|
|||
extracted_double_value);
|
||||
}
|
||||
m->setNumericValue(this, default_unit_, convert(extracted_double_value, decoded_unit, default_unit_));
|
||||
t->addMoreExplanation(dve->offset, renderJson(m, &m->conversions()));
|
||||
t->addMoreExplanation(dve->offset, renderJson(m, dve, &m->conversions()));
|
||||
found = true;
|
||||
}
|
||||
return found;
|
||||
|
@ -2939,7 +2964,7 @@ bool FieldInfo::extractString(Meter *m, Telegram *t, DVEntry *dve)
|
|||
assert(key == "" || dve->dif_vif_key.str() == key);
|
||||
|
||||
// Generate the json field name:
|
||||
string field_name = generateFieldName(dve);
|
||||
string field_name = generateFieldNameNoUnit(dve);
|
||||
|
||||
uint64_t extracted_bits {};
|
||||
if (lookup_.hasLookups() || (print_properties_.hasJOINTPLSTATUS()))
|
||||
|
|
|
@ -62,7 +62,6 @@ LIST_OF_METER_TYPES
|
|||
X(auto, 0, AutoMeter, AUTO, Auto) \
|
||||
X(unknown, 0, UnknownMeter, UNKNOWN, Unknown) \
|
||||
X(eurisii, T1_bit, HeatCostAllocationMeter, EURISII, EurisII) \
|
||||
X(evo868, T1_bit, WaterMeter, EVO868, EVO868) \
|
||||
X(gransystems,T1_bit, ElectricityMeter, CCx01, CCx01) \
|
||||
X(hydrocalm3, T1_bit, HeatMeter, HYDROCALM3, HydrocalM3) \
|
||||
X(hydrodigit, T1_bit, WaterMeter, HYDRODIGIT, Hydrodigit) \
|
||||
|
@ -350,13 +349,14 @@ struct FieldInfo
|
|||
void performCalculation(Meter *m);
|
||||
|
||||
string renderJsonOnlyDefaultUnit(Meter *m);
|
||||
string renderJson(Meter *m, vector<Unit> *additional_conversions);
|
||||
string renderJson(Meter *m, DVEntry *dve, vector<Unit> *additional_conversions);
|
||||
string renderJsonText(Meter *m);
|
||||
// Render the field name based on the actual field from the telegram.
|
||||
// A FieldInfo can be declared to handle any number of storage fields of a certain range.
|
||||
// The vname is then a pattern total_at_month_{storage_counter} that gets translated into
|
||||
// total_at_month_2 (for the dventry with storage nr 2.)
|
||||
string generateFieldName(DVEntry *dve);
|
||||
string generateFieldNameWithUnit(DVEntry *dve);
|
||||
string generateFieldNameNoUnit(DVEntry *dve);
|
||||
// Check if the meter object stores a value for this field.
|
||||
bool hasValue(Meter *m);
|
||||
|
||||
|
|
|
@ -1790,6 +1790,9 @@ LIST_OF_QUANTITIES
|
|||
test_si_convert(1.0, 60.0, Unit::Minute, "min", Unit::Second, "s", Quantity::Time, &from_set, &to_set);
|
||||
// 1 day is 1/365.2425 year
|
||||
test_si_convert(1.0, 1.0/365.2425, Unit::Day, "d", Unit::Year, "y", Quantity::Time, &from_set, &to_set);
|
||||
// 1 month is 30.437 days
|
||||
test_si_convert(2.0, 30.437*2, Unit::Month, "month", Unit::Day, "d", Quantity::Time, &from_set, &to_set);
|
||||
test_si_convert(30.437*2, 2.0, Unit::Day, "d", Unit::Month, "month", Quantity::Time, &from_set, &to_set);
|
||||
// 100 hours is 100/24 days.
|
||||
test_si_convert(100.0, 100.0/24.0, Unit::Hour, "h", Unit::Day, "d", Quantity::Time, &from_set, &to_set);
|
||||
// 1 year is 365.2425 days.
|
||||
|
|
|
@ -93,6 +93,7 @@ using namespace std;
|
|||
X(Minute, 60.0, SIExp().s(1)) \
|
||||
X(Hour, 3600.0, SIExp().s(1)) \
|
||||
X(Day, 3600.0*24, SIExp().s(1)) \
|
||||
X(Month, 3600.0*24*30.437, SIExp().s(1)) \
|
||||
X(Year, 3600.0*24*365.2425, SIExp().s(1)) \
|
||||
X(DateTimeUT, 1.0, SIExp().s(1)) \
|
||||
X(DateTimeUTC, 1.0, SIExp().s(1)) \
|
||||
|
|
|
@ -102,6 +102,7 @@ LIST_OF_QUANTITIES
|
|||
X(Minute,min,"min",Time,"minute") \
|
||||
X(Hour,h,"h",Time,"hour") \
|
||||
X(Day,d,"d",Time,"day") \
|
||||
X(Month,month,"month",Time,"month") \
|
||||
X(Year,y,"y",Time,"year") \
|
||||
X(DateTimeUT,ut,"ut",PointInTime,"unix timestamp") \
|
||||
X(DateTimeUTC,utc,"utc",PointInTime,"coordinated universal time") \
|
||||
|
|
6
test.sh
6
test.sh
|
@ -9,6 +9,12 @@ then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq > /dev/null
|
||||
then
|
||||
echo "You have to install jq !"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$TESTINTERNAL
|
||||
if [ "$?" = "0" ]; then
|
||||
echo OK: test internals
|
||||
|
|
|
@ -8,15 +8,15 @@ TEST=testoutput
|
|||
TESTNAME="Test ANYID"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
cat <<EOF | jq --sort-keys . > $TEST/test_expected.txt
|
||||
{"media":"cold water","meter":"multical21","name":"Vatten","id":"76348799","status":"DRY","total_m3":6.408,"target_m3":6.408,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||
EOF
|
||||
|
||||
$PROG --format=json 2A442D2C998734761B168D2091D37CAC21576C7802FF207100041308190000441308190000615B7F616713 \
|
||||
Vatten auto ANYID NOKEY > $TEST/test_output.txt 2>&1
|
||||
Vatten auto 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
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||
diff $TEST/test_expected.txt $TEST/test_responses.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
|
|
|
@ -9,7 +9,7 @@ TEST=testoutput
|
|||
TESTNAME="Test C1 meters"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
cat simulations/simulation_c1.txt | grep '^{' > $TEST/test_expected.txt
|
||||
cat simulations/simulation_c1.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt
|
||||
$PROG --format=json simulations/simulation_c1.txt \
|
||||
MyHeater multical302 67676767 NOKEY \
|
||||
MyHeaterMj multical302 46464646 NOKEY \
|
||||
|
@ -27,11 +27,11 @@ $PROG --format=json simulations/simulation_c1.txt \
|
|||
Smokey ei6500 00012811 NOKEY \
|
||||
Vatten weh_07 86868686 NOKEY \
|
||||
Mino minomess 55036410 NOKEY \
|
||||
> $TEST/test_output.txt 2> $TEST/test_stderr.txt
|
||||
| jq --sort-keys . > $TEST/test_output.txt 2> $TEST/test_stderr.txt
|
||||
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||
diff $TEST/test_expected.txt $TEST/test_responses.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
|
|
|
@ -41,8 +41,6 @@ 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
|
||||
|
|
|
@ -8,9 +8,9 @@ TEST=testoutput
|
|||
TESTNAME="Test list-envs and list-fields"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
$PROG --listenvs=amiplus > $TEST/test_output.txt 2>&1
|
||||
$PROG --listenvs=amiplus | sort > $TEST/test_output.txt 2>&1
|
||||
|
||||
cat <<EOF > $TEST/test_expected.txt
|
||||
cat <<EOF | sort > $TEST/test_expected.txt
|
||||
METER_JSON
|
||||
METER_ID
|
||||
METER_NAME
|
||||
|
@ -54,9 +54,9 @@ then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
$PROG --listfields=amiplus > $TEST/test_output.txt 2>&1
|
||||
$PROG --listfields=amiplus | sort > $TEST/test_output.txt 2>&1
|
||||
|
||||
cat <<EOF > $TEST/test_expected.txt
|
||||
cat <<EOF | sort > $TEST/test_expected.txt
|
||||
id The meter id number.
|
||||
name Your name for the meter.
|
||||
media What does the meter measure?
|
||||
|
|
|
@ -255,11 +255,6 @@ Received telegram from: 66336633
|
|||
type: Heat meter (0x43)
|
||||
ver: 0x39
|
||||
driver: compact5
|
||||
Received telegram from: 79787776
|
||||
manufacturer: (MAD) Maddalena, Italy (0x3424)
|
||||
type: Water meter (0x07)
|
||||
ver: 0x50
|
||||
driver: evo868
|
||||
Received telegram from: 18046178
|
||||
manufacturer: (GSS) R D Gran System S, Belarus (0x1e73)
|
||||
type: Electricity meter (0x02)
|
||||
|
|
|
@ -9,15 +9,15 @@ TEST=testoutput
|
|||
TESTNAME="Test mbus"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
cat simulations/simulation_mbus.txt | grep '^{' > $TEST/test_expected.txt
|
||||
cat simulations/simulation_mbus.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt
|
||||
$PROG --format=json simulations/simulation_mbus.txt \
|
||||
MyUltra ultraheat 70444600 NOKEY \
|
||||
MySenso sensostar 10484075 NOKEY \
|
||||
> $TEST/test_output.txt 2> $TEST/test_stderr.txt
|
||||
2> $TEST/test_stderr.txt | 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
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||
diff $TEST/test_expected.txt $TEST/test_responses.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
|
|
|
@ -14,11 +14,11 @@ METERS="HCA auto 91835132 NOKEY \
|
|||
QWW auto 11121314 NOKEY \
|
||||
QWWW auto 12417708 NOKEY"
|
||||
|
||||
cat simulations/simulation_s1.txt | grep '^{' > $TEST/test_expected.txt
|
||||
$PROG --format=json simulations/simulation_s1.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt
|
||||
cat simulations/simulation_s1.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt
|
||||
$PROG --format=json simulations/simulation_s1.txt $METERS 2> $TEST/test_stderr.txt | 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
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||
diff $TEST/test_expected.txt $TEST/test_responses.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
|
|
|
@ -24,7 +24,6 @@ METERS="MyWarmWater supercom587 12345678 NOKEY
|
|||
Gran101 gransystems 18046178 NOKEY
|
||||
Gran301 gransystems 20100117 NOKEY
|
||||
HeatMeter eurisii 88018801 NOKEY
|
||||
Votchka evo868 79787776 NOKEY
|
||||
Smokeo lansensm 00010204 NOKEY
|
||||
Tempoo lansenth 00010203 NOKEY
|
||||
Dooro lansendw 00010205 NOKEY
|
||||
|
@ -70,11 +69,11 @@ METERS="MyWarmWater supercom587 12345678 NOKEY
|
|||
QSmokep qsmoke 48128850 NOKEY"
|
||||
|
||||
|
||||
cat simulations/simulation_t1.txt | grep '^{' > $TEST/test_expected.txt
|
||||
$PROG --format=json simulations/simulation_t1.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt
|
||||
cat simulations/simulation_t1.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt
|
||||
$PROG --format=json simulations/simulation_t1.txt $METERS 2> $TEST/test_stderr.txt | 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
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||
diff $TEST/test_expected.txt $TEST/test_responses.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
|
|
Ładowanie…
Reference in New Issue