Added donotprobe=<device> and fixed so that it does not probe if a device is specified or rtlwmbus is specified.

pull/179/head
Fredrik Öhrström 2020-11-01 15:18:13 +01:00
rodzic 171887e2f9
commit 5e273ddb7e
21 zmienionych plików z 440 dodań i 287 usunięć

Wyświetl plik

@ -1,4 +1,12 @@
Added --donotprobe=/dev/ttyUSB0
(Also made sure that specifying only rtlwmbus/rtl433 will not probe
serial ttys. Specifying fully specified devices like
/dev/ttyUSB0:im871a:c1 will also not probe the other serial ttys.
Only specifying auto or a generic device type im871a that requires a
serial tty, will probe the serial devices, exluding donotprobe ones.)
Added support for the sontex868 heat cost allocator.
Version 1.0.1: 2020-10-26

Wyświetl plik

@ -49,11 +49,24 @@ You can trigger a reload of the config files with `sudo killall -HUP wmbusmeters
(Note! make install only works for GNU/Linux. For MacOSX try to start
`wmbusmetersd /tmp/thepidfile` from a script instead.)
Check the config file /etc/wmbusmeters.conf and edit the device to
point to your dongle or use auto
Check the config file /etc/wmbusmeters.conf and edit the device. For example:
`auto:c1` or `im871a:c1`
Adding a device like auto or im871a will trigger an automatic probe of all serial ttys.
If you specify a full device path like `/dev/ttyUSB0:im871a:c1` or `rtlwmbus` or `rtl433`
then it will not probe the serial devices. If you must be really sure that it will not probe something
you can add `donotprobe=/dev/ttyUSB0` or `donotprobe=all`.
You can specify combinations like: `device=rc1180:t1` `device=auto:c1`
to set the rc1180 dongle to t1 but any other auto-detected dongle to c1.
```
loglevel=normal
device=im871a:c1
# Search for a wmbus device and set it to c1.
device=auto:c1
# But do not probe this serial tty.
donotprobe=/dev/ttyACM2
logtelegrams=false
format=json
meterfiles=/var/log/wmbusmeters/meter_readings
@ -147,6 +160,7 @@ As <options> you can use:
--alarmshell=<cmdline> invokes cmdline when an alarm triggers
--alarmtimeout=<time> Expect a telegram to arrive within <time> seconds, eg 60s, 60m, 24h during expected activity.
--debug for a lot of information
--donotprobe=<tty> do not auto-probe this tty. Use multiple times for several ttys or specify "all" for all ttys.
--exitafter=<time> exit program after time, eg 20h, 10m 5s
--format=<hr/json/fields> for human readable, json or semicolon separated fields
--json_xxx=yyy always add "xxx"="yyy" to the json output and add shell env METER_xxx=yyy

Wyświetl plik

