kopia lustrzana https://github.com/weetmuts/wmbusmeters
Parse and decode a hex string supplied as argument on command line.
rodzic
2baff2207b
commit
89cf1bad80
5
CHANGES
5
CHANGES
|
@ -1,4 +1,9 @@
|
|||
|
||||
You can now decode a telegram just by supplying hex on the command line:
|
||||
wmbusmeters --format=json <hex_string> MyMeter auto 12345678 NOKEY
|
||||
or just
|
||||
wmbusmeters <hex_string>
|
||||
|
||||
Added --listunits to list all quantities and the units for each quantity
|
||||
that wmbusmeteres knows about.
|
||||
|
||||
|
|
10
src/bus.cc
10
src/bus.cc
|
@ -383,7 +383,7 @@ void BusManager::detectAndConfigureWmbusDevices(Configuration *config, Detection
|
|||
continue;
|
||||
}
|
||||
}
|
||||
if (specified_device.file == "" && specified_device.command == "")
|
||||
if (specified_device.file == "" && specified_device.command == "" && specified_device.hex == "")
|
||||
{
|
||||
// File/tty/command not specified, use auto scan later to find actual device file/tty.
|
||||
must_auto_find_ttys |= usesTTY(specified_device.type);
|
||||
|
@ -404,6 +404,12 @@ void BusManager::detectAndConfigureWmbusDevices(Configuration *config, Detection
|
|||
specified_device.handled = true;
|
||||
openBusDeviceAndPotentiallySetLinkmodes(config, "config", &detected);
|
||||
}
|
||||
if (specified_device.hex != "")
|
||||
{
|
||||
Detected detected = detectWMBusDeviceWithFileOrHex(specified_device, config->default_device_linkmodes, serial_manager_);
|
||||
openBusDeviceAndPotentiallySetLinkmodes(config, "config", &detected);
|
||||
specified_device.handled = true;
|
||||
}
|
||||
if (specified_device.file != "")
|
||||
{
|
||||
shared_ptr<SerialDevice> sd = serial_manager_->lookup(specified_device.file);
|
||||
|
@ -450,7 +456,7 @@ void BusManager::detectAndConfigureWmbusDevices(Configuration *config, Detection
|
|||
continue;
|
||||
}
|
||||
|
||||
Detected detected = detectWMBusDeviceWithFile(specified_device, config->default_device_linkmodes, serial_manager_);
|
||||
Detected detected = detectWMBusDeviceWithFileOrHex(specified_device, config->default_device_linkmodes, serial_manager_);
|
||||
|
||||
if (detected.found_type == DEVICE_UNKNOWN)
|
||||
{
|
||||
|
|
|
@ -539,19 +539,19 @@ shared_ptr<Configuration> parseCommandLine(int argc, char **argv) {
|
|||
error("Unknown option \"%s\"\n", argv[i]);
|
||||
}
|
||||
|
||||
bool found_at_least_one_device = false;
|
||||
bool found_at_least_one_device_or_hex = false;
|
||||
while (argv[i])
|
||||
{
|
||||
bool ok = handleDevice(c, argv[i]);
|
||||
bool ok = handleDeviceOrHex(c, argv[i]);
|
||||
if (ok)
|
||||
{
|
||||
found_at_least_one_device = true;
|
||||
found_at_least_one_device_or_hex = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!found_at_least_one_device)
|
||||
if (!found_at_least_one_device_or_hex)
|
||||
{
|
||||
error("At least one valid device must be supplied!\n");
|
||||
error("At least one valid device (or hex) must be supplied!\n");
|
||||
}
|
||||
// There are more arguments...
|
||||
break;
|
||||
|
|
|
@ -241,13 +241,23 @@ void handleResetAfter(Configuration *c, string s)
|
|||
}
|
||||
}
|
||||
|
||||
bool handleDevice(Configuration *c, string devicefile)
|
||||
bool handleDeviceOrHex(Configuration *c, string devicefilehex)
|
||||
{
|
||||
SpecifiedDevice specified_device;
|
||||
bool ok = specified_device.parse(devicefile);
|
||||
if (!ok && SpecifiedDevice::isLikelyDevice(devicefile))
|
||||
bool invalid_hex = false;
|
||||
bool is_hex = isHexString(devicefilehex, &invalid_hex);
|
||||
if (is_hex)
|
||||
{
|
||||
error("Not a valid device \"%s\"\n", devicefile.c_str());
|
||||
if (invalid_hex)
|
||||
{
|
||||
error("Hex string must have an even lenght of hexadecimal characters.\n");
|
||||
}
|
||||
}
|
||||
|
||||
SpecifiedDevice specified_device;
|
||||
bool ok = specified_device.parse(devicefilehex);
|
||||
if (!ok && SpecifiedDevice::isLikelyDevice(devicefilehex))
|
||||
{
|
||||
error("Not a valid device \"%s\"\n", devicefilehex.c_str());
|
||||
}
|
||||
|
||||
if (!ok) return false;
|
||||
|
@ -259,7 +269,7 @@ bool handleDevice(Configuration *c, string devicefile)
|
|||
{
|
||||
if (!specified_device.linkmodes.empty())
|
||||
{
|
||||
error("An mbus device must not have linkmode set. \"%s\"\n", devicefile.c_str());
|
||||
error("An mbus device must not have linkmode set. \"%s\"\n", devicefilehex.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -594,7 +604,7 @@ shared_ptr<Configuration> loadConfiguration(string root, string device_override,
|
|||
if (p.first == "loglevel") handleLoglevel(c, p.second);
|
||||
else if (p.first == "internaltesting") handleInternalTesting(c, p.second);
|
||||
else if (p.first == "ignoreduplicates") handleIgnoreDuplicateTelegrams(c, p.second);
|
||||
else if (p.first == "device") handleDevice(c, p.second);
|
||||
else if (p.first == "device") handleDeviceOrHex(c, p.second);
|
||||
else if (p.first == "donotprobe") handleDoNotProbe(c, p.second);
|
||||
else if (p.first == "listento") handleListenTo(c, p.second);
|
||||
else if (p.first == "logtelegrams") handleLogtelegrams(c, p.second);
|
||||
|
@ -653,7 +663,7 @@ shared_ptr<Configuration> loadConfiguration(string root, string device_override,
|
|||
device_override = "rtlwmbus";
|
||||
}
|
||||
debug("(config) overriding device with \"%s\"\n", device_override.c_str());
|
||||
handleDevice(c, device_override);
|
||||
handleDeviceOrHex(c, device_override);
|
||||
}
|
||||
if (listento_override != "")
|
||||
{
|
||||
|
|
|
@ -125,7 +125,7 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file);
|
|||
void handleConversions(Configuration *c, string s);
|
||||
void handleSelectedFields(Configuration *c, string s);
|
||||
void handleAddedFields(Configuration *c, string s);
|
||||
bool handleDevice(Configuration *c, string devicefile);
|
||||
bool handleDeviceOrHex(Configuration *c, string devicefilehex);
|
||||
|
||||
enum class LinkModeCalculationResultType
|
||||
{
|
||||
|
|
|
@ -42,6 +42,7 @@ void test_meters();
|
|||
void test_months();
|
||||
void test_aes();
|
||||
void test_sbc();
|
||||
void test_hex();
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
@ -71,6 +72,7 @@ int main(int argc, char **argv)
|
|||
test_months();
|
||||
test_aes();
|
||||
test_sbc();
|
||||
test_hex();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -982,3 +984,23 @@ void test_aes()
|
|||
printf("ERROR! aes encrypt decrypt (no iv) failed!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void test_is_hex(const char *hex, bool expected_ok, bool expected_invalid)
|
||||
{
|
||||
bool got_invalid;
|
||||
bool got_ok = isHexString(hex, &got_invalid);
|
||||
|
||||
if (got_ok != expected_ok || got_invalid != expected_invalid)
|
||||
{
|
||||
printf("ERROR! hex string %s was expected to be %d (invalid %d) but got %d (invalid %d)\n",
|
||||
hex,
|
||||
expected_ok, expected_invalid, got_ok, got_invalid);
|
||||
}
|
||||
}
|
||||
void test_hex()
|
||||
{
|
||||
test_is_hex("00112233445566778899aabbccddeeff", true, false);
|
||||
test_is_hex("00112233445566778899AABBCCDDEEFF", true, false);
|
||||
test_is_hex("00112233445566778899AABBCCDDEEF", true, true);
|
||||
test_is_hex("00112233445566778899AABBCCDDEEFG", false, false);
|
||||
}
|
||||
|
|
24
src/util.cc
24
src/util.cc
|
@ -158,6 +158,30 @@ uchar reverse(uchar c)
|
|||
return ((c&15)<<4) | (c>>4);
|
||||
}
|
||||
|
||||
bool isHexString(const string &txt, bool *invalid)
|
||||
{
|
||||
return isHexString(txt.c_str(), invalid);
|
||||
}
|
||||
|
||||
bool isHexString(const char* txt, bool *invalid)
|
||||
{
|
||||
*invalid = false;
|
||||
// An empty string is not an hex string.
|
||||
if (*txt == 0) return false;
|
||||
|
||||
const char *i = txt;
|
||||
int n = 0;
|
||||
for (;;)
|
||||
{
|
||||
char c = *i++;
|
||||
if (c == 0) break;
|
||||
n++;
|
||||
if (char2int(c) == -1) return false;
|
||||
}
|
||||
if (n%2 == 1) *invalid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hex2bin(const char* src, vector<uchar> *target)
|
||||
{
|
||||
if (!src) return false;
|
||||
|
|
|
@ -39,6 +39,8 @@ typedef unsigned char uchar;
|
|||
uchar bcd2bin(uchar c);
|
||||
uchar revbcd2bin(uchar c);
|
||||
uchar reverse(uchar c);
|
||||
bool isHexString(const char* txt, bool *invalid);
|
||||
bool isHexString(const std::string &txt, bool *invalid);
|
||||
bool hex2bin(const char* src, std::vector<uchar> *target);
|
||||
bool hex2bin(std::string &src, std::vector<uchar> *target);
|
||||
bool hex2bin(std::vector<uchar> &src, std::vector<uchar> *target);
|
||||
|
|
37
src/wmbus.cc
37
src/wmbus.cc
|
@ -4392,9 +4392,9 @@ bool is_command(string b, string *cmd)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool check_file(string f, bool *is_tty, bool *is_stdin, bool *is_file, bool *is_simulation)
|
||||
bool check_file(string f, bool *is_tty, bool *is_stdin, bool *is_file, bool *is_simulation, bool *is_hex_simulation)
|
||||
{
|
||||
*is_tty = *is_stdin = *is_file = *is_simulation = false;
|
||||
*is_tty = *is_stdin = *is_file = *is_simulation = *is_hex_simulation = false;
|
||||
if (f == "stdin")
|
||||
{
|
||||
*is_stdin = true;
|
||||
|
@ -4407,6 +4407,16 @@ bool check_file(string f, bool *is_tty, bool *is_stdin, bool *is_file, bool *is_
|
|||
// a file will confuse the user to no end....
|
||||
return false;
|
||||
}
|
||||
// A hex string becomes a simulation file with a single line containing a telegram defined by the hex string.
|
||||
bool invalid_hex = false;
|
||||
if (isHexString(f.c_str(), &invalid_hex))
|
||||
{
|
||||
*is_simulation = true;
|
||||
*is_hex_simulation = true;
|
||||
assert(!invalid_hex);
|
||||
return true;
|
||||
}
|
||||
// A command line arguments simulation_xxyyzz.txt is detected as a simulation file.
|
||||
if (checkIfSimulationFile(f.c_str()))
|
||||
{
|
||||
*is_simulation = true;
|
||||
|
@ -4526,6 +4536,7 @@ string SpecifiedDevice::str()
|
|||
r += bus_alias+"=";
|
||||
}
|
||||
if (file != "") r += file+":";
|
||||
if (hex != "") r += "<"+hex+">:";
|
||||
if (type != WMBusDeviceType::DEVICE_UNKNOWN)
|
||||
{
|
||||
r += toString(type);
|
||||
|
@ -4604,10 +4615,17 @@ bool SpecifiedDevice::parse(string &arg)
|
|||
// then the specified device string is faulty.
|
||||
return false;
|
||||
}
|
||||
if (!file_checked && check_file(p, &is_tty, &is_stdin, &is_file, &is_simulation))
|
||||
if (!file_checked && check_file(p, &is_tty, &is_stdin, &is_file, &is_simulation, &is_hex_simulation))
|
||||
{
|
||||
file_checked = true;
|
||||
file = p;
|
||||
if (!is_hex_simulation)
|
||||
{
|
||||
file = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
hex = p;
|
||||
}
|
||||
}
|
||||
else if (!typeidextras_checked && is_type_id_extras(p, &type, &id, &extras))
|
||||
{
|
||||
|
@ -4750,16 +4768,17 @@ Detected detectWMBusDeviceOnTTY(string tty,
|
|||
return detected;
|
||||
}
|
||||
|
||||
Detected detectWMBusDeviceWithFile(SpecifiedDevice &specified_device,
|
||||
Detected detectWMBusDeviceWithFileOrHex(SpecifiedDevice &specified_device,
|
||||
LinkModeSet default_linkmodes,
|
||||
shared_ptr<SerialCommunicationManager> handler)
|
||||
{
|
||||
assert(specified_device.file != "");
|
||||
assert(specified_device.file != "" || specified_device.hex != "");
|
||||
assert(specified_device.command == "");
|
||||
debug("(lookup) with file \"%s\"\n", specified_device.file.c_str());
|
||||
debug("(lookup) with file/hex \"%s%s\"\n", specified_device.file.c_str(), specified_device.hex.c_str());
|
||||
|
||||
Detected detected;
|
||||
detected.found_file = specified_device.file;
|
||||
detected.found_hex = specified_device.hex;
|
||||
detected.setSpecifiedDevice(specified_device);
|
||||
// If <device>:c1 is missing :c1 then use --c1.
|
||||
LinkModeSet lms = specified_device.linkmodes;
|
||||
|
@ -4769,8 +4788,8 @@ Detected detectWMBusDeviceWithFile(SpecifiedDevice &specified_device,
|
|||
}
|
||||
if (specified_device.is_simulation)
|
||||
{
|
||||
debug("(lookup) driver: simulation file\n");
|
||||
// A simulation file has a lms of all by default, eg no simulation_foo.txt:t1 nor --t1
|
||||
debug("(lookup) driver: simulation %s\n", specified_device.hex != "" ? "hex" : "file");
|
||||
// A simulation file/hex has a lms of all by default, eg no simulation_foo.txt:t1 nor --t1
|
||||
if (specified_device.linkmodes.empty()) lms.setAll();
|
||||
detected.setAsFound("", DEVICE_SIMULATION, 0 , false, lms);
|
||||
return detected;
|
||||
|
|
10
src/wmbus.h
10
src/wmbus.h
|
@ -157,7 +157,8 @@ struct SpecifiedDevice
|
|||
std::string bus_alias; // A bus alias, necessary for C2/T2 meters and mbus.
|
||||
int index; // 0,1,2,3 the order on the command line / config file.
|
||||
std::string file; // simulation_meter.txt, stdin, file.raw, /dev/ttyUSB0
|
||||
bool is_tty{}, is_stdin{}, is_file{}, is_simulation{};
|
||||
std::string hex; // a hex string supplied on the command line becomes a hex simulation.
|
||||
bool is_tty{}, is_stdin{}, is_file{}, is_simulation{}, is_hex_simulation{};
|
||||
WMBusDeviceType type; // im871a, rtlwmbus
|
||||
std::string id; // 12345678 for wmbus dongles or 0,1 for rtlwmbus indexes.
|
||||
std::string extras; // Extra device specific settings.
|
||||
|
@ -180,6 +181,7 @@ struct Detected
|
|||
SpecifiedDevice specified_device {}; // Device as specified from the command line / config file.
|
||||
|
||||
string found_file; // The device file to use.
|
||||
string found_hex; // An immediate hex string is supplied.
|
||||
string found_device_id; // An "unique" identifier, typically the id used by the dongle as its own wmbus id, if it transmits.
|
||||
WMBusDeviceType found_type {}; // IM871A, AMB8465 etc.
|
||||
int found_bps {}; // Serial speed of tty.
|
||||
|
@ -610,9 +612,9 @@ struct WMBus
|
|||
virtual ~WMBus() = 0;
|
||||
};
|
||||
|
||||
Detected detectWMBusDeviceWithFile(SpecifiedDevice &specified_device,
|
||||
LinkModeSet default_linkmodes,
|
||||
shared_ptr<SerialCommunicationManager> manager);
|
||||
Detected detectWMBusDeviceWithFileOrHex(SpecifiedDevice &specified_device,
|
||||
LinkModeSet default_linkmodes,
|
||||
shared_ptr<SerialCommunicationManager> manager);
|
||||
Detected detectWMBusDeviceWithCommand(SpecifiedDevice &specified_device,
|
||||
LinkModeSet default_linkmodes,
|
||||
shared_ptr<SerialCommunicationManager> handler);
|
||||
|
|
|
@ -47,7 +47,7 @@ struct WMBusSimulator : public WMBusCommonImplementation
|
|||
void simulate();
|
||||
string device() { return file_; }
|
||||
|
||||
WMBusSimulator(string alias, string file, shared_ptr<SerialCommunicationManager> manager);
|
||||
WMBusSimulator(string alias, string file, string hex, shared_ptr<SerialCommunicationManager> manager);
|
||||
|
||||
private:
|
||||
vector<uchar> received_payload_;
|
||||
|
@ -62,15 +62,23 @@ shared_ptr<WMBus> openSimulator(Detected detected, shared_ptr<SerialCommunicatio
|
|||
{
|
||||
string bus_alias = detected.specified_device.bus_alias;
|
||||
string device = detected.found_file;
|
||||
WMBusSimulator *imp = new WMBusSimulator(bus_alias, device, manager);
|
||||
string hex = detected.found_hex;
|
||||
WMBusSimulator *imp = new WMBusSimulator(bus_alias, device, hex, manager);
|
||||
return shared_ptr<WMBus>(imp);
|
||||
}
|
||||
|
||||
WMBusSimulator::WMBusSimulator(string bus_alias, string file, shared_ptr<SerialCommunicationManager> manager)
|
||||
WMBusSimulator::WMBusSimulator(string bus_alias, string file, string hex, shared_ptr<SerialCommunicationManager> manager)
|
||||
: WMBusCommonImplementation(bus_alias, DEVICE_SIMULATION, manager, NULL, false), file_(file)
|
||||
{
|
||||
assert(file != "");
|
||||
loadFile(file, &lines_);
|
||||
assert(file != "" || hex != "");
|
||||
if (hex != "")
|
||||
{
|
||||
lines_.push_back("telegram="+hex);
|
||||
}
|
||||
if (file != "")
|
||||
{
|
||||
loadFile(file, &lines_);
|
||||
}
|
||||
}
|
||||
|
||||
bool WMBusSimulator::ping()
|
||||
|
|
3
test.sh
3
test.sh
|
@ -111,6 +111,9 @@ if [ "$?" != "0" ]; then RC="1"; fi
|
|||
./tests/test_meter_extras.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
./tests/test_hex_cmdline.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
if [ -x ../additional_tests.sh ]
|
||||
then
|
||||
(cd ..; ./additional_tests.sh $PROG)
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
#!/bin/sh
|
||||
|
||||
PROG="$1"
|
||||
TEST=testoutput
|
||||
mkdir -p $TEST
|
||||
|
||||
TESTNAME="Test hex on commandline"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
No meters configured. Printing id:s of all telegrams heard!
|
||||
Received telegram from: 76348799
|
||||
manufacturer: (KAM) Kamstrup Energi (0x2c2d)
|
||||
type: Cold water meter (0x16)
|
||||
ver: 0x1b
|
||||
driver: multical21
|
||||
EOF
|
||||
|
||||
$PROG 2A442D2C998734761B168D2091D37CAC21E1D68CDAFFCD3DC452BD802913FF7B1706CA9E355D6C2701CC24 2> $TEST/test_output.txt 1>&2
|
||||
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
diff $TEST/test_expected.txt $TEST/test_output.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
echo OK: $TESTNAME
|
||||
TESTRESULT="OK"
|
||||
fi
|
||||
else
|
||||
echo "wmbusmeters returned error code: $?"
|
||||
cat $TEST/test_output.txt
|
||||
fi
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
{"media":"cold water","meter":"multical21","name":"MyWater","id":"76348799","total_m3":6.408,"target_m3":6.408,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||
EOF
|
||||
|
||||
TESTNAME="Test hex on commandline with meter"
|
||||
|
||||
$PROG --format=json 2A442D2C998734761B168D2091D37CAC21E1D68CDAFFCD3DC452BD802913FF7B1706CA9E355D6C2701CC24 \
|
||||
MyWater auto 76348799 28F64A24988064A079AA2C807D6102AE > $TEST/test_output.txt 2> $TEST/test_stderr.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 OK: $TESTNAME
|
||||
TESTRESULT="OK"
|
||||
fi
|
||||
else
|
||||
echo "wmbusmeters returned error code: $?"
|
||||
cat $TEST/test_output.txt
|
||||
cat $TEST/test_stderr.txt
|
||||
fi
|
||||
|
||||
TESTNAME="Test invalid hex on commandline"
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
Hex string must have an even lenght of hexadecimal characters.
|
||||
EOF
|
||||
|
||||
$PROG 2A442D2C998734761B168D2091D37CAC21E1D68CDAFFCD3DC452BD802913FF7B1706CA9E355D6C2701CC2 2> $TEST/test_output.txt 1>&2
|
||||
|
||||
if [ "$?" = "1" ]
|
||||
then
|
||||
diff $TEST/test_expected.txt $TEST/test_output.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
echo OK: $TESTNAME
|
||||
TESTRESULT="OK"
|
||||
fi
|
||||
else
|
||||
echo "wmbusmeters returned error code 0 but we expected failure!"
|
||||
echo "======================="
|
||||
cat $TEST/test_output.txt
|
||||
echo "======================="
|
||||
cat $TEST/test_stderr.txt
|
||||
fi
|
Ładowanie…
Reference in New Issue