Add meterinfo parsing that can additionally handle bus,extras and bps.

pull/273/head
Fredrik Öhrström 2021-03-13 18:35:47 +01:00
rodzic d66cfeebc3
commit 5eea15b18c
13 zmienionych plików z 267 dodań i 75 usunięć

Wyświetl plik

@ -90,6 +90,11 @@ void BusManager::openBusDeviceAndPotentiallySetLinkmodes(Configuration *config,
}
string using_link_modes = lms.hr();
string bus = detected->specified_device.alias.c_str();
if (bus != "")
{
bus = bus+" ";
}
string id = detected->found_device_id.c_str();
if (id != "") id = "["+id+"]";
string extras = detected->specified_device.extras.c_str();
@ -112,8 +117,9 @@ void BusManager::openBusDeviceAndPotentiallySetLinkmodes(Configuration *config,
fq.c_str(),
cmd.c_str());
}
string started = tostrprintf("Started %s %s%s%s%s%s\n",
string started = tostrprintf("Started %s %s%s%s%s%s%s\n",
how.c_str(),
bus.c_str(),
toLowerCaseString(detected->found_type),
id.c_str(),
extras.c_str(),

Wyświetl plik

@ -572,46 +572,31 @@ shared_ptr<Configuration> parseCommandLine(int argc, char **argv) {
{
string bus;
string name = argv[m*4+i+0];
string type = argv[m*4+i+1];
string driver = argv[m*4+i+1];
string id = argv[m*4+i+2];
string key = argv[m*4+i+3];
LinkModeSet modes;
int bps = 0;
size_t colon = type.find(':');
MeterDriver mt = toMeterDriver(type);
if (colon != string::npos)
{
// The config can be supplied after the type, like this:
// apator162:c1
string modess = type.substr(colon+1);
type = type.substr(0, colon);
mt = toMeterDriver(type);
if (mt == MeterDriver::UNKNOWN) error("Not a valid meter type \"%s\"\n", type.c_str());
modes = parseLinkModes(modess);
LinkModeSet default_modes = toMeterLinkModeSet(type);
if (!default_modes.hasAll(modes))
{
string want = modes.hr();
string has = default_modes.hr();
error("(cmdline) cannot set link modes to: %s because meter %s only transmits on: %s\n",
want.c_str(), type.c_str(), has.c_str());
}
string modeshr = modes.hr();
debug("(cmdline) setting link modes to %s for meter %s\n",
modeshr.c_str(), name.c_str());
}
else {
modes = toMeterLinkModeSet(type);
}
mt = toMeterDriver(type);
MeterInfo mi;
mi.parse(name, driver, id, key);
if (mt == MeterDriver::UNKNOWN) error("Not a valid meter type \"%s\"\n", type.c_str());
if (mi.driver == MeterDriver::UNKNOWN) error("Not a valid meter driver \"%s\"\n", driver.c_str());
if (!isValidMatchExpressions(id, true)) error("Not a valid id nor a valid meter match expression \"%s\"\n", id.c_str());
if (!isValidKey(key, mt)) error("Not a valid meter key \"%s\"\n", key.c_str());
vector<string> no_meter_shells, no_meter_jsons;
vector<string> ids = splitMatchExpressions(id);
c->meters.push_back(MeterInfo(bus, name, mt, ids, key, modes, bps, no_meter_shells, no_meter_jsons));
if (!isValidKey(key, mi.driver)) error("Not a valid meter key \"%s\"\n", key.c_str());
c->meters.push_back(mi);
LinkModeSet default_modes = toMeterLinkModeSet(mi.driver);
// Check if the devices can listen to the meter link mode(s).
if (!default_modes.hasAll(mi.link_modes))
{
string want = mi.link_modes.hr();
string has = default_modes.hr();
error("(cmdline) cannot set link modes to: %s because meter %s only transmits on: %s\n",
want.c_str(), toString(mi.driver).c_str(), has.c_str());
}
string modeshr = mi.link_modes.hr();
debug("(cmdline) setting link modes to %s for meter %s\n",
mi.link_modes.hr().c_str(), name.c_str());
}
return shared_ptr<Configuration>(c);

Wyświetl plik

@ -182,7 +182,7 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
}
if (use) {
vector<string> ids = splitMatchExpressions(id);
c->meters.push_back(MeterInfo(bus, name, mt, ids, key, modes, bps, telegram_shells, jsons));
c->meters.push_back(MeterInfo(bus, name, mt, "", ids, key, modes, bps, telegram_shells, jsons));
}
return;
@ -686,7 +686,7 @@ LinkModeCalculationResult calculateLinkModes(Configuration *config, WMBus *wmbus
{
meters_union.unionLinkModeSet(m.link_modes);
string meter = m.link_modes.hr();
debug("(config) meter %s link mode(s): %s\n", toMeterDriver(m.driver).c_str(), meter.c_str());
debug("(config) meter %s link mode(s): %s\n", toString(m.driver).c_str(), meter.c_str());
}
string metersu = meters_union.hr();
debug("(config) all possible link modes that the meters might transmit on: %s\n", metersu.c_str());

Wyświetl plik

@ -316,7 +316,7 @@ void MeterFlowIQ2200::processContent(Telegram *t)
(flowiq2200) 4c: 67 vif (External temperature °C)
(flowiq2200) 4d: * 13 external temperature (19.000000 °C)
*/
string meter_name = toMeterDriver(driver()).c_str();
string meter_name = toString(driver()).c_str();
int offset;
string key;
@ -473,7 +473,7 @@ string MeterFlowIQ2200::statusHumanReadable()
string MeterFlowIQ2200::decodeTime(int time)
{
if (time>7) {
string meter_name = toMeterDriver(driver()).c_str();
string meter_name = toString(driver()).c_str();
warning("(%s) warning: Cannot decode time %d should be 0-7.\n", meter_name.c_str(), time);
}
switch (time) {

Wyświetl plik

@ -287,7 +287,7 @@ void MeterMultical21::processContent(Telegram *t)
// 2d: 0F vife (?)
// 2e: * 0D external temperature (13.000000 °C)
string meter_name = toMeterDriver(driver()).c_str();
string meter_name = toString(driver()).c_str();
int offset;
string key;
@ -423,7 +423,7 @@ string MeterMultical21::statusHumanReadable()
string MeterMultical21::decodeTime(int time)
{
if (time>7) {
string meter_name = toMeterDriver(driver()).c_str();
string meter_name = toString(driver()).c_str();
warning("(%s) warning: Cannot decode time %d should be 0-7.\n", meter_name.c_str(), time);
}
switch (time) {

Wyświetl plik

@ -182,7 +182,7 @@ public:
verbose("(meter) used meter template %s %s %s to match %s\n",
mi.name.c_str(),
mi.idsc.c_str(),
toMeterDriver(mi.driver).c_str(),
toString(mi.driver).c_str(),
idsc.c_str());
if (is_daemon_)
@ -191,7 +191,7 @@ public:
meter->index(),
mi.name.c_str(),
tmp.idsc.c_str(),
toMeterDriver(mi.driver).c_str());
toString(mi.driver).c_str());
}
else
{
@ -199,7 +199,7 @@ public:
meter->index(),
mi.name.c_str(),
tmp.idsc.c_str(),
toMeterDriver(mi.driver).c_str());
toString(mi.driver).c_str());
}
bool match = false;
@ -210,7 +210,7 @@ public:
// but it still did not match! This is probably an error in wmbusmeters!
warning("(meter) newly created meter (%s %s %s) did not match telegram! ",
"Please open an issue at https://github.com/weetmuts/wmbusmeters/\n",
meter->name().c_str(), meter->idsc().c_str(), toMeterDriver(meter->driver()).c_str());
meter->name().c_str(), meter->idsc().c_str(), toString(meter->driver()).c_str());
}
else if (!h)
{
@ -218,7 +218,7 @@ public:
// but it still did not handle it! This can happen if the wrong
// decryption key was used.
warning("(meter) newly created meter (%s %s %s) did not handle telegram!\n",
meter->name().c_str(), meter->idsc().c_str(), toMeterDriver(meter->driver()).c_str());
meter->name().c_str(), meter->idsc().c_str(), toString(meter->driver()).c_str());
}
else
{
@ -408,7 +408,7 @@ LIST_OF_METERS
return false;
}
string toMeterDriver(MeterDriver mt)
string toString(MeterDriver mt)
{
#define X(mname,link,info,type,cname) if (mt == MeterDriver::type) return #mname;
LIST_OF_METERS
@ -432,6 +432,14 @@ LIST_OF_METERS
return LinkModeSet();
}
LinkModeSet toMeterLinkModeSet(MeterDriver d)
{
#define X(mname,linkmodes,info,driver,cname) if (d == MeterDriver::driver) return LinkModeSet(linkmodes);
LIST_OF_METERS
#undef X
return LinkModeSet();
}
bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, MeterInfo *mi)
{
string name;
@ -499,7 +507,7 @@ bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, Me
warning("(meter) %s: meter detection did not match the selected driver %s! correct driver is: %s\n"
"(meter) Not printing this warning agin for id: %02x%02x%02x%02x mfct: (%s) %s (0x%02x) type: %s (0x%02x) ver: 0x%02x\n",
name.c_str(),
toMeterDriver(driver).c_str(),
toString(driver).c_str(),
possible_drivers.c_str(),
t->dll_id_b[3], t->dll_id_b[2], t->dll_id_b[1], t->dll_id_b[0],
manufacturerFlag(t->dll_mfct).c_str(),
@ -936,7 +944,7 @@ ELLSecurityMode MeterCommonImplementation::expectedELLSecurityMode()
void detectMeterDrivers(int manufacturer, int media, int version, vector<string> *drivers)
{
#define X(TY,MA,ME,VE) { if (manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { drivers->push_back(toMeterDriver(MeterDriver::TY)); }}
#define X(TY,MA,ME,VE) { if (manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { drivers->push_back(toString(MeterDriver::TY)); }}
METER_DETECTION
#undef X
}
@ -992,3 +1000,122 @@ LIST_OF_METERS
}
return newm;
}
bool is_driver_extras(string t, MeterDriver *out_driver, string *out_extras)
{
// piigth(jump=foo)
// multical21
size_t ps = t.find('(');
size_t pe = t.find(')');
size_t te = 0; // Position after type end.
bool found_parentheses = (ps != string::npos && pe != string::npos);
if (!found_parentheses)
{
// No brackets nor parentheses found, is t a known wmbus device? like im871a amb8465 etc....
MeterDriver md = toMeterDriver(t);
if (md == MeterDriver::UNKNOWN) return false;
*out_driver = md;
*out_extras = "";
return true;
}
// Parentheses must be last.
if (! (ps > 0 && ps < pe && pe == t.length()-1)) return false;
te = ps;
string type = t.substr(0, te);
MeterDriver md = toMeterDriver(type);
if (md == MeterDriver::UNKNOWN) return false;
*out_driver = md;
string extras = t.substr(ps+1, pe-ps-1);
*out_extras = extras;
return true;
}
string MeterInfo::str()
{
string r;
r += toString(driver);
if (extras != "")
{
r += "("+extras+")";
}
r += ":";
if (bus != "") r += bus+":";
if (bps != 0) r += bps+":";
if (!link_modes.empty()) r += link_modes.hr()+":";
if (r.size() > 0) r.pop_back();
return r;
}
bool MeterInfo::parse(string n, string d, string i, string k)
{
clear();
name = n;
ids = splitMatchExpressions(i);
key = k;
bool driverextras_checked = false;
bool bus_checked = false;
bool bps_checked = false;
bool link_modes_checked = false;
// For the moment the colon : is forbidden in file names and commands.
// It cannot occur in type,fq or bps.
vector<string> parts = splitString(d, ':');
// Example piigth:MAIN:2400
// multical21:c1
// telco:BUS2:c2
// driver ( extras ) : bus_alias : bps : linkmodes
for (auto& p : parts)
{
if (!driverextras_checked && is_driver_extras(p, &driver, &extras))
{
driverextras_checked = true;
}
else if (!bus_checked && isValidAlias(p) && !isValidBps(p) && !isValidLinkModes(p))
{
driverextras_checked = true;
bus_checked = true;
bus = p;
}
else if (!bps_checked && isValidBps(p) && !isValidLinkModes(p))
{
driverextras_checked = true;
bus_checked = true;
bps_checked = true;
bps = atoi(p.c_str());
}
else if (!link_modes_checked && isValidLinkModes(p))
{
driverextras_checked = true;
bus_checked = true;
bps_checked = true;
link_modes_checked = true;
link_modes = parseLinkModes(p);
}
else
{
// Unknown part....
return false;
}
}
if (!link_modes_checked)
{
// No explicit link mode set, set to the default link modes
// that the meter can transmit on.
link_modes = toMeterLinkModeSet(driver);
}
return true;
}

