From 5962e727ff4a41786d1730eae44f00d6d6d0d7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Sat, 2 Mar 2024 22:46:50 +0100 Subject: [PATCH] Handle more address rules. --- src/address.cc | 29 ++++++++++++++++---- src/cmdline.cc | 4 +-- src/config.cc | 10 +++---- src/main.cc | 8 ++++++ src/metermanager.cc | 1 + src/testinternals.cc | 63 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 12 deletions(-) diff --git a/src/address.cc b/src/address.cc index 4115dbc..b846d4b 100644 --- a/src/address.cc +++ b/src/address.cc @@ -27,7 +27,8 @@ bool isValidMatchExpression(const std::string& s, bool *has_wildcard); bool doesIdMatchExpression(const std::string& id, std::string match_rule); bool doesAddressMatchExpressions(Address &address, std::vector& address_expressions, - bool *used_wildcard); + bool *used_wildcard, + bool *filtered_out); bool isValidMatchExpression(const string& s, bool *has_wildcard) { @@ -94,7 +95,7 @@ vector splitSequenceOfAddressExpressionsAtCommas(const string& mes) auto i = v.begin(); for (;;) { - auto id = eatTo(v, i, ',', 16, &eof, &err); + auto id = eatTo(v, i, ',', 64, &eof, &err); if (err) break; trimWhitespace(&id); if (id == "ANYID") id = "*"; @@ -277,6 +278,19 @@ bool AddressExpression::parse(const string &in) bool ok = flagToManufacturer(&parts[i][2], &mfct); if (!ok) return false; } + else if (parts[i].size() == 6) // M=abcd explicit hex version + { + if (parts[i][1] != '=') return false; + if (parts[i][0] != 'M') return false; + + vector data; + bool ok = hex2bin(&parts[i][2], &data); + if (!ok) return false; + if (data.size() != 2) return false; + + mfct = data[1] << 8 | data[0]; + if (!ok) return false; + } else { return false; @@ -396,21 +410,25 @@ bool doesTelegramMatchExpressions(std::vector
&addresses, bool *used_wildcard) { bool match = false; + bool filtered_out = false; for (Address &a : addresses) { - if (doesAddressMatchExpressions(a, address_expressions, used_wildcard)) + if (doesAddressMatchExpressions(a, address_expressions, used_wildcard, &filtered_out)) { match = true; } // Go through all ids even though there is an early match. // This way we can see if theres an exact match later. } + // If any expression triggered a filter out, then the whole telegram does not match. + if (filtered_out) match = false; return match; } bool doesAddressMatchExpressions(Address &address, vector& address_expressions, - bool *used_wildcard) + bool *used_wildcard, + bool *filtered_out) { bool found_match = false; bool found_negative_match = false; @@ -433,7 +451,7 @@ bool doesAddressMatchExpressions(Address &address, bool has_wildcard = ae.has_wildcard; bool is_negative_rule = ae.filter_out; - bool m = doesIdMatchExpression(address.id, ae.id); + bool m = ae.match(address.id, address.mfct, address.version, address.type); if (is_negative_rule) { @@ -453,6 +471,7 @@ bool doesAddressMatchExpressions(Address &address, } if (found_negative_match) { + *filtered_out = true; return false; } if (found_match) diff --git a/src/cmdline.cc b/src/cmdline.cc index 7589a15..129d185 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -728,11 +728,11 @@ static shared_ptr parseNormalCommandLine(Configuration *c, int ar string bus; string name = argv[m*4+i+0]; string driver = argv[m*4+i+1]; - string id = argv[m*4+i+2]; + string address_expressions = argv[m*4+i+2]; string key = argv[m*4+i+3]; MeterInfo mi; - mi.parse(name, driver, id, key); + mi.parse(name, driver, address_expressions, key); mi.poll_interval = c->pollinterval; if (mi.driver_name.str() == "") diff --git a/src/config.cc b/src/config.cc index d780519..0e0c2da 100644 --- a/src/config.cc +++ b/src/config.cc @@ -53,7 +53,7 @@ void parseMeterConfig(Configuration *c, vector &buf, string file) string bus; string name; string driver = "auto"; - string id; + string address_expressions; string key = ""; string linkmodes; int poll_interval = 0; @@ -108,7 +108,7 @@ void parseMeterConfig(Configuration *c, vector &buf, string file) else if (p.first == "driver") driver = p.second; else - if (p.first == "id") id = p.second; + if (p.first == "id") address_expressions = p.second; else if (p.first == "key") { @@ -176,11 +176,11 @@ void parseMeterConfig(Configuration *c, vector &buf, string file) MeterInfo mi; - mi.parse(name, driver, id, key); // sets driver, extras, name, bus, bps, link_modes, ids, name, key + mi.parse(name, driver, address_expressions, key); // sets driver, extras, name, bus, bps, link_modes, ids, name, key mi.poll_interval = poll_interval; - if (!isValidSequenceOfAddressExpressions(id)) { - warning("Not a valid meter id nor a valid sequence of match expression \"%s\"\n", id.c_str()); + if (!isValidSequenceOfAddressExpressions(address_expressions)) { + warning("Not a valid meter id nor a valid sequence of match expression \"%s\"\n", address_expressions.c_str()); use = false; } if (!isValidKey(key, mi)) { diff --git a/src/main.cc b/src/main.cc index ebd95ef..9233b45 100644 --- a/src/main.cc +++ b/src/main.cc @@ -403,6 +403,14 @@ void log_start_information(Configuration *config) verbose("(config) using device: %s \n", specified_device.str().c_str()); } verbose("(config) number of meters: %d\n", config->meters.size()); + if (isDebugEnabled()) + { + for (MeterInfo &m : config->meters) + { + string aes = AddressExpression::concat(m.address_expressions); + debug("(config) template %s %s %s\n", m.name.c_str(), aes.c_str(), m.str().c_str()); + } + } } void oneshot_check(Configuration *config, Telegram *t, Meter *meter) diff --git a/src/metermanager.cc b/src/metermanager.cc index c5d1579..a23d29a 100644 --- a/src/metermanager.cc +++ b/src/metermanager.cc @@ -187,6 +187,7 @@ public: aes.push_back(AddressExpression(t.addresses.back())); meter_info.address_expressions = aes; + // Overwrite the mfct,version and type. if (meter_info.driverName().str() == "auto") { // Look up the proper meter driver! diff --git a/src/testinternals.cc b/src/testinternals.cc index c2c7d6e..07089cc 100644 --- a/src/testinternals.cc +++ b/src/testinternals.cc @@ -575,6 +575,42 @@ void tst_address_match(string expr, string id, uint16_t m, uchar v, uchar t, boo } } +void tst_telegram_match(string addresses, string expressions, bool match, bool uw) +{ + vector exprs = splitAddressExpressions(expressions); + vector as = splitAddressExpressions(addresses); + vector
addrs; + + for (auto &ad : as) + { + Address a; + a.id = ad.id; + a.mfct = ad.mfct; + a.version = ad.version; + a.type = ad.type; + + addrs.push_back(a); + } + + bool used_wildcard = false; + bool m = doesTelegramMatchExpressions(addrs, exprs, &used_wildcard); + + if (m != match) + { + printf("Expected addresses %s to %smatch expressions %s\n", + addresses.c_str(), + match?"":"NOT ", + expressions.c_str()); + } + if (uw != used_wildcard) + { + printf("Expected addresses %s from match expression %s %susing wildcard\n", + addresses.c_str(), + expressions.c_str(), + uw?"":"NOT "); + } +} + void test_addresses() { tst_address("12345678", @@ -634,6 +670,33 @@ void test_addresses() tst_address_match("!9*.V=06", "89999999", MANUFACTURER_ABB, 0x06, 1, false, true); tst_address_match("!9*.V=06", "99999999", MANUFACTURER_ABB, 0x07, 1, false, true); tst_address_match("!9*.V=06", "89999999", MANUFACTURER_ABB, 0x07, 1, false, true); + + tst_telegram_match("12345678", "12345678", true, false); + tst_telegram_match("11111111,22222222", "12345678,22*", true, true); + tst_telegram_match("11111111,22222222", "12345678,22222222", true, false); + tst_telegram_match("11111111.M=KAM,22222222.M=PII", "11111111.M=KAM", true, false); + tst_telegram_match("11111111.M=KAF", "11111111.M=KAM", false, false); + + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.M=KAM", true, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.M=KAF", false, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111", true, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.M=KAM", true, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.V=1b", true, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.T=16", true, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.M=KAM.T=16", true, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.M=KAM.V=1b", true, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.T=16.V=1b", true, false); + + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.M=KAL", false, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.V=1c", false, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.T=17", false, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.M=KAM.T=17", false, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.M=KAL.V=1b", false, false); + tst_telegram_match("11111111.M=KAM.V=1b.T=16", "11111111.T=17.V=1b", false, false); + + // Test * matches both 11111111 and 2222222 but the only the 111111 matches the filter out V=1b. + // Verify that the filter out !1*.V=1b will override successfull match (with no filter out) * for 22222222. + tst_telegram_match("11111111.M=KAM.V=1b.T=16,22222222.M=XXX.V=aa.T=99", "*,!1*.V=1b", false, true); } void eq(string a, string b, const char *tn)