kopia lustrzana https://github.com/weetmuts/wmbusmeters
New advanced addressing works.
rodzic
78e7c47503
commit
7634b95438
12
CHANGES
12
CHANGES
|
@ -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.
|
||||||
|
|
14
README.md
14
README.md
|
@ -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
|
||||||
|
|
245
src/address.cc
245
src/address.cc
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
135
src/meters.cc
135
src/meters.cc
|
@ -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;
|
||||||
|
|
22
src/meters.h
22
src/meters.h
|
@ -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;
|
||||||
|
|
|
@ -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_ {};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
18
src/util.cc
18
src/util.cc
|
@ -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) {
|
||||||
|
|
57
src/wmbus.cc
57
src/wmbus.cc
|
@ -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],
|
||||||
|
|
12
src/wmbus.h
12
src/wmbus.h
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Ładowanie…
Reference in New Issue