kopia lustrzana https://github.com/weetmuts/wmbusmeters
New source file address.cc for mbus addressing.
rodzic
c21efd1d69
commit
9d27ab3fb3
1
Makefile
1
Makefile
|
@ -153,6 +153,7 @@ $(BUILD)/%.o: src/%.c $(wildcard src/%.h)
|
||||||
$(CXX) -I/usr/include/libxml2 -fpermissive $(CXXFLAGS) $< -MMD -c -o $@
|
$(CXX) -I/usr/include/libxml2 -fpermissive $(CXXFLAGS) $< -MMD -c -o $@
|
||||||
|
|
||||||
PROG_OBJS:=\
|
PROG_OBJS:=\
|
||||||
|
$(BUILD)/address.o \
|
||||||
$(BUILD)/aes.o \
|
$(BUILD)/aes.o \
|
||||||
$(BUILD)/aescmac.o \
|
$(BUILD)/aescmac.o \
|
||||||
$(BUILD)/bus.o \
|
$(BUILD)/bus.o \
|
||||||
|
|
|
@ -0,0 +1,258 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2017-2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include"address.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
bool isValidMatchExpression(const string& s, bool non_compliant)
|
||||||
|
{
|
||||||
|
string me = s;
|
||||||
|
|
||||||
|
// Examples of valid match expressions:
|
||||||
|
// 12345678
|
||||||
|
// *
|
||||||
|
// 123*
|
||||||
|
// !12345677
|
||||||
|
// 2222222*
|
||||||
|
// !22222222
|
||||||
|
|
||||||
|
// A match expression cannot be empty.
|
||||||
|
if (me.length() == 0) return false;
|
||||||
|
|
||||||
|
// An me can be negated with an exclamation mark first.
|
||||||
|
if (me.front() == '!') me.erase(0, 1);
|
||||||
|
|
||||||
|
// A match expression cannot be only a negation mark.
|
||||||
|
if (me.length() == 0) return false;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
if (non_compliant)
|
||||||
|
{
|
||||||
|
// Some non-compliant meters have full hex in the id,
|
||||||
|
// but according to the standard there should only be bcd here...
|
||||||
|
while (me.length() > 0 &&
|
||||||
|
((me.front() >= '0' && me.front() <= '9') ||
|
||||||
|
(me.front() >= 'a' && me.front() <= 'f')))
|
||||||
|
{
|
||||||
|
me.erase(0,1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// But compliant meters use only a bcd subset.
|
||||||
|
while (me.length() > 0 &&
|
||||||
|
(me.front() >= '0' && me.front() <= '9'))
|
||||||
|
{
|
||||||
|
me.erase(0,1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wildcard_used = false;
|
||||||
|
// An expression can end with a *
|
||||||
|
if (me.length() > 0 && me.front() == '*')
|
||||||
|
{
|
||||||
|
me.erase(0,1);
|
||||||
|
wildcard_used = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we should have eaten the whole expression.
|
||||||
|
if (me.length() > 0) return false;
|
||||||
|
|
||||||
|
// Check the length of the matching bcd/hex
|
||||||
|
// If no wildcard is used, then the match expression must be exactly 8 digits.
|
||||||
|
if (!wildcard_used) return count == 8;
|
||||||
|
|
||||||
|
// If wildcard is used, then the match expressions must be 7 or less digits,
|
||||||
|
// even zero is allowed which means a single *, which matches any bcd/hex id.
|
||||||
|
return count <= 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValidMatchExpressions(const string& mes, bool non_compliant)
|
||||||
|
{
|
||||||
|
vector<string> v = splitMatchExpressions(mes);
|
||||||
|
|
||||||
|
for (string me : v)
|
||||||
|
{
|
||||||
|
if (!isValidMatchExpression(me, non_compliant)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValidId(const string& id, bool accept_non_compliant)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (size_t i=0; i<id.length(); ++i)
|
||||||
|
{
|
||||||
|
if (id[i] >= '0' && id[i] <= '9') continue;
|
||||||
|
if (accept_non_compliant)
|
||||||
|
{
|
||||||
|
if (id[i] >= 'a' && id[i] <= 'f') continue;
|
||||||
|
if (id[i] >= 'A' && id[i] <= 'F') continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doesIdMatchExpression(const string& s, string match)
|
||||||
|
{
|
||||||
|
string id = s;
|
||||||
|
if (id.length() == 0) return false;
|
||||||
|
|
||||||
|
// Here we assume that the match expression has been
|
||||||
|
// verified to be valid.
|
||||||
|
bool can_match = true;
|
||||||
|
|
||||||
|
// Now match bcd/hex until end of id, or '*' in match.
|
||||||
|
while (id.length() > 0 && match.length() > 0 && match.front() != '*')
|
||||||
|
{
|
||||||
|
if (id.front() != match.front())
|
||||||
|
{
|
||||||
|
// We hit a difference, it cannot match.
|
||||||
|
can_match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
id.erase(0,1);
|
||||||
|
match.erase(0,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wildcard_used = false;
|
||||||
|
if (match.length() && match.front() == '*')
|
||||||
|
{
|
||||||
|
wildcard_used = true;
|
||||||
|
match.erase(0,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (can_match)
|
||||||
|
{
|
||||||
|
// Ok, now the match expression should be empty.
|
||||||
|
// If wildcard is true, then the id can still have digits,
|
||||||
|
// otherwise it must also be empty.
|
||||||
|
if (wildcard_used)
|
||||||
|
{
|
||||||
|
can_match = match.length() == 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
can_match = match.length() == 0 && id.length() == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return can_match;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasWildCard(const string& mes)
|
||||||
|
{
|
||||||
|
return mes.find('*') != string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doesIdsMatchExpressions(vector<string> &ids, vector<string>& mes, bool *used_wildcard)
|
||||||
|
{
|
||||||
|
bool match = false;
|
||||||
|
for (string &id : ids)
|
||||||
|
{
|
||||||
|
if (doesIdMatchExpressions(id, mes, 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 doesIdMatchExpressions(const string& id, vector<string>& mes, bool *used_wildcard)
|
||||||
|
{
|
||||||
|
bool found_match = false;
|
||||||
|
bool found_negative_match = false;
|
||||||
|
bool exact_match = false;
|
||||||
|
*used_wildcard = 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 (string me : mes)
|
||||||
|
{
|
||||||
|
bool has_wildcard = hasWildCard(me);
|
||||||
|
bool is_negative_rule = (me.length() > 0 && me.front() == '!');
|
||||||
|
if (is_negative_rule)
|
||||||
|
{
|
||||||
|
me.erase(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m = doesIdMatchExpression(id, me);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
string toIdsCommaSeparated(vector<string> &ids)
|
||||||
|
{
|
||||||
|
string cs;
|
||||||
|
for (string& s: ids)
|
||||||
|
{
|
||||||
|
cs += s;
|
||||||
|
cs += ",";
|
||||||
|
}
|
||||||
|
if (cs.length() > 0) cs.pop_back();
|
||||||
|
return cs;
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2017-2022 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ADDRESS_H_
|
||||||
|
#define ADDRESS_H_
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct Address
|
||||||
|
{
|
||||||
|
// Example address: 12345678
|
||||||
|
// Or fully qualified: 12345678.M=PII.T=1b.V=01
|
||||||
|
// which means manufacturer triplet PII, type/media=0x1b, version=0x01
|
||||||
|
std::string id;
|
||||||
|
bool wildcard_used {}; // The id contains a *
|
||||||
|
bool mbus_primary {}; // Signals that the id is 0-250
|
||||||
|
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.
|
||||||
|
bool negate {}; // When used for testing this address was negated. !12345678
|
||||||
|
|
||||||
|
bool parse(std::string &s);
|
||||||
|
bool match(Address *a);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool isValidMatchExpression(const std::string& s, bool non_compliant);
|
||||||
|
bool isValidMatchExpressions(const std::string& s, bool non_compliant);
|
||||||
|
bool doesIdMatchExpression(const std::string& id, std::string match_rule);
|
||||||
|
bool doesIdMatchExpressions(const std::string& id, std::vector<std::string>& match_rules, bool *used_wildcard);
|
||||||
|
bool doesIdsMatchExpressions(std::vector<std::string> &ids, std::vector<std::string>& match_rules, bool *used_wildcard);
|
||||||
|
std::string toIdsCommaSeparated(std::vector<std::string> &ids);
|
||||||
|
|
||||||
|
bool isValidId(const std::string& id, bool accept_non_compliant);
|
||||||
|
|
||||||
|
std::vector<std::string> splitMatchExpressions(const std::string& mes);
|
||||||
|
|
||||||
|
#endif
|
|
@ -2593,17 +2593,19 @@ bool Address::parse(string &s)
|
||||||
// Example: 12345678
|
// Example: 12345678
|
||||||
// or 12345678.M=PII.T=1B.V=01
|
// or 12345678.M=PII.T=1B.V=01
|
||||||
// or 1234*
|
// or 1234*
|
||||||
// or 1234*.PII
|
// or 1234*.M=PII
|
||||||
// or 1234*.V01
|
// or 1234*.V=01
|
||||||
// or 12 // mbus primary
|
// or 12 // mbus primary
|
||||||
// or 0 // mbus primary
|
// or 0 // mbus primary
|
||||||
// or 250.MPII.T1B.V01 // mbus primary
|
// or 250.MPII.V01.T1B // mbus primary
|
||||||
|
// or !12345678
|
||||||
|
// or !*.M=ABC
|
||||||
id = "";
|
id = "";
|
||||||
mbus_primary = false;
|
mbus_primary = false;
|
||||||
mfct = 0;
|
mfct = 0xffff;
|
||||||
type = 0;
|
type = 0xff;
|
||||||
version = 0;
|
version = 0xff;
|
||||||
|
negate = false;
|
||||||
|
|
||||||
if (s.size() == 0) return false;
|
if (s.size() == 0) return false;
|
||||||
|
|
||||||
|
@ -2626,7 +2628,7 @@ bool Address::parse(string &s)
|
||||||
}
|
}
|
||||||
id = parts[0];
|
id = parts[0];
|
||||||
|
|
||||||
for (size_t i=1; i<parts[i].size(); ++i)
|
for (size_t i=1; i<parts.size(); ++i)
|
||||||
{
|
{
|
||||||
if (parts[i].size() == 4) // V=xy or T=xy
|
if (parts[i].size() == 4) // V=xy or T=xy
|
||||||
{
|
{
|
||||||
|
|
17
src/meters.h
17
src/meters.h
|
@ -18,6 +18,7 @@
|
||||||
#ifndef METER_H_
|
#ifndef METER_H_
|
||||||
#define METER_H_
|
#define METER_H_
|
||||||
|
|
||||||
|
#include"address.h"
|
||||||
#include"dvparser.h"
|
#include"dvparser.h"
|
||||||
#include"formula.h"
|
#include"formula.h"
|
||||||
#include"util.h"
|
#include"util.h"
|
||||||
|
@ -80,22 +81,6 @@ bool isValidKey(const string& key, MeterInfo &mt);
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
typedef unsigned char uchar;
|
|
||||||
|
|
||||||
struct Address
|
|
||||||
{
|
|
||||||
// Example address: 12345678
|
|
||||||
// Or fully qualified: 12345678.M=PII.T=1b.V=01
|
|
||||||
// which means manufacturer triplet PII, type/media=0x1b, version=0x01
|
|
||||||
string id;
|
|
||||||
bool wildcard_used {}; // The id contains a *
|
|
||||||
bool mbus_primary {}; // Signals that the id is 0-250
|
|
||||||
uint16_t mfct {};
|
|
||||||
uchar type {};
|
|
||||||
uchar version {};
|
|
||||||
|
|
||||||
bool parse(string &s);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MeterInfo
|
struct MeterInfo
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,7 +37,8 @@ using namespace std;
|
||||||
bool verbose_ = false;
|
bool verbose_ = false;
|
||||||
|
|
||||||
#define LIST_OF_TESTS \
|
#define LIST_OF_TESTS \
|
||||||
X(dynamic_loading)\
|
X(addresses) \
|
||||||
|
X(dynamic_loading) \
|
||||||
X(crc) \
|
X(crc) \
|
||||||
X(dvparser) \
|
X(dvparser) \
|
||||||
X(devices) \
|
X(devices) \
|
||||||
|
@ -75,6 +76,7 @@ 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
|
||||||
|
@ -495,7 +497,9 @@ void test_ids()
|
||||||
test_does_id_match_expression("78563413", "*,!00156327,!00048713", true, true);
|
test_does_id_match_expression("78563413", "*,!00156327,!00048713", true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_address(string s, bool valid, string id, string mfct, uchar type, uchar version)
|
void tst_address(string s, bool valid, string id,
|
||||||
|
string mfct, uchar version, uchar type,
|
||||||
|
bool mbus_primary, bool wildcard_used)
|
||||||
{
|
{
|
||||||
Address a;
|
Address a;
|
||||||
bool ok = a.parse(s);
|
bool ok = a.parse(s);
|
||||||
|
@ -511,38 +515,46 @@ void tst_address(string s, bool valid, string id, string mfct, uchar type, uchar
|
||||||
string smfct = manufacturerFlag(a.mfct);
|
string smfct = manufacturerFlag(a.mfct);
|
||||||
if (id != a.id ||
|
if (id != a.id ||
|
||||||
mfct != smfct ||
|
mfct != smfct ||
|
||||||
|
version != a.version ||
|
||||||
type != a.type ||
|
type != a.type ||
|
||||||
version != a.version)
|
wildcard_used != a.wildcard_used ||
|
||||||
|
mbus_primary != a.mbus_primary ||
|
||||||
|
wildcard_used != a.wildcard_used)
|
||||||
{
|
{
|
||||||
printf("Expected parse of address \"%s\" to return (id=%s mfct=%s type=%02x version=%02x) "
|
printf("Expected parse of address \"%s\" to return (id=%s mfct=%s version=%02x type=%02x mp=%d wu=%d)\n"
|
||||||
"but got (id=%s mfct=%s type=%02x version=%02x)\n",
|
"but got (id=%s mfct=%s version=%02x type=%02x mp=%d wu=%d)\n",
|
||||||
s.c_str(),
|
s.c_str(),
|
||||||
id.c_str(), mfct.c_str(), type, version,
|
id.c_str(), mfct.c_str(), version, type, mbus_primary, wildcard_used,
|
||||||
a.id.c_str(), smfct.c_str(), a.type, a.version);
|
a.id.c_str(), smfct.c_str(), a.version, a.type, a.mbus_primary, a.wildcard_used);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_addresses()
|
void test_addresses()
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
tst_address("12345678",
|
tst_address("12345678",
|
||||||
true,
|
true,
|
||||||
"12345678", // id
|
"12345678", // id
|
||||||
"@@@", // mfct
|
"___", // mfct
|
||||||
0, // type
|
0xff, // type
|
||||||
0 // version
|
0xff, // version
|
||||||
|
false, // mbus primary
|
||||||
|
false // wildcard used
|
||||||
);
|
);
|
||||||
|
tst_address("123k45678", false, "", "", 0xff, 0xff, false, false);
|
||||||
|
tst_address("1234", false, "", "", 0xff, 0xff, false, false);
|
||||||
|
tst_address("0", true, "0", "___", 0xff, 0xff, true,false);
|
||||||
|
tst_address("250", true, "250", "___", 0xff, 0xff, true, false);
|
||||||
|
tst_address("251", false, "", "", 0xff, 0xff, false, false);
|
||||||
|
tst_address("0.M=PII.V=01.T=1b", true, "0", "PII", 0x01, 0x1b, true, false);
|
||||||
|
tst_address("123.V=11.M=FOO.T=ff", true, "123", "FOO", 0x11, 0xff, true, false);
|
||||||
|
tst_address("123.M=FOO", true, "123", "FOO", 0xff, 0xff, true, false);
|
||||||
|
tst_address("123.M=FOO.V=33", true, "123", "FOO", 0x33, 0xff, true, false);
|
||||||
|
tst_address("123.T=33", true, "123", "___", 0xff, 0x33, true, false);
|
||||||
|
tst_address("1.V=33", true, "1", "___", 0x33, 0xff, true, false);
|
||||||
|
tst_address("16.M=BAR", true, "16", "BAR", 0xff, 0xff, true, false);
|
||||||
|
|
||||||
tst_address("123k45678", false, "", "", 0, 0);
|
// tst_address("12*", true, "12*", "___", 0xff, 0xff, false, true);
|
||||||
tst_address("1234", false, "", "", 0, 0);
|
|
||||||
tst_address("0", true, "0", "@@@", 0, 0);
|
|
||||||
tst_address("250", true, "250", "@@@", 0, 0);
|
|
||||||
tst_address("251", false, "", "", 0, 0);
|
|
||||||
tst_address("0.M=PII.T=1b.V=01", true, "0", "PII", 0x1b, 0x01);
|
|
||||||
tst_address("123.V=11.M=FOO.T=ff", true, "123", "FOO", 0xff, 0x11);
|
|
||||||
tst_address("16.M=BAR", true, "16", "BAR", 0, 0);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void eq(string a, string b, const char *tn)
|
void eq(string a, string b, const char *tn)
|
||||||
|
|
238
src/util.cc
238
src/util.cc
|
@ -654,244 +654,6 @@ bool isValidAlias(const string& alias)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValidMatchExpression(const string& s, bool non_compliant)
|
|
||||||
{
|
|
||||||
string me = s;
|
|
||||||
|
|
||||||
// Examples of valid match expressions:
|
|
||||||
// 12345678
|
|
||||||
// *
|
|
||||||
// 123*
|
|
||||||
// !12345677
|
|
||||||
// 2222222*
|
|
||||||
// !22222222
|
|
||||||
|
|
||||||
// A match expression cannot be empty.
|
|
||||||
if (me.length() == 0) return false;
|
|
||||||
|
|
||||||
// An me can be negated with an exclamation mark first.
|
|
||||||
if (me.front() == '!') me.erase(0, 1);
|
|
||||||
|
|
||||||
// A match expression cannot be only a negation mark.
|
|
||||||
if (me.length() == 0) return false;
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
if (non_compliant)
|
|
||||||
{
|
|
||||||
// Some non-compliant meters have full hex in the id,
|
|
||||||
// but according to the standard there should only be bcd here...
|
|
||||||
while (me.length() > 0 &&
|
|
||||||
((me.front() >= '0' && me.front() <= '9') ||
|
|
||||||
(me.front() >= 'a' && me.front() <= 'f')))
|
|
||||||
{
|
|
||||||
me.erase(0,1);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// But compliant meters use only a bcd subset.
|
|
||||||
while (me.length() > 0 &&
|
|
||||||
(me.front() >= '0' && me.front() <= '9'))
|
|
||||||
{
|
|
||||||
me.erase(0,1);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wildcard_used = false;
|
|
||||||
// An expression can end with a *
|
|
||||||
if (me.length() > 0 && me.front() == '*')
|
|
||||||
{
|
|
||||||
me.erase(0,1);
|
|
||||||
wildcard_used = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we should have eaten the whole expression.
|
|
||||||
if (me.length() > 0) return false;
|
|
||||||
|
|
||||||
// Check the length of the matching bcd/hex
|
|
||||||
// If no wildcard is used, then the match expression must be exactly 8 digits.
|
|
||||||
if (!wildcard_used) return count == 8;
|
|
||||||
|
|
||||||
// If wildcard is used, then the match expressions must be 7 or less digits,
|
|
||||||
// even zero is allowed which means a single *, which matches any bcd/hex id.
|
|
||||||
return count <= 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isValidMatchExpressions(const string& mes, bool non_compliant)
|
|
||||||
{
|
|
||||||
vector<string> v = splitMatchExpressions(mes);
|
|
||||||
|
|
||||||
for (string me : v)
|
|
||||||
{
|
|
||||||
if (!isValidMatchExpression(me, non_compliant)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isValidId(const string& id, bool accept_non_compliant)
|
|
||||||
{
|
|
||||||
|
|
||||||
for (size_t i=0; i<id.length(); ++i)
|
|
||||||
{
|
|
||||||
if (id[i] >= '0' && id[i] <= '9') continue;
|
|
||||||
if (accept_non_compliant)
|
|
||||||
{
|
|
||||||
if (id[i] >= 'a' && id[i] <= 'f') continue;
|
|
||||||
if (id[i] >= 'A' && id[i] <= 'F') continue;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool doesIdMatchExpression(const string& s, string match)
|
|
||||||
{
|
|
||||||
string id = s;
|
|
||||||
if (id.length() == 0) return false;
|
|
||||||
|
|
||||||
// Here we assume that the match expression has been
|
|
||||||
// verified to be valid.
|
|
||||||
bool can_match = true;
|
|
||||||
|
|
||||||
// Now match bcd/hex until end of id, or '*' in match.
|
|
||||||
while (id.length() > 0 && match.length() > 0 && match.front() != '*')
|
|
||||||
{
|
|
||||||
if (id.front() != match.front())
|
|
||||||
{
|
|
||||||
// We hit a difference, it cannot match.
|
|
||||||
can_match = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
id.erase(0,1);
|
|
||||||
match.erase(0,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wildcard_used = false;
|
|
||||||
if (match.length() && match.front() == '*')
|
|
||||||
{
|
|
||||||
wildcard_used = true;
|
|
||||||
match.erase(0,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (can_match)
|
|
||||||
{
|
|
||||||
// Ok, now the match expression should be empty.
|
|
||||||
// If wildcard is true, then the id can still have digits,
|
|
||||||
// otherwise it must also be empty.
|
|
||||||
if (wildcard_used)
|
|
||||||
{
|
|
||||||
can_match = match.length() == 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
can_match = match.length() == 0 && id.length() == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return can_match;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasWildCard(const string& mes)
|
|
||||||
{
|
|
||||||
return mes.find('*') != string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool doesIdsMatchExpressions(vector<string> &ids, vector<string>& mes, bool *used_wildcard)
|
|
||||||
{
|
|
||||||
bool match = false;
|
|
||||||
for (string &id : ids)
|
|
||||||
{
|
|
||||||
if (doesIdMatchExpressions(id, mes, 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 doesIdMatchExpressions(const string& id, vector<string>& mes, bool *used_wildcard)
|
|
||||||
{
|
|
||||||
bool found_match = false;
|
|
||||||
bool found_negative_match = false;
|
|
||||||
bool exact_match = false;
|
|
||||||
*used_wildcard = 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 (string me : mes)
|
|
||||||
{
|
|
||||||
bool has_wildcard = hasWildCard(me);
|
|
||||||
bool is_negative_rule = (me.length() > 0 && me.front() == '!');
|
|
||||||
if (is_negative_rule)
|
|
||||||
{
|
|
||||||
me.erase(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool m = doesIdMatchExpression(id, me);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
string toIdsCommaSeparated(vector<string> &ids)
|
|
||||||
{
|
|
||||||
string cs;
|
|
||||||
for (string& s: ids)
|
|
||||||
{
|
|
||||||
cs += s;
|
|
||||||
cs += ",";
|
|
||||||
}
|
|
||||||
if (cs.length() > 0) cs.pop_back();
|
|
||||||
return cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFrequency(const string& fq)
|
bool isFrequency(const string& fq)
|
||||||
{
|
{
|
||||||
int len = fq.length();
|
int len = fq.length();
|
||||||
|
|
|
@ -155,19 +155,10 @@ void setAlarmShells(std::vector<std::string> &alarm_shells);
|
||||||
|
|
||||||
bool isValidAlias(const std::string& alias);
|
bool isValidAlias(const std::string& alias);
|
||||||
bool isValidBps(const std::string& b);
|
bool isValidBps(const std::string& b);
|
||||||
bool isValidMatchExpression(const std::string& s, bool non_compliant);
|
|
||||||
bool isValidMatchExpressions(const std::string& s, bool non_compliant);
|
|
||||||
bool doesIdMatchExpression(const std::string& id, std::string match_rule);
|
|
||||||
bool doesIdMatchExpressions(const std::string& id, std::vector<std::string>& match_rules, bool *used_wildcard);
|
|
||||||
bool doesIdsMatchExpressions(std::vector<std::string> &ids, std::vector<std::string>& match_rules, bool *used_wildcard);
|
|
||||||
std::string toIdsCommaSeparated(std::vector<std::string> &ids);
|
|
||||||
|
|
||||||
bool isValidId(const std::string& id, bool accept_non_compliant);
|
|
||||||
|
|
||||||
bool isFrequency(const std::string& fq);
|
bool isFrequency(const std::string& fq);
|
||||||
bool isNumber(const std::string& fq);
|
bool isNumber(const std::string& fq);
|
||||||
|
|
||||||
std::vector<std::string> splitMatchExpressions(const std::string& mes);
|
|
||||||
// Split s into strings separated by c.
|
// Split s into strings separated by c.
|
||||||
std::vector<std::string> splitString(const std::string &s, char c);
|
std::vector<std::string> splitString(const std::string &s, char c);
|
||||||
// Split s into strings separated by c and store inte set.
|
// Split s into strings separated by c and store inte set.
|
||||||
|
|
|
@ -231,6 +231,7 @@ LIST_OF_MANUFACTURERS
|
||||||
|
|
||||||
void Telegram::addId(const vector<uchar>::iterator &pos)
|
void Telegram::addId(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));
|
||||||
string id = tostrprintf("%02x%02x%02x%02x", *(pos+3), *(pos+2), *(pos+1), *(pos+0));
|
string id = tostrprintf("%02x%02x%02x%02x", *(pos+3), *(pos+2), *(pos+1), *(pos+0));
|
||||||
ids.push_back(id);
|
ids.push_back(id);
|
||||||
if (idsc.empty()) {
|
if (idsc.empty()) {
|
||||||
|
|
Ładowanie…
Reference in New Issue