@ -107,27 +107,37 @@ shared_ptr<Configuration> parseCommandLine(int argc, char **argv) {
i++;
continue;
}
if (!strncmp(argv[i], "--listento=", 11)) {
if (!strncmp(argv[i], "--donotprobe=", 13))
{
string df = string(argv[i]+13);
debug("(cmdline) do not probe \"%s\"\n", df.c_str());
c->do_not_probe_ttys.insert(df);
i++;
continue;
}
if (!strncmp(argv[i], "--listento=", 11))
{
LinkModeSet lms = parseLinkModes(argv[i]+11);
if (lms.bits() == 0) {
error("Unknown link mode \"%s\"!\n", argv[i]+11);
if (lms.empty())
{
error("Unknown default link mode \"%s\"!\n", argv[i]+11);
}
if (c->linkmodes_configured) {
error("You have already specified a link mode!\n");
if (!c->default_device_linkmodes.empty()) {
error("You have already specified a default link mode!\n");
}
c->linkmodes = lms;
c->linkmodes_configured = true;
c->default_device_linkmodes = lms;
i++;
continue;
}
LinkMode lm = isLinkModeOption(argv[i]);
if (lm != LinkMode::UNKNOWN) {
if (c->linkmodes_configured) {
error("You have already specified a link mode!\n");
if (!c->default_device_linkmodes.empty())
{
error("You have already specified a default link mode!\n");
}
c->linkmodes.addLinkMode(lm);
c->linkmodes_configured = true;
// Add to the empty set.
c->default_device_linkmodes.addLinkMode(lm);
i++;
continue;
}
@ -476,7 +486,7 @@ shared_ptr<Configuration> parseCommandLine(int argc, char **argv) {
}
if (c->supplied_wmbus_devices.size() == 0 &&
c->use_auto_detect == false &&
c->use_auto_device_detect == false &&
!c->list_shell_envs &&
!c->list_fields &&
!c->list_meters)

Wyświetl plik

@ -229,31 +229,26 @@ bool handleDevice(Configuration *c, string devicefile)
// Number the devices
specified_device.index = c->supplied_wmbus_devices.size();
if (specified_device.linkmodes != "")
{
// Prevent an early warning in start
// since we at least have one configured linkmode.
c->linkmodes_configured = true;
c->linkmodes.unionLinkModeSet(parseLinkModes(specified_device.linkmodes));
}
else
if (specified_device.linkmodes.empty())
{
// No linkmode set, but if simulation, stdin and file,
// then assume that it will produce telegrams on all linkmodes.
if (specified_device.is_simulation || specified_device.is_stdin || specified_device.is_file)
{
c->linkmodes_configured = true;
c->linkmodes.unionLinkModeSet(LinkModeBits::Any_bit);
// Essentially link mode calculations are now irrelevant.
specified_device.linkmodes.addLinkMode(LinkMode::Any);
}
if (specified_device.type == "rtlwmbus")
else
if (specified_device.type == WMBusDeviceType::DEVICE_RTLWMBUS ||
specified_device.type == WMBusDeviceType::DEVICE_RTL433)
{
c->linkmodes_configured = true;
c->linkmodes.addLinkMode(LinkMode::C1);
c->linkmodes.addLinkMode(LinkMode::T1);
c->all_device_linkmodes_specified.addLinkMode(LinkMode::C1);
c->all_device_linkmodes_specified.addLinkMode(LinkMode::T1);
}
}
c->all_device_linkmodes_specified.unionLinkModeSet(specified_device.linkmodes);
if (specified_device.is_stdin ||
specified_device.is_file ||
specified_device.is_simulation ||
@ -263,16 +258,19 @@ bool handleDevice(Configuration *c, string devicefile)
{
error("You can only specify one stdin or one file or one command!\n");
}
if (c->use_auto_detect)
if (c->use_auto_device_detect)
{
error("You cannot mix auto with stdin or a file.\n");
}
if (specified_device.is_simulation) c->simulation_found = true;
c->single_device_override = true;
}
if (specified_device.type == "auto")
if (specified_device.type == WMBusDeviceType::DEVICE_AUTO)
{
c->use_auto_detect = true;
c->use_auto_device_detect = true;
c->auto_device_linkmodes = specified_device.linkmodes;
#if defined(__APPLE__) && defined(__MACH__)
error("You cannot use auto on macosx. You must specify the device tty or rtlwmbus.\n");
#endif
@ -285,20 +283,25 @@ bool handleDevice(Configuration *c, string devicefile)
return ok;
}
bool handleDoNotProbe(Configuration *c, string devicefile)
{
c->do_not_probe_ttys.insert(devicefile);
return true;
}
void handleListenTo(Configuration *c, string mode)
{
LinkModeSet lms = parseLinkModes(mode.c_str());
if (lms.bits() == 0)
if (lms.empty())
{
error("Unknown link mode \"%s\"!\n", mode.c_str());
error("Unknown link modes \"%s\"!\n", mode.c_str());
}
if (c->linkmodes_configured)
if (!c->default_device_linkmodes.empty())
{
error("You have already specified a link mode!\n");
error("You have already specified the default link modes!\n");
}
c->linkmodes = lms;
c->linkmodes_configured = true;
c->default_device_linkmodes = lms;
}
void handleLogtelegrams(Configuration *c, string logtelegrams)
@ -524,6 +527,7 @@ shared_ptr<Configuration> loadConfiguration(string root, string device_override,
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 == "donotprobe") handleDoNotProbe(c, p.second);
else if (p.first == "listento") handleListenTo(c, p.second);
else if (p.first == "logtelegrams") handleLogtelegrams(c, p.second);
else if (p.first == "meterfiles") handleMeterfiles(c, p.second);
@ -602,45 +606,21 @@ LinkModeCalculationResult calculateLinkModes(Configuration *config, WMBus *wmbus
}
string metersu = meters_union.hr();
debug("(config) all possible link modes that the meters might transmit on: %s\n", metersu.c_str());
if (meters_union.bits() == 0)
if (meters_union.empty())
{
if (link_modes_matter && !config->linkmodes_configured)
if (link_modes_matter && config->all_device_linkmodes_specified.empty())
{
string msg;
strprintf(msg,"(config) No meters supplied. You must supply which link modes to listen to. Eg. --listento=<modes>");
strprintf(msg,"(config) No meters supplied. You must supply which link modes to listen to. 22 Eg. auto:t1");
debug("%s\n", msg.c_str());
return { LinkModeCalculationResultType::NoMetersMustSupplyModes , msg};
}
return { LinkModeCalculationResultType::Success, "" };
}
if (!config->linkmodes_configured)
{
// A listen_to link mode has not been set explicitly. Pick a listen_to link
// mode that is supported by the wmbus dongle and works for the meters.
config->linkmodes = wmbus->supportedLinkModes();
config->linkmodes.disjunctionLinkModeSet(meters_union);
if (!wmbus->canSetLinkModes(config->linkmodes))
{
// The automatically calculated link modes cannot be set in the dongle.
// Ie the dongle needs help....
string msg;
strprintf(msg,"(config) Automatic deduction of which link mode to listen to failed since the meters might transmit using: %s\n"
"(config) But the dongle can only listen to: %s Please supply the exact link mode(s) to listen to, eg: --listento=<mode>\n"
"(config) and/or specify the expected transmit modes for the meters, eg: apator162:t1\n"
"(config) or use a dongle that can listen to all the required link modes at the same time.",
metersu.c_str(), dongle.c_str());
debug("%s\n", msg.c_str());
return { LinkModeCalculationResultType::AutomaticDeductionFailed , msg};
}
config->linkmodes_configured = true;
}
else
{
string listen = config->linkmodes.hr();
debug("(config) explicitly listening to: %s\n", listen.c_str());
}
string all_lms = config->all_device_linkmodes_specified.hr();
verbose("(config) all specified link modes: %s\n", all_lms.c_str());
/*
string listen = config->linkmodes.hr();
if (!wmbus->canSetLinkModes(config->linkmodes))
{
@ -650,19 +630,28 @@ LinkModeCalculationResult calculateLinkModes(Configuration *config, WMBus *wmbus
debug("%s\n", msg.c_str());
return { LinkModeCalculationResultType::DongleCannotListenTo , msg};
}
if (!config->linkmodes.hasAll(meters_union))
*/
/*
string listen = config->linkmodes.hr();
if (!wmbus->canSetLinkModes(config->linkmodes))
{
string msg;
strprintf(msg, "(config) You have specified to listen to the link modes: %s but the dongle can only listen to: %s",
listen.c_str(), dongle.c_str());
debug("%s\n", msg.c_str());
return { LinkModeCalculationResultType::DongleCannotListenTo , msg};
}
*/
if (!config->all_device_linkmodes_specified.hasAll(meters_union))
{
string msg;
strprintf(msg, "(config) You have specified to listen to the link modes: %s but the meters might transmit on: %s\n"
"(config) Therefore you might miss telegrams! Please specify the expected transmit mode for the meters, eg: apator162:t1\n"
"(config) Or use a dongle that can listen to all the required link modes at the same time.",
listen.c_str(), metersu.c_str());
all_lms.c_str(), metersu.c_str());
debug("%s\n", msg.c_str());
return { LinkModeCalculationResultType::MightMissTelegrams, msg};
}
debug("(config) listen link modes calculated to be: %s\n", listen.c_str());
return { LinkModeCalculationResultType::Success, "" };
}

Wyświetl plik

@ -22,6 +22,7 @@
#include"util.h"
#include"wmbus.h"
#include"meters.h"
#include<set>
#include<vector>
using namespace std;
@ -86,11 +87,17 @@ struct Configuration
int exitafter {}; // Seconds to exit.
int resetafter {}; // Reset the wmbus devices regularly.
std::vector<SpecifiedDevice> supplied_wmbus_devices; // /dev/ttyUSB0, simulation.txt, rtlwmbus, /dev/ttyUSB1:9600
bool use_auto_detect {}; // Set to true if auto was supplied as device.
bool use_auto_device_detect {}; // Set to true if auto was supplied as device.
std::set<std::string> do_not_probe_ttys; // Do not probe these ttys! all = all of them.
LinkModeSet auto_device_linkmodes; // The linkmodes specified by auto:c1,t1
bool single_device_override {}; // Set to true if there is a stdin/file or simulation device.
bool simulation_found {};
LinkModeSet linkmodes; // If --c1 or auto:c1 then store c1 here.
bool linkmodes_configured {}; // Either auto:c1 or --c1 is specified.
LinkModeSet default_device_linkmodes; // Backwards compatible --listento=c1 or --c1 will set the default_linkmodes.
// A device without a :t1 suffix, will use this linkmode.
// Is empty when not set.
LinkModeSet all_device_linkmodes_specified; // A union of all device specified linkmodes.
// Eventually not all devices might be found, so a realtime check is done later.
LinkModeSet all_meters_linkmodes_specified; // A union of all meters linkmodes.
string telegram_reader;
// A set of all link modes (union) that the user requests the wmbus dongle to listen to.
bool no_init {};

Wyświetl plik

@ -58,7 +58,6 @@ void list_meters(Configuration *config);
void log_start_information(Configuration *config);
void oneshot_check(Configuration *config, Telegram *t, Meter *meter);
void open_wmbus_device_and_set_linkmodes(Configuration *config, string how, Detected *detected);
void perform_auto_scan_of_devices(Configuration *config);
void perform_auto_scan_of_serial_devices(Configuration *config);
void perform_auto_scan_of_swradio_devices(Configuration *config);
void regular_checkup(Configuration *config);
@ -325,6 +324,10 @@ shared_ptr<WMBus> create_wmbus_object(Detected *detected, Configuration *config,
switch (detected->found_type)
{
case DEVICE_AUTO:
assert(0);
error("Internal error DEVICE_AUTO should not be used here!\n");
break;
case DEVICE_IM871A:
verbose("(im871a) on %s\n", detected->found_file.c_str());
wmbus = openIM871A(detected->found_file, manager, serial_override);
@ -486,14 +489,24 @@ void detect_and_configure_wmbus_devices(Configuration *config)
{
check_for_dead_wmbus_devices(config);
bool must_auto_find = false;
bool must_auto_find_ttys = false;
bool must_auto_find_rtlsdrs = false;
// The device=auto has been specified....
if (config->use_auto_device_detect)
{
must_auto_find_ttys = true;
must_auto_find_rtlsdrs = true;
}
for (SpecifiedDevice &specified_device : config->supplied_wmbus_devices)
{
specified_device.handled = false;
if (specified_device.file == "" && specified_device.command == "")
{
// File/tty/command not specified, use auto scan later to find actual device file/tty.
must_auto_find = true;
must_auto_find_ttys |= usesTTY(specified_device.type);
must_auto_find_rtlsdrs |= usesRTLSDR(specified_device.type);
continue;
}
if (specified_device.command != "")
@ -506,8 +519,7 @@ void detect_and_configure_wmbus_devices(Configuration *config)
specified_device.handled = true;
continue;
}
Detected detected = detectWMBusDeviceWithCommand(specified_device, serial_manager_);
Detected detected = detectWMBusDeviceWithCommand(specified_device, config->default_device_linkmodes, serial_manager_);
specified_device.handled = true;
open_wmbus_device_and_set_linkmodes(config, "config", &detected);
}
@ -534,7 +546,7 @@ void detect_and_configure_wmbus_devices(Configuration *config)
continue;
}
Detected detected = detectWMBusDeviceWithFile(specified_device, serial_manager_);
Detected detected = detectWMBusDeviceWithFile(specified_device, config->default_device_linkmodes, serial_manager_);
if (detected.found_type == DEVICE_UNKNOWN)
{
@ -552,9 +564,14 @@ void detect_and_configure_wmbus_devices(Configuration *config)
specified_device.handled = true;
}
if (config->use_auto_detect || must_auto_find)
if (must_auto_find_ttys)
{
perform_auto_scan_of_devices(config);
perform_auto_scan_of_serial_devices(config);
}
if (must_auto_find_rtlsdrs)
{
perform_auto_scan_of_swradio_devices(config);
}
for (shared_ptr<WMBus> &wmbus : wmbus_devices_)
@ -588,7 +605,7 @@ bool find_specified_device_and_update_detected(Configuration *c, Detected *d)
// This will find specified devices like: im871a[12345678]
for (SpecifiedDevice & sd : c->supplied_wmbus_devices)
{
if (sd.file == "" && sd.id != "" && sd.id == d->found_device_id && toWMBusDeviceType(sd.type) == d->found_type)
if (sd.file == "" && sd.id != "" && sd.id == d->found_device_id && sd.type == d->found_type)
{
d->specified_device = sd;
debug("(main) found specified device (%s) that matches detected device (%s)\n",
@ -602,7 +619,7 @@ bool find_specified_device_and_update_detected(Configuration *c, Detected *d)
// This will find specified devices like: im871a, rtlwmbus
for (SpecifiedDevice & sd : c->supplied_wmbus_devices)
{
if (sd.file == "" && sd.id == "" && toWMBusDeviceType(sd.type) == d->found_type)
if (sd.file == "" && sd.id == "" && sd.type == d->found_type)
{
d->specified_device = sd;
debug("(main) found specified device (%s) that matches detected device (%s)\n",
@ -621,7 +638,7 @@ void find_specified_device_and_mark_as_handled(Configuration *c, Detected *d)
// This will find specified devices like: im871a[12345678]
for (SpecifiedDevice & sd : c->supplied_wmbus_devices)
{
if (sd.file == "" && sd.id != "" && sd.id == d->found_device_id && toWMBusDeviceType(sd.type) == d->found_type)
if (sd.file == "" && sd.id != "" && sd.id == d->found_device_id && sd.type == d->found_type)
{
sd.handled = true;
}
@ -631,7 +648,7 @@ void find_specified_device_and_mark_as_handled(Configuration *c, Detected *d)
// This will find specified devices like: im871a, rtlwmbus
for (SpecifiedDevice & sd : c->supplied_wmbus_devices)
{
if (sd.file == "" && sd.id == "" && toWMBusDeviceType(sd.type) == d->found_type)
if (sd.file == "" && sd.id == "" && sd.type == d->found_type)
{
sd.handled = true;
}
@ -743,30 +760,14 @@ void open_wmbus_device_and_set_linkmodes(Configuration *config, string how, Dete
if (detected->found_type == DEVICE_UNKNOWN)
{
// This is a manual config, lets detect and check the device properly.
AccessCheck ac = detectDevice(detected, serial_manager_);
AccessCheck ac = reDetectDevice(detected, serial_manager_);
if (ac != AccessCheck::AccessOK)
{
error("Could not open device %s\n", detected->specified_device.str().c_str());
}
}
LinkModeSet lms {};
string linkmodes = detected->specified_device.linkmodes;
if (linkmodes != "")
{
lms = parseLinkModes(detected->specified_device.linkmodes);
debug("(main) parsed specified device link modes %s to %s\n",
linkmodes.c_str(), lms.hr().c_str());
}
else
{
lms = config->linkmodes;
debug("(main) using globally set link modes %s\n",
lms.hr().c_str());
}
assert(lms.bits() != 0);
LinkModeSet lms = detected->calculated_linkmodes;
string using_link_modes = lms.hr();
string id = detected->found_device_id.c_str();
@ -791,7 +792,7 @@ void open_wmbus_device_and_set_linkmodes(Configuration *config, string how, Dete
cmd.c_str());
// A newly plugged in device has been manually configured or automatically detected! Start using it!
if (config->use_auto_detect || detected->found_type != DEVICE_SIMULATION)
if (config->use_auto_device_detect || detected->found_type != DEVICE_SIMULATION)
{
notice("%s", started.c_str());
}
@ -840,12 +841,6 @@ void open_wmbus_device_and_set_linkmodes(Configuration *config, string how, Dete
wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity);
}
void perform_auto_scan_of_devices(Configuration *config)
{
perform_auto_scan_of_serial_devices(config);
perform_auto_scan_of_swradio_devices(config);
}
void perform_auto_scan_of_serial_devices(Configuration *config)
{
// Enumerate all serial devices that might connect to a wmbus device.
@ -862,12 +857,27 @@ void perform_auto_scan_of_serial_devices(Configuration *config)
trace("[MAIN] skipping already probed not wmbus serial device %s\n", tty.c_str());
continue;
}
if (config->do_not_probe_ttys.count("all") > 0 ||
config->do_not_probe_ttys.count(tty) > 0)
{
trace("[MAIN] not probing forbidden tty %s\n", tty.c_str());
continue;
}
shared_ptr<SerialDevice> sd = serial_manager_->lookup(tty);
if (!sd)
{
debug("(main) device %s not currently used, detect contents...\n", tty.c_str());
// This serial device is not in use, but is there a device on it?
Detected detected = detectWMBusDeviceOnTTY(tty, serial_manager_);
debug("(main) device %s not currently used, detect contents...\n", tty.c_str());
// What should the desired linkmodes be? We have no specified device since this an auto detect.
// But we might have an auto linkmodes?
LinkModeSet desired_linkmodes = config->auto_device_linkmodes;
if (desired_linkmodes.empty())
{
// Nope, lets fall back on the default_linkmodes.
desired_linkmodes = config->default_device_linkmodes;
}
Detected detected = detectWMBusDeviceOnTTY(tty, desired_linkmodes, serial_manager_);
if (detected.found_type != DEVICE_UNKNOWN)
{
// See if we had a specified device without a file,
@ -1047,9 +1057,9 @@ bool start(Configuration *config)
// Configure where the logging information should end up.
setup_log_file(config);
if (config->meters.size() == 0 && !config->linkmodes_configured)
if (config->meters.size() == 0 && config->all_device_linkmodes_specified.empty())
{
error("No meters supplied. You must supply which link modes to listen to. Eg. --listento=<modes>\n");
error("No meters supplied. You must supply which link modes to listen to. 11 Eg. auto:c1\n");
}
// Configure settings.

Wyświetl plik

@ -103,6 +103,11 @@ int indexFromRtlSdrSerial(std::string serialnr)
AccessCheck detectRTLSDR(string serialnr, Detected *detected)
{
if (detected->specified_device.type != WMBusDeviceType::DEVICE_RTLWMBUS ||
detected->specified_device.type != WMBusDeviceType::DEVICE_RTL433)
{
return AccessCheck::NotThere;
}
uint32_t n = rtlsdr_get_device_count();
char mfct[256];
@ -112,9 +117,12 @@ AccessCheck detectRTLSDR(string serialnr, Detected *detected)
for (uint32_t i=0; i<n; ++i)
{
rtlsdr_get_device_usb_strings(i, mfct, product, serial);
if (serialnr == serial)
if (serialnr == "" || serialnr == serial)
{
detected->setAsFound(serialnr, WMBusDeviceType::DEVICE_RTLWMBUS, 0, false, false);
LinkModeSet lms;
lms.addLinkMode(LinkMode::C1);
lms.addLinkMode(LinkMode::T1);
detected->setAsFound(serialnr, detected->specified_device.type, 0, false, false, lms);
return AccessCheck::AccessOK;
}
}

Wyświetl plik

@ -244,7 +244,6 @@ int test_linkmodes()
Configuration nometers_config;
// Check that if no meters are supplied then you must set a link mode.
nometers_config.linkmodes_configured = false;
lmcr = calculateLinkModes(&nometers_config, wmbus_im871a.get());
if (lmcr.type != LinkModeCalculationResultType::NoMetersMustSupplyModes)
{
@ -252,8 +251,7 @@ int test_linkmodes()
}
debug("test0 OK\n\n");
nometers_config.linkmodes_configured = true;
nometers_config.linkmodes.addLinkMode(LinkMode::T1);
nometers_config.default_device_linkmodes.addLinkMode(LinkMode::T1);
lmcr = calculateLinkModes(&nometers_config, wmbus_im871a.get());
if (lmcr.type != LinkModeCalculationResultType::Success)
{
@ -271,7 +269,6 @@ int test_linkmodes()
// Check that if no explicit link modes are provided to apator162, then
// automatic deduction will fail, since apator162 can be configured to transmit
// either C1 or T1 telegrams.
apator_config.linkmodes_configured = false;
lmcr = calculateLinkModes(&apator_config, wmbus_im871a.get());
if (lmcr.type != LinkModeCalculationResultType::AutomaticDeductionFailed)
{
@ -281,10 +278,9 @@ int test_linkmodes()
// Check that if we supply the link mode T1 when using an apator162, then
// automatic deduction will succeeed.
apator_config.linkmodes_configured = true;
apator_config.linkmodes = LinkModeSet();
apator_config.linkmodes.addLinkMode(LinkMode::T1);
apator_config.linkmodes.addLinkMode(LinkMode::C1);
apator_config.default_device_linkmodes = LinkModeSet();
apator_config.default_device_linkmodes.addLinkMode(LinkMode::T1);
apator_config.default_device_linkmodes.addLinkMode(LinkMode::C1);
lmcr = calculateLinkModes(&apator_config, wmbus_im871a.get());
if (lmcr.type != LinkModeCalculationResultType::DongleCannotListenTo)
{
@ -313,7 +309,6 @@ int test_linkmodes()
// Check that meters that transmit on two different link modes cannot be listened to
// at the same time using im871a.
multical21_and_supercom587_config.linkmodes_configured = false;
lmcr = calculateLinkModes(&multical21_and_supercom587_config, wmbus_im871a.get());
if (lmcr.type != LinkModeCalculationResultType::AutomaticDeductionFailed)
{
@ -322,9 +317,8 @@ int test_linkmodes()
debug("test4 OK\n\n");
// Explicitly set T1
multical21_and_supercom587_config.linkmodes_configured = true;
multical21_and_supercom587_config.linkmodes = LinkModeSet();
multical21_and_supercom587_config.linkmodes.addLinkMode(LinkMode::T1);
multical21_and_supercom587_config.default_device_linkmodes = LinkModeSet();
multical21_and_supercom587_config.default_device_linkmodes.addLinkMode(LinkMode::T1);
lmcr = calculateLinkModes(&multical21_and_supercom587_config, wmbus_im871a.get());
if (lmcr.type != LinkModeCalculationResultType::MightMissTelegrams)
{
@ -333,9 +327,8 @@ int test_linkmodes()
debug("test5 OK\n\n");
// Explicitly set N1a, but the meters transmit on C1 and T1.
multical21_and_supercom587_config.linkmodes_configured = true;
multical21_and_supercom587_config.linkmodes = LinkModeSet();
multical21_and_supercom587_config.linkmodes.addLinkMode(LinkMode::N1a);
multical21_and_supercom587_config.default_device_linkmodes = LinkModeSet();
multical21_and_supercom587_config.default_device_linkmodes.addLinkMode(LinkMode::N1a);
lmcr = calculateLinkModes(&multical21_and_supercom587_config, wmbus_im871a.get());
if (lmcr.type != LinkModeCalculationResultType::MightMissTelegrams)
{
@ -344,9 +337,8 @@ int test_linkmodes()
debug("test6 OK\n\n");
// Explicitly set N1a, but it is an amber dongle.
multical21_and_supercom587_config.linkmodes_configured = true;
multical21_and_supercom587_config.linkmodes = LinkModeSet();
multical21_and_supercom587_config.linkmodes.addLinkMode(LinkMode::N1a);
multical21_and_supercom587_config.default_device_linkmodes = LinkModeSet();
multical21_and_supercom587_config.default_device_linkmodes.addLinkMode(LinkMode::N1a);
lmcr = calculateLinkModes(&multical21_and_supercom587_config, wmbus_amb8465.get());
if (lmcr.type != LinkModeCalculationResultType::DongleCannotListenTo)
{
@ -526,12 +518,13 @@ void testd(string arg, bool xok, string xfile, string xtype, string xid, string
return;
}
if (ok == false) return;
if (d.file != xfile ||
d.type != xtype ||
toString(d.type) != xtype ||
d.id != xid ||
d.fq != xfq ||
d.bps != xbps ||
d.linkmodes != xlm ||
d.linkmodes.hr() != xlm ||
d.command != xcmd)
{
printf("ERROR in device parsing parts \"%s\"\n", arg.c_str());
@ -573,7 +566,7 @@ void test_devices()
"", // id
"", // fq
"", // bps
"", // linkmodes
"none", // linkmodes
""); // command
testd("/dev/ttyUSB0:rawtty:9600", true,
@ -582,7 +575,7 @@ void test_devices()
"", // id
"", // fq
"9600", // bps
"", // linkmodes
"none", // linkmodes
""); // command
// testinternals must be run from a location where
@ -593,7 +586,7 @@ void test_devices()
"", // id
"", // fq
"", // bps
"", // linkmodes
"none", // linkmodes
""); // command
testd("auto:c1,t1", true,
@ -611,7 +604,7 @@ void test_devices()
"", // id
"", // fq
"", // bps
"", // linkmodes
"none", // linkmodes
""); // command
testd("Vatten", false,
@ -620,7 +613,7 @@ void test_devices()
"", // id
"", // fq
"", // bps
"", // linkmodes
"none", // linkmodes
""); // command

Wyświetl plik

@ -138,13 +138,14 @@ bool isValidLinkModes(string m)
return true;
}
void LinkModeSet::addLinkMode(LinkMode lm)
LinkModeSet &LinkModeSet::addLinkMode(LinkMode lm)
{
for (auto& s : link_modes_) {
if (s.mode == lm) {
set_ |= s.val;
}
}
return *this;
}
void LinkModeSet::unionLinkModeSet(LinkModeSet lms)
@ -3193,8 +3194,10 @@ WMBusCommonImplementation::~WMBusCommonImplementation()
WMBusCommonImplementation::WMBusCommonImplementation(WMBusDeviceType t,
shared_ptr<SerialCommunicationManager> manager,
shared_ptr<SerialDevice> serial)
shared_ptr<SerialDevice> serial,
bool is_serial)
: manager_(manager),
is_serial_(is_serial),
is_working_(true),
type_(t),
serial_(serial),
@ -3220,6 +3223,17 @@ string WMBusCommonImplementation::hr()
return cached_hr_;
}
bool WMBusCommonImplementation::isSerial()
{
return is_serial_;
}
void WMBusCommonImplementation::markAsNoLongerSerial()
{
// When you override the serial device with a file for an im871a, then
// it is no longer a serial device.
is_serial_ = false;
}
WMBusDeviceType WMBusCommonImplementation::type()
{
@ -3928,7 +3942,7 @@ const char *toString(WMBusDeviceType t)
{
switch (t)
{
#define X(name,text) case DEVICE_ ## name: return #name;
#define X(name,text,tty,rtlsdr) case DEVICE_ ## name: return #text;
LIST_OF_MBUS_DEVICES
#undef X
@ -3940,7 +3954,7 @@ const char *toLowerCaseString(WMBusDeviceType t)
{
switch (t)
{
#define X(name,text) case DEVICE_ ## name: return #text;
#define X(name,text,tty,rtlsdr) case DEVICE_ ## name: return #text;
LIST_OF_MBUS_DEVICES
#undef X
@ -3950,7 +3964,7 @@ LIST_OF_MBUS_DEVICES
WMBusDeviceType toWMBusDeviceType(string &t)
{
#define X(name,text) if (t == #text) return DEVICE_ ## name;
#define X(name,text,tty,rtlsdr) if (t == #text) return DEVICE_ ## name;
LIST_OF_MBUS_DEVICES
#undef X
return DEVICE_UNKNOWN;
@ -4015,14 +4029,14 @@ bool check_file(string f, bool *is_tty, bool *is_stdin, bool *is_file, bool *is_
return false;
}
bool is_type_id(string t, string *out_type, string *out_id)
bool is_type_id(string t, WMBusDeviceType *out_type, string *out_id)
{
// im871a im871a(12345678)
// auto
// rtlwmbus
if (t == "auto")
{
*out_type = t;
*out_type = WMBusDeviceType::DEVICE_AUTO;
*out_id = "";
return true;
}
@ -4035,7 +4049,7 @@ bool is_type_id(string t, string *out_type, string *out_id)
// No parentheses found, is t a known wmbus device? like im871a amb8465 etc....
WMBusDeviceType tt = toWMBusDeviceType(t);
if (tt == DEVICE_UNKNOWN) return false;
*out_type = t;
*out_type = toWMBusDeviceType(t);
*out_id = "";
return true;
}
@ -4048,7 +4062,7 @@ bool is_type_id(string t, string *out_type, string *out_id)
WMBusDeviceType tt = toWMBusDeviceType(type);
if (tt == DEVICE_UNKNOWN) return false;
if (!isValidId(id, true)) return false;
*out_type = type;
*out_type = toWMBusDeviceType(type);
*out_id = id;
return true;
}
@ -4060,17 +4074,17 @@ bool is_type_id(string t, string *out_type, string *out_id)
void SpecifiedDevice::clear()
{
file = "";
type = "";
type = WMBusDeviceType::DEVICE_UNKNOWN;
id = "";
fq = "";
linkmodes = "";
linkmodes.clear();
}
string SpecifiedDevice::str()
{
string r;
if (file != "") r += file+":";
if (type != "")
if (type != WMBusDeviceType::DEVICE_UNKNOWN)
{
r += type;
if (id != "")
@ -4081,7 +4095,7 @@ string SpecifiedDevice::str()
}
if (bps != "") r += bps+":";
if (fq != "") r += fq+":";
if (linkmodes != "") r += linkmodes+":";
if (!linkmodes.empty()) r += linkmodes.hr()+":";
if (command != "") r += "CMD("+command+"):";
if (r.size() > 0) r.pop_back();
@ -4112,7 +4126,7 @@ bool SpecifiedDevice::parse(string &arg)
// file type id bps fq linkmodes command
for (auto& p : parts)
{
if (file_checked && typeid_checked && file == "" && type == "" && id == "")
if (file_checked && typeid_checked && file == "" && type == WMBusDeviceType::DEVICE_UNKNOWN && id == "")
{
// There must be either a file and/or type(id). If none are found,
// then the specified device string is faulty.
@ -4150,7 +4164,7 @@ bool SpecifiedDevice::parse(string &arg)
bps_checked = true;
fq_checked = true;
linkmodes_checked = true;
linkmodes = p;
linkmodes = parseLinkModes(p);
}
else if (!command_checked && is_command(p, &command))
{
@ -4169,18 +4183,21 @@ bool SpecifiedDevice::parse(string &arg)
}
// Auto is only allowed to be combined with linkmodes and/or frequencies!
if (type == "auto" && (file != "" || bps != "")) return false;
if (type == WMBusDeviceType::DEVICE_AUTO && (file != "" || bps != "")) return false;
// You cannot combine a file with a command.
if (file != "" && command != "") return false;
return true;
}
Detected detectWMBusDeviceOnTTY(string tty, shared_ptr<SerialCommunicationManager> handler)
Detected detectWMBusDeviceOnTTY(string tty,
LinkModeSet desired_linkmodes,
shared_ptr<SerialCommunicationManager> handler)
{
Detected detected;
// Fake a specified device.
detected.found_file = tty;
detected.specified_device.is_tty = true;
detected.specified_device.linkmodes = desired_linkmodes;
// If im87a is tested first, a delay of 1s must be inserted
// before amb8465 is tested, lest it will not respond properly.
@ -4222,6 +4239,7 @@ Detected detectWMBusDeviceOnTTY(string tty, shared_ptr<SerialCommunicationManage
}
Detected detectWMBusDeviceWithFile(SpecifiedDevice &specified_device,
LinkModeSet default_linkmodes,
shared_ptr<SerialCommunicationManager> handler)
{
assert(specified_device.file != "");
@ -4231,44 +4249,59 @@ Detected detectWMBusDeviceWithFile(SpecifiedDevice &specified_device,
Detected detected;
detected.found_file = specified_device.file;
detected.setSpecifiedDevice(specified_device);
// If <device>:c1 is missing :c1 then use --c1.
LinkModeSet lms = specified_device.linkmodes;
if (lms.empty())
{
lms = default_linkmodes;
}
if (specified_device.is_simulation)
{
debug("(lookup) driver: simulation file\n");
detected.setAsFound("", DEVICE_SIMULATION, 0 , false, false);
specified_device.linkmodes = "any";
// A simulation file 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, false, lms);
return detected;
}
// Special case to cater for /dev/ttyUSB0:9600, ie the rawtty is implicit.
if (specified_device.type == "" && specified_device.bps != "" && specified_device.is_tty)
if (specified_device.type == WMBusDeviceType::DEVICE_UNKNOWN && specified_device.bps != "" && specified_device.is_tty)
{
debug("(lookup) driver: rawtty\n");
detected.setAsFound("", DEVICE_RAWTTY, atoi(specified_device.bps.c_str()), false, false);
// A rawtty 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_RAWTTY, atoi(specified_device.bps.c_str()), false, false, lms);
return detected;
}
// Special case to cater for raw_data.bin, ie the rawtty is implicit.
if (specified_device.type == "" && !specified_device.is_tty)
if (specified_device.type == WMBusDeviceType::DEVICE_UNKNOWN && !specified_device.is_tty)
{
debug("(lookup) driver: raw file\n");
detected.setAsFound("", DEVICE_RAWTTY, 0, true, false);
// A rawtty 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_RAWTTY, 0, true, false, lms);
return detected;
}
// Now handle all files with specified type.
if (specified_device.type != "")
if (specified_device.type != WMBusDeviceType::DEVICE_UNKNOWN &&
specified_device.type != WMBusDeviceType::DEVICE_AUTO)
{
debug("(lookup) driver: %s\n", specified_device.type.c_str());
detected.setAsFound("", toWMBusDeviceType(specified_device.type), 0, specified_device.is_file || specified_device.is_stdin, false);
debug("(lookup) driver: %s\n", toString(specified_device.type));
assert(!lms.empty());
detected.setAsFound("", specified_device.type, 0, specified_device.is_file || specified_device.is_stdin,
false, lms);
return detected;
}
// Ok, we are left with a single /dev/ttyUSB0 lets talk to it
// to figure out what is connected to it.
return detectWMBusDeviceOnTTY(specified_device.file, handler);
LinkModeSet desired_linkmodes = default_linkmodes;
return detectWMBusDeviceOnTTY(specified_device.file, desired_linkmodes, handler);
}
Detected detectWMBusDeviceWithCommand(SpecifiedDevice &specified_device,
LinkModeSet default_linkmodes,
shared_ptr<SerialCommunicationManager> handler)
{
assert(specified_device.file == "");
@ -4278,7 +4311,10 @@ Detected detectWMBusDeviceWithCommand(SpecifiedDevice &specified_device,
Detected detected;
detected.found_command = specified_device.command;
detected.setSpecifiedDevice(specified_device);
detected.setAsFound("", toWMBusDeviceType(specified_device.type), 0, false, true);
LinkModeSet lms = specified_device.linkmodes;
// If the specified device did not set any linkmodes fall back on the default linkmodes.
if (lms.empty()) lms = default_linkmodes;
detected.setAsFound("", specified_device.type, 0, false, true, lms);
return detected;
}
@ -4293,14 +4329,40 @@ AccessCheck detectSIMULATION(Detected *detected, shared_ptr<SerialCommunicationM
return AccessCheck::NotThere;
}
AccessCheck detectDevice(Detected *detected, shared_ptr<SerialCommunicationManager> handler)
AccessCheck detectAUTO(Detected *detected, shared_ptr<SerialCommunicationManager> handler)
{
string type = detected->specified_device.type;
// Detection of auto is currently not implemented here, but elsewhere.
return AccessCheck::NotThere;
}
#define X(name,text) if (type == #text) return detect ## name(detected,handler);
AccessCheck reDetectDevice(Detected *detected, shared_ptr<SerialCommunicationManager> handler)
{
WMBusDeviceType type = detected->specified_device.type;
#define X(name,text,tty,rtlsdr) if (type == WMBusDeviceType::DEVICE_ ## name) return detect ## name(detected,handler);
LIST_OF_MBUS_DEVICES
#undef X
assert(0);
return AccessCheck::NotThere;
}
bool usesRTLSDR(WMBusDeviceType t)
{
#define X(name,text,tty,rtlsdr) if (t == WMBusDeviceType::DEVICE_ ## name) return rtlsdr;
LIST_OF_MBUS_DEVICES
#undef X
assert(0);
return false;
}
bool usesTTY(WMBusDeviceType t)
{
#define X(name,text,tty,rtlsdr) if (t == WMBusDeviceType::DEVICE_ ## name) return tty;
LIST_OF_MBUS_DEVICES
#undef X
assert(0);
return false;
}

Wyświetl plik

@ -30,87 +30,44 @@
bool trimCRCsFrameFormatA(std::vector<uchar> &payload);
bool trimCRCsFrameFormatB(std::vector<uchar> &payload);
// A wmbus specified device is supplied on the command line or in the config file.
// It has this format "file:type(id):fq:bps:linkmods:CMD(command)"
struct SpecifiedDevice
{
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 type; // im871a, rtlwmbus
std::string id; // 12345678 for wmbus dongles or 0,1 for rtlwmbus indexes.
std::string fq; // 868.95M
std::string bps; // 9600
std::string linkmodes; // c1,t1,s1
std::string command; // command line of background process that streams data into wmbusmeters
bool handled {}; // Set to true when this device has been detected/handled.
time_t last_alarm {}; // Last time an alarm was sent for this device not being found.
void clear();
string str();
bool parse(string &s);
};
#define LIST_OF_MBUS_DEVICES \
X(UNKNOWN,unknown) \
X(AMB8465,amb8465) \
X(CUL,cul) \
X(IM871A,im871a) \
X(RAWTTY,rawtty) \
X(RC1180,rc1180) \
X(RTL433,rtl433) \
X(RTLWMBUS,rtlwmbus) \
X(SIMULATION,simulation)
X(UNKNOWN,unknown,false,false) \
X(AUTO,auto,false,false) \
X(AMB8465,amb8465,true,false) \
X(CUL,cul,true,false) \
X(IM871A,im871a,true,false) \
X(RAWTTY,rawtty,true,false) \
X(RC1180,rc1180,true,false) \
X(RTL433,rtl433,false,true) \
X(RTLWMBUS,rtlwmbus,false,true)\
X(SIMULATION,simulation,false,false)
enum WMBusDeviceType {
#define X(name,text) DEVICE_ ## name,
#define X(name,text,tty,rtlsdr) DEVICE_ ## name,
LIST_OF_MBUS_DEVICES
#undef X
};
bool usesTTY(WMBusDeviceType t);
bool usesRTLSDR(WMBusDeviceType t);
const char *toString(WMBusDeviceType t);
const char *toLowerCaseString(WMBusDeviceType t);
WMBusDeviceType toWMBusDeviceType(string &t);
void setIgnoreDuplicateTelegrams(bool idt);
struct Detected
{
SpecifiedDevice specified_device {}; // Device as specified from the command line / config file.
// In link mode S1, is used when both the transmitter and receiver are stationary.
// It can be transmitted relatively seldom.
string found_file; // The device file to use.
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, overrides
bool found_tty_override {}; // override tty
bool found_cmd_override {}; // override cmd
string found_command;
// In link mode T1, the meter transmits a telegram every few seconds or minutes.
// Suitable for drive-by/walk-by collection of meter values.
void setSpecifiedDeviceAsAuto()
{
specified_device.clear();
}
// Link mode C1 is like T1 but uses less energy when transmitting due to
// a different radio encoding. Also significant is:
// S1/T1 usually uses the A format for the data link layer, more CRCs.
// C1 usually uses the B format for the data link layer, less CRCs = less overhead.
void setSpecifiedDevice(SpecifiedDevice sd)
{
specified_device = sd;
}
void setAsFound(string id, WMBusDeviceType t, int b, bool to, bool co)
{
found_device_id = id;
found_type = t;
found_bps = b;
found_tty_override = to;
found_cmd_override = co;
}
std::string str()
{
return found_file+":"+string(toString(found_type))+"["+found_device_id+"]"+":"+to_string(found_bps)+"/"+to_string(found_tty_override);
}
};
// The im871a can for example receive C1a, but it is unclear if there are any meters that use it.
#define LIST_OF_LINK_MODES \
X(Any,any,--anylinkmode,0xffff) \
@ -126,19 +83,6 @@ struct Detected
X(N1f,n1f,--n1f,0x200) \
X(UNKNOWN,unknown,----,0x0)
// In link mode S1, is used when both the transmitter and receiver are stationary.
// It can be transmitted relatively seldom.
// In link mode T1, the meter transmits a telegram every few seconds or minutes.
// Suitable for drive-by/walk-by collection of meter values.
// Link mode C1 is like T1 but uses less energy when transmitting due to
// a different radio encoding. Also significant is:
// S1/T1 usually uses the A format for the data link layer, more CRCs.
// C1 usually uses the B format for the data link layer, less CRCs = less overhead.
// The im871a can for example receive C1a, but it is unclear if there are any meters that use it.
enum class LinkMode {
#define X(name,lcname,option,val) name,
LIST_OF_LINK_MODES
@ -157,7 +101,7 @@ LinkMode isLinkModeOption(const char *arg);
struct LinkModeSet
{
// Add the link mode to the set of link modes.
void addLinkMode(LinkMode lm);
LinkModeSet &addLinkMode(LinkMode lm);
void unionLinkModeSet(LinkModeSet lms);
void disjunctionLinkModeSet(LinkModeSet lms);
// Does this set support listening to the given link mode set?
@ -177,8 +121,14 @@ struct LinkModeSet
bool has(LinkMode lm);
// Check if all link modes are supported.
bool hasAll(LinkModeSet lms);
int bits() { return set_; }
// Check if any link mode has been set.
bool empty() { return set_ == 0; }
// Clear the set to empty.
void clear() { set_ = 0; }
// Mark set as all linkmodes!
void setAll() { set_ = (int)LinkMode::Any; }
// For bit counting etc.
int asBits() { return set_; }
// Return a human readable string.
std::string hr();
@ -194,6 +144,67 @@ private:
LinkModeSet parseLinkModes(string modes);
bool isValidLinkModes(string modes);
// A wmbus specified device is supplied on the command line or in the config file.
// It has this format "file:type(id):fq:bps:linkmods:CMD(command)"
struct SpecifiedDevice
{
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{};
WMBusDeviceType type; // im871a, rtlwmbus
std::string id; // 12345678 for wmbus dongles or 0,1 for rtlwmbus indexes.
std::string fq; // 868.95M
std::string bps; // 9600
LinkModeSet linkmodes; // c1,t1,s1
std::string command; // command line of background process that streams data into wmbusmeters
bool handled {}; // Set to true when this device has been detected/handled.
time_t last_alarm {}; // Last time an alarm was sent for this device not being found.
void clear();
string str();
bool parse(string &s);
};
struct Detected
{
SpecifiedDevice specified_device {}; // Device as specified from the command line / config file.
string found_file; // The device file to use.
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, overrides
bool found_tty_override {}; // override tty
bool found_cmd_override {}; // override cmd
string found_command;
LinkModeSet calculated_linkmodes; // Calculated from specified lms and default lms.
void setSpecifiedDeviceAsAuto()
{
specified_device.clear();
}
void setSpecifiedDevice(SpecifiedDevice sd)
{
specified_device = sd;
}
void setAsFound(string id, WMBusDeviceType t, int b, bool to, bool co, LinkModeSet clm)
{
found_device_id = id;
found_type = t;
found_bps = b;
found_tty_override = to;
found_cmd_override = co;
calculated_linkmodes = clm;
}
std::string str()
{
return found_file+":"+string(toString(found_type))+"["+found_device_id+"]"+":"+to_string(found_bps)+"/"+to_string(found_tty_override);
}
};
enum class CONNECTION
{
MBUS, WMBUS, BOTH
@ -511,8 +522,9 @@ struct WMBus
// The im871a and amb8465 dongles does have a unique, immutable id as well.
// Not all dongles have this.
virtual string getDeviceUniqueId() = 0;
// Human readable explanation of this device, eg: /dev/ttysUB0:im871a[12345678]:t1
virtual std::string hr() = 0;
virtual bool isSerial() = 0;
virtual LinkModeSet getLinkModes() = 0;
virtual bool ping() = 0;
@ -548,8 +560,10 @@ struct WMBus
};
Detected detectWMBusDeviceWithFile(SpecifiedDevice &specified_device,
LinkModeSet default_linkmodes,
shared_ptr<SerialCommunicationManager> manager);
Detected detectWMBusDeviceWithCommand(SpecifiedDevice &specified_device,
LinkModeSet default_linkmodes,
shared_ptr<SerialCommunicationManager> handler);
@ -624,8 +638,9 @@ FrameStatus checkWMBusFrame(vector<uchar> &data,
int *payload_len_out,
int *payload_offset);
AccessCheck detectDevice(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck reDetectDevice(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectAUTO(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectAMB8465(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectCUL(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
AccessCheck detectD1TC(Detected *detected, shared_ptr<SerialCommunicationManager> manager);
@ -640,6 +655,8 @@ AccessCheck detectWMB13U(Detected *detected, shared_ptr<SerialCommunicationManag
// restore to factory settings.
AccessCheck factoryResetAMB8465(string tty, shared_ptr<SerialCommunicationManager> handler, int *was_baud);
Detected detectWMBusDeviceOnTTY(string tty, shared_ptr<SerialCommunicationManager> handler);
Detected detectWMBusDeviceOnTTY(string tty,
LinkModeSet desired_linkmodes,
shared_ptr<SerialCommunicationManager> handler);
#endif

Wyświetl plik

@ -95,14 +95,14 @@ struct WMBusAmber : public virtual WMBusCommonImplementation
int numConcurrentLinkModes() { return 1; }
bool canSetLinkModes(LinkModeSet desired_modes)
{
if (0 == countSetBits(desired_modes.bits())) return false;
if (desired_modes.empty()) return false;
// Simple check first, are they all supported?
if (!supportedLinkModes().supports(desired_modes)) return false;
// So far so good, is the desired combination supported?
// If only a single bit is desired, then it is supported.
if (1 == countSetBits(desired_modes.bits())) return true;
if (1 == countSetBits(desired_modes.asBits())) return true;
// More than 2 listening modes at the same time will always fail.
if (2 != countSetBits(desired_modes.bits())) return false;
if (2 != countSetBits(desired_modes.asBits())) return false;
// C1 and T1 can be listened to at the same time!
if (desired_modes.has(LinkMode::C1) && desired_modes.has(LinkMode::T1)) return true;
// Likewise for S1 and S1-m
@ -146,6 +146,7 @@ shared_ptr<WMBus> openAMB8465(string device, shared_ptr<SerialCommunicationManag
if (serial_override)
{
WMBusAmber *imp = new WMBusAmber(serial_override, manager);
imp->markAsNoLongerSerial();
return shared_ptr<WMBus>(imp);
}
@ -155,7 +156,7 @@ shared_ptr<WMBus> openAMB8465(string device, shared_ptr<SerialCommunicationManag
}
WMBusAmber::WMBusAmber(shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
WMBusCommonImplementation(DEVICE_AMB8465, manager, serial)
WMBusCommonImplementation(DEVICE_AMB8465, manager, serial, true)
{
rssi_expected_ = true;
reset();
@ -604,7 +605,8 @@ AccessCheck detectAMB8465(Detected *detected, shared_ptr<SerialCommunicationMana
ConfigAMB8465 config;
config.decode(response);
detected->setAsFound(config.dongleId(), WMBusDeviceType::DEVICE_AMB8465, 9600, false, false);
detected->setAsFound(config.dongleId(), WMBusDeviceType::DEVICE_AMB8465, 9600, false, false,
detected->specified_device.linkmodes);
verbose("(amb8465) detect %s\n", config.str().c_str());
verbose("(amb8465) are you there? yes %s\n", config.dongleId().c_str());

Wyświetl plik

@ -24,10 +24,14 @@
struct WMBusCommonImplementation : public virtual WMBus
{
WMBusCommonImplementation(WMBusDeviceType t, shared_ptr<SerialCommunicationManager> manager, shared_ptr<SerialDevice> serial_override);
WMBusCommonImplementation(WMBusDeviceType t,
shared_ptr<SerialCommunicationManager> manager,
shared_ptr<SerialDevice> serial_override,
bool is_serial);
~WMBusCommonImplementation();
string hr();
bool isSerial();
WMBusDeviceType type();
void onTelegram(function<bool(AboutTelegram&,vector<uchar>)> cb);
bool handleTelegram(AboutTelegram &about, vector<uchar> frame);
@ -52,6 +56,7 @@ struct WMBusCommonImplementation : public virtual WMBus
void close();
void setDetected(Detected detected) { detected_ = detected; }
Detected *getDetected() { return &detected_; }
void markAsNoLongerSerial();
protected:
@ -68,6 +73,8 @@ struct WMBusCommonImplementation : public virtual WMBus
private:
// Uses a serial tty?
bool is_serial_ {};
bool is_working_ {};
vector<function<bool(AboutTelegram&,vector<uchar>)>> telegram_listeners_;
WMBusDeviceType type_ {};

Wyświetl plik

@ -54,11 +54,11 @@ struct WMBusCUL : public virtual WMBusCommonImplementation
int numConcurrentLinkModes() { return 1; }
bool canSetLinkModes(LinkModeSet lms)
{
if (0 == countSetBits(lms.bits())) return false;
if (lms.empty()) return false;
if (!supportedLinkModes().supports(lms)) return false;
// Ok, the supplied link modes are compatible,
// but im871a can only listen to one at a time.
return 1 == countSetBits(lms.bits());
return 1 == countSetBits(lms.asBits());
}
void processSerialData();
void simulate();
@ -86,6 +86,7 @@ shared_ptr<WMBus> openCUL(string device, shared_ptr<SerialCommunicationManager>
if (serial_override)
{
WMBusCUL *imp = new WMBusCUL(serial_override, manager);
imp->markAsNoLongerSerial();
return shared_ptr<WMBus>(imp);
}
@ -95,7 +96,7 @@ shared_ptr<WMBus> openCUL(string device, shared_ptr<SerialCommunicationManager>
}
WMBusCUL::WMBusCUL(shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
WMBusCommonImplementation(DEVICE_CUL, manager, serial)
WMBusCommonImplementation(DEVICE_CUL, manager, serial, true)
{
reset();
}
@ -391,7 +392,7 @@ AccessCheck detectCUL(Detected *detected, shared_ptr<SerialCommunicationManager>
return AccessCheck::NotThere;
}
detected->setAsFound("", WMBusDeviceType::DEVICE_CUL, 38400, false, false);
detected->setAsFound("", WMBusDeviceType::DEVICE_CUL, 38400, false, false, detected->specified_device.linkmodes);
return AccessCheck::AccessOK;
}

Wyświetl plik

@ -176,11 +176,11 @@ struct WMBusIM871A : public virtual WMBusCommonImplementation
int numConcurrentLinkModes() { return 1; }
bool canSetLinkModes(LinkModeSet lms)
{
if (0 == countSetBits(lms.bits())) return false;
if (lms.empty()) return false;
if (!supportedLinkModes().supports(lms)) return false;
// Ok, the supplied link modes are compatible,
// but im871a can only listen to one at a time.
return 1 == countSetBits(lms.bits());
return 1 == countSetBits(lms.asBits());
}
void processSerialData();
void simulate() { }
@ -234,6 +234,7 @@ shared_ptr<WMBus> openIM871A(string device, shared_ptr<SerialCommunicationManage
if (serial_override)
{
WMBusIM871A *imp = new WMBusIM871A(serial_override, manager);
imp->markAsNoLongerSerial();
return shared_ptr<WMBus>(imp);
}
@ -243,7 +244,7 @@ shared_ptr<WMBus> openIM871A(string device, shared_ptr<SerialCommunicationManage
}
WMBusIM871A::WMBusIM871A(shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
WMBusCommonImplementation(DEVICE_IM871A, manager, serial)
WMBusCommonImplementation(DEVICE_IM871A, manager, serial, true)
{
reset();
}
@ -910,7 +911,8 @@ AccessCheck detectIM871A(Detected *detected, shared_ptr<SerialCommunicationManag
debug("(im871a) config: %s\n", co.str().c_str());
detected->setAsFound(co.dongleId(), WMBusDeviceType::DEVICE_IM871A, 57600, false, false);
detected->setAsFound(co.dongleId(), WMBusDeviceType::DEVICE_IM871A, 57600, false, false,
detected->specified_device.linkmodes);
verbose("(im871a) are you there? yes %s\n", co.dongleId().c_str());

Wyświetl plik

@ -60,6 +60,7 @@ shared_ptr<WMBus> openRawTTY(string device, int baudrate, shared_ptr<SerialCommu
if (serial_override)
{
WMBusRawTTY *imp = new WMBusRawTTY(serial_override, manager);
imp->markAsNoLongerSerial();
return shared_ptr<WMBus>(imp);
}
auto serial = manager->createSerialDeviceTTY(device.c_str(), baudrate, "rawtty");
@ -68,7 +69,7 @@ shared_ptr<WMBus> openRawTTY(string device, int baudrate, shared_ptr<SerialCommu
}
WMBusRawTTY::WMBusRawTTY(shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
WMBusCommonImplementation(DEVICE_RAWTTY, manager, serial)
WMBusCommonImplementation(DEVICE_RAWTTY, manager, serial, true)
{
reset();
}
@ -158,7 +159,8 @@ AccessCheck detectRAWTTY(Detected *detected, shared_ptr<SerialCommunicationManag
serial->close();
detected->setAsFound("", WMBusDeviceType::DEVICE_RAWTTY, bps, false, false);
detected->setAsFound("", WMBusDeviceType::DEVICE_RAWTTY, bps, false, false,
detected->specified_device.linkmodes);
return AccessCheck::AccessOK;
}

Wyświetl plik

@ -131,11 +131,11 @@ struct WMBusRC1180 : public virtual WMBusCommonImplementation
int numConcurrentLinkModes() { return 1; }
bool canSetLinkModes(LinkModeSet lms)
{
if (0 == countSetBits(lms.bits())) return false;
if (lms.empty()) return false;
if (!supportedLinkModes().supports(lms)) return false;
// Ok, the supplied link modes are compatible,
// but rc1180 can only listen to one at a time.
return 1 == countSetBits(lms.bits());
return 1 == countSetBits(lms.asBits());
}
void processSerialData();
void simulate();
@ -168,6 +168,7 @@ shared_ptr<WMBus> openRC1180(string device, shared_ptr<SerialCommunicationManage
if (serial_override)
{
WMBusRC1180 *imp = new WMBusRC1180(serial_override, manager);
imp->markAsNoLongerSerial();
return shared_ptr<WMBus>(imp);
}
@ -177,7 +178,7 @@ shared_ptr<WMBus> openRC1180(string device, shared_ptr<SerialCommunicationManage
}
WMBusRC1180::WMBusRC1180(shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
WMBusCommonImplementation(DEVICE_RC1180, manager, serial)
WMBusCommonImplementation(DEVICE_RC1180, manager, serial, true)
{
reset();
}
@ -384,6 +385,22 @@ AccessCheck detectRC1180(Detected *detected, shared_ptr<SerialCommunicationManag
debug("(rc1180) config: %s\n", co.str().c_str());
/*
Modification of the non-volatile memory should be done using the
wmbusmeters-admin program. So this code should not execute here.
if (co.rssi_mode == 0)
{
// Change the config so that the device appends an rssi byte.
vector<uchar> updat(4);
update[0] = 'M';
update[1] = 0x05; // Register 5, rssi_mode
update[2] = 1; // Set value to 1 = enabled.
update[3] = 0xff; // Stop modifying memory.
serial->send(update);
usleep(1000*200);
// Reboot dongle.
}
*/
// Now exit config mode and continue listeing.
msg[0] = 'X';
serial->send(msg);
@ -392,7 +409,8 @@ AccessCheck detectRC1180(Detected *detected, shared_ptr<SerialCommunicationManag
serial->close();
detected->setAsFound(co.dongleId(), WMBusDeviceType::DEVICE_RC1180, 19200, false, false);
detected->setAsFound(co.dongleId(), WMBusDeviceType::DEVICE_RC1180, 19200, false, false,
detected->specified_device.linkmodes);
verbose("(rc1180) are you there? yes %s\n", co.dongleId().c_str());

Wyświetl plik

@ -93,7 +93,7 @@ shared_ptr<WMBus> openRTL433(string identifier, string command, shared_ptr<Seria
}
WMBusRTL433::WMBusRTL433(shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
WMBusCommonImplementation(DEVICE_RTL433, manager, serial)
WMBusCommonImplementation(DEVICE_RTL433, manager, serial, false)
{
reset();
}
@ -305,7 +305,8 @@ FrameStatus WMBusRTL433::checkRTL433Frame(vector<uchar> &data,
AccessCheck detectRTL433(Detected *detected, shared_ptr<SerialCommunicationManager> handler)
{
detected->setAsFound("", WMBusDeviceType::DEVICE_RTLWMBUS, 0, false, false);
detected->setAsFound("", WMBusDeviceType::DEVICE_RTL433, 0, false, false,
detected->specified_device.linkmodes);
return AccessCheck::AccessOK;
}

Wyświetl plik

@ -98,7 +98,7 @@ shared_ptr<WMBus> openRTLWMBUS(string serialnr, string command, shared_ptr<Seria
}
WMBusRTLWMBUS::WMBusRTLWMBUS(string serialnr, shared_ptr<SerialDevice> serial, shared_ptr<SerialCommunicationManager> manager) :
WMBusCommonImplementation(DEVICE_RTLWMBUS, manager, serial), serialnr_(serialnr)
WMBusCommonImplementation(DEVICE_RTLWMBUS, manager, serial, false), serialnr_(serialnr)
{
reset();
}

Wyświetl plik

@ -65,7 +65,7 @@ shared_ptr<WMBus> openSimulator(string device, shared_ptr<SerialCommunicationMan
}
WMBusSimulator::WMBusSimulator(string file, shared_ptr<SerialCommunicationManager> manager)
: WMBusCommonImplementation(DEVICE_SIMULATION, manager, NULL), file_(file)
: WMBusCommonImplementation(DEVICE_SIMULATION, manager, NULL, false), file_(file)
{
assert(file != "");
loadFile(file, &lines_);

Wyświetl plik

@ -25,7 +25,7 @@ cat /tmp/wmbusmeters_alarm_test
echo "---------------------------------------"
cat > $TEST/test_expected.txt <<EOF
(alarm DeviceInactivity) 4 seconds of inactivity resetting simulations/simulation_alarm.txt SIMULATION (timeout 4s expected mon-sun(00-23) now 1111-11-11 11:11)
(alarm DeviceInactivity) 4 seconds of inactivity resetting simulations/simulation_alarm.txt simulation (timeout 4s expected mon-sun(00-23) now 1111-11-11 11:11)
(wmbus) successfully reset wmbus device
EOF
@ -35,7 +35,7 @@ METER =={"media":"cold water","meter":"multical21","name":"Water","id":"76348799
EOF
cat > /tmp/wmbusmeters_alarm_expected <<EOF
ALARM_SHELL DeviceInactivity (alarm DeviceInactivity) 4 seconds of inactivity resetting simulations/simulation_alarm.txt SIMULATION (timeout 4s expected mon-sun(00-23) now 1111-11-11 11:11)
ALARM_SHELL DeviceInactivity (alarm DeviceInactivity) 4 seconds of inactivity resetting simulations/simulation_alarm.txt simulation (timeout 4s expected mon-sun(00-23) now 1111-11-11 11:11)
EOF
cat $TEST/test_stderr.txt | sed 's/now ....-..-.. ..:../now 1111-11-11 11:11/' > $TEST/test_responses.txt

Wyświetl plik

@ -31,6 +31,8 @@ mqtt_publish) sent to a REST API (eg curl) or store it in a database
\fB\--debug\fR for a lot of information
\fB\--donotprobe=\fR<tty> do not auto-probe this tty. Use multiple times for several ttys or specify "all" for all ttys.
\fB\--exitafter=\fR<time> exit program after time, eg 20h, 10m 5s
\fB\--format=\fR(hr|json|fields) for human readable, json or semicolon separated fields