kopia lustrzana https://github.com/weetmuts/wmbusmeters
Ignoreduplices enabled by defailt. New C++ object create for each unique meter, despite wildcards.
rodzic
73c4085a00
commit
301c91ea68
18
CHANGES
18
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!
|
||||
|
|
38
Makefile
38
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 {};
|
||||
|
|
34
src/main.cc
34
src/main.cc
|
@ -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;
|
||||
|
|
209
src/meters.cc
209
src/meters.cc
|
@ -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;
|
||||
|
|
33
src/meters.h
33
src/meters.h
|
@ -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
|
||||
|
|
|
@ -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_ {};
|
||||
|
|
|
@ -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,
|
||||
|
|
12
src/util.cc
12
src/util.cc
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -26,6 +26,7 @@ fi
|
|||
if [ "$TESTRESULT" = "ERROR" ]; then echo ERROR: $TESTNAME; exit 1; fi
|
||||
|
||||
|
||||
|
||||
TESTNAME="Test additional shell envs from cmdline"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue