From 9facddf01915b28928b515096b191600ea770fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Thu, 28 Mar 2024 12:40:50 +0100 Subject: [PATCH] Add support for the same fully specified secondary address printed by libmbus after doing a bus scan. --- README.md | 4 ++- src/address.cc | 43 +++++++++++++++++++++++++ src/cmdline.cc | 11 +++++++ src/config.cc | 16 +++++---- src/meters.cc | 16 ++++++--- test.sh | 3 ++ tests/test_bad_driver.sh | 34 +++++++++---------- tests/test_libmbus_secondary_address.sh | 40 +++++++++++++++++++++++ tests/test_loadable_drivers.sh | 2 +- 9 files changed, 139 insertions(+), 30 deletions(-) create mode 100755 tests/test_libmbus_secondary_address.sh diff --git a/README.md b/README.md index 1c81c90..05244f7 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,9 @@ 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` +`id=22222222,!22222222.V=77` You can also use the fully specified secondary address that is +printed by libmbus after doing a bus scan, ie `100002842941011B` which is equivalent to +`10000284.M=PII.V=01.T=1B` When matching all meters from the command line you can use `ANYID` instead of `*` to avoid shell quotes. diff --git a/src/address.cc b/src/address.cc index 9ae39d0..90b6cf5 100644 --- a/src/address.cc +++ b/src/address.cc @@ -45,6 +45,8 @@ bool isValidMatchExpression(const string& s, bool *has_wildcard) // !12345677 // 2222222* // !22222222 + // We also accept an secondary libmbus address: + // 100002842941011B // A match expression cannot be empty. if (me.length() == 0) return false; @@ -64,12 +66,22 @@ bool isValidMatchExpression(const string& s, bool *has_wildcard) // We accept hex anyway. while (me.length() > 0 && ((me.front() >= '0' && me.front() <= '9') || + (me.front() >= 'A' && me.front() <= 'F') || (me.front() >= 'a' && me.front() <= 'f'))) { me.erase(0,1); count++; } + if (me.length() == 0 && count == 16) + { + // A secondary libmbus address: 100002842941011B + // Strictly speaking the leading 8 digits should be bcd, + // but we accept hex as well. + *has_wildcard = false; + return true; + } + bool wildcard_used = false; // An expression can end with a * if (me.length() > 0 && me.front() == '*') @@ -243,6 +255,8 @@ bool AddressExpression::parse(const string &in) // or 250.MPII.V01.T1B // mbus primary // or !12345678 // or !*.M=ABC + // or libmbus secondary style: + // 123456782941011B id = ""; mbus_primary = false; mfct = 0xffff; @@ -280,6 +294,35 @@ bool AddressExpression::parse(const string &in) mbus_primary = true; } + if (parts.size() == 1 && id.length() == 16) + { + // This is a secondary libmbus address. + string mfct_hex = id.substr(8,4); + string version_hex = id.substr(12,2); + string type_hex = id.substr(14,2); + id = id.substr(0,8); + + vector data; + bool ok = hex2bin(mfct_hex.c_str(), &data); + if (!ok) return false; + if (data.size() != 2) return false; + mfct = data[1] << 8 | data[0]; + + data.clear(); + ok = hex2bin(version_hex.c_str(), &data); + if (!ok) return false; + if (data.size() != 1) return false; + version = data[0]; + + data.clear(); + ok = hex2bin(type_hex.c_str(), &data); + if (!ok) return false; + if (data.size() != 1) return false; + type = data[0]; + + return true; + } + for (size_t i=1; i parseNormalCommandLine(Configuration *c, int ar string key = argv[m*4+i+3]; MeterInfo mi; + + if (!isValidSequenceOfAddressExpressions(address_expressions)) + { + error("Not a valid meter id nor a valid sequence of match expression \"%s\"\n", address_expressions.c_str()); + } + mi.parse(name, driver, address_expressions, key); mi.poll_interval = c->pollinterval; mi.identity_mode = c->identity_mode; + if (!isValidKey(key, mi)) + { + error("Not a valid meter key \"%s\"\n", key.c_str()); + } + if (mi.driver_name.str() == "") { error("Not a valid meter driver \"%s\"\n", driver.c_str()); diff --git a/src/config.cc b/src/config.cc index c7cddc0..1205828 100644 --- a/src/config.cc +++ b/src/config.cc @@ -186,18 +186,22 @@ void parseMeterConfig(Configuration *c, vector &buf, string file) MeterInfo mi; + if (!isValidSequenceOfAddressExpressions(address_expressions)) + { + warning("In config, not a valid meter id nor a valid sequence of match expression \"%s\"\n", address_expressions.c_str()); + use = false; + } + mi.parse(name, driver, address_expressions, key); // sets driver, extras, name, bus, bps, link_modes, ids, name, key mi.poll_interval = poll_interval; mi.identity_mode = identity_mode; - 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)) { - warning("Not a valid meter key \"%s\"\n", key.c_str()); + if (!isValidKey(key, mi)) + { + warning("In config, not a valid meter key in config \"%s\"\n", key.c_str()); use = false; } + if (use) { mi.extra_constant_fields = extra_constant_fields; diff --git a/src/meters.cc b/src/meters.cc index d455f38..a48f6bc 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -678,10 +678,16 @@ void MeterCommonImplementation::poll(shared_ptr bus_manager) return; } + if (addressExpressions().size() == 0) + { + warning("(meter) not polling from \"%s\" since no valid id\n", name().c_str()); + return; + } + AddressExpression &ae = addressExpressions().back(); if (ae.has_wildcard) { - debug("(meter) not polling from id \"%s\" since poll id must not have a wildcard\n", ae.id.c_str()); + warning("(meter) not polling from id \"%s\" since poll id must not have a wildcard\n", ae.id.c_str()); return; } @@ -691,7 +697,7 @@ void MeterCommonImplementation::poll(shared_ptr bus_manager) if (idnum < 0 || idnum > 250) { - debug("(meter) not polling from bad id \"%s\"\n", ae.id.c_str()); + warning("(meter) not polling from bad id \"%s\"\n", ae.id.c_str()); return; } @@ -721,7 +727,7 @@ void MeterCommonImplementation::poll(shared_ptr bus_manager) if (!ok || idhex.size() != 4) { - debug("(meter) not polling from bad id \"%s\"\n", ae.id.c_str()); + warning("(meter) not polling from bad id \"%s\"\n", ae.id.c_str()); return; } @@ -739,8 +745,8 @@ void MeterCommonImplementation::poll(shared_ptr bus_manager) buf[8] = idhex[2]; // id 56 buf[9] = idhex[1]; // id 34 buf[10] = idhex[0]; // id 12 - buf[11] = (ae.mfct >> 8) & 0xff; // use 0xff as a wildcard - buf[12] = ae.mfct & 0xff; // mfct + buf[11] = ae.mfct & 0xff; // mfct + buf[12] = (ae.mfct >> 8) & 0xff; // use 0xff as a wildcard buf[13] = ae.version; // version/generation buf[14] = ae.type; // type/media/device diff --git a/test.sh b/test.sh index bb41a1c..41828dc 100755 --- a/test.sh +++ b/test.sh @@ -36,6 +36,9 @@ if [ "$?" != "0" ]; then RC="1"; fi tests/test_mbus.sh $PROG if [ "$?" != "0" ]; then RC="1"; fi +tests/test_libmbus_secondary_address.sh $PROG +if [ "$?" != "0" ]; then RC="1"; fi + tests/test_anyid.sh $PROG if [ "$?" != "0" ]; then RC="1"; fi diff --git a/tests/test_bad_driver.sh b/tests/test_bad_driver.sh index 79c44a6..b24970f 100755 --- a/tests/test_bad_driver.sh +++ b/tests/test_bad_driver.sh @@ -44,7 +44,7 @@ A driver file looks like this: driver { name = abc123 ... } Failed to load driver from file: testoutput/driver.xmq EOF -$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -62,7 +62,7 @@ The driver name must consist of lower case ascii a-z and digits 0-9. Failed to load driver from file: testoutput/driver.xmq EOF -$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -93,7 +93,7 @@ WaterMeter Failed to load driver from file: testoutput/driver.xmq EOF -$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -123,7 +123,7 @@ WaterMeter Failed to load driver from file: testoutput/driver.xmq EOF -$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -142,7 +142,7 @@ Where you change total_m3 to your meters most important field. Failed to load driver from file: testoutput/driver.xmq EOF -$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -164,7 +164,7 @@ or as 4 lower case hex digits. Failed to load driver from file: testoutput/driver.xmq EOF -$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -192,7 +192,7 @@ or as 4 lower case hex digits. Failed to load driver from file: testoutput/driver.xmq EOF -$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -213,7 +213,7 @@ Remember to add for example: field { name = total ... } Hej;?total_m3? EOF -$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -235,7 +235,7 @@ Either indirectly based on the quantity or directly based on the display_unit. Hej;?total_m3? EOF -$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -279,7 +279,7 @@ Dimensionless Hej;?total_m3? EOF -$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -322,7 +322,7 @@ Dimensionless Hej;?total_m3? EOF -$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -340,7 +340,7 @@ cat > $TEST/test_expected.txt < $TEST/test_output.txt 2>&1 || true +$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -357,7 +357,7 @@ cat > $TEST/test_expected.txt < $TEST/test_output.txt 2>&1 || true +$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -384,7 +384,7 @@ Any Hej ?total_m3? EOF -$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -410,7 +410,7 @@ Any Hej ?total_m3? EOF -$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -477,7 +477,7 @@ AnyPowerVIF Hej ?total_m3? EOF -$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true +$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck @@ -494,6 +494,6 @@ cat > $TEST/test_expected.txt < $TEST/test_output.txt 2>&1 || true +$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 || true performCheck diff --git a/tests/test_libmbus_secondary_address.sh b/tests/test_libmbus_secondary_address.sh new file mode 100755 index 0000000..62ae5b5 --- /dev/null +++ b/tests/test_libmbus_secondary_address.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +PROG="$1" + +mkdir -p testoutput + +TEST=testoutput + +######################################################## +TESTNAME="Using libmbus secondary address fully specified format" +TESTRESULT="ERROR" + +OUT=$($PROG --pollinterval=1s --format=fields --selectfields=temperature_c 68383868080072840200102941011B0D0000000265FE0842653009820165E70802FB1A480142FB1A45018201FB1A4E010C788402001002FD0F21000F0316 MyTempMeter piigth 100002842941011B NOKEY) + +if [ "$OUT" != "23.02" ] +then + echo "ERROR: Test 1 $TESTNAME" + echo "Expected answer 23.02" + exit 1 +fi + +OUT=$($PROG --pollinterval=1s --format=fields --selectfields=temperature_c 68383868080072840200102941011B0D0000000265FE0842653009820165E70802FB1A480142FB1A45018201FB1A4E010C788402001002FD0F21000F0316 MyTempMeter piigth 10000284.M=PII.V=01.T=1B NOKEY) + +if [ "$OUT" != "23.02" ] +then + echo "ERROR: Test 2 $TESTNAME" + echo "Expected answer 23.02" + exit 1 +fi + +OUT=$($PROG --pollinterval=1s --format=fields --selectfields=temperature_c 68383868080072840200102941011B0D0000000265FE0842653009820165E70802FB1A480142FB1A45018201FB1A4E010C788402001002FD0F21000F0316 MyTempMeter piigth 100002842941011C NOKEY) + +if [ "$OUT" != "" ] +then + echo "ERROR: Test 3 $TESTNAME" + echo "Did not expect answer! $OUT" + exit 1 +fi + +echo "OK: $TESTNAME" diff --git a/tests/test_loadable_drivers.sh b/tests/test_loadable_drivers.sh index c64ec2c..eb8aaf0 100755 --- a/tests/test_loadable_drivers.sh +++ b/tests/test_loadable_drivers.sh @@ -69,6 +69,6 @@ cat > $TEST/test_expected.txt < $TEST/test_output.txt 2>&1 +$PROG --format=json 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NOKEY > $TEST/test_output.txt 2>&1 performCheck