Ignoreduplices enabled by defailt. New C++ object create for each unique meter, despite wildcards.

pull/267/head
Fredrik Öhrström 2021-02-20 22:21:01 +01:00
rodzic 73c4085a00
commit 301c91ea68
19 zmienionych plików z 322 dodań i 106 usunięć

18
CHANGES
Wyświetl plik

@ -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!

Wyświetl plik

@ -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)

Wyświetl plik

@ -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 <options> you can use:
--listmeters=<search> list all meter types containing the text <search>
--logfile=<file> 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=<bool> ignore duplicate telegrams, remember the last 10 telegrams
--meterfiles=<dir> 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

Wyświetl plik

@ -374,7 +374,25 @@ shared_ptr<Configuration> 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<Configuration> 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<string> 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<string> ids = splitMatchExpressions(id);
c->meters.push_back(MeterInfo(bus, name, mt, ids, key, modes, bps, no_meter_shells, no_meter_jsons));
}
return shared_ptr<Configuration>(c);

Wyświetl plik

@ -177,7 +177,8 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
use = false;
}
if (use) {
c->meters.push_back(MeterInfo(bus, name, type, id, key, modes, bps, telegram_shells, jsons));
vector<string> 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());

Wyświetl plik

@ -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 {};

Wyświetl plik

@ -691,7 +691,8 @@ void list_shell_envs(Configuration *config, string meter_type)
vector<string> envs;
Telegram t;
MeterInfo mi;
shared_ptr<Meter> meter = createMeter(config, toMeterType(meter_type), &mi);
mi.type = toMeterType(meter_type);
shared_ptr<Meter> 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> meter = createMeter(config, toMeterType(meter_type), &mi);
mi.type = toMeterType(meter_type);
shared_ptr<Meter> 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;

Wyświetl plik