Wyświetl plik

@ -143,11 +143,14 @@ struct MeterInfo
{
}
MeterInfo(string b, string n, MeterDriver d, vector<string> i, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &j)
string str();
MeterInfo(string b, string n, MeterDriver d, string e, vector<string> i, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &j)
{
bus = b;
name = n;
driver = d;
extras = e,
ids = i;
idsc = toIdsCommaSeparated(ids);
key = k;
@ -156,6 +159,22 @@ struct MeterInfo
link_modes = lms;
bps = baud;
}
void clear()
{
bus = "";
name = "";
driver = MeterDriver::UNKNOWN;
ids.clear();
idsc = "";
key = "";
shells.clear();
jsons.clear();
link_modes.clear();
bps = 0;
}
bool parse(string name, string driver, string id, string key);
};
struct Print
@ -337,9 +356,10 @@ struct PulseCounter : public virtual Meter
struct Generic : public virtual Meter {
};
string toMeterDriver(MeterDriver driver);
string toString(MeterDriver driver);
MeterDriver toMeterDriver(string& driver);
LinkModeSet toMeterLinkModeSet(string& driver);
LinkModeSet toMeterLinkModeSet(MeterDriver driver);
#define X(mname,linkmode,info,type,cname) shared_ptr<info> create##cname(MeterInfo &m);
LIST_OF_METERS

