diff --git a/CHANGES b/CHANGES index 527c5a4..52e85d8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,20 @@ -Version 1.0.6: 2021-02-20 + +Ignore duplicates is now enabled by default. Turn it off with --ignoreduplicates=false + +Fixed problem with state maintaining meter drivers and wildcard ids. +A HCA driver was producing telegrams with values that jumped up and down. +This was due to the fact that the meter state in the driver was updated +through multiple different telegrams (this is how the meter works). +Clearly a single state object could not properly maintain the state for all +possible meters matching the wildcard, the state became a random mix +of whater telegrams arrived. + +The fix now, is that a meter C++ object for a uniqe id (not wildcard) +is created only when the first telegram arrives that matches the wildcard. +Thus each meter will have its own C++ object, in which the correct state +is maintained. + +Version 1.1.0: 2021-02-20 Vincent Privat added code for properly decoding several types of izar meter. He also added full support for the heat meter sharky 775! diff --git a/Makefile b/Makefile index 8a31580..189d79d 100644 --- a/Makefile +++ b/Makefile @@ -119,7 +119,26 @@ METER_OBJS:=\ $(BUILD)/dvparser.o \ $(BUILD)/mbus_rawtty.o \ $(BUILD)/meters.o \ + $(BUILD)/manufacturer_specificities.o \ + $(BUILD)/printer.o \ + $(BUILD)/rtlsdr.o \ + $(BUILD)/serial.o \ + $(BUILD)/shell.o \ + $(BUILD)/sha256.o \ + $(BUILD)/threads.o \ + $(BUILD)/util.o \ + $(BUILD)/units.o \ + $(BUILD)/wmbus.o \ $(BUILD)/meter_amiplus.o \ + $(BUILD)/wmbus_amb8465.o \ + $(BUILD)/wmbus_im871a.o \ + $(BUILD)/wmbus_cul.o \ + $(BUILD)/wmbus_rtlwmbus.o \ + $(BUILD)/wmbus_rtl433.o \ + $(BUILD)/wmbus_simulator.o \ + $(BUILD)/wmbus_rawtty.o \ + $(BUILD)/wmbus_rc1180.o \ + $(BUILD)/wmbus_utils.o \ $(BUILD)/meter_apator08.o \ $(BUILD)/meter_apator162.o \ $(BUILD)/meter_cma12w.o \ @@ -170,25 +189,6 @@ METER_OBJS:=\ $(BUILD)/meter_whe5x.o \ $(BUILD)/meter_sensostar.o \ $(BUILD)/meter_gransystems_ccx01.o \ - $(BUILD)/manufacturer_specificities.o \ - $(BUILD)/printer.o \ - $(BUILD)/rtlsdr.o \ - $(BUILD)/serial.o \ - $(BUILD)/shell.o \ - $(BUILD)/sha256.o \ - $(BUILD)/threads.o \ - $(BUILD)/util.o \ - $(BUILD)/units.o \ - $(BUILD)/wmbus.o \ - $(BUILD)/wmbus_amb8465.o \ - $(BUILD)/wmbus_im871a.o \ - $(BUILD)/wmbus_cul.o \ - $(BUILD)/wmbus_rtlwmbus.o \ - $(BUILD)/wmbus_rtl433.o \ - $(BUILD)/wmbus_simulator.o \ - $(BUILD)/wmbus_rawtty.o \ - $(BUILD)/wmbus_rc1180.o \ - $(BUILD)/wmbus_utils.o all: $(BUILD)/wmbusmeters $(BUILD)/wmbusmeters-admin $(BUILD)/testinternals @$(STRIP_BINARY) diff --git a/README.md b/README.md index f8b3a85..b88af49 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ shell=/usr/bin/mosquitto_pub -h localhost -t wmbusmeters/$METER_ID -m "$METER_JS alarmshell=/usr/bin/mosquitto_pub -h localhost -t wmbusmeters_alarm -m "$ALARM_TYPE $ALARM_MESSAGE" alarmtimeout=1h alarmexpectedactivity=mon-sun(00-23) -ignoreduplicates=false +ignoreduplicates=true ``` Then add a meter file in /etc/wmbusmeters.d/MyTapWater @@ -172,7 +172,7 @@ As you can use: --listmeters= list all meter types containing the text --logfile= use this file instead of stdout --logtelegrams log the contents of the telegrams for easy replay - --ignoreduplicates ignore duplicate telegrams, remember the last 10 telegrams + --ignoreduplicates= ignore duplicate telegrams, remember the last 10 telegrams --meterfiles= store meter readings in dir --meterfilesaction=(overwrite|append) overwrite or append to the meter readings file --meterfilesnaming=(name|id|name-id) the meter file is the meter's: name, id or name-id diff --git a/src/cmdline.cc b/src/cmdline.cc index cf25e0d..81ab0dc 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -374,7 +374,25 @@ shared_ptr parseCommandLine(int argc, char **argv) { continue; } if (!strncmp(argv[i], "--ignoreduplicates", 18)) { - c->ignore_duplicate_telegrams = true; + if (argv[i][18] == 0) + { + c->ignore_duplicate_telegrams = true; + } + else + { + if (!strcmp(argv[i]+18, "=true")) + { + c->ignore_duplicate_telegrams = true; + } + else if (!strcmp(argv[i]+18, "=false")) + { + c->ignore_duplicate_telegrams = false; + } + else + { + error("You must specify true or false after --ignoreduplicates=\n"); + } + } i++; continue; } @@ -564,7 +582,8 @@ shared_ptr parseCommandLine(int argc, char **argv) { if (!isValidMatchExpressions(id, true)) error("Not a valid id nor a valid meter match expression \"%s\"\n", id.c_str()); if (!isValidKey(key, mt)) error("Not a valid meter key \"%s\"\n", key.c_str()); vector no_meter_shells, no_meter_jsons; - c->meters.push_back(MeterInfo(bus, name, type, id, key, modes, bps, no_meter_shells, no_meter_jsons)); + vector ids = splitMatchExpressions(id); + c->meters.push_back(MeterInfo(bus, name, mt, ids, key, modes, bps, no_meter_shells, no_meter_jsons)); } return shared_ptr(c); diff --git a/src/config.cc b/src/config.cc index aae9dab..0e6a73f 100644 --- a/src/config.cc +++ b/src/config.cc @@ -177,7 +177,8 @@ void parseMeterConfig(Configuration *c, vector &buf, string file) use = false; } if (use) { - c->meters.push_back(MeterInfo(bus, name, type, id, key, modes, bps, telegram_shells, jsons)); + vector ids = splitMatchExpressions(id); + c->meters.push_back(MeterInfo(bus, name, mt, ids, key, modes, bps, telegram_shells, jsons)); } return; @@ -659,7 +660,7 @@ LinkModeCalculationResult calculateLinkModes(Configuration *config, WMBus *wmbus { meters_union.unionLinkModeSet(m.link_modes); string meter = m.link_modes.hr(); - debug("(config) meter %s link mode(s): %s\n", m.type.c_str(), meter.c_str()); + debug("(config) meter %s link mode(s): %s\n", toMeterName(m.type).c_str(), meter.c_str()); } string metersu = meters_union.hr(); debug("(config) all possible link modes that the meters might transmit on: %s\n", metersu.c_str()); diff --git a/src/config.h b/src/config.h index 977437a..a03a957 100644 --- a/src/config.h +++ b/src/config.h @@ -67,7 +67,7 @@ struct Configuration MeterFileTimestamp meterfiles_timestamp {}; // Default is never. bool use_logfile {}; bool use_stderr_for_log = true; // Default is to use stderr for logging. - bool ignore_duplicate_telegrams = false; // Default is to report all telegrams. + bool ignore_duplicate_telegrams = true; // Default is to ignore duplicates. std::string logfile; bool json {}; bool fields {}; diff --git a/src/main.cc b/src/main.cc index d0e3204..5e2df29 100644 --- a/src/main.cc +++ b/src/main.cc @@ -691,7 +691,8 @@ void list_shell_envs(Configuration *config, string meter_type) vector envs; Telegram t; MeterInfo mi; - shared_ptr meter = createMeter(config, toMeterType(meter_type), &mi); + mi.type = toMeterType(meter_type); + shared_ptr meter = createMeter(&mi); meter->printMeter(&t, &ignore1, &ignore2, config->separator, @@ -711,7 +712,8 @@ void list_shell_envs(Configuration *config, string meter_type) void list_fields(Configuration *config, string meter_type) { MeterInfo mi; - shared_ptr meter = createMeter(config, toMeterType(meter_type), &mi); + mi.type = toMeterType(meter_type); + shared_ptr meter = createMeter(&mi); int width = 0; for (auto &p : meter->prints()) @@ -1112,8 +1114,8 @@ void setup_meters(Configuration *config, MeterManager *manager) { for (auto &m : config->meters) { - auto meter = createMeter(config, toMeterType(m.type), &m); - manager->addMeter(meter); + m.conversions = config->conversions; + manager->addMeterTemplate(m); } } @@ -1154,24 +1156,20 @@ bool start(Configuration *config) // or sent to shell invocations. printer_ = create_printer(config); - meter_manager_ = createMeterManager(); + meter_manager_ = createMeterManager(config->daemon); + + // When a meter is updated, print it, shell it, log it, etc. + meter_manager_->whenMeterUpdated( + [&](Telegram *t,Meter *meter) + { + printer_->print(t, meter, &config->jsons, &config->selected_fields); + oneshot_check(config, t, meter); + } + ); // Create the Meter objects from the configuration. setup_meters(config, meter_manager_.get()); - // Attach a received-telegram-callback from the meter and - // attach it to the printer. - meter_manager_->forEachMeter( - [&](Meter *meter) - { - meter->onUpdate([&](Telegram *t,Meter *meter) - { - printer_->print(t, meter, &config->jsons, &config->selected_fields); - oneshot_check(config, t, meter); - }); - } - ); - // Detect and initialize any devices. // Future changes are triggered through this callback. printed_warning_ = true; diff --git a/src/meters.cc b/src/meters.cc index 476bdaa..8646d45 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -30,9 +30,23 @@ struct MeterManagerImplementation : public virtual MeterManager { +private: + bool is_daemon_ {}; + vector meter_templates_; + vector> meters_; + function)> on_telegram_; + function on_meter_updated_; + +public: + void addMeterTemplate(MeterInfo &mi) + { + meter_templates_.push_back(mi); + } + void addMeter(shared_ptr meter) { meters_.push_back(meter); + meter->setIndex(meters_.size()); } Meter *lastAddedMeter() @@ -65,28 +79,115 @@ struct MeterManagerImplementation : public virtual MeterManager bool hasMeters() { - return meters_.size() != 0; + return meters_.size() != 0 || meter_templates_.size() != 0; } - bool handleTelegram(AboutTelegram &about, vector data, bool simulated) + bool handleTelegram(AboutTelegram &about, vector input_frame, bool simulated) { if (!hasMeters()) { if (on_telegram_) { - on_telegram_(about, data); + on_telegram_(about, input_frame); } return true; } bool handled = false; + bool exact_id_match = false; string ids; for (auto &m : meters_) { - bool h = m->handleTelegram(about, data, simulated, &ids); + bool h = m->handleTelegram(about, input_frame, simulated, &ids, &exact_id_match); if (h) handled = true; } + + // If not properly handled, and there was no exact id match. + // then lets check if there is a template that can create a meter for it. + if (!handled && !exact_id_match) + { + debug("(meter) no meter handled %s checking %d templates.\n", ids.c_str(), meter_templates_.size()); + // Not handled, maybe we have a template to create a new meter instance for this telegram? + Telegram t; + t.about = about; + bool ok = t.parseHeader(input_frame); + if (simulated) t.markAsSimulated(); + + if (ok) + { + ids = t.idsc; + for (auto &mi : meter_templates_) + { + if (MeterCommonImplementation::isTelegramForMeter(&t, NULL, &mi)) + { + // We found a match, make a copy of the meter info. + MeterInfo tmp = mi; + // Overwrite the wildcard pattern with the highest level id. + // The last id in the t.ids is the highest level id. + // For example: a telegram can have dll_id,tpl_id + // This will pick the tpl_id. + // Or a telegram can have a single dll_id, + // then the dll_id will be picked. + vector tmp_ids; + tmp_ids.push_back(t.ids.back()); + tmp.ids = tmp_ids; + tmp.idsc = t.ids.back(); + // Now build a meter object with for this exact id. + auto meter = createMeter(&tmp); + meter->onUpdate(on_meter_updated_); + + addMeter(meter); + string idsc = toIdsCommaSeparated(t.ids); + verbose("(meter) used meter template %s %s %s to match %s\n", + mi.name.c_str(), + mi.idsc.c_str(), + toMeterName(mi.type).c_str(), + idsc.c_str()); + + if (is_daemon_) + { + notice("(wmbusmeters) started meter %d (%s %s %s)\n", + meter->index(), + mi.name.c_str(), + tmp.idsc.c_str(), + toMeterName(mi.type).c_str()); + } + else + { + verbose("(meter) started meter %d (%s %s %s)\n", + meter->index(), + mi.name.c_str(), + tmp.idsc.c_str(), + toMeterName(mi.type).c_str()); + } + + bool match = false; + bool h = meter->handleTelegram(about, input_frame, simulated, &ids, &match); + if (!match) + { + // Oups, we added a new meter object tailored for this telegram + // but it still did not match! This is probably an error in wmbusmeters! + warning("(meter) newly created meter (%s %s %s) did not match telegram! ", + "Please open an issue at https://github.com/weetmuts/wmbusmeters/\n", + meter->name().c_str(), meter->idsc().c_str(), toMeterName(meter->type()).c_str()); + } + else if (!h) + { + // Oups, we added a new meter object tailored for this telegram + // but it still did not handle it! This can happen if the wrong + // decryption key was used. + warning("(meter) newly created meter (%s %s %s) did not handle telegram!\n", + meter->name().c_str(), meter->idsc().c_str(), toMeterName(meter->type()).c_str()); + } + else + { + handled = true; + } + } + } + } + } if (isVerboseEnabled() && !handled) { verbose("(wmbus) telegram from %s ignored by all configured meters!\n", ids.c_str()); @@ -98,32 +199,31 @@ struct MeterManagerImplementation : public virtual MeterManager { on_telegram_ = cb; } + void whenMeterUpdated(std::function cb) + { + on_meter_updated_ = cb; + } + + MeterManagerImplementation(bool daemon) : is_daemon_(daemon) {} ~MeterManagerImplementation() {} - -private: - - vector> meters_; - function)> on_telegram_; }; -shared_ptr createMeterManager() +shared_ptr createMeterManager(bool daemon) { - return shared_ptr(new MeterManagerImplementation); + return shared_ptr(new MeterManagerImplementation(daemon)); } MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi, MeterType type) : type_(type), name_(mi.name) { - ids_ = splitMatchExpressions(mi.id); + ids_ = mi.ids; + idsc_ = toIdsCommaSeparated(ids_); + if (mi.key.length() > 0) { hex2bin(mi.key, &meter_keys_.confidentiality_key); } - /*if (bus->type() == DEVICE_SIMULATION) - { - meter_keys_.simulation = true; - }*/ for (auto s : mi.shells) { addShell(s); } @@ -195,11 +295,16 @@ void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, prints_.push_back( { vname, vquantity, defaultUnitForQuantity(vquantity), NULL, getValueFunc, help, field, json, vname } ); } -vector MeterCommonImplementation::ids() +vector& MeterCommonImplementation::ids() { return ids_; } +string MeterCommonImplementation::idsc() +{ + return idsc_; +} + vector MeterCommonImplementation::fields() { return fields_; @@ -266,23 +371,46 @@ LIST_OF_METERS return LinkModeSet(); } -bool MeterCommonImplementation::isTelegramForMe(Telegram *t) +bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, MeterInfo *mi) { - debug("(meter) %s: for me? %s\n", name_.c_str(), t->idsc.c_str()); + string name; + vector ids; + string idsc; + MeterType type; + + assert((meter && !mi) || + (!meter && mi)); + + if (meter) + { + name = meter->name(); + ids = meter->ids(); + idsc = meter->idsc(); + type = meter->type(); + } + else + { + name = mi->name; + ids = mi->ids; + idsc = mi->idsc; + type = mi->type; + } + + debug("(meter) %s: for me? %s\n", name.c_str(), idsc.c_str()); bool used_wildcard = false; - bool id_match = doesIdsMatchExpressions(t->ids, ids_, &used_wildcard); + bool id_match = doesIdsMatchExpressions(t->ids, ids, &used_wildcard); if (!id_match) { // The id must match. - debug("(meter) %s: not for me: not my id\n", name_.c_str()); + debug("(meter) %s: not for me: not my id\n", name.c_str()); return false; } - bool valid_driver = isMeterDriverValid(type_, t->dll_mfct, t->dll_type, t->dll_version); + bool valid_driver = isMeterDriverValid(type, t->dll_mfct, t->dll_type, t->dll_version); if (!valid_driver && t->tpl_id_found) { - valid_driver = isMeterDriverValid(type_, t->tpl_mfct, t->tpl_type, t->tpl_version); + valid_driver = isMeterDriverValid(type, t->tpl_mfct, t->tpl_type, t->tpl_version); } if (!valid_driver) @@ -309,8 +437,8 @@ bool MeterCommonImplementation::isTelegramForMe(Telegram *t) string possible_drivers = t->autoDetectPossibleDrivers(); warning("(meter) %s: meter detection did not match the selected driver %s! correct driver is: %s\n" "(meter) Not printing this warning agin for id: %02x%02x%02x%02x mfct: (%s) %s (0x%02x) type: %s (0x%02x) ver: 0x%02x\n", - name_.c_str(), - toMeterName(type()).c_str(), + name.c_str(), + toMeterName(type).c_str(), possible_drivers.c_str(), t->dll_id_b[3], t->dll_id_b[2], t->dll_id_b[1], t->dll_id_b[0], manufacturerFlag(t->dll_mfct).c_str(), @@ -327,7 +455,7 @@ bool MeterCommonImplementation::isTelegramForMe(Telegram *t) } } - debug("(meter) %s: yes for me\n", name_.c_str()); + debug("(meter) %s: yes for me\n", name.c_str()); return true; } @@ -356,6 +484,16 @@ uint16_t MeterCommonImplementation::getRecordAsUInt16(string record) return 0; } +int MeterCommonImplementation::index() +{ + return index_; +} + +void MeterCommonImplementation::setIndex(int i) +{ + index_ = i; +} + void MeterCommonImplementation::triggerUpdate(Telegram *t) { datetime_of_update_ = time(NULL); @@ -486,7 +624,7 @@ string concatFields(Meter *m, Telegram *t, char c, vector &prints, vector return s; } -bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector input_frame, bool simulated, string *ids) +bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector input_frame, bool simulated, string *ids, bool *id_match) { Telegram t; t.about = about; @@ -496,12 +634,13 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector createMeter(Configuration *config, MeterType type, MeterInfo *mi) +shared_ptr createMeter(MeterInfo *mi) { shared_ptr newm; const char *keymsg = (mi->key[0] == 0) ? "not-encrypted" : "encrypted"; - switch (type) + switch (mi->type) { #define X(mname,link,info,type,cname) \ case MeterType::type: \ - { \ + { \ newm = create##cname(*mi); \ - newm->addConversions(config->conversions); \ - verbose("(main) configured \"%s\" \"" #mname "\" \"%s\" %s\n", \ - mi->name.c_str(), mi->id.c_str(), keymsg); \ + newm->addConversions(mi->conversions); \ + verbose("(meter) created \"%s\" \"" #mname "\" \"%s\" %s\n", \ + mi->name.c_str(), mi->idsc.c_str(), keymsg); \ return newm; \ } \ break; LIST_OF_METERS #undef X case MeterType::UNKNOWN: - error("No such meter type \"%s\"\n", mi->type.c_str()); + error("No such meter type \"%s\"\n", toMeterName(mi->type).c_str()); break; } return newm; diff --git a/src/meters.h b/src/meters.h index 0c28976..7f693a0 100644 --- a/src/meters.h +++ b/src/meters.h @@ -22,8 +22,9 @@ #include"units.h" #include"wmbus.h" -#include #include +#include +#include #include #define LIST_OF_METERS \ @@ -201,24 +202,27 @@ struct MeterInfo // A bus can be an mbus or a wmbus dongle. // The bus can be the empty string, which means that it will fallback to the first defined bus. string name; // User specified name of this (group of) meters. - string type; // Driver - string id; // How to identify the meter on the bus. + MeterType type {}; // Driver + vector ids; // Match expressions for ids. + string idsc; // Comma separated ids. string key; // Decryption key. LinkModeSet link_modes; int bps {}; // For mbus communication you need to know the baud rate. vector shells; vector jsons; // Additional static jsons that are added to each message. + vector conversions; // Additional units desired in json. MeterInfo() { } - MeterInfo(string b, string n, string t, string i, string k, LinkModeSet lms, int baud, vector &s, vector &j) + MeterInfo(string b, string n, MeterType t, vector i, string k, LinkModeSet lms, int baud, vector &s, vector &j) { bus = b; name = n; type = t; - id = i; + ids = i; + idsc = toIdsCommaSeparated(ids); key = k; shells = s; jsons = j; @@ -242,8 +246,14 @@ struct Print struct Meter { + // Meters are instantiated on the fly from a template, when a telegram arrives + // and no exact meter exists. Index 1 is the first meter created etc. + virtual int index() = 0; + virtual void setIndex(int i) = 0; // This meter listens to these ids. - virtual vector ids() = 0; + virtual vector &ids() = 0; + // Comma separated ids. + virtual string idsc() = 0; // This meter can report these fields, like total_m3, temp_c. virtual vector fields() = 0; virtual vector prints() = 0; @@ -267,8 +277,8 @@ struct Meter // The handleTelegram expects an input_frame where the DLL crcs have been removed. // Returns true of this meter handled this telegram! - virtual bool handleTelegram(AboutTelegram &about, vector input_frame, bool simulated, string *id) = 0; - virtual bool isTelegramForMe(Telegram *t) = 0; + // Sets id_match to true, if there was an id match, even though the telegram could not be properly handled. + virtual bool handleTelegram(AboutTelegram &about, vector input_frame, bool simulated, string *id, bool *id_match) = 0; virtual MeterKeys *meterKeys() = 0; // Dynamically access all data received for the meter. @@ -285,6 +295,7 @@ struct Meter struct MeterManager { + virtual void addMeterTemplate(MeterInfo &mi) = 0; virtual void addMeter(shared_ptr meter) = 0; virtual Meter*lastAddedMeter() = 0; virtual void removeAllMeters() = 0; @@ -293,10 +304,12 @@ struct MeterManager virtual bool hasAllMetersReceivedATelegram() = 0; virtual bool hasMeters() = 0; virtual void onTelegram(function)> cb) = 0; + virtual void whenMeterUpdated(std::function cb) = 0; + virtual ~MeterManager() = default; }; -shared_ptr createMeterManager(); +shared_ptr createMeterManager(bool daemon); struct WaterMeter : public virtual Meter { @@ -401,6 +414,6 @@ Generic *createGeneric(WMBus *bus, MeterInfo &m); struct Configuration; struct MeterInfo; -shared_ptr createMeter(Configuration *config, MeterType type, MeterInfo *mi); +shared_ptr createMeter(MeterInfo *mi); #endif diff --git a/src/meters_common_implementation.h b/src/meters_common_implementation.h index a4f2155..fd2bb6e 100644 --- a/src/meters_common_implementation.h +++ b/src/meters_common_implementation.h @@ -26,9 +26,12 @@ struct MeterCommonImplementation : public virtual Meter { - vector ids(); - vector fields(); - vector prints(); + int index(); + void setIndex(int i); + vector& ids(); + string idsc(); + vector fields(); + vector prints(); string name(); MeterType type(); @@ -41,7 +44,7 @@ struct MeterCommonImplementation : public virtual Meter void onUpdate(function cb); int numUpdates(); - bool isTelegramForMe(Telegram *t); + static bool isTelegramForMeter(Telegram *t, Meter *meter, MeterInfo *mi); MeterKeys *meterKeys(); std::vector getRecords(); @@ -74,7 +77,7 @@ protected: // Print the dimensionless Text quantity, no unit is needed. void addPrint(string vname, Quantity vquantity, function getValueFunc, string help, bool field, bool json); - bool handleTelegram(AboutTelegram &about, vector frame, bool simulated, string *id); + bool handleTelegram(AboutTelegram &about, vector frame, bool simulated, string *id, bool *id_match); void printMeter(Telegram *t, string *human_readable, string *fields, char separator, @@ -87,12 +90,14 @@ protected: private: + int index_ {}; MeterType type_ {}; MeterKeys meter_keys_ {}; ELLSecurityMode expected_ell_sec_mode_ {}; TPLSecurityMode expected_tpl_sec_mode_ {}; string name_; vector ids_; + string idsc_; vector> on_update_; int num_updates_ {}; time_t datetime_of_update_ {}; diff --git a/src/testinternals.cc b/src/testinternals.cc index 93b72bd..6a6ca26 100644 --- a/src/testinternals.cc +++ b/src/testinternals.cc @@ -263,11 +263,16 @@ int test_linkmodes() Configuration apator_config; string apator162 = "apator162"; - apator_config.meters.push_back(MeterInfo("", "m1", apator162, "12345678", "", - toMeterLinkModeSet(apator162), - 0, - no_meter_shells, - no_meter_jsons)); + vector ids = { "12345678" }; + apator_config.meters.push_back(MeterInfo("", // bus + "m1", // name + MeterType::APATOR162, // driver/type + ids, // ids + "", // Key + toMeterLinkModeSet(apator162), // link mode set + 0, // baud + no_meter_shells, // shells + no_meter_jsons)); // jsons // Check that if no explicit link modes are provided to apator162, then // automatic deduction will fail, since apator162 can be configured to transmit @@ -301,12 +306,13 @@ int test_linkmodes() Configuration multical21_and_supercom587_config; string multical21 = "multical21"; string supercom587 = "supercom587"; - multical21_and_supercom587_config.meters.push_back(MeterInfo("", "m1", multical21, "12345678", "", + + multical21_and_supercom587_config.meters.push_back(MeterInfo("", "m1", MeterType::MULTICAL21, ids, "", toMeterLinkModeSet(multical21), 0, no_meter_shells, no_meter_jsons)); - multical21_and_supercom587_config.meters.push_back(MeterInfo("", "m2", supercom587, "12345678", "", + multical21_and_supercom587_config.meters.push_back(MeterInfo("", "m2", MeterType::SUPERCOM587, ids, "", toMeterLinkModeSet(supercom587), 0, no_meter_shells, diff --git a/src/util.cc b/src/util.cc index 067ba07..fa58b1c 100644 --- a/src/util.cc +++ b/src/util.cc @@ -725,6 +725,18 @@ bool doesIdMatchExpressions(string id, vector& mes, bool *used_wildcard) return false; } +string toIdsCommaSeparated(std::vector &ids) +{ + string cs; + for (string& s: ids) + { + cs += s; + cs += ","; + } + if (cs.length() > 0) cs.pop_back(); + return cs; +} + bool isValidKey(string& key, MeterType mt) { if (key.length() == 0) return true; diff --git a/src/util.h b/src/util.h index 96fda90..8728a31 100644 --- a/src/util.h +++ b/src/util.h @@ -108,6 +108,8 @@ bool isValidMatchExpressions(std::string ids, bool non_compliant); bool doesIdMatchExpression(std::string id, std::string match_rule); bool doesIdMatchExpressions(std::string id, std::vector& match_rules, bool *used_wildcard); bool doesIdsMatchExpressions(std::vector &ids, std::vector& match_rules, bool *used_wildcard); +std::string toIdsCommaSeparated(std::vector &ids); + bool isValidId(std::string id, bool accept_non_compliant); bool isValidKey(std::string& key, MeterType mt); diff --git a/tests/config7/etc/wmbusmeters.conf b/tests/config7/etc/wmbusmeters.conf index 51025e3..f91b2ff 100644 --- a/tests/config7/etc/wmbusmeters.conf +++ b/tests/config7/etc/wmbusmeters.conf @@ -14,3 +14,4 @@ alarmtimeout=4s # expected to be transmitting. Some meters disable transmissions during nights # and weekends. Change this to mon-fri(08-19) alarmexpectedactivity=mon-sun(00-23) +ignoreduplicates=false \ No newline at end of file diff --git a/tests/test_additional_json.sh b/tests/test_additional_json.sh index 65a2776..dbdd4ae 100755 --- a/tests/test_additional_json.sh +++ b/tests/test_additional_json.sh @@ -26,6 +26,7 @@ fi if [ "$TESTRESULT" = "ERROR" ]; then echo ERROR: $TESTNAME; exit 1; fi + TESTNAME="Test additional shell envs from cmdline" TESTRESULT="ERROR" diff --git a/tests/test_alarm.sh b/tests/test_alarm.sh index 86e5723..c8638f2 100755 --- a/tests/test_alarm.sh +++ b/tests/test_alarm.sh @@ -59,7 +59,7 @@ if [ ! -z "$REST" ] then echo ERROR TELEGRAMS: $TESTNAME echo ----------------- - diff /tmp/wmbusmeters_telegram_expected /tmp/wmbusmeters_telegram + diff /tmp/wmbusmeters_telegram_expected /tmp/wmbusmeters_telegram_output echo ----------------- TESTRESULT="ERROR" fi diff --git a/tests/test_ignore_duplicates.sh b/tests/test_ignore_duplicates.sh index 8a578fb..5c0016c 100755 --- a/tests/test_ignore_duplicates.sh +++ b/tests/test_ignore_duplicates.sh @@ -43,7 +43,7 @@ cat > $TEST/test_expected.txt < $TEST/test_output.txt diff --git a/tests/test_unknown.sh b/tests/test_unknown.sh index 088c145..b6a9b97 100755 --- a/tests/test_unknown.sh +++ b/tests/test_unknown.sh @@ -25,7 +25,7 @@ cat > $TEST/test_expected.txt < $TEST/test_output.txt +$PROG --format=json --ignoreduplicates=false --usestdoutforlogging simulations/simulation_unknown.txt $METERS > $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 diff --git a/tests/test_wrongkeys.sh b/tests/test_wrongkeys.sh index a78e870..0fb986d 100755 --- a/tests/test_wrongkeys.sh +++ b/tests/test_wrongkeys.sh @@ -2,7 +2,7 @@ PROG="$1" -TEST=testaes +TEST=testoutput rm -rf $TEST mkdir -p $TEST @@ -29,8 +29,11 @@ fi cat < $TEST/test_expected.txt Started config rtlwmbus on stdin listening on any (wmbus) decrypted content failed check, did you use the correct decryption key? Permanently ignoring telegrams from id: 88888888 mfct: (APA) Apator, Poland (0x601) type: Water meter (0x07) ver: 0x05 +(meter) newly created meter (ApWater 88888888 apator162) did not handle telegram! (wmbus) decrypted payload crc failed check, did you use the correct decryption key? 979f payload crc (calculated 3431) Permanently ignoring telegrams from id: 76348799 mfct: (KAM) Kamstrup Energi (0x2c2d) type: Cold water meter (0x16) ver: 0x1b +(meter) newly created meter (Vatten 76348799 multical21) did not handle telegram! (wmbus) decrypted content failed check, did you use the correct decryption key? Permanently ignoring telegrams from id: 77777777 mfct: (SON) Sontex, Switzerland (0x4dee) type: Water meter (0x07) ver: 0x3c +(meter) newly created meter (Wasser 77777777 supercom587) did not handle telegram! EOF diff $TEST/test_expected.txt $TEST/test_stderr.txt