@ -30,9 +30,23 @@
struct MeterManagerImplementation : public virtual MeterManager
{
private:
bool is_daemon_ {};
vector<MeterInfo> meter_templates_;
vector<shared_ptr<Meter>> meters_;
function<void(AboutTelegram&,vector<uchar>)> on_telegram_;
function<void(Telegram*t,Meter*)> on_meter_updated_;
public:
void addMeterTemplate(MeterInfo &mi)
{
meter_templates_.push_back(mi);
}
void addMeter(shared_ptr<Meter> 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<uchar> data, bool simulated)
bool handleTelegram(AboutTelegram &about, vector<uchar> 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<string> 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<void(Telegram*t,Meter*)> cb)
{
on_meter_updated_ = cb;
}
MeterManagerImplementation(bool daemon) : is_daemon_(daemon) {}
~MeterManagerImplementation() {}
private:
vector<shared_ptr<Meter>> meters_;
function<void(AboutTelegram&,vector<uchar>)> on_telegram_;
};
shared_ptr<MeterManager> createMeterManager()
shared_ptr<MeterManager> createMeterManager(bool daemon)
{
return shared_ptr<MeterManager>(new MeterManagerImplementation);
return shared_ptr<MeterManager>(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<string> MeterCommonImplementation::ids()
vector<string>& MeterCommonImplementation::ids()
{
return ids_;
}
string MeterCommonImplementation::idsc()
{
return idsc_;
}
vector<string> 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<string> 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<Print> &prints, vector
return s;
}
bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<uchar> input_frame, bool simulated, string *ids)
bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<uchar> input_frame, bool simulated, string *ids, bool *id_match)
{
Telegram t;
t.about = about;
@ -496,12 +634,13 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<ucha
*ids = t.idsc;
if (!ok || !isTelegramForMe(&t))
if (!ok || !isTelegramForMeter(&t, this, NULL))
{
// This telegram is not intended for this meter.
return false;
}
*id_match = true;
verbose("(meter) %s %s handling telegram from %s\n", name().c_str(), meterName().c_str(), t.ids.back().c_str());
if (isDebugEnabled())
@ -738,28 +877,28 @@ METER_DETECTION
return false;
}
shared_ptr<Meter> createMeter(Configuration *config, MeterType type, MeterInfo *mi)
shared_ptr<Meter> createMeter(MeterInfo *mi)
{
shared_ptr<Meter> 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;

Wyświetl plik

@ -22,8 +22,9 @@
#include"units.h"
#include"wmbus.h"
#include<string>
#include<functional>
#include<numeric>
#include<string>
#include<vector>
#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<string> 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<string> shells;
vector<string> jsons; // Additional static jsons that are added to each message.
vector<Unit> conversions; // Additional units desired in json.
MeterInfo()
{
}
MeterInfo(string b, string n, string t, string i, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &j)
MeterInfo(string b, string n, MeterType t, vector<string> i, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &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<string> ids() = 0;
virtual vector<string> &ids() = 0;
// Comma separated ids.
virtual string idsc() = 0;
// This meter can report these fields, like total_m3, temp_c.
virtual vector<string> fields() = 0;
virtual vector<Print> 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<uchar> 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<uchar> 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> 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<void(AboutTelegram&,vector<uchar>)> cb) = 0;
virtual void whenMeterUpdated(std::function<void(Telegram*t,Meter*)> cb) = 0;
virtual ~MeterManager() = default;
};
shared_ptr<MeterManager> createMeterManager();
shared_ptr<MeterManager> createMeterManager(bool daemon);
struct WaterMeter : public virtual Meter
{
@ -401,6 +414,6 @@ Generic *createGeneric(WMBus *bus, MeterInfo &m);
struct Configuration;
struct MeterInfo;
shared_ptr<Meter> createMeter(Configuration *config, MeterType type, MeterInfo *mi);
shared_ptr<Meter> createMeter(MeterInfo *mi);
#endif

Wyświetl plik

@ -26,9 +26,12 @@
struct MeterCommonImplementation : public virtual Meter
{
vector<string> ids();
vector<string> fields();
vector<Print> prints();
int index();
void setIndex(int i);
vector<string>& ids();
string idsc();
vector<string> fields();
vector<Print> prints();
string name();
MeterType type();
@ -41,7 +44,7 @@ struct MeterCommonImplementation : public virtual Meter
void onUpdate(function<void(Telegram*,Meter*)> cb);
int numUpdates();
bool isTelegramForMe(Telegram *t);
static bool isTelegramForMeter(Telegram *t, Meter *meter, MeterInfo *mi);
MeterKeys *meterKeys();
std::vector<std::string> getRecords();
@ -74,7 +77,7 @@ protected:
// Print the dimensionless Text quantity, no unit is needed.
void addPrint(string vname, Quantity vquantity,
function<std::string()> getValueFunc, string help, bool field, bool json);
bool handleTelegram(AboutTelegram &about, vector<uchar> frame, bool simulated, string *id);
bool handleTelegram(AboutTelegram &about, vector<uchar> 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<string> ids_;
string idsc_;
vector<function<void(Telegram*,Meter*)>> on_update_;
int num_updates_ {};
time_t datetime_of_update_ {};

Wyświetl plik

@ -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<string> 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,

Wyświetl plik

@ -725,6 +725,18 @@ bool doesIdMatchExpressions(string id, vector<string>& mes, bool *used_wildcard)
return false;
}
string toIdsCommaSeparated(std::vector<std::string> &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;

Wyświetl plik

@ -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<std::string>& match_rules, bool *used_wildcard);
bool doesIdsMatchExpressions(std::vector<std::string> &ids, std::vector<std::string>& match_rules, bool *used_wildcard);
std::string toIdsCommaSeparated(std::vector<std::string> &ids);
bool isValidId(std::string id, bool accept_non_compliant);
bool isValidKey(std::string& key, MeterType mt);

Wyświetl plik

@ -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

Wyświetl plik

@ -26,6 +26,7 @@ fi
if [ "$TESTRESULT" = "ERROR" ]; then echo ERROR: $TESTNAME; exit 1; fi
TESTNAME="Test additional shell envs from cmdline"
TESTRESULT="ERROR"

Wyświetl plik

@ -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

Wyświetl plik

@ -43,7 +43,7 @@ cat > $TEST/test_expected.txt <<EOF
{"media":"smoke detector","meter":"lansensm","name":"Rummet","id":"01000273","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
EOF
$PROG --format=json simulations/simulation_duplicates.txt \
$PROG --format=json --ignoreduplicates=false simulations/simulation_duplicates.txt \
Rummet lansensm 01000273 "" \
| grep Rummet > $TEST/test_output.txt

Wyświetl plik

@ -25,7 +25,7 @@ cat > $TEST/test_expected.txt <<EOF
{"media":"reserved","meter":"lansensm","name":"Forren","id":"00010206","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
EOF
$PROG --format=json --usestdoutforlogging simulations/simulation_unknown.txt $METERS > $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

Wyświetl plik

@ -2,7 +2,7 @@
PROG="$1"
TEST=testaes
TEST=testoutput
rm -rf $TEST
mkdir -p $TEST
@ -29,8 +29,11 @@ fi
cat <<EOF > $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