Wyświetl plik

@ -56,7 +56,7 @@ struct MeterCommonImplementation : public virtual Meter
~MeterCommonImplementation() = default;
string meterDriver() { return toMeterDriver(driver_); }
string meterDriver() { return toString(driver_); }
protected:

Wyświetl plik

@ -37,6 +37,7 @@ void test_ids();
void test_kdf();
void test_periods();
void test_devices();
void test_meters();
void test_months();
int main(int argc, char **argv)
@ -58,6 +59,7 @@ int main(int argc, char **argv)
test_dvparser();
test_test();
test_devices();
test_meters();
/*
test_linkmodes();*/
test_ids();
@ -268,6 +270,7 @@ int test_linkmodes()
apator_config.meters.push_back(MeterInfo("", // bus
"m1", // name
MeterDriver::APATOR162, // driver/type
"", // extras
ids, // ids
"", // Key
toMeterLinkModeSet(apator162), // link mode set
@ -308,12 +311,12 @@ int test_linkmodes()
string multical21 = "multical21";
string supercom587 = "supercom587";
multical21_and_supercom587_config.meters.push_back(MeterInfo("", "m1", MeterDriver::MULTICAL21, ids, "",
multical21_and_supercom587_config.meters.push_back(MeterInfo("", "m1", MeterDriver::MULTICAL21, "", ids, "",
toMeterLinkModeSet(multical21),
0,
no_meter_shells,
no_meter_jsons));
multical21_and_supercom587_config.meters.push_back(MeterInfo("", "m2", MeterDriver::SUPERCOM587, ids, "",
multical21_and_supercom587_config.meters.push_back(MeterInfo("", "m2", MeterDriver::SUPERCOM587, "", ids, "",
toMeterLinkModeSet(supercom587),
0,
no_meter_shells,
@ -757,3 +760,46 @@ void test_months()
// 2100 is not a leap year since %100=0 and not overriden %400 != 0.
test_month(2000,02,29, 12*100, "2000-02-29", "2100-02-28");
}
// Vatten multical21:BUS1:c1 12345678 KEY
// Tempmeter piigth(info=123):BUS2:2400 0 NOKEY
void testm(string arg, bool xok,
string xdriver, string xextras, string xbus, string xbps, string xlm)
{
MeterInfo mi;
bool ok = mi.parse("", arg, "12345678", "");
if (ok != xok)
{
printf("ERROR in meter parse test \"%s\" expected %s but got %s\n", arg.c_str(), xok?"OK":"FALSE", ok?"OK":"FALSE");
return;
}
if (ok == false) return;
if (toString(mi.driver) != xdriver,
mi.extras != xextras ||
mi.bus != xbus ||
to_string(mi.bps) != xbps ||
mi.link_modes.hr() != xlm)
{
printf("ERROR in meter parsing parts \"%s\"\n", arg.c_str());
}
}
void test_meters()
{
testm("piigth:BUS1:2400", true,
"piigth", // driver
"", // extras
"BUS1", // bus
"2400", // bps
"mbus"); // linkmodes
testm("multical21:c1", true,
"multical21", // driver
"", // extras
"", // bus
"0", // bps
"c1"); // linkmodes
}

Wyświetl plik

@ -527,10 +527,19 @@ bool is_ascii_alnum(char c)
return false;
}
bool is_ascii(char c)
{
if (c >= 'A' && c <= 'Z') return true;
if (c >= 'a' && c <= 'z') return true;
return false;
}
bool isValidAlias(string alias)
{
if (alias.length() == 0) return false;
if (!is_ascii(alias[0])) return false;
for (char c : alias)
{
if (!is_ascii_alnum(c)) return false;
@ -1934,3 +1943,19 @@ void checkIfMultipleWmbusMetersRunning()
}
}
}
bool isValidBps(string b)
{
if (b == "300") return true;
if (b == "600") return true;
if (b == "1200") return true;
if (b == "2400") return true;
if (b == "4800") return true;
if (b == "9600") return true;
if (b == "14400") return true;
if (b == "19200") return true;
if (b == "38400") return true;
if (b == "57600") return true;
if (b == "115200") return true;
return false;
}

Wyświetl plik

@ -112,6 +112,7 @@ void logAlarm(Alarm type, std::string info);
void setAlarmShells(std::vector<std::string> &alarm_shells);
bool isValidAlias(std::string alias);
bool isValidBps(std::string b);
bool isValidMatchExpression(std::string id, bool non_compliant);
bool isValidMatchExpressions(std::string ids, bool non_compliant);
bool doesIdMatchExpression(std::string id, std::string match_rule);

Wyświetl plik

@ -4328,22 +4328,6 @@ bool is_command(string b, string *cmd)
return true;
}
bool is_bps(string b)
{
if (b == "300") return true;
if (b == "600") return true;
if (b == "1200") return true;
if (b == "2400") return true;
if (b == "4800") return true;
if (b == "9600") return true;
if (b == "14400") return true;
if (b == "19200") return true;
if (b == "38400") return true;
if (b == "57600") return true;
if (b == "115200") return true;
return false;
}
bool check_file(string f, bool *is_tty, bool *is_stdin, bool *is_file, bool *is_simulation)
{
*is_tty = *is_stdin = *is_file = *is_simulation = false;
@ -4560,7 +4544,7 @@ bool SpecifiedDevice::parse(string &arg)
file_checked = true;
typeidextras_checked = true;
}
else if (!bps_checked && is_bps(p))
else if (!bps_checked && isValidBps(p))
{
file_checked = true;
typeidextras_checked = true;

Wyświetl plik

@ -37,7 +37,7 @@ mqtt_publish) sent to a REST API (eg curl) or store it in a database
\fB\--format=\fR(hr|json|fields) for human readable, json or semicolon separated fields
\fB\--ignoreduplicates\fR ignore telegram duplicates (when using multiple receiving dongles or repeaters)
\fB\--ignoreduplicates\fR=true ignore telegram duplicates (when using multiple receiving dongles or repeaters)
\fB\--json_xxx=yyy\fR always add "xxx"="yyy" to the json output and add shell env METER_xxx=yyy
@ -61,8 +61,6 @@ mqtt_publish) sent to a REST API (eg curl) or store it in a database
\fB\--logtelegrams\fR log the contents of the telegrams for easy replay
\fB\--ignoreduplicates\fR ignore duplicate telegrams, remember the last 10 telegrams
\fB\--meterfiles=\fR<dir> store meter readings in dir
\fB\--meterfilesaction=\fR(overwrite|append) overwrite or append to the meter readings file