diff --git a/simulations/simulation_t1.txt b/simulations/simulation_t1.txt old mode 100644 new mode 100755 index 3c7bf39..c19d5ef --- a/simulations/simulation_t1.txt +++ b/simulations/simulation_t1.txt @@ -63,14 +63,14 @@ telegram=|5744b40988227711101b7ab20800000265a00842658f088201659f08226589081265a0 # Test IZAR RC 868 I R4 PL water meter telegram telegram=|1944304C72242421D401A2|013D4013DD8B46A4999C1293E582CC| -{"media":"water","meter":"izar","name":"IzarWater","id":"21242472","total_m3":3.488,"last_month_total_m3":3.486,"timestamp":"1111-11-11T11:11:11Z"} -|IzarWater;21242472;3.488000;3.486000;1111-11-11 11:11.11 +{"media":"water","meter":"izar","name":"IzarWater","id":"21242472","total_m3":3.488,"last_month_total_m3":3.486,"last_month_measure_date":"2019-09-30","remaining_battery_life_y":14.5,"current_alarms":"meter_blocked,underflow","previous_alarms":"no_alarm","timestamp":"1111-11-11T11:11:11Z"} +IzarWater;21242472;3.488000;3.486000;2019-09-30;14.500000;meter_blocked,underflow;no_alarm;1111-11-11 11:11.11 # Test new version of IZAR telegram=|2944A511780729662366A20118001378D3B3DB8CEDD77731F25832AAF3DA8CADF9774EA673172E8C61F2| -{"media":"water","meter":"izar","name":"IzarWater2","id":"66290778","total_m3":16.76,"last_month_total_m3":11.84,"timestamp":"1111-11-11T11:11:11Z"} -|IzarWater2;66290778;16.760000;11.840000;1111-11-11 11:11.11 +{"media":"water","meter":"izar","name":"IzarWater2","id":"66290778","total_m3":16.76,"last_month_total_m3":11.84,"last_month_measure_date":"2019-11-30","remaining_battery_life_y":12,"current_alarms":"no_alarm","previous_alarms":"no_alarm","timestamp":"1111-11-11T11:11:11Z"} +IzarWater2;66290778;16.760000;11.840000;2019-11-30;12.000000;no_alarm;no_alarm;1111-11-11 11:11.11 # Test Hydrus water meter telegram telegram=|4E44A5116464646470077AED004005|2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000| diff --git a/src/meter_izar.cc b/src/meter_izar.cc old mode 100644 new mode 100755 index cccbeae..b90b8d9 --- a/src/meter_izar.cc +++ b/src/meter_izar.cc @@ -20,13 +20,30 @@ #include"wmbus.h" #include"wmbus_utils.h" -#include +#include +#include using namespace std; #define PRIOS_DEFAULT_KEY1 "39BC8A10E66D83F8" #define PRIOS_DEFAULT_KEY2 "51728910E66D83F8" +/** Contains all the booleans required to store the alarms of a PRIOS device. */ +typedef struct _izar_alarms { + bool general_alarm; + bool leakage_currently; + bool leakage_previously; + bool meter_blocked; + bool back_flow; + bool underflow; + bool overflow; + bool submarine; + bool sensor_fraud_currently; + bool sensor_fraud_previously; + bool mechanical_fraud_currently; + bool mechanical_fraud_previously; +} izar_alarms; + struct MeterIzar : public virtual WaterMeter, public virtual MeterCommonImplementation { MeterIzar(WMBus *bus, MeterInfo &mi); @@ -35,6 +52,9 @@ struct MeterIzar : public virtual WaterMeter, public virtual MeterCommonImplemen bool hasTotalWaterConsumption(); double lastMonthTotalWaterConsumption(Unit u); + string setH0Date(); + string currentAlarmsText(); + string previousAlarmsText(); private: @@ -44,8 +64,13 @@ private: uint32_t uint32FromBytes(const vector &data, int offset, bool reverse = false); vector decodePrios(const vector &payload, uint32_t key); + double remaining_battery_life; + uint16_t h0_year; + uint8_t h0_month; + uint8_t h0_day; double total_water_consumption_l_ {}; double last_month_total_water_consumption_l_ {}; + izar_alarms alarms; vector keys; }; @@ -88,6 +113,27 @@ MeterIzar::MeterIzar(WMBus *bus, MeterInfo &mi) : [&](Unit u){ return lastMonthTotalWaterConsumption(u); }, "The total water consumption recorded by this meter around end of last month.", true, true); + + addPrint("last_month_measure_date", Quantity::Text, + [&](){ return setH0Date(); }, + "The date when the meter recorded the most recent billing value.", + true, true); + + addPrint("remaining_battery_life", Quantity::Time, Unit::Year, + [&](Unit u){ return convert(remaining_battery_life, Unit::Year, u); }, + "How many more years the battery is expected to last", + true, true); + + addPrint("current_alarms", Quantity::Text, + [&](){ return currentAlarmsText(); }, + "Alarms currently reported by the meter.", + true, true); + + addPrint("previous_alarms", Quantity::Text, + [&](){ return previousAlarmsText(); }, + "Alarms currently reported by the meter.", + true, true); + } double MeterIzar::totalWaterConsumption(Unit u) @@ -107,6 +153,69 @@ double MeterIzar::lastMonthTotalWaterConsumption(Unit u) return convert(last_month_total_water_consumption_l_, Unit::L, u); } +string MeterIzar::setH0Date() +{ + char result[11]; + snprintf(result, sizeof(result), "%04d-%02d-%02d", h0_year, h0_month, h0_day); + return result; +} + +string MeterIzar::currentAlarmsText() +{ + string s; + if (alarms.leakage_currently) { + s.append("leakage,"); + } + if (alarms.meter_blocked) { + s.append("meter_blocked,"); + } + if (alarms.back_flow) { + s.append("back_flow,"); + } + if (alarms.underflow) { + s.append("underflow,"); + } + if (alarms.overflow) { + s.append("overflow,"); + } + if (alarms.submarine) { + s.append("submarine,"); + } + if (alarms.sensor_fraud_currently) { + s.append("sensor_fraud,"); + } + if (alarms.mechanical_fraud_currently) { + s.append("mechanical_fraud,"); + } + if (s.length() > 0) { + if (alarms.general_alarm) { + return "general_alarm"; + } + s.pop_back(); + return s; + } + return "no_alarm"; +} + +string MeterIzar::previousAlarmsText() +{ + string s; + if (alarms.leakage_previously) { + s.append("leakage,"); + } + if (alarms.sensor_fraud_previously) { + s.append("sensor_fraud,"); + } + if (alarms.mechanical_fraud_previously) { + s.append("mechanical_fraud,"); + } + if (s.length() > 0) { + s.pop_back(); + return s; + } + return "no_alarm"; +} + uint32_t MeterIzar::uint32FromBytes(const vector &data, int offset, bool reverse) { if (reverse) @@ -156,9 +265,36 @@ void MeterIzar::processContent(Telegram *t) return; } + // get the remaining battery life (in year) + remaining_battery_life = (frame[12] & 0x1F) / 2.0; + total_water_consumption_l_ = uint32FromBytes(decoded_content, 1, true); last_month_total_water_consumption_l_ = uint32FromBytes(decoded_content, 5, true); + // get the date when the second measurement was taken + h0_year = ((decoded_content[10] & 0xF0) >> 1) + ((decoded_content[9] & 0xE0) >> 5); + if (h0_year > 80) { + h0_year += 1900; + } else { + h0_year += 2000; + } + h0_month = decoded_content[10] & 0xF; + h0_day = decoded_content[9] & 0x1F; + + // read the alarms: + alarms.general_alarm = frame[11] >> 7; + alarms.leakage_currently = frame[12] >> 7; + alarms.leakage_previously = frame[12] >> 6 & 0x1; + alarms.meter_blocked = frame[12] >> 5 & 0x1; + alarms.back_flow = frame[13] >> 7; + alarms.underflow = frame[13] >> 6 & 0x1; + alarms.overflow = frame[13] >> 5 & 0x1; + alarms.submarine = frame[13] >> 4 & 0x1; + alarms.sensor_fraud_currently = frame[13] >> 3 & 0x1; + alarms.sensor_fraud_previously = frame[13] >> 2 & 0x1; + alarms.mechanical_fraud_currently = frame[13] >> 1 & 0x1; + alarms.mechanical_fraud_previously = frame[13] & 0x1; + // override incorrectly reported medium (oil) t->dll_type = 7; } diff --git a/src/meters.cc b/src/meters.cc index 468370f..30672f8 100755 --- a/src/meters.cc +++ b/src/meters.cc @@ -104,6 +104,12 @@ void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, prints_.push_back( { vname, vquantity, defaultUnitForQuantity(vquantity), getValueFunc, NULL, help, field, json }); } +void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, Unit unit, + function getValueFunc, string help, bool field, bool json) +{ + prints_.push_back( { vname, vquantity, unit, getValueFunc, NULL, help, field, json }); +} + void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, function getValueFunc, string help, bool field, bool json) diff --git a/src/meters_common_implementation.h b/src/meters_common_implementation.h old mode 100644 new mode 100755 index b6676f5..ffb5a37 --- a/src/meters_common_implementation.h +++ b/src/meters_common_implementation.h @@ -84,6 +84,8 @@ protected: void addManufacturer(int m); void addPrint(string vname, Quantity vquantity, function getValueFunc, string help, bool field, bool json); + void addPrint(string vname, Quantity vquantity, Unit unit, + function getValueFunc, string help, bool field, bool json); void addPrint(string vname, Quantity vquantity, function getValueFunc, string help, bool field, bool json); bool handleTelegram(vector frame); diff --git a/src/units.cc b/src/units.cc old mode 100644 new mode 100755 index a5a1593..976b1e6 --- a/src/units.cc +++ b/src/units.cc @@ -23,6 +23,10 @@ using namespace std; #define LIST_OF_CONVERSIONS \ X(Second, Hour, {vto=vfrom/3600.0;}) \ X(Hour, Second, {vto=vfrom*3600.0;}) \ + X(Year, Second, {vto=vfrom*3600.0*24.0*365;}) \ + X(Second, Year, {vto=vfrom/3600.0/24.0/365;}) \ + X(Hour, Year, {vto=vfrom/24.0/365;}) \ + X(Year, Hour, {vto=vfrom*24.0*365;}) \ X(KWH, GJ, {vto=vfrom*0.0036;}) \ X(GJ, KWH,{vto=vfrom/0.0036;}) \ X(M3, L, {vto=vfrom*1000.0;}) \ diff --git a/src/units.h b/src/units.h old mode 100644 new mode 100755 index ac0c7ce..cfb2364 --- a/src/units.h +++ b/src/units.h @@ -45,7 +45,8 @@ X(HCA,hca,"hca",HCA,"heat cost allocation") \ X(TXT,txt,"txt",Text,"text") \ X(Second,s,"s",Time,"second") \ - X(Hour,h,"h",Time,"hour") + X(Hour,h,"h",Time,"hour") \ + X(Year,y,"y",Time,"year") enum class Unit {