diff --git a/src/serial.cc b/src/serial.cc index 52f0024..7d8da02 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -338,7 +338,7 @@ int SerialDeviceCommand::receive(vector *data) } if (isDebugEnabled()) { - string msg = bin2hex(*data); + string msg = safeString(*data); debug("(serialcmd) received \"%s\"\n", msg.c_str()); } diff --git a/src/util.cc b/src/util.cc index c894109..52c9362 100644 --- a/src/util.cc +++ b/src/util.cc @@ -159,6 +159,22 @@ std::string bin2hex(vector::iterator data, vector::iterator end, i return str; } +std::string safeString(vector &target) { + std::string str; + for (size_t i = 0; i < target.size(); ++i) { + const char ch = target[i]; + if (ch >= 32 && ch < 127 && ch != '<' && ch != '>') { + str += ch; + } else { + str += '<'; + str.append(&hex[(ch & 0xF0) >> 4], 1); + str.append(&hex[ch & 0xF], 1); + str += '>'; + } + } + return str; +} + void strprintf(std::string &s, const char* fmt, ...) { char buf[4096]; diff --git a/src/util.h b/src/util.h index 3022fd2..f9021db 100644 --- a/src/util.h +++ b/src/util.h @@ -37,6 +37,7 @@ bool hex2bin(std::string &src, std::vector *target); bool hex2bin(std::vector &src, std::vector *target); std::string bin2hex(std::vector &target); std::string bin2hex(std::vector::iterator data, std::vector::iterator end, int len); +std::string safeString(std::vector &target); void strprintf(std::string &s, const char* fmt, ...); // Return for example: 2010-03-21 std::string strdate(struct tm *date); diff --git a/src/wmbus_rtlwmbus.cc b/src/wmbus_rtlwmbus.cc index 65d3ed4..ed67ea1 100644 --- a/src/wmbus_rtlwmbus.cc +++ b/src/wmbus_rtlwmbus.cc @@ -131,7 +131,18 @@ void WMBusRTLWMBUS::processSerialData() if (hex_payload_len > 0) { vector hex; hex.insert(hex.end(), read_buffer_.begin()+hex_payload_offset, read_buffer_.begin()+hex_payload_offset+hex_payload_len); - hex2bin(hex, &payload); + bool ok = hex2bin(hex, &payload); + if (!ok) { + if (hex.size() % 2 == 1) { + payload.clear(); + warning("(rtlwmbus) warning: the hex string is not an even multiple of two! Dropping last char.\n"); + hex.pop_back(); + ok = hex2bin(hex, &payload); + } + if (!ok) { + warning("(rtlwmbus) warning: the hex string contains bad characters! Decode stopped partway.\n"); + } + } } read_buffer_.erase(read_buffer_.begin(), read_buffer_.begin()+frame_length); @@ -158,10 +169,11 @@ FrameStatus WMBusRTLWMBUS::checkRTLWMBUSFrame(vector &data, int *hex_payload_offset) { //C1;1;1;2019-02-09 07:14:18.000;117;102;94740459;0x49449344590474943508780dff5f3500827f0000f10007b06effff530100005f2c620100007f2118010000008000800080008000000000000000000e003f005500d4ff2f046d10086922 + // There might be a second telegram on the same line ;0x4944....... if (data.size() == 0) return PartialFrame; int payload_len = 0; size_t eolp = 0; - // Look for end of line. + // Look for end of line for (; eolp < data.size(); ++eolp) { if (data[eolp] == '\n') break; } @@ -169,14 +181,15 @@ FrameStatus WMBusRTLWMBUS::checkRTLWMBUSFrame(vector &data, // We got a full line, but if it is too short, then // there is something wrong. Discard the data. - if (data.size() < 72) return ErrorInFrame; + if (data.size() < 10) return ErrorInFrame; - // Discard lines that are not T1 or C1 telegrams - if (data[0] != 'T' && data[0] != 'C') return ErrorInFrame; - - // And the checksums should match. - if (strncmp((const char*)&data[1], "1;1;1", 5)) return ErrorInFrame; + if (data[0] != '0' || data[1] != 'x') { + // Discard lines that are not T1 or C1 telegrams + if (data[0] != 'T' && data[0] != 'C') return ErrorInFrame; + // And the checksums should match. + if (strncmp((const char*)&data[1], "1;1;1", 5)) return ErrorInFrame; + } // Look for start of telegram 0x size_t i = 0; for (; i+1 < data.size(); ++i) { @@ -185,11 +198,19 @@ FrameStatus WMBusRTLWMBUS::checkRTLWMBUSFrame(vector &data, if (i+1 >= data.size()) return ErrorInFrame; // No 0x found, then discard the frame. i+=2; // Skip 0x + // Look for end of line or semicolon. + for (eolp=i; eolp < data.size(); ++eolp) { + if (data[eolp] == '\n') break; + if (data[eolp] == ';' && data[eolp+1] == '0' && data[eolp+2] == 'x') break; + } + if (eolp >= data.size()) return PartialFrame; + payload_len = eolp-i; *hex_payload_len_out = payload_len; *hex_payload_offset = i; *hex_frame_length = eolp+1; + debug("(rtlwmbus) got full frame\n"); return FullFrame; } diff --git a/test.sh b/test.sh index 2a2ee6e..c5198b3 100755 --- a/test.sh +++ b/test.sh @@ -19,3 +19,4 @@ tests/test_listen_to_all.sh $PROG tests/test_multiple_ids.sh $PROG #tests/test_oneshot.sh $PROG broken test tests/test_wrongkeys.sh $PROG +tests/test_rtlwmbus.sh $PROG diff --git a/tests/rtlwmbus_water.sh b/tests/rtlwmbus_water.sh new file mode 100755 index 0000000..29058b6 --- /dev/null +++ b/tests/rtlwmbus_water.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "T1;1;1;2019-04-03 19:00:42.000;97;148;88888888;0x6e4401068888888805077a85006085bc2630713819512eb4cd87fba554fb43f67cf9654a68ee8e194088160df752e716238292e8af1ac20986202ee561d743602466915e42f1105d9c6782a54504e4f099e65a7656b930c73a30775122d2fdf074b5035cfaa7e0050bf32faae03a77" +#{"media":"water","meter":"apator162","name":"ApWater","id":"88888888","total_m3":4.848000,"timestamp":"1111-11-11T11:11:11Z"} diff --git a/tests/test_rtlwmbus.sh b/tests/test_rtlwmbus.sh new file mode 100755 index 0000000..6b7f418 --- /dev/null +++ b/tests/test_rtlwmbus.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +PROG="$1" + +mkdir -p testoutput + +TEST=testoutput + +cat tests/rtlwmbus_water.sh | grep '^#{' | tr -d '#' > $TEST/test_expected.txt +$PROG --format=json "rtlwmbus:tests/rtlwmbus_water.sh" \ + ApWater apator162 88888888 00000000000000000000000000000000 \ + | grep -v "(rtlwmbus) child process exited! Command was:" \ + > $TEST/test_output.txt + +if [ "$?" == "0" ] +then + cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt + diff $TEST/test_expected.txt $TEST/test_responses.txt + if [ "$?" == "0" ] + then + echo RTLWMBUS OK + fi +else + echo Failure. + exit 1 +fi