New advanced addressing works.

pull/1091/head
Fredrik Öhrström 2024-03-02 15:30:25 +01:00
rodzic 78e7c47503
commit 7634b95438
22 zmienionych plików z 505 dodań i 272 usunięć

12
CHANGES
Wyświetl plik

@ -1,3 +1,15 @@
New improved address specification. E.g. use 12345678.M=KAM.V=1b.T=16
to listen to exactly the telegrams with id 12345678 manufacturer KAM,
version 0x1b and type 0x16. You if you do not specify any M,V or T, they
become wildcards which will be the old default behaviour.
If you receive multiple telegram versions from the same id, and you want to
filter out some versions, do: 12345678,!12345678.V=77
You can now specify p0 to p250, to read from an mbus using the primary address.
E.g. wmbusmeters --pollinterval=5s /dev/ttyUSB1:mbus:2400 TEMP piigth:mbus p0 NOKEY
Version 1.16.1 2024-02-22 Version 1.16.1 2024-02-22
Fix docker file generation. Fix docker file generation.

Wyświetl plik

@ -130,9 +130,11 @@ bus the mbus poll request should be sent to.
wmbusmeters --pollinterval=60s MAIN=/dev/ttyUSB0:mbus:2400 MyTempMeter piigth:MAIN:mbus 12001932 NOKEY wmbusmeters --pollinterval=60s MAIN=/dev/ttyUSB0:mbus:2400 MyTempMeter piigth:MAIN:mbus 12001932 NOKEY
``` ```
If you want to poll an mbus meter using the primary address, just use If you want to poll an mbus meter using the primary address, use p0 to p250 (deciman numbers)
a number between 0 and 250 instead of the full 8 digit secondary instead of the full 8 digit secondary address.
address. ```
wmbusmeters --pollinterval=60s MAIN=/dev/ttyUSB0:mbus:2400 MyTempMeter piigth:MAIN:mbus p0 NOKEY
```
# Example wmbusmeter.conf file # Example wmbusmeter.conf file
@ -217,9 +219,13 @@ The latest reading of the meter can also be found here: `/var/lib/wmbusmeters/me
You can use several ids using `id=1111111,2222222,3333333` or you can listen to all You can use several ids using `id=1111111,2222222,3333333` or you can listen to all
meters of a certain type `id=*` or you can suffix with star `id=8765*` to match meters of a certain type `id=*` or you can suffix with star `id=8765*` to match
all meters with a given prefix. If you supply at least one positive match rule, then you all meters with a given prefix. If you supply at least one positive match rule, then you
can add negative match rules as well. For example `id=*,!2222*` can add filter out rules as well. For example `id=*,!2222*`
which will match all meter ids, except those that begin with 2222. which will match all meter ids, except those that begin with 2222.
You can also specify the exact manufacturer, version and type: `id=11111111.M=KAM.V=1b.T=16`
or a subset: `id=11111111.T=16` or all telegrams from 22222222 except those with version 77:
`id=22222222,!22222222.V=77`
When matching all meters from the command line you can use `ANYID` instead of `*` to avoid shell quotes. When matching all meters from the command line you can use `ANYID` instead of `*` to avoid shell quotes.
# Add static and calculated fields to the output # Add static and calculated fields to the output

Wyświetl plik

@ -22,6 +22,8 @@
using namespace std; using namespace std;
vector<string> splitSequenceOfAddressExpressionsAtCommas(const string& mes);
bool isValidMatchExpression(const string& s, bool *has_wildcard) bool isValidMatchExpression(const string& s, bool *has_wildcard)
{ {
string me = s; string me = s;
@ -37,9 +39,12 @@ bool isValidMatchExpression(const string& s, bool *has_wildcard)
// A match expression cannot be empty. // A match expression cannot be empty.
if (me.length() == 0) return false; if (me.length() == 0) return false;
// An me can be negated with an exclamation mark first. // An me can be filtered out with an exclamation mark first.
if (me.front() == '!') me.erase(0, 1); if (me.front() == '!') me.erase(0, 1);
// More than one negation is not allowed.
if (me.front() == '!') return false;
// A match expression cannot be only a negation mark. // A match expression cannot be only a negation mark.
if (me.length() == 0) return false; if (me.length() == 0) return false;
@ -76,29 +81,51 @@ bool isValidMatchExpression(const string& s, bool *has_wildcard)
return count <= 7; return count <= 7;
} }
bool isValidMatchExpressions(const string& mes) vector<string> splitSequenceOfAddressExpressionsAtCommas(const string& mes)
{ {
vector<string> v = splitMatchExpressions(mes); vector<string> r;
bool eof, err;
vector<uchar> v (mes.begin(), mes.end());
auto i = v.begin();
for (;;) {
auto id = eatTo(v, i, ',', 16, &eof, &err);
if (err) break;
trimWhitespace(&id);
if (id == "ANYID") id = "*";
r.push_back(id);
if (eof) break;
}
return r;
}
bool isValidSequenceOfAddressExpressions(const string& mes)
{
vector<string> v = splitSequenceOfAddressExpressionsAtCommas(mes);
for (string me : v) for (string me : v)
{ {
if (!isValidMatchExpression(me, NULL)) return false; AddressExpression ae;
if (!ae.parse(me)) return false;
} }
return true; return true;
} }
bool isValidId(const string& id) vector<AddressExpression> splitAddressExpressions(const string &aes)
{ {
vector<string> v = splitSequenceOfAddressExpressionsAtCommas(aes);
for (size_t i=0; i<id.length(); ++i) vector<AddressExpression> r;
for (string me : v)
{ {
if (id[i] >= '0' && id[i] <= '9') continue; AddressExpression ae;
// Some non-compliant meters have hex in their id. if (ae.parse(me))
if (id[i] >= 'a' && id[i] <= 'f') continue; {
if (id[i] >= 'A' && id[i] <= 'F') continue; r.push_back(ae);
return false; }
} }
return true; return r;
} }
bool doesIdMatchExpression(const string& s, string match) bool doesIdMatchExpression(const string& s, string match)
@ -153,12 +180,12 @@ bool hasWildCard(const string& mes)
return mes.find('*') != string::npos; return mes.find('*') != string::npos;
} }
bool doesIdsMatchExpressions(vector<string> &ids, vector<string>& mes, bool *used_wildcard) bool doesIdsMatchExpressionss(vector<string> &ids, vector<string>& mes, bool *used_wildcard)
{ {
bool match = false; bool match = false;
for (string &id : ids) for (string &id : ids)
{ {
if (doesIdMatchExpressions(id, mes, used_wildcard)) if (doesIdMatchExpressionss(id, mes, used_wildcard))
{ {
match = true; match = true;
} }
@ -168,7 +195,7 @@ bool doesIdsMatchExpressions(vector<string> &ids, vector<string>& mes, bool *use
return match; return match;
} }
bool doesIdMatchExpressions(const string& id, vector<string>& mes, bool *used_wildcard) bool doesIdMatchExpressionss(const string& id, vector<string>& mes, bool *used_wildcard)
{ {
bool found_match = false; bool found_match = false;
bool found_negative_match = false; bool found_negative_match = false;
@ -315,6 +342,18 @@ string toIdsCommaSeparated(vector<string> &ids)
return cs; return cs;
} }
string toIdsCommaSeparated(vector<AddressExpression> &ids)
{
string cs;
for (AddressExpression& ae: ids)
{
cs += ae.str();
cs += ",";
}
if (cs.length() > 0) cs.pop_back();
return cs;
}
bool AddressExpression::match(const std::string &i, uint16_t m, uchar v, uchar t) bool AddressExpression::match(const std::string &i, uint16_t m, uchar v, uchar t)
{ {
if (!(mfct == 0xffff || mfct == m)) return false; if (!(mfct == 0xffff || mfct == m)) return false;
@ -351,8 +390,9 @@ bool AddressExpression::parse(const string &in)
{ {
filter_out = true; filter_out = true;
s = s.substr(1); s = s.substr(1);
// Double ! not allowed.
if (s.size() > 1 && s[0] == '!') return false;
} }
vector<string> parts = splitString(s, '.'); vector<string> parts = splitString(s, '.');
assert(parts.size() > 0); assert(parts.size() > 0);
@ -425,3 +465,176 @@ bool flagToManufacturer(const char *s, uint16_t *out_mfct)
*out_mfct = MANFCODE(s[0],s[1],s[2]); *out_mfct = MANFCODE(s[0],s[1],s[2]);
return true; return true;
} }
string AddressExpression::str()
{
string s;
if (filter_out) s = "!";
s.append(id);
if (mfct != 0xffff)
{
s += ".M="+manufacturerFlag(mfct);
}
if (version != 0xff)
{
s += ".V="+tostrprintf("%02x", version);
}
if (type != 0xff)
{
s += ".T="+tostrprintf("%02x", type);
}
return s;
}
string Address::str()
{
string s;
s.append(id);
if (mfct != 0xffff)
{
s += ".M="+manufacturerFlag(mfct);
}
if (version != 0xff)
{
s += ".V="+tostrprintf("%02x", version);
}
if (type != 0xff)
{
s += ".T="+tostrprintf("%02x", type);
}
return s;
}
string Address::concat(std::vector<Address> &addresses)
{
string s;
for (Address& a: addresses)
{
if (s.size() > 0) s.append(",");
s.append(a.str());
}
return s;
}
string AddressExpression::concat(std::vector<AddressExpression> &address_expressions)
{
string s;
for (AddressExpression& a: address_expressions)
{
if (s.size() > 0) s.append(",");
s.append(a.str());
}
return s;
}
string manufacturerFlag(int m_field) {
char a = (m_field/1024)%32+64;
char b = (m_field/32)%32+64;
char c = (m_field)%32+64;
string flag;
flag += a;
flag += b;
flag += c;
return flag;
}
void Address::decodeMfctFirst(const vector<uchar>::iterator &pos)
{
mfct = *(pos+1) << 8 | *(pos+0);
id = tostrprintf("%02x%02x%02x%02x", *(pos+5), *(pos+4), *(pos+3), *(pos+2));
version = *(pos+6);
type = *(pos+7);
}
void Address::decodeIdFirst(const vector<uchar>::iterator &pos)
{
id = tostrprintf("%02x%02x%02x%02x", *(pos+3), *(pos+2), *(pos+1), *(pos+0));
mfct = *(pos+5) << 8 | *(pos+4);
version = *(pos+6);
type = *(pos+7);
}
bool doesTelegramMatchExpressions(std::vector<Address> &addresses,
std::vector<AddressExpression>& address_expressions,
bool *used_wildcard)
{
bool match = false;
for (Address &a : addresses)
{
if (doesAddressMatchExpressions(a, address_expressions, used_wildcard))
{
match = true;
}
// Go through all ids even though there is an early match.
// This way we can see if theres an exact match later.
}
return match;
}
bool doesAddressMatchExpressions(Address &address,
vector<AddressExpression>& address_expressions,
bool *used_wildcard)
{
bool found_match = false;
bool found_negative_match = false;
bool exact_match = false;
// Goes through all possible match expressions.
// If no expression matches, neither positive nor negative,
// then the result is false. (ie no match)
// If more than one positive match is found, and no negative,
// then the result is true.
// If more than one negative match is found, irrespective
// if there is any positive matches or not, then the result is false.
// If a positive match is found, using a wildcard not any exact match,
// then *used_wildcard is set to true.
for (AddressExpression &ae : address_expressions)
{
bool has_wildcard = ae.has_wildcard;
bool is_negative_rule = ae.filter_out;
bool m = doesIdMatchExpression(address.id, ae.id);
if (is_negative_rule)
{
if (m) found_negative_match = true;
}
else
{
if (m)
{
found_match = true;
if (!has_wildcard)
{
exact_match = true;
}
}
}
}
if (found_negative_match)
{
return false;
}
if (found_match)
{
if (exact_match)
{
*used_wildcard = false;
}
else
{
*used_wildcard = true;
}
return true;
}
return false;
}

Wyświetl plik

@ -21,6 +21,20 @@
#include "util.h" #include "util.h"
#include <string> #include <string>
struct Address
{
std::string id; // p1 or 12345678 or non-compliant hex: 1234abcd
uint16_t mfct {};
uchar type {};
uchar version {};
void decodeMfctFirst(const std::vector<uchar>::iterator &pos);
void decodeIdFirst(const std::vector<uchar>::iterator &pos);
std::string str();
static std::string concat(std::vector<Address> &addresses);
};
struct AddressExpression struct AddressExpression
{ {
// An address expression is used to select which telegrams to decode for a driver. // An address expression is used to select which telegrams to decode for a driver.
@ -35,37 +49,61 @@ struct AddressExpression
// Or every telegram which is does not start with 12 and is not from ABB: // Or every telegram which is does not start with 12 and is not from ABB:
// !12*.M!=ABB // !12*.M!=ABB
std::string id; // 1 or 12345678 or non-compliant hex: 1234abcd std::string id; // p1 or 12345678 or non-compliant hex: 1234abcd
bool has_wildcard {}; // The id contains a * bool has_wildcard {}; // The id contains a *
bool mbus_primary {}; // Signals that the id is 0-250 bool mbus_primary {}; // Signals that the id is 0-250
uint16_t mfct {}; // If 0xffff then any mfct matches this address. uint16_t mfct {}; // If 0xffff then any mfct matches this address.
uchar type {}; // If 0xff then any type matches this address.
uchar version {}; // If 0xff then any version matches this address. uchar version {}; // If 0xff then any version matches this address.
uchar type {}; // If 0xff then any type matches this address.
bool filter_out {}; // Telegrams matching this rule should be filtered out! bool filter_out {}; // Telegrams matching this rule should be filtered out!
AddressExpression() {}
AddressExpression(Address &a) : id(a.id), mfct(a.mfct), version(a.version), type(a.type) { }
bool parse(const std::string &s); bool parse(const std::string &s);
bool match(const std::string &id, uint16_t mfct, uchar version, uchar type); bool match(const std::string &id, uint16_t mfct, uchar version, uchar type);
std::string str();
static std::string concat(std::vector<AddressExpression> &address_expressions);
}; };
/**
isValidSequenceOfAddressExpressions:
Valid sequenes look like this:
12345678
12345678,22334455,34*
12*.T=16,!*.M=XYZ
!*.V=33
*/
bool isValidSequenceOfAddressExpressions(const std::string& s);
bool isValidMatchExpression(const std::string& s, bool *has_wildcard); bool isValidMatchExpression(const std::string& s, bool *has_wildcard);
bool isValidMatchExpressions(const std::string& s);
bool doesIdMatchExpression(const std::string& id, bool doesIdMatchExpression(const std::string& id,
std::string match_rule); std::string match_rule);
bool doesIdMatchExpressions(const std::string& id, bool doesIdMatchExpressionss(const std::string& id,
std::vector<std::string>& match_rules, std::vector<std::string>& match_rules,
bool *used_wildcard); bool *used_wildcard);
bool doesIdsMatchExpressions(std::vector<std::string> &ids, bool doesIdsMatchExpressionss(std::vector<std::string> &ids,
std::vector<std::string>& match_rules, std::vector<std::string>& match_rules,
bool *used_wildcard); bool *used_wildcard);
std::string toIdsCommaSeparated(std::vector<std::string> &ids); std::string toIdsCommaSeparated(std::vector<std::string> &ids);
std::string toIdsCommaSeparated(std::vector<AddressExpression> &ids);
bool isValidId(const std::string& id); std::vector<AddressExpression> splitAddressExpressions(const std::string &aes);
std::vector<std::string> splitMatchExpressions(const std::string& mes);
bool flagToManufacturer(const char *s, uint16_t *out_mfct); bool flagToManufacturer(const char *s, uint16_t *out_mfct);
std::string manufacturerFlag(int m_field);
bool doesTelegramMatchExpressions(std::vector<Address> &addresses,
std::vector<AddressExpression>& address_expressions,
bool *used_wildcard);
bool doesAddressMatchExpressions(Address &address,
std::vector<AddressExpression>& address_expressions,
bool *used_wildcard);
#endif #endif

Wyświetl plik

@ -740,38 +740,7 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
error("Not a valid meter driver \"%s\"\n", driver.c_str()); error("Not a valid meter driver \"%s\"\n", driver.c_str());
} }
//LinkModeSet default_modes = toMeterLinkModeSet(mi.driver);
/*
if (default_modes.has(LinkMode::MBUS))
{
// MBus primary address 0-250
// secondary hex address iiiiiiiimmmmvvmm
}
else
{
// WMBus ids are 8 hex digits iiiiiiii
if (!isValidMatchExpressions(id, true)) error("Not a valid id nor a valid meter match expression \"%s\"\n", id.c_str());
}
if (!isValidKey(key, mi)) error("Not a valid meter key \"%s\"\n", key.c_str());
*/
c->meters.push_back(mi); c->meters.push_back(mi);
// Check if the devices can listen to the meter link mode(s).
/*
Ignore this check for now until all meters have been refactored.
if (!default_modes.hasAll(mi.link_modes))
{
string want = mi.link_modes.hr();
string has = default_modes.hr();
error("(cmdline) cannot set link modes to: %s because meter %s only transmits on: %s\n",
want.c_str(), mi.driverName().str().c_str(), has.c_str());
}
string modeshr = mi.link_modes.hr();
debug("(cmdline) setting link modes to %s for meter %s\n",
mi.link_modes.hr().c_str(), name.c_str());
*/
} }
return shared_ptr<Configuration>(c); return shared_ptr<Configuration>(c);

Wyświetl plik

@ -179,34 +179,20 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
mi.parse(name, driver, id, key); // sets driver, extras, name, bus, bps, link_modes, ids, name, key mi.parse(name, driver, id, key); // sets driver, extras, name, bus, bps, link_modes, ids, name, key
mi.poll_interval = poll_interval; mi.poll_interval = poll_interval;
/* if (!isValidSequenceOfAddressExpressions(id)) {
Ignore link mode checking until all drivers have been refactored. warning("Not a valid meter id nor a valid sequence of match expression \"%s\"\n", id.c_str());
LinkModeSet default_modes = toMeterLinkModeSet(mi.driver);
if (!default_modes.hasAll(mi.link_modes))
{
string want = mi.link_modes.hr();
string has = default_modes.hr();
error("(cmdline) cannot set link modes to: %s because meter %s only transmits on: %s\n",
want.c_str(), mi.driverName().str().c_str(), has.c_str());
}
string modeshr = mi.link_modes.hr();
debug("(cmdline) setting link modes to %s for meter %s\n",
mi.link_modes.hr().c_str(), name.c_str());
*/
if (!isValidMatchExpressions(id)) {
warning("Not a valid meter id nor a valid meter match expression \"%s\"\n", id.c_str());
use = false; use = false;
} }
if (!isValidKey(key, mi)) { if (!isValidKey(key, mi)) {
warning("Not a valid meter key \"%s\"\n", key.c_str()); warning("Not a valid meter key \"%s\"\n", key.c_str());
use = false; use = false;
} }
if (use) { if (use)
{
mi.extra_constant_fields = extra_constant_fields; mi.extra_constant_fields = extra_constant_fields;
mi.extra_calculated_fields = extra_calculated_fields; mi.extra_calculated_fields = extra_calculated_fields;
mi.shells = telegram_shells; mi.shells = telegram_shells;
mi.meter_shells = meter_shells; mi.meter_shells = meter_shells;
mi.idsc = toIdsCommaSeparated(mi.ids);
mi.selected_fields = selected_fields; mi.selected_fields = selected_fields;
c->meters.push_back(mi); c->meters.push_back(mi);
} }

Wyświetl plik

@ -105,6 +105,7 @@ bool DriverDynamic::load(DriverInfo *di, const string &file_name, const char *co
catch (...) catch (...)
{ {
xmqFreeDoc(doc); xmqFreeDoc(doc);
di->setDynamic(file, NULL);
return false; return false;
} }
} }
@ -150,7 +151,7 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *d
mvt.c_str(), mvt.c_str(),
line, line,
line); line);
throw 1; return XMQ_CONTINUE;
} }
string mfct = fields[0]; string mfct = fields[0];
@ -175,7 +176,7 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *d
mfct.c_str(), mfct.c_str(),
line, line,
line); line);
throw 1; return XMQ_CONTINUE;
} }
mfct_code = toMfctCode(a, b, c); mfct_code = toMfctCode(a, b, c);
} }
@ -193,7 +194,7 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *d
mfct.c_str(), mfct.c_str(),
line, line,
line); line);
throw 1; return XMQ_CONTINUE;
} }
} }
@ -207,7 +208,7 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *d
version, version,
line, line,
line); line);
throw 1; return XMQ_CONTINUE;
} }
if (type > 255 || type < 0) if (type > 255 || type < 0)
@ -220,7 +221,7 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *d
type, type,
line, line,
line); line);
throw 1; return XMQ_CONTINUE;
} }
string mfct_flag = manufacturerFlag(mfct_code); string mfct_flag = manufacturerFlag(mfct_code);

Wyświetl plik

@ -160,8 +160,11 @@ namespace
vector<uchar> v; vector<uchar> v;
auto entry = it->second.second; auto entry = it->second.second;
hex2bin(entry.value.substr(0, 8), &v); hex2bin(entry.value.substr(0, 8), &v);
t->addId(v.begin()); // FIXME PROBLEM
std::string info = "*** " + entry.value.substr(0, 8) + " tpl-id (" + t->ids.back() + ")"; Address a;
a.id = tostrprintf("%02x%02x%02x%02x", v[3], v[2], v[1], v[0]);
t->addresses.push_back(a);
std::string info = "*** " + entry.value.substr(0, 8) + " tpl-id (" + t->addresses.back().id + ")";
t->addSpecialExplanation(entry.offset, 4, KindOfData::CONTENT, Understanding::FULL, info.c_str()); t->addSpecialExplanation(entry.offset, 4, KindOfData::CONTENT, Understanding::FULL, info.c_str());
v.clear(); v.clear();

Wyświetl plik

@ -581,12 +581,12 @@ bool start(Configuration *config)
vector<string> envs; vector<string> envs;
string id = ""; string id = "";
if (meter->ids().size() > 0) if (meter->addressExpressions().size() > 0)
{ {
id = meter->idsc().c_str(); id = meter->addressExpressions().back().id;
} }
meter->createMeterEnv(&id, &envs, &config->extra_constant_fields); meter->createMeterEnv(id, &envs, &config->extra_constant_fields);
for (auto &s : *shells) { for (auto &s : *shells) {
vector<string> args; vector<string> args;

Wyświetl plik

@ -144,11 +144,12 @@ public:
bool handled = false; bool handled = false;
bool exact_id_match = false; bool exact_id_match = false;
string verbose_info;
string ids; vector<Address> addresses;
for (auto &m : meters_) for (auto &m : meters_)
{ {
bool h = m->handleTelegram(about, input_frame, simulated, &ids, &exact_id_match); bool h = m->handleTelegram(about, input_frame, simulated, &addresses, &exact_id_match);
if (h) handled = true; if (h) handled = true;
} }
@ -156,7 +157,12 @@ public:
// then lets check if there is a template that can create a meter for it. // then lets check if there is a template that can create a meter for it.
if (!handled && !exact_id_match) if (!handled && !exact_id_match)
{ {
debug("(meter) no meter handled %s checking %d templates.\n", ids.c_str(), meter_templates_.size()); if (isDebugEnabled())
{
string idsc = Address::concat(addresses);
debug("(meter) no meter handled %s checking %d templates.\n",
idsc.c_str(), meter_templates_.size());
}
// Not handled, maybe we have a template to create a new meter instance for this telegram? // Not handled, maybe we have a template to create a new meter instance for this telegram?
Telegram t; Telegram t;
t.about = about; t.about = about;
@ -165,7 +171,6 @@ public:
if (ok) if (ok)
{ {
ids = t.idsc;
for (auto &mi : meter_templates_) for (auto &mi : meter_templates_)
{ {
if (MeterCommonImplementation::isTelegramForMeter(&t, NULL, &mi)) if (MeterCommonImplementation::isTelegramForMeter(&t, NULL, &mi))
@ -178,10 +183,9 @@ public:
// This will pick the tpl_id. // This will pick the tpl_id.
// Or a telegram can have a single dll_id, // Or a telegram can have a single dll_id,
// then the dll_id will be picked. // then the dll_id will be picked.
vector<string> tmp_ids; vector<AddressExpression> aes;
tmp_ids.push_back(t.ids.back()); aes.push_back(AddressExpression(t.addresses.back()));
meter_info.ids = tmp_ids; meter_info.address_expressions = aes;
meter_info.idsc = t.ids.back();
if (meter_info.driverName().str() == "auto") if (meter_info.driverName().str() == "auto")
{ {
@ -203,47 +207,55 @@ public:
// Now build a meter object with for this exact id. // Now build a meter object with for this exact id.
auto meter = createMeter(&meter_info); auto meter = createMeter(&meter_info);
addMeter(meter); addMeter(meter);
string idsc = toIdsCommaSeparated(t.ids); if (isVerboseEnabled())
verbose("(meter) used meter template %s %s %s to match %s\n", {
mi.name.c_str(), string idsc = Address::concat(t.addresses);
mi.idsc.c_str(), string mi_idsc = AddressExpression::concat(mi.address_expressions);
mi.driverName().str().c_str(), verbose("(meter) used meter template %s %s %s to match %s\n",
idsc.c_str()); mi.name.c_str(),
mi_idsc.c_str(),
mi.driverName().str().c_str(),
idsc.c_str());
}
if (is_daemon_) if (is_daemon_)
{ {
string mi_idsc = AddressExpression::concat(mi.address_expressions);
notice("(wmbusmeters) started meter %d (%s %s %s)\n", notice("(wmbusmeters) started meter %d (%s %s %s)\n",
meter->index(), meter->index(),
mi.name.c_str(), mi.name.c_str(),
meter_info.idsc.c_str(), mi_idsc.c_str(),
mi.driverName().str().c_str()); mi.driverName().str().c_str());
} }
else else
{ {
string mi_idsc = AddressExpression::concat(mi.address_expressions);
verbose("(meter) started meter %d (%s %s %s)\n", verbose("(meter) started meter %d (%s %s %s)\n",
meter->index(), meter->index(),
mi.name.c_str(), mi.name.c_str(),
meter_info.idsc.c_str(), mi_idsc.c_str(),
mi.driverName().str().c_str()); mi.driverName().str().c_str());
} }
bool match = false; bool match = false;
bool h = meter->handleTelegram(about, input_frame, simulated, &ids, &match); bool h = meter->handleTelegram(about, input_frame, simulated, &addresses, &match);
if (!match) if (!match)
{ {
string aesc = AddressExpression::concat(meter->addressExpressions());
// Oups, we added a new meter object tailored for this telegram // Oups, we added a new meter object tailored for this telegram
// but it still did not match! This is probably an error in wmbusmeters! // 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! ", warning("(meter) newly created meter (%s %s %s) did not match telegram! ",
"Please open an issue at https://github.com/wmbusmeters/wmbusmeters/\n", "Please open an issue at https://github.com/wmbusmeters/wmbusmeters/\n",
meter->name().c_str(), meter->idsc().c_str(), meter->driverName().str().c_str()); meter->name().c_str(), aesc.c_str(), meter->driverName().str().c_str());
} }
else if (!h) else if (!h)
{ {
string aesc = AddressExpression::concat(meter->addressExpressions());
// Oups, we added a new meter object tailored for this telegram // Oups, we added a new meter object tailored for this telegram
// but it still did not handle it! This can happen if the wrong // but it still did not handle it! This can happen if the wrong
// decryption key was used. // decryption key was used.
warning("(meter) newly created meter (%s %s %s) did not handle telegram!\n", warning("(meter) newly created meter (%s %s %s) did not handle telegram!\n",
meter->name().c_str(), meter->idsc().c_str(), meter->driverName().str().c_str()); meter->name().c_str(), aesc.c_str(), meter->driverName().str().c_str());
} }
else else
{ {
@ -259,7 +271,7 @@ public:
} }
if (isVerboseEnabled() && !handled) if (isVerboseEnabled() && !handled)
{ {
verbose("(wmbus) telegram from %s ignored by all configured meters!\n", ids.c_str()); verbose("(wmbus) telegram from %s ignored by all configured meters!\n", "TODO");
} }
return handled; return handled;
} }
@ -344,8 +356,8 @@ public:
auto meter = createMeter(&mi); auto meter = createMeter(&mi);
bool match = false; bool match = false;
string id; vector<Address> addresses;
bool h = meter->handleTelegram(about, input_frame, simulated, &id, &match, &t); bool h = meter->handleTelegram(about, input_frame, simulated, &addresses, &match, &t);
if (!match) if (!match)
{ {
@ -353,11 +365,12 @@ public:
} }
else if (!h) else if (!h)
{ {
string aesc = AddressExpression::concat(meter->addressExpressions());
// Oups, we added a new meter object tailored for this telegram // Oups, we added a new meter object tailored for this telegram
// but it still did not handle it! This can happen if the wrong // but it still did not handle it! This can happen if the wrong
// decryption key was used. But it is ok if analyzing.... // decryption key was used. But it is ok if analyzing....
debug("Newly created meter (%s %s %s) did not handle telegram!\n", debug("Newly created meter (%s %s %s) did not handle telegram!\n",
meter->name().c_str(), meter->idsc().c_str(), meter->driverName().str().c_str()); meter->name().c_str(), aesc.c_str(), meter->driverName().str().c_str());
} }
else else
{ {
@ -406,9 +419,8 @@ public:
// Overwrite the id with the id from the telegram to be analyzed. // Overwrite the id with the id from the telegram to be analyzed.
MeterInfo mi; MeterInfo mi;
mi.key = analyze_key_; mi.key = analyze_key_;
mi.ids.clear(); mi.address_expressions.clear();
mi.ids.push_back(t.ids.back()); mi.address_expressions.push_back(AddressExpression(t.addresses.back()));
mi.idsc = t.ids.back();
// This will be the driver that will actually decode and print with. // This will be the driver that will actually decode and print with.
string using_driver; string using_driver;
@ -459,7 +471,6 @@ public:
assert(meter != NULL); assert(meter != NULL);
bool match = false; bool match = false;
string id;
if (should_profile_ > 0) if (should_profile_ > 0)
{ {
@ -473,7 +484,8 @@ public:
for (int k=0; k<should_profile_; ++k) for (int k=0; k<should_profile_; ++k)
{ {
meter->handleTelegram(about, input_frame, simulated, &id, &match, &t); vector<Address> addresses;
meter->handleTelegram(about, input_frame, simulated, &addresses, &match, &t);
string hr, fields, json; string hr, fields, json;
vector<string> envs, more_json, selected_fields; vector<string> envs, more_json, selected_fields;
@ -500,7 +512,8 @@ public:
return; return;
} }
meter->handleTelegram(about, input_frame, simulated, &id, &match, &t); vector<Address> addresses;
meter->handleTelegram(about, input_frame, simulated, &addresses, &match, &t);
int u = 0; int u = 0;
int l = 0; int l = 0;

Wyświetl plik

@ -327,8 +327,7 @@ MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi,
has_process_content_(di.hasProcessContent()), has_process_content_(di.hasProcessContent()),
waiting_for_poll_response_sem_("waiting_for_poll_response") waiting_for_poll_response_sem_("waiting_for_poll_response")
{ {
ids_ = mi.ids; address_expressions_ = mi.address_expressions;
idsc_ = toIdsCommaSeparated(ids_);
link_modes_ = mi.link_modes; link_modes_ = mi.link_modes;
poll_interval_= mi.poll_interval; poll_interval_= mi.poll_interval;
@ -673,25 +672,25 @@ void MeterCommonImplementation::poll(shared_ptr<BusManager> bus_manager)
if (!bus_device) if (!bus_device)
{ {
warning("(meter) warning! no bus specified for meter %s %s\n", name().c_str(), idsc().c_str()); string aesc = AddressExpression::concat(addressExpressions());
warning("(meter) warning! no bus specified for meter %s %s\n", name().c_str(), aesc.c_str());
return; return;
} }
string id = ids().back(); AddressExpression &ae = addressExpressions().back();
if (id.length() != 2 && id.length() != 3 && id.length() != 8) if (ae.has_wildcard)
{ {
debug("(meter) not polling from bad id \"%s\" with wrong length\n", id.c_str()); debug("(meter) not polling from id \"%s\" since poll id must not have a wildcard\n", ae.id.c_str());
return; return;
} }
if (id.length() == 2 || id.length() == 3) if (ae.mbus_primary)
{ {
vector<uchar> idhex; int idnum = atoi(ae.id.c_str()+1);
int idnum = atoi(id.c_str());
if (idnum < 0 || idnum > 250 || idhex.size() != 1) if (idnum < 0 || idnum > 250)
{ {
debug("(meter) not polling from bad id \"%s\"\n", id.c_str()); debug("(meter) not polling from bad id \"%s\"\n", ae.id.c_str());
return; return;
} }
@ -699,7 +698,7 @@ void MeterCommonImplementation::poll(shared_ptr<BusManager> bus_manager)
buf.resize(5); buf.resize(5);
buf[0] = 0x10; // Start buf[0] = 0x10; // Start
buf[1] = 0x5b; // REQ_UD2 buf[1] = 0x5b; // REQ_UD2
buf[2] = idhex[0]; buf[2] = idnum & 0xff;
uchar cs = 0; uchar cs = 0;
for (int i=1; i<3; ++i) cs += buf[i]; for (int i=1; i<3; ++i) cs += buf[i];
buf[3] = cs; // checksum buf[3] = cs; // checksum
@ -707,21 +706,21 @@ void MeterCommonImplementation::poll(shared_ptr<BusManager> bus_manager)
verbose("(meter) polling %s %s (primary) with req ud2 on bus %s\n", verbose("(meter) polling %s %s (primary) with req ud2 on bus %s\n",
name().c_str(), name().c_str(),
id.c_str(), ae.id.c_str(),
bus_device->busAlias().c_str(),id.c_str()); bus_device->busAlias().c_str(),ae.id.c_str());
bus_device->serial()->send(buf); bus_device->serial()->send(buf);
} }
if (id.length() == 8) if (!ae.mbus_primary)
{ {
// A full secondary address 12345678 was specified. // A full secondary address 12345678 was specified.
vector<uchar> idhex; vector<uchar> idhex;
bool ok = hex2bin(id, &idhex); bool ok = hex2bin(ae.id, &idhex);
if (!ok || idhex.size() != 4) if (!ok || idhex.size() != 4)
{ {
debug("(meter) not polling from bad id \"%s\"\n", id.c_str()); debug("(meter) not polling from bad id \"%s\"\n", ae.id.c_str());
return; return;
} }
@ -739,19 +738,19 @@ void MeterCommonImplementation::poll(shared_ptr<BusManager> bus_manager)
buf[8] = idhex[2]; // id 56 buf[8] = idhex[2]; // id 56
buf[9] = idhex[1]; // id 34 buf[9] = idhex[1]; // id 34
buf[10] = idhex[0]; // id 12 buf[10] = idhex[0]; // id 12
// Use wildcards instead of exact matching here. buf[11] = (ae.mfct >> 8) & 0xff; // use 0xff as a wildcard
// TODO add selection based on these values as well. buf[12] = ae.mfct & 0xff; // mfct
buf[11] = 0xff; // mfct buf[13] = ae.version; // version/generation
buf[12] = 0xff; // mfct buf[14] = ae.type; // type/media/device
buf[13] = 0xff; // version/generation
buf[14] = 0xff; // type/media/device
uchar cs = 0; uchar cs = 0;
for (int i=4; i<15; ++i) cs += buf[i]; for (int i=4; i<15; ++i) cs += buf[i];
buf[15] = cs; // checksum buf[15] = cs; // checksum
buf[16] = 0x16; // Stop buf[16] = 0x16; // Stop
debug("(meter) secondary addressing bus %s to address %s\n", bus_device->busAlias().c_str(), id.c_str()); debug("(meter) secondary addressing bus %s to address %s\n",
bus_device->busAlias().c_str(),
ae.id.c_str());
bus_device->serial()->send(buf); bus_device->serial()->send(buf);
usleep(1000*500); usleep(1000*500);
@ -767,26 +766,21 @@ void MeterCommonImplementation::poll(shared_ptr<BusManager> bus_manager)
verbose("(meter) polling %s %s (secondary) with req ud2 bus %s\n", verbose("(meter) polling %s %s (secondary) with req ud2 bus %s\n",
name().c_str(), name().c_str(),
id.c_str(), ae.id.c_str(),
bus_device->busAlias().c_str()); bus_device->busAlias().c_str());
bus_device->serial()->send(buf); bus_device->serial()->send(buf);
} }
bool ok = waiting_for_poll_response_sem_.wait(); bool ok = waiting_for_poll_response_sem_.wait();
if (!ok) if (!ok)
{ {
warning("(meter) %s %s did not send a response!\n", name().c_str(), idsc().c_str()); warning("(meter) %s %s did not send a response!\n", name().c_str(), ae.id.c_str());
} }
} }
} }
vector<string>& MeterCommonImplementation::ids() vector<AddressExpression>& MeterCommonImplementation::addressExpressions()
{ {
return ids_; return address_expressions_;
}
string MeterCommonImplementation::idsc()
{
return idsc_;
} }
vector<FieldInfo> &MeterCommonImplementation::fieldInfos() vector<FieldInfo> &MeterCommonImplementation::fieldInfos()
@ -852,7 +846,10 @@ void MeterCommonImplementation::setPollInterval(time_t interval)
poll_interval_ = interval; poll_interval_ = interval;
if (usesPolling() && poll_interval_ == 0) if (usesPolling() && poll_interval_ == 0)
{ {
warning("(meter) %s %s needs polling but has no pollinterval set!\n", name().c_str(), idsc().c_str()); string aesc = AddressExpression::concat(addressExpressions());
warning("(meter) %s %s needs polling but has no pollinterval set!\n",
name().c_str(),
aesc.c_str());
} }
} }
@ -906,8 +903,7 @@ string toString(DriverInfo &di)
bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, MeterInfo *mi) bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, MeterInfo *mi)
{ {
string name; string name;
vector<string> ids; vector<AddressExpression> address_expressions;
string idsc;
string driver_name; string driver_name;
assert((meter && !mi) || assert((meter && !mi) ||
@ -916,22 +912,27 @@ bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, Me
if (meter) if (meter)
{ {
name = meter->name(); name = meter->name();
ids = meter->ids(); address_expressions = meter->addressExpressions();
idsc = meter->idsc();
driver_name = meter->driverName().str(); driver_name = meter->driverName().str();
} }
else else
{ {
name = mi->name; name = mi->name;
ids = mi->ids; address_expressions = mi->address_expressions;
idsc = mi->idsc;
driver_name = mi->driver_name.str(); driver_name = mi->driver_name.str();
} }
debug("(meter) %s: for me? %s in %s\n", name.c_str(), t->idsc.c_str(), idsc.c_str()); if (isDebugEnabled())
{
// Telegram addresses
string t_idsc = Address::concat(t->addresses);
// Meter/MeterInfo address expressions
string m_idsc = AddressExpression::concat(address_expressions);
debug("(meter) %s: for me? %s in %s\n", name.c_str(), t_idsc.c_str(), m_idsc.c_str());
}
bool used_wildcard = false; bool used_wildcard = false;
bool id_match = doesIdsMatchExpressions(t->ids, ids, &used_wildcard); bool id_match = doesTelegramMatchExpressions(t->addresses, address_expressions, &used_wildcard);
if (!id_match) { if (!id_match) {
// The id must match. // The id must match.
@ -1050,7 +1051,7 @@ bool checkCommonField(string *buf, string desired_field, Meter *m, Telegram *t,
} }
if (desired_field == "id") if (desired_field == "id")
{ {
*buf += t->ids.back() + c; *buf += t->addresses.back().id + c;
return true; return true;
} }
if (desired_field == "timestamp") if (desired_field == "timestamp")
@ -1194,7 +1195,8 @@ string concatFields(Meter *m, Telegram *t, char c, vector<FieldInfo> &prints, bo
} }
bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<uchar> input_frame, bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<uchar> input_frame,
bool simulated, string *ids, bool *id_match, Telegram *out_analyzed) bool simulated, vector<Address> *addresses,
bool *id_match, Telegram *out_analyzed)
{ {
Telegram t; Telegram t;
t.about = about; t.about = about;
@ -1203,7 +1205,7 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<ucha
if (simulated) t.markAsSimulated(); if (simulated) t.markAsSimulated();
if (out_analyzed != NULL) t.markAsBeingAnalyzed(); if (out_analyzed != NULL) t.markAsBeingAnalyzed();
*ids = t.idsc; *addresses = t.addresses;
if (!ok || !isTelegramForMeter(&t, this, NULL)) if (!ok || !isTelegramForMeter(&t, this, NULL))
{ {
@ -1212,12 +1214,19 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<ucha
} }
*id_match = true; *id_match = true;
verbose("(meter) %s(%d) %s handling telegram from %s\n", name().c_str(), index(), driverName().str().c_str(), t.ids.back().c_str()); if (isVerboseEnabled())
{
verbose("(meter) %s(%d) %s handling telegram from %s\n",
name().c_str(),
index(),
driverName().str().c_str(),
t.addresses.back().str().c_str());
}
if (isDebugEnabled()) if (isDebugEnabled())
{ {
string msg = bin2hex(input_frame); string msg = bin2hex(input_frame);
debug("(meter) %s %s \"%s\"\n", name().c_str(), t.ids.back().c_str(), msg.c_str()); debug("(meter) %s %s \"%s\"\n", name().c_str(), t.addresses.back().str().c_str(), msg.c_str());
} }
// For older meters with manufacturer specific data without a nice 0f dif marker. // For older meters with manufacturer specific data without a nice 0f dif marker.
@ -1744,11 +1753,11 @@ string FieldInfo::renderJson(Meter *m, DVEntry *dve)
return s; return s;
} }
void MeterCommonImplementation::createMeterEnv( string *id, void MeterCommonImplementation::createMeterEnv(string id,
vector<string> *envs, vector<string> *envs,
vector<string> *extra_constant_fields) vector<string> *extra_constant_fields)
{ {
envs->push_back(string("METER_ID="+*id)); envs->push_back(string("METER_ID="+id));
envs->push_back(string("METER_NAME=")+name()); envs->push_back(string("METER_NAME=")+name());
envs->push_back(string("METER_TYPE=")+driverName().str()); envs->push_back(string("METER_TYPE=")+driverName().str());
@ -1791,9 +1800,9 @@ void MeterCommonImplementation::printMeter(Telegram *t,
} }
string id = ""; string id = "";
if (t->ids.size() > 0) if (t->addresses.size() > 0)
{ {
id = t->ids.back(); id = t->addresses.back().id;
} }
string indent = ""; string indent = "";
@ -1941,7 +1950,7 @@ void MeterCommonImplementation::printMeter(Telegram *t,
s += "}"; s += "}";
*json = s; *json = s;
createMeterEnv(&id, envs, extra_constant_fields); createMeterEnv(id, envs, extra_constant_fields);
envs->push_back(string("METER_JSON=")+*json); envs->push_back(string("METER_JSON=")+*json);
envs->push_back(string("METER_MEDIA=")+media); envs->push_back(string("METER_MEDIA=")+media);
@ -2093,11 +2102,15 @@ shared_ptr<Meter> createMeter(MeterInfo *mi)
{ {
newm->setSelectedFields(di->defaultFields()); newm->setSelectedFields(di->defaultFields());
} }
verbose("(meter) created %s %s %s %s\n", if (isVerboseEnabled())
mi->name.c_str(), {
di->name().str().c_str(), string aesc = AddressExpression::concat(mi->address_expressions);
mi->idsc.c_str(), verbose("(meter) created %s %s %s %s\n",
keymsg); mi->name.c_str(),
di->name().str().c_str(),
aesc.c_str(),
keymsg);
}
return newm; return newm;
} }
@ -2165,12 +2178,12 @@ string MeterInfo::str()
return r; return r;
} }
bool MeterInfo::parse(string n, string d, string i, string k) bool MeterInfo::parse(string n, string d, string aes, string k)
{ {
clear(); clear();
name = n; name = n;
ids = splitMatchExpressions(i); address_expressions = splitAddressExpressions(aes);
key = k; key = k;
bool driverextras_checked = false; bool driverextras_checked = false;
bool bus_checked = false; bool bus_checked = false;

Wyświetl plik

@ -90,8 +90,7 @@ struct MeterInfo
string name; // User specified name of this (group of) meters. string name; // User specified name of this (group of) meters.
DriverName driver_name; // Will replace MeterDriver. DriverName driver_name; // Will replace MeterDriver.
string extras; // Extra driver specific settings. string extras; // Extra driver specific settings.
vector<string> ids; // Match expressions for ids. vector<AddressExpression> address_expressions; // Match expressions for ids.
string idsc; // Comma separated ids.
string key; // Decryption key. string key; // Decryption key.
LinkModeSet link_modes; LinkModeSet link_modes;
int bps {}; // For mbus communication you need to know the baud rate. int bps {}; // For mbus communication you need to know the baud rate.
@ -111,13 +110,12 @@ struct MeterInfo
string str(); string str();
DriverName driverName(); DriverName driverName();
MeterInfo(string b, string n, string e, vector<string> i, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &ms, vector<string> &j, vector<string> &calcfs) MeterInfo(string b, string n, string e, vector<AddressExpression> aes, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &ms, vector<string> &j, vector<string> &calcfs)
{ {
bus = b; bus = b;
name = n; name = n;
extras = e, extras = e,
ids = i; address_expressions = aes;
idsc = toIdsCommaSeparated(ids);
key = k; key = k;
shells = s; shells = s;
meter_shells = ms; meter_shells = ms;
@ -131,8 +129,7 @@ struct MeterInfo
{ {
bus = ""; bus = "";
name = ""; name = "";
ids.clear(); address_expressions.clear();
idsc = "";
key = ""; key = "";
shells.clear(); shells.clear();
meter_shells.clear(); meter_shells.clear();
@ -374,10 +371,8 @@ struct Meter
virtual void setIndex(int i) = 0; virtual void setIndex(int i) = 0;
// Use this bus to send messages to the meter. // Use this bus to send messages to the meter.
virtual string bus() = 0; virtual string bus() = 0;
// This meter listens to these ids. // This meter listens to these address expressions.
virtual vector<string> &ids() = 0; virtual std::vector<AddressExpression>& addressExpressions() = 0;
// Comma separated ids.
virtual string idsc() = 0;
// This meter can report these fields, like total_m3, temp_c. // This meter can report these fields, like total_m3, temp_c.
virtual vector<FieldInfo> &fieldInfos() = 0; virtual vector<FieldInfo> &fieldInfos() = 0;
virtual vector<string> &extraConstantFields() = 0; virtual vector<string> &extraConstantFields() = 0;
@ -407,7 +402,7 @@ struct Meter
virtual void onUpdate(std::function<void(Telegram*t,Meter*)> cb) = 0; virtual void onUpdate(std::function<void(Telegram*t,Meter*)> cb) = 0;
virtual int numUpdates() = 0; virtual int numUpdates() = 0;
virtual void createMeterEnv(string *id, virtual void createMeterEnv(string id,
vector<string> *envs, vector<string> *envs,
vector<string> *more_json) = 0; vector<string> *more_json) = 0;
virtual void printMeter(Telegram *t, virtual void printMeter(Telegram *t,
@ -423,7 +418,8 @@ struct Meter
// Returns true of this meter handled this telegram! // Returns true of this meter handled this telegram!
// Sets id_match to true, if there was an id match, even though the telegram could not be properly handled. // 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, virtual bool handleTelegram(AboutTelegram &about, vector<uchar> input_frame,
bool simulated, string *id, bool *id_match, Telegram *out_t = NULL) = 0; bool simulated, std::vector<Address> *addresses,
bool *id_match, Telegram *out_t = NULL) = 0;
virtual MeterKeys *meterKeys() = 0; virtual MeterKeys *meterKeys() = 0;
virtual void addExtraCalculatedField(std::string ecf) = 0; virtual void addExtraCalculatedField(std::string ecf) = 0;

Wyświetl plik

@ -59,8 +59,7 @@ struct MeterCommonImplementation : public virtual Meter
int index(); int index();
void setIndex(int i); void setIndex(int i);
string bus(); string bus();
vector<string>& ids(); vector<AddressExpression>& addressExpressions();
string idsc();
vector<FieldInfo> &fieldInfos(); vector<FieldInfo> &fieldInfos();
vector<string> &extraConstantFields(); vector<string> &extraConstantFields();
string name(); string name();
@ -163,8 +162,9 @@ protected:
// Override for mbus meters that need to be queried and likewise for C2/T2 wmbus-meters. // Override for mbus meters that need to be queried and likewise for C2/T2 wmbus-meters.
void poll(shared_ptr<BusManager> bus); void poll(shared_ptr<BusManager> bus);
bool handleTelegram(AboutTelegram &about, vector<uchar> frame, bool handleTelegram(AboutTelegram &about, vector<uchar> frame,
bool simulated, string *id, bool *id_match, Telegram *out_analyzed = NULL); bool simulated, std::vector<Address> *addresses,
void createMeterEnv(string *id, bool *id_match, Telegram *out_analyzed = NULL);
void createMeterEnv(string id,
vector<string> *envs, vector<string> *envs,
vector<string> *more_json); // Add this json "key"="value" strings. vector<string> *more_json); // Add this json "key"="value" strings.
void printMeter(Telegram *t, void printMeter(Telegram *t,
@ -221,8 +221,7 @@ private:
ELLSecurityMode expected_ell_sec_mode_ {}; ELLSecurityMode expected_ell_sec_mode_ {};
TPLSecurityMode expected_tpl_sec_mode_ {}; TPLSecurityMode expected_tpl_sec_mode_ {};
string name_; string name_;
vector<string> ids_; vector<AddressExpression> address_expressions_;
string idsc_;
vector<function<void(Telegram*,Meter*)>> on_update_; vector<function<void(Telegram*,Meter*)>> on_update_;
int num_updates_ {}; int num_updates_ {};
time_t datetime_of_update_ {}; time_t datetime_of_update_ {};

Wyświetl plik

@ -92,10 +92,10 @@ void Printer::printFiles(Meter *meter, Telegram *t, string &human_readable, stri
snprintf(filename, 127, "%s/%s", meterfiles_dir_.c_str(), meter->name().c_str()); snprintf(filename, 127, "%s/%s", meterfiles_dir_.c_str(), meter->name().c_str());
break; break;
case MeterFileNaming::Id: case MeterFileNaming::Id:
snprintf(filename, 127, "%s/%s", meterfiles_dir_.c_str(), t->ids.back().c_str()); snprintf(filename, 127, "%s/%s", meterfiles_dir_.c_str(), t->addresses.back().id.c_str());
break; break;
case MeterFileNaming::NameId: case MeterFileNaming::NameId:
snprintf(filename, 127, "%s/%s-%s", meterfiles_dir_.c_str(), meter->name().c_str(), t->ids.back().c_str()); snprintf(filename, 127, "%s/%s-%s", meterfiles_dir_.c_str(), meter->name().c_str(), t->addresses.back().id.c_str());
break; break;
} }
string stamp; string stamp;

Wyświetl plik

@ -39,7 +39,6 @@ bool verbose_ = false;
#define LIST_OF_TESTS \ #define LIST_OF_TESTS \
X(addresses) \ X(addresses) \
/*
X(dynamic_loading) \ X(dynamic_loading) \
X(crc) \ X(crc) \
X(dvparser) \ X(dvparser) \
@ -77,8 +76,6 @@ bool verbose_ = false;
X(formulas_dventries) \ X(formulas_dventries) \
X(formulas_stringinterpolation) \ X(formulas_stringinterpolation) \
*/
#define X(t) void test_##t(); #define X(t) void test_##t();
LIST_OF_TESTS LIST_OF_TESTS
#undef X #undef X
@ -427,7 +424,7 @@ void test_linkmodes()
void test_valid_match_expression(string s, bool expected) void test_valid_match_expression(string s, bool expected)
{ {
bool b = isValidMatchExpressions(s); bool b = isValidSequenceOfAddressExpressions(s);
if (b == expected) return; if (b == expected) return;
if (expected == true) if (expected == true)
{ {
@ -441,9 +438,13 @@ void test_valid_match_expression(string s, bool expected)
void test_does_id_match_expression(string id, string mes, bool expected, bool expected_uw) void test_does_id_match_expression(string id, string mes, bool expected, bool expected_uw)
{ {
vector<string> expressions = splitMatchExpressions(mes); Address a;
a.id = id;
vector<Address> as;
as.push_back(a);
vector<AddressExpression> expressions = splitAddressExpressions(mes);
bool uw = false; bool uw = false;
bool b = doesIdMatchExpressions(id, expressions, &uw); bool b = doesTelegramMatchExpressions(as, expressions, &uw);
if (b != expected) if (b != expected)
{ {
if (expected == true) if (expected == true)
@ -2254,9 +2255,9 @@ void test_formulas_building_meters()
MeterKeys mk; MeterKeys mk;
t.parse(frame, &mk, true); t.parse(frame, &mk, true);
string id; vector<Address> addresses;
bool match; bool match;
meter->handleTelegram(t.about, frame, true, &id, &match, &t); meter->handleTelegram(t.about, frame, true, &addresses, &match, &t);
f->clear(); f->clear();
f->setMeter(meter.get()); f->setMeter(meter.get());
@ -2305,7 +2306,7 @@ void test_formulas_building_meters()
MeterKeys mk; MeterKeys mk;
t.parse(frame, &mk, true); t.parse(frame, &mk, true);
string id; vector<Address> id;
bool match; bool match;
meter->handleTelegram(t.about, frame, true, &id, &match, &t); meter->handleTelegram(t.about, frame, true, &id, &match, &t);
@ -2489,7 +2490,7 @@ void test_formulas_parsing_1()
MeterKeys mk; MeterKeys mk;
t.parse(frame, &mk, true); t.parse(frame, &mk, true);
string id; vector<Address> id;
bool match; bool match;
meter->handleTelegram(t.about, frame, true, &id, &match, &t); meter->handleTelegram(t.about, frame, true, &id, &match, &t);
@ -2539,7 +2540,7 @@ void test_formulas_parsing_2()
MeterKeys mk; MeterKeys mk;
t.parse(frame, &mk, true); t.parse(frame, &mk, true);
string id; vector<Address> id;
bool match; bool match;
meter->handleTelegram(t.about, frame, true, &id, &match, &t); meter->handleTelegram(t.about, frame, true, &id, &match, &t);

Wyświetl plik

@ -676,24 +676,6 @@ bool isNumber(const string& fq)
return true; return true;
} }
vector<string> splitMatchExpressions(const string& mes)
{
vector<string> r;
bool eof, err;
vector<uchar> v (mes.begin(), mes.end());
auto i = v.begin();
for (;;) {
auto id = eatTo(v, i, ',', 16, &eof, &err);
if (err) break;
trimWhitespace(&id);
if (id == "ANYID") id = "*";
r.push_back(id);
if (eof) break;
}
return r;
}
void incrementIV(uchar *iv, size_t len) { void incrementIV(uchar *iv, size_t len) {
uchar *p = iv+len-1; uchar *p = iv+len-1;
while (p >= iv) { while (p >= iv) {

Wyświetl plik

@ -229,17 +229,18 @@ LIST_OF_MANUFACTURERS
} }
void Telegram::addId(const vector<uchar>::iterator &pos) void Telegram::addAddressMfctFirst(const vector<uchar>::iterator &pos)
{ {
// string id = tostrprintf("%02x%02x%02x%02x_%02x_%02x", *(pos+3), *(pos+2), *(pos+1), *(pos+0), *(pos+4), *(pos+5)); Address a;
string id = tostrprintf("%02x%02x%02x%02x", *(pos+3), *(pos+2), *(pos+1), *(pos+0)); a.decodeMfctFirst(pos);
ids.push_back(id); addresses.push_back(a);
if (idsc.empty()) { }
idsc = id;
} void Telegram::addAddressIdFirst(const vector<uchar>::iterator &pos)
else { {
idsc += "," + id; Address a;
} a.decodeIdFirst(pos);
addresses.push_back(a);
} }
void Telegram::print() void Telegram::print()
@ -453,18 +454,6 @@ string manufacturer(int m_field) {
return "Unknown"; return "Unknown";
} }
string manufacturerFlag(int m_field) {
char a = (m_field/1024)%32+64;
char b = (m_field/32)%32+64;
char c = (m_field)%32+64;
string flag;
flag += a;
flag += b;
flag += c;
return flag;
}
string mediaType(int a_field_device_type, int m_field) { string mediaType(int a_field_device_type, int m_field) {
switch (a_field_device_type) { switch (a_field_device_type) {
case 0: return "Other"; case 0: return "Other";
@ -915,9 +904,10 @@ bool Telegram::parseMBusDLLandTPL(vector<uchar>::iterator &pos)
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-a primary (%d)", mbus_primary_address, mbus_primary_address); addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-a primary (%d)", mbus_primary_address, mbus_primary_address);
// Add dll_id to ids. // Add dll_id to ids.
string id = tostrprintf("%02x", mbus_primary_address); string id = tostrprintf("p%d", mbus_primary_address);
ids.push_back(id); Address a;
idsc = id; a.id = id;
addresses.push_back(a);
mbus_ci = *pos; mbus_ci = *pos;
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x tpl-ci (%s)", mbus_ci, mbusCiField(mbus_ci)); addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x tpl-ci (%s)", mbus_ci, mbusCiField(mbus_ci));
@ -943,6 +933,9 @@ bool Telegram::parseDLL(vector<uchar>::iterator &pos)
dll_c = *pos; dll_c = *pos;
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-c (%s)", dll_c, cType(dll_c).c_str()); addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-c (%s)", dll_c, cType(dll_c).c_str());
CHECK(8)
addAddressMfctFirst(pos);
dll_mfct_b[0] = *(pos+0); dll_mfct_b[0] = *(pos+0);
dll_mfct_b[1] = *(pos+1); dll_mfct_b[1] = *(pos+1);
dll_mfct = dll_mfct_b[1] <<8 | dll_mfct_b[0]; dll_mfct = dll_mfct_b[1] <<8 | dll_mfct_b[0];
@ -962,9 +955,8 @@ bool Telegram::parseDLL(vector<uchar>::iterator &pos)
} }
} }
// Add dll_id to ids. // Add dll_id to ids.
addId(pos);
addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x%02x%02x dll-id (%s)", addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x%02x%02x dll-id (%s)",
*(pos+0), *(pos+1), *(pos+2), *(pos+3), ids.back().c_str()); *(pos+0), *(pos+1), *(pos+2), *(pos+3), addresses.back().id.c_str());
dll_version = *(pos+0); dll_version = *(pos+0);
dll_type = *(pos+1); dll_type = *(pos+1);
@ -1038,6 +1030,9 @@ bool Telegram::parseELL(vector<uchar>::iterator &pos)
if (has_target_mft_address) if (has_target_mft_address)
{ {
CHECK(8);
addAddressMfctFirst(pos);
ell_mfct_b[0] = *(pos+0); ell_mfct_b[0] = *(pos+0);
ell_mfct_b[1] = *(pos+1); ell_mfct_b[1] = *(pos+1);
ell_mfct = ell_mfct_b[1] << 8 | ell_mfct_b[0]; ell_mfct = ell_mfct_b[1] << 8 | ell_mfct_b[0];
@ -1052,7 +1047,6 @@ bool Telegram::parseELL(vector<uchar>::iterator &pos)
ell_id_b[3] = *(pos+3); ell_id_b[3] = *(pos+3);
// Add ell_id to ids. // Add ell_id to ids.
addId(pos);
addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x%02x%02x ell-id", addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x%02x%02x ell-id",
ell_id_b[0], ell_id_b[1], ell_id_b[2], ell_id_b[3]); ell_id_b[0], ell_id_b[1], ell_id_b[2], ell_id_b[3]);
@ -1448,7 +1442,9 @@ bool Telegram::parseShortTPL(std::vector<uchar>::iterator &pos)
bool Telegram::parseLongTPL(std::vector<uchar>::iterator &pos) bool Telegram::parseLongTPL(std::vector<uchar>::iterator &pos)
{ {
CHECK(4); CHECK(8);
addAddressIdFirst(pos);
tpl_id_found = true; tpl_id_found = true;
tpl_id_b[0] = *(pos+0); tpl_id_b[0] = *(pos+0);
tpl_id_b[1] = *(pos+1); tpl_id_b[1] = *(pos+1);
@ -1461,9 +1457,6 @@ bool Telegram::parseLongTPL(std::vector<uchar>::iterator &pos)
tpl_a[i] = *(pos+i); tpl_a[i] = *(pos+i);
} }
// Add the tpl_id to ids.
addId(pos);
addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL, addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL,
"%02x%02x%02x%02x tpl-id (%02x%02x%02x%02x)", "%02x%02x%02x%02x tpl-id (%02x%02x%02x%02x)",
tpl_id_b[0], tpl_id_b[1], tpl_id_b[2], tpl_id_b[3], tpl_id_b[0], tpl_id_b[1], tpl_id_b[2], tpl_id_b[3],

Wyświetl plik

@ -18,6 +18,7 @@
#ifndef WMBUS_H #ifndef WMBUS_H
#define WMBUS_H #define WMBUS_H
#include"address.h"
#include"dvparser.h" #include"dvparser.h"
#include"manufacturers.h" #include"manufacturers.h"
#include"serial.h" #include"serial.h"
@ -416,10 +417,9 @@ public:
// If a warning is printed mark this. // If a warning is printed mark this.
bool triggered_warning {}; bool triggered_warning {};
// The different ids found, the first is th dll_id, ell_id, nwl_id, and the last is the tpl_id. // The different addresses found,
vector<string> ids; // the first is the dll_id_mvt, ell_id_mvt, nwl_id_mvt, and the last is the tpl_id_mvt.
// Ids separated by commas vector<Address> addresses;
string idsc;
// If decryption failed, set this to true, to prevent further processing. // If decryption failed, set this to true, to prevent further processing.
bool decryption_failed {}; bool decryption_failed {};
@ -536,7 +536,8 @@ public:
bool parseHANHeader(vector<uchar> &input_frame); bool parseHANHeader(vector<uchar> &input_frame);
bool parseHAN(vector<uchar> &input_frame, MeterKeys *mk, bool warn); bool parseHAN(vector<uchar> &input_frame, MeterKeys *mk, bool warn);
void addId(const vector<uchar>::iterator &pos); void addAddressMfctFirst(const vector<uchar>::iterator &pos);
void addAddressIdFirst(const vector<uchar>::iterator &pos);
void print(); void print();
@ -741,7 +742,6 @@ shared_ptr<BusDevice> openSimulator(Detected detected,
shared_ptr<SerialDevice> serial_override); shared_ptr<SerialDevice> serial_override);
string manufacturer(int m_field); string manufacturer(int m_field);
string manufacturerFlag(int m_field);
string mediaType(int a_field_device_type, int m_field); string mediaType(int a_field_device_type, int m_field);
string mediaTypeJSON(int a_field_device_type, int m_field); string mediaTypeJSON(int a_field_device_type, int m_field);
bool isCiFieldOfType(int ci_field, CI_TYPE type); bool isCiFieldOfType(int ci_field, CI_TYPE type);

Wyświetl plik

@ -36,11 +36,11 @@ TESTRESULT="ERROR"
cat > $TEST/test_expected.txt <<EOF cat > $TEST/test_expected.txt <<EOF
(wmbus) WARNING!! telegram should have been fully encrypted, but was not! id: 88888888 mfct: (APA) Apator, Poland (0x601) type: Water meter (0x07) ver: 0x05 (wmbus) WARNING!! telegram should have been fully encrypted, but was not! id: 88888888 mfct: (APA) Apator, Poland (0x601) type: Water meter (0x07) ver: 0x05
(meter) newly created meter (ApWater 88888888 apator162) did not handle telegram! (meter) newly created meter (ApWater 88888888.M=APA.V=05.T=07 apator162) did not handle telegram!
(wmbus) WARNING! decrypted payload crc failed check, did you use the correct decryption key? e1d6 payload crc (calculated a528) Permanently ignoring telegrams from id: 76348799 mfct: (KAM) Kamstrup Energi (0x2c2d) type: Cold water meter (0x16) ver: 0x1b (wmbus) WARNING! decrypted payload crc failed check, did you use the correct decryption key? e1d6 payload crc (calculated a528) 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! (meter) newly created meter (Vatten 76348799.M=KAM.V=1b.T=16 multical21) did not handle telegram!
(wmbus) WARNING!! telegram should have been fully encrypted, but was not! id: 77777777 mfct: (SON) Sontex, Switzerland (0x4dee) type: Water meter (0x07) ver: 0x3c (wmbus) WARNING!! telegram should have been fully encrypted, but was not! id: 77777777 mfct: (SON) Sontex, Switzerland (0x4dee) type: Water meter (0x07) ver: 0x3c
(meter) newly created meter (Wasser 77777777 supercom587) did not handle telegram! (meter) newly created meter (Wasser 77777777.M=SON.V=3c.T=07 supercom587) did not handle telegram!
(wmbus) WARNING!!! telegram should have been encrypted, but was not! id: 77777777 mfct: (SON) Sontex, Switzerland (0x4dee) type: Water meter (0x07) ver: 0x3c (wmbus) WARNING!!! telegram should have been encrypted, but was not! id: 77777777 mfct: (SON) Sontex, Switzerland (0x4dee) type: Water meter (0x07) ver: 0x3c
EOF EOF

Wyświetl plik

@ -181,6 +181,14 @@ cat > $TEST/test_expected.txt <<EOF
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
There should be three fields, for example: mvt = AAA,07,05 There should be three fields, for example: mvt = AAA,07,05
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
(driver) error in testoutput/driver.xmq, cannot find any detection triplets: driver/detect/mvt
-------------------------------------------------------------------------------
Remember to add: detect { mvt = AAA,05,07 mvt = AAA,06,07 ... }
The triplets consists of MANUFACTURER,VERSION,TYPE
You can see these values when listening to all meters.
The manufacturer can be given as three uppercase characters A-Z
or as 4 lower case hex digits.
-------------------------------------------------------------------------------
Failed to load driver from file: testoutput/driver.xmq Failed to load driver from file: testoutput/driver.xmq
EOF EOF

Wyświetl plik

@ -13,7 +13,7 @@ $PROG --format=json simulations/simulation_bad_keys.txt room fhkvdataiv 03065716
cat > $TEST/expected_err.txt <<EOF cat > $TEST/expected_err.txt <<EOF
(wmbus) WARNING! no key to decrypt payload! Permanently ignoring telegrams from id: 03065716 mfct: (TCH) Techem Service (0x5068) type: Heat Cost Allocator (0x08) ver: 0x94 (wmbus) WARNING! no key to decrypt payload! Permanently ignoring telegrams from id: 03065716 mfct: (TCH) Techem Service (0x5068) type: Heat Cost Allocator (0x08) ver: 0x94
(meter) newly created meter (room 03065716 fhkvdataiv) did not handle telegram! (meter) newly created meter (room 03065716.M=TCH.V=94.T=08 fhkvdataiv) did not handle telegram!
EOF EOF
diff $TEST/test_stderr.txt $TEST/expected_err.txt diff $TEST/test_stderr.txt $TEST/expected_err.txt
@ -28,7 +28,7 @@ $PROG --format=json simulations/simulation_bad_keys.txt room fhkvdataiv 03065716
cat > $TEST/expected_err.txt <<EOF cat > $TEST/expected_err.txt <<EOF
(wmbus) WARNING!! decrypted content failed check, did you use the correct decryption key? Permanently ignoring telegrams from id: 03065716 mfct: (TCH) Techem Service (0x5068) type: Heat Cost Allocator (0x08) ver: 0x94 (wmbus) WARNING!! decrypted content failed check, did you use the correct decryption key? Permanently ignoring telegrams from id: 03065716 mfct: (TCH) Techem Service (0x5068) type: Heat Cost Allocator (0x08) ver: 0x94
(meter) newly created meter (room 03065716 fhkvdataiv) did not handle telegram! (meter) newly created meter (room 03065716.M=TCH.V=94.T=08 fhkvdataiv) did not handle telegram!
EOF EOF
diff $TEST/test_stderr.txt $TEST/expected_err.txt diff $TEST/test_stderr.txt $TEST/expected_err.txt

Wyświetl plik

@ -28,9 +28,9 @@ fi
cat <<EOF > $TEST/test_expected.txt cat <<EOF > $TEST/test_expected.txt
Started config rtlwmbus on stdin listening on any Started config rtlwmbus on stdin listening on any
(wmbus) WARNING!! 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 (wmbus) WARNING!! 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! (meter) newly created meter (ApWater 88888888.M=APA.V=05.T=07 apator162) did not handle telegram!
(wmbus) WARNING! 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 (wmbus) WARNING! 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! (meter) newly created meter (Vatten 76348799.M=KAM.V=1b.T=16 multical21) did not handle telegram!
EOF EOF
diff $TEST/test_expected.txt $TEST/test_stderr.txt diff $TEST/test_expected.txt $TEST/test_stderr.txt