diff --git a/docker/Dockerfile b/docker/Dockerfile index bb1cfb7..1d80b0d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,7 @@ FROM alpine AS build RUN apk add --no-cache alpine-sdk gcc linux-headers libxml2-dev cmake libusb-dev bash samurai + +ADD https://api.github.com/repos/wmbusmeters/wmbusmeters/git/refs/heads/master version.json RUN git clone https://github.com/steve-m/librtlsdr.git && \ git clone https://github.com/wmbusmeters/wmbusmeters.git && \ git clone https://github.com/weetmuts/rtl-wmbus.git && \ diff --git a/src/driver_dynamic.cc b/src/driver_dynamic.cc index 618efaa..c158091 100644 --- a/src/driver_dynamic.cc +++ b/src/driver_dynamic.cc @@ -35,8 +35,12 @@ string get_translation(XMQDoc *doc, XMQNode *node, string name, string lang); string check_calculate(const char *formula, DriverDynamic *dd); Unit check_display_unit(const char *display_unit, DriverDynamic *dd); -void check_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd); -void check_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd); +void checked_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd); +void checked_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd); +void checked_set_storagenr_range(const char *storagenr_range_s, FieldMatcher *fm, DriverDynamic *dd); +void checked_set_tariffnr_range(const char *tariffnr_range_s, FieldMatcher *fm, DriverDynamic *dd); +void checked_set_subunitnr_range(const char *subunitnr_range_s, FieldMatcher *fm, DriverDynamic *dd); +void checked_add_vif_combinable(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd); const char *line = "-------------------------------------------------------------------------------"; @@ -107,26 +111,19 @@ bool DriverDynamic::load(DriverInfo *di, const string &file_name, const char *co DriverDynamic::DriverDynamic(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di), file_name_(di.getDynamicFileName()) { - XMQDoc *doc = di.getDynamicDriver(); - assert(doc); - - verbose("(driver) constructing driver %s from already loaded file %s\n", - di.name().str().c_str(), - fileName().c_str()); - try { + XMQDoc *doc = di.getDynamicDriver(); + assert(doc); + + verbose("(driver) constructing driver %s from already loaded file %s\n", + di.name().str().c_str(), + fileName().c_str()); + xmqForeach(doc, NULL, "/driver/use", (XMQNodeCallback)add_use, this); - } - catch (...) - { - } - - try - { xmqForeach(doc, NULL, "/driver/field", (XMQNodeCallback)add_field, this); } - catch (...) + catch(...) { } } @@ -163,9 +160,9 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *d char a = mfct[0]; char b = mfct[1]; char c = mfct[2]; - if (!(a >= 'A' && a < 'Z' && - b >= 'A' && b < 'Z' && - c >= 'A' && c < 'Z')) + if (!(a >= 'A' && a <= 'Z' && + b >= 'A' && b <= 'Z' && + c >= 'A' && c <= 'Z')) { warning("(driver) error in %s, bad manufacturer in mvt triplet: %s\n" "%s\n" @@ -339,22 +336,22 @@ XMQProceed DriverDynamic::add_match(XMQDoc *doc, XMQNode *match, DriverDynamic * { FieldMatcher *fm = dd->tmp_matcher_; - check_set_measurement_type(xmqGetString(doc, match, "measurement_type"), fm, dd); + checked_set_measurement_type(xmqGetString(doc, match, "measurement_type"), fm, dd); - check_set_vif_range(xmqGetString(doc, match, "vif_range"), fm, dd); + checked_set_vif_range(xmqGetString(doc, match, "vif_range"), fm, dd); - const char *vif_range_s = xmqGetString(doc, match, "vif_range"); + checked_set_storagenr_range(xmqGetString(doc, match, "storage_nr"), fm, dd); - if (vif_range_s) - { - VIFRange vif_range = toVIFRange(vif_range_s); - if (vif_range == VIFRange::None) - { - warning("(driver) error unknown measurement type %s\n", vif_range_s); - throw 1; - } - fm->set(vif_range); - } + xmqForeach(doc, match, "add_combinable", (XMQNodeCallback)add_combinable, dd); + + return XMQ_CONTINUE; +} + +XMQProceed DriverDynamic::add_combinable(XMQDoc *doc, XMQNode *match, DriverDynamic *dd) +{ + FieldMatcher *fm = dd->tmp_matcher_; + + checked_add_vif_combinable(xmqGetString(doc, match, "."), fm, dd); return XMQ_CONTINUE; } @@ -622,7 +619,7 @@ Unit check_display_unit(const char *display_unit_s, DriverDynamic *dd) return u; } -void check_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd) +void checked_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd) { if (!measurement_type_s) { @@ -665,7 +662,7 @@ void check_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm fm->set(measurement_type); } -void check_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd) +void checked_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd) { if (!vif_range_s) { @@ -701,3 +698,58 @@ void check_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynami fm->set(vif_range); } + +void checked_set_storagenr_range(const char *storagenr_range_s, FieldMatcher *fm, DriverDynamic *dd) +{ + if (!storagenr_range_s) return; + + auto fields = splitString(storagenr_range_s, ','); + bool ok = isNumber(fields[0]); + if (fields.size() > 1) + { + ok &= isNumber(fields[1]); + } + if (!ok || fields.size() > 2) + { + warning("(driver) error in %s, bad storagenr_range: %s\n" + "%s\n", + dd->fileName().c_str(), + storagenr_range_s, + line); + throw 1; + } + + if (fields.size() == 1) + { + fm->set(StorageNr(atoi(fields[0].c_str()))); + } + else + { + fm->set(StorageNr(atoi(fields[0].c_str())), + StorageNr(atoi(fields[1].c_str()))); + } +} + +void checked_add_vif_combinable(const char *vif_combinable_s, FieldMatcher *fm, DriverDynamic *dd) +{ + if (!vif_combinable_s) return; + + VIFCombinable vif_combinable = toVIFCombinable(vif_combinable_s); + + if (vif_combinable == VIFCombinable::None) + { + warning("(driver) error in %s, bad vif_combinable: %s\n" + "%s\n" + "Available vif combinables:\n" + "%s\n" + "%s\n", + dd->fileName().c_str(), + vif_combinable_s, + line, + availableVIFCombinables().c_str(), + line); + throw 1; + } + + fm->add(vif_combinable); +} diff --git a/src/driver_dynamic.h b/src/driver_dynamic.h index cc72acb..b0a9c1e 100644 --- a/src/driver_dynamic.h +++ b/src/driver_dynamic.h @@ -29,6 +29,7 @@ struct DriverDynamic : public virtual MeterCommonImplementation static XMQProceed add_use(XMQDoc *doc, XMQNode *field, DriverDynamic *dd); static XMQProceed add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *dd); static XMQProceed add_match(XMQDoc *doc, XMQNode *match, DriverDynamic *dd); + static XMQProceed add_combinable(XMQDoc *doc, XMQNode *match, DriverDynamic *dd); const string &fileName() { return file_name_; } diff --git a/src/dvparser.cc b/src/dvparser.cc index 549736e..04a0b32 100644 --- a/src/dvparser.cc +++ b/src/dvparser.cc @@ -62,6 +62,17 @@ LIST_OF_VIF_RANGES return VIFRange::None; } +VIFCombinable toVIFCombinable(const char *s) +{ + if (!strcmp(s, "None")) return VIFCombinable::None; + if (!strcmp(s, "Any")) return VIFCombinable::Any; +#define X(name,from,to) if (!strcmp(s, #name)) return VIFCombinable::name; +LIST_OF_VIF_COMBINABLES +#undef X + + return VIFCombinable::None; +} + const char *toString(VIFCombinable v) { switch (v) { @@ -1299,13 +1310,16 @@ bool FieldMatcher::matches(DVEntry &dv_entry) } // Test ranges and types. - bool b = - (!match_vif_range || isInsideVIFRange(dv_entry.vif, vif_range)) && - (!match_vif_raw || dv_entry.vif == vif_raw) && - (!match_measurement_type || dv_entry.measurement_type == measurement_type) && - (!match_storage_nr || (dv_entry.storage_nr >= storage_nr_from && dv_entry.storage_nr <= storage_nr_to)) && - (!match_tariff_nr || (dv_entry.tariff_nr >= tariff_nr_from && dv_entry.tariff_nr <= tariff_nr_to)) && - (!match_subunit_nr || (dv_entry.subunit_nr >= subunit_nr_from && dv_entry.subunit_nr <= subunit_nr_to)); + bool range = (!match_vif_range || isInsideVIFRange(dv_entry.vif, vif_range)); + bool raw = (!match_vif_raw || dv_entry.vif == vif_raw); + bool type = (!match_measurement_type || dv_entry.measurement_type == measurement_type); + bool storage = (!match_storage_nr || (dv_entry.storage_nr >= storage_nr_from && dv_entry.storage_nr <= storage_nr_to)); + bool tariff = (!match_tariff_nr || (dv_entry.tariff_nr >= tariff_nr_from && dv_entry.tariff_nr <= tariff_nr_to)); + bool subunit = (!match_subunit_nr || (dv_entry.subunit_nr >= subunit_nr_from && dv_entry.subunit_nr <= subunit_nr_to)); + + //printf("Match? range=%d raw=%d type=%d storage=%d tariff=%d subunit=%d \n", range, raw, type, storage, tariff, subunit); + + bool b = range & raw & type & storage & tariff & subunit; if (!b) return false; @@ -1501,3 +1515,18 @@ LIST_OF_VIF_RANGES available_vif_ranges_.pop_back(); return available_vif_ranges_; } + +string available_vif_combinables_; + +const string &availableVIFCombinables() +{ + if (available_vif_combinables_ != "") return available_vif_combinables_; + +#define X(n,from,to) available_vif_combinables_ += string(#n) + "\n"; +LIST_OF_VIF_COMBINABLES +#undef X + + // Remove last newline + available_vif_combinables_.pop_back(); + return available_vif_combinables_; +} diff --git a/src/dvparser.h b/src/dvparser.h index 5d9169e..5df1ffe 100644 --- a/src/dvparser.h +++ b/src/dvparser.h @@ -39,7 +39,7 @@ X(ExternalTemperature,0x64,0x67, Quantity::Temperature, Unit::C) \ X(Pressure,0x68,0x6B, Quantity::Pressure, Unit::BAR) \ X(HeatCostAllocation,0x6E,0x6E, Quantity::HCA, Unit::HCA) \ - X(Date,0x6C,0x6C, Quantity::PointInTime, Unit::DateTimeLT) \ + X(Date,0x6C,0x6C, Quantity::PointInTime, Unit::DateLT) \ X(DateTime,0x6D,0x6D, Quantity::PointInTime, Unit::DateTimeLT) \ X(EnergyMJ,0x08,0x0F, Quantity::Energy, Unit::MJ) \ X(EnergyWh,0x00,0x07, Quantity::Energy, Unit::KWH) \ @@ -208,6 +208,7 @@ struct VIFCombinableRaw { uint16_t value; }; +VIFCombinable toVIFCombinable(const char *s); VIFCombinable toVIFCombinable(int i); const char *toString(VIFCombinable v); @@ -588,5 +589,6 @@ bool extractDVdate(std::map> *values, const std::string &availableVIFRanges(); +const std::string &availableVIFCombinables(); #endif diff --git a/src/meters.cc b/src/meters.cc index 3ec4e6b..0e26872 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -1313,7 +1313,6 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t) if (fi.hasMatcher() && fi.matches(dve)) { current_match_nr++; - if (fi.matcher().index_nr != IndexNr(current_match_nr) && !fi.matcher().expectedToMatchAgainstMultipleEntries()) { diff --git a/src/testinternals.cc b/src/testinternals.cc index ca7efa1..6e3df0f 100644 --- a/src/testinternals.cc +++ b/src/testinternals.cc @@ -37,6 +37,7 @@ using namespace std; bool verbose_ = false; #define LIST_OF_TESTS \ + X(dynamic_loading)\ X(crc) \ X(dvparser) \ X(devices) \ @@ -2028,6 +2029,8 @@ LIST_OF_QUANTITIES test_si_convert(2211717, 2211717, Unit::FACTOR, "counter", Unit::COUNTER, "counter", Quantity::Dimensionless, &from_set, &to_set); test_si_convert(2211717, 2211717, Unit::NUMBER, "counter", Unit::COUNTER, "counter", Quantity::Dimensionless, &from_set, &to_set); test_si_convert(2211717, 2211717, Unit::FACTOR, "counter", Unit::NUMBER, "counter", Quantity::Dimensionless, &from_set, &to_set); + test_si_convert(2211717, 2211717, Unit::PERCENTAGE, "counter", Unit::NUMBER, "counter", Quantity::Dimensionless, &from_set, &to_set); + test_si_convert(2211717, 2211717, Unit::NUMBER, "counter", Unit::PERCENTAGE, "counter", Quantity::Dimensionless, &from_set, &to_set); check_units_tested(from_set, to_set, Quantity::Dimensionless); @@ -2588,3 +2591,22 @@ void test_formulas_stringinterpolation() } } + +void test_dynamic_loading() +{ + VIFRange vr = toVIFRange("Date"); + + if (vr != VIFRange::Date) + { + printf("ERROR in dynamic loading got %s but expected %s!\n", + toString(vr), toString(VIFRange::Date)); + } + + vr = toVIFRange("DateTime"); + + if (vr != VIFRange::DateTime) + { + printf("ERROR in dynamic loading got %s but expected %s!\n", + toString(vr), toString(VIFRange::DateTime)); + } +} diff --git a/src/units.cc b/src/units.cc index 9595b66..4018a7c 100644 --- a/src/units.cc +++ b/src/units.cc @@ -63,6 +63,8 @@ using namespace std; X(NUMBER, COUNTER, {vto=vfrom;}) \ X(FACTOR, NUMBER, {vto=vfrom;}) \ X(NUMBER, FACTOR, {vto=vfrom;}) \ + X(PERCENTAGE, NUMBER, {vto=vfrom;}) \ + X(NUMBER, PERCENTAGE, {vto=vfrom;}) \ X(UnixTimestamp,DateTimeLT, {vto=vfrom; }) \ X(DateTimeLT,UnixTimestamp, {vto=vfrom; }) \ X(DateLT,UnixTimestamp, {vto=vfrom; }) \ @@ -122,6 +124,7 @@ using namespace std; X(COUNTER, 1.0, SIExp()) \ X(FACTOR, 1.0, SIExp()) \ X(NUMBER, 1.0, SIExp()) \ + X(PERCENTAGE, 1.0, SIExp()) \ X(TXT, 1.0, SIExp()) \ @@ -830,7 +833,7 @@ const char *availableUnits() if (available_units_[0]) return available_units_; #define X(n,suffix,sn,q,ln) if (Unit::n != Unit::Unknown) { \ - strcat(available_units_, #suffix); strcat(available_units_, "\n"); \ + strcat(available_units_, #suffix); strcat(available_units_, " "); \ assert(strlen(available_units_) < 1024); } LIST_OF_UNITS #undef X