Improve minomess driver. Add default fields setting to drivers.

pull/599/head
Fredrik Öhrström 2022-09-01 17:44:59 +02:00
rodzic f7c1556f02
commit eafa1d9fc1
7 zmienionych plików z 119 dodań i 33 usunięć

Wyświetl plik

@ -1,4 +1,6 @@
Improve minomess driver to handle telegrams from wired m-bus module.
Added Enercal F2 heat meter.
Paulo Rossi added support for the AMB3665-M wmbus dongle for N-mode 169 MHz telegrams. Thanks Paulo!
@ -11,7 +13,7 @@ Eventually the old fields will go away in drivers.
ATTENTION! When a field is not optional in the driver description,
but alas, no data has arrived in the telegram, then the json now
contains a null for the value! Previousy 0 was used, which is misleading.
contains a null for the value! Previousy 0 was used, which was misleading.
ATTENTION! The default location of the meter_readings directory has changed
from /var/log/wmbusmeters/meter_readings to /var/lib/wmbusmeters/meter_readings

Wyświetl plik

@ -59,9 +59,12 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
vector<string> telegram_shells;
vector<string> alarm_shells;
vector<string> extra_constant_fields;
vector<string> selected_fields;
debug("(config) loading meter file %s\n", file.c_str());
for (;;) {
for (;;)
{
pair<string,string> p = getNextKeyValue(buf, i);
if (p.first == "") break;
@ -131,6 +134,16 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
alarm_shells.push_back(p.second);
}
else
if (p.first == "selectedfields")
{
if (selected_fields.size() > 0)
{
warning("(warning) selectfields already used! Ignoring selectfields %s", p.second.c_str());
return;
}
selected_fields = splitString(p.second, ',');
}
else
if (startsWith(p.first, "json_") ||
startsWith(p.first, "field_"))
{
@ -183,7 +196,7 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
mi.extra_constant_fields = extra_constant_fields;
mi.shells = telegram_shells;
mi.idsc = toIdsCommaSeparated(mi.ids);
mi.selected_fields = selected_fields;
c->meters.push_back(mi);
}
@ -629,15 +642,7 @@ void handleSelectedFields(Configuration *c, string s)
warning("(warning) selectfields already used! Ignoring selectfields %s", s.c_str());
return;
}
char buf[s.length()+1];
strcpy(buf, s.c_str());
char *saveptr {};
const char *tok = strtok_r(buf, ",", &saveptr);
while (tok != NULL)
{
c->selected_fields.push_back(tok);
tok = strtok_r(NULL, ",", &saveptr);
}
c->selected_fields = splitString(s, ',');
}
void handleShell(Configuration *c, string cmdline)

Wyświetl plik

@ -28,6 +28,7 @@ namespace
static bool ok = registerDriver([](DriverInfo&di)
{
di.setName("minomess");
di.setDefaultFields("name,id,total_m3,target_m3,status,timestamp");
di.setMeterType(MeterType::WaterMeter);
di.addLinkMode(LinkMode::C1);
di.addDetection(MANUFACTURER_ZRI, 0x07, 0x00);
@ -36,16 +37,9 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addNumericFieldWithExtractor(
"total",
"The total water consumption recorded by this meter.",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
);
addOptionalCommonFields();
addOptionalFlowRelatedFields();
addStringFieldWithExtractor(
"meter_date",
@ -59,10 +53,12 @@ namespace
/* If the meter is recently commissioned, the target water consumption value is bogus.
The bits store 0xffffffff. Should we deal with this? Now a very large value is printed in the json. */
// The wmbus telegram contains only storage 8 for target_date and total.
addNumericFieldWithExtractor(
"target",
"The total water consumption recorded at the beginning of this month.",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
@ -74,13 +70,38 @@ namespace
addStringFieldWithExtractor(
"target_date",
"Date when target water consumption was recorded.",
PrintProperty::JSON,
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Date)
.set(StorageNr(8))
);
// The wire mbus telegram contains 4 totals and dates. For the moment we only print nr 1 which is the latest.
addNumericFieldWithExtractor(
"target",
"The total water consumption recorded at the beginning of this month.",
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(StorageNr(1))
);
addStringFieldWithExtractor(
"target_date",
"Date when target water consumption was recorded.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Date)
.set(StorageNr(1))
);
/*
According to data sheet, there are two status/info bytes, byte A and byte B.
Unfortunately we do not now is byte A is the first or second byte. Oh well.
@ -198,4 +219,9 @@ namespace
// Test: Mino minomess 15503451 NOKEY
// telegram=|6644496A1064035514377251345015496A0007EE0050052F2F#0C1359000000026CBE2B82046CA12B8C0413FFFFFFFF8D0493132CFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FD1700002F2F|
// {"media":"water","meter":"minomess","name":"Mino","id":"15503451","total_m3":0.059,"meter_date":"2021-11-30","target_m3":244444.442,"target_date":"2021-11-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |Mino;15503451;0.059000;244444.442000;OK;1111-11-11 11:11.11
// |Mino;15503451;0.059;244444.442;OK;1111-11-11 11:11.11
// Test: Minowired minomess 57575757 NOKEY
// telegram=|6874746808007257575757496A000712000000_0C7857575757046D2414DE280413000000000C943C000000004413FFFFFFFF426CFFFF840113FFFFFFFF82016CFFFFC40113FFFFFFFFC2016CFFFF840213FFFFFFFF82026CFFFF043B000000000422E62F000004260000000034220000000002FD1700001F5716|
// {"media":"water","meter":"minomess","name":"Minowired","id":"57575757","fabrication_no":"57575757","operating_time_h":0,"on_time_h":3.406111,"on_time_at_error_h":0,"meter_datetime":"2022-08-30 20:36","total_m3":0,"total_backward_m3":0,"volume_flow_m3h":0,"meter_date":null,"target_m3":4294967.295,"target_date":"2127-15-31","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |Minowired;57575757;0;4294967.295;OK;1111-11-11 11:11.11

Wyświetl plik

@ -137,5 +137,5 @@ namespace
// Test: qualco qualcosonic 03016408 NOKEY
// telegram=|76440907086401030B0d7a78000000046d030fB726346d0000010134fd170000000004200f13cf0104240f13cf0104863B0000000004863cdc0000000413B5150600042B86f1ffff043B030200000259c002025d2c05026194fd0c780864010384086d3B17Bf258408863B000000008408863c0B000000|
// {"media":"heat/cooling load","meter":"qualcosonic","name":"qualco","id":"03016408","fabrication_no":"03016408","operating_time_h":2.34167,"on_time_h":2.34167,"meter_datetime":"2021-06-23 15:03","meter_error_datetime":"2000-01-01 00:00","total_m3":398.773,"flow_temperature_c":7.04,"return_temperature_c":13.24,"flow_return_temperature_difference_c":-6.2,"volume_flow_m3h":0.515,"status":"OK","total_heat_energy_kwh":0,"total_cooling_energy_kwh":220,"power_kw":-3.706,"target_datetime":"2021-05-31 23:59","target_heat_energy_kwh":0,"target_cooling_energy_kwh":11,"timestamp":"1111-11-11T11:11:11Z"}
// {"media":"heat/cooling load","meter":"qualcosonic","name":"qualco","id":"03016408","fabrication_no":"03016408","operating_time_h":2.34167,"on_time_h":2.34167,"meter_datetime":"2021-06-23 15:03","meter_datetime_at_error":"2000-01-01 00:00","total_m3":398.773,"flow_temperature_c":7.04,"return_temperature_c":13.24,"flow_return_temperature_difference_c":-6.2,"volume_flow_m3h":0.515,"status":"OK","total_heat_energy_kwh":0,"total_cooling_energy_kwh":220,"power_kw":-3.706,"target_datetime":"2021-05-31 23:59","target_heat_energy_kwh":0,"target_cooling_energy_kwh":11,"timestamp":"1111-11-11T11:11:11Z"}
// |qualco;03016408;OK;0.000000;220.000000;-3.706000;2021-05-31 23:59;0.000000;11.000000;1111-11-11 11:11.11

Wyświetl plik

@ -1635,13 +1635,20 @@ bool checkConstantField(string *buf, string field, char c, vector<string> *extra
return false;
}
string concatFields(Meter *m, Telegram *t, char c, vector<FieldInfo> &prints, vector<Unit> &cs, bool hr,
vector<string> *selected_fields, vector<string> *extra_constant_fields)
{
if (selected_fields == NULL || selected_fields->size() == 0)
{
return concatAllFields(m, t, c, prints, cs, hr, extra_constant_fields);
// No global override, but is there a meter driver setting?
if (m->selectedFields().size() > 0)
{
selected_fields = &m->selectedFields();
}
else
{
return concatAllFields(m, t, c, prints, cs, hr, extra_constant_fields);
}
}
string buf = "";
@ -2225,6 +2232,14 @@ shared_ptr<Meter> createMeter(MeterInfo *mi)
shared_ptr<Meter> newm = di->construct(*mi);
newm->addConversions(mi->conversions);
newm->setPollInterval(mi->poll_interval);
if (mi->selected_fields.size() > 0)
{
newm->setSelectedFields(mi->selected_fields);
}
else
{
newm->setSelectedFields(di->defaultFields());
}
verbose("(meter) created %s %s %s %s\n",
mi->name.c_str(),
di->name().str().c_str(),
@ -2758,6 +2773,17 @@ void MeterCommonImplementation::addOptionalCommonFields()
.set(VIFRange::OnTime)
);
addNumericFieldWithExtractor(
"on_time_at_error",
"How long the meter has been in an error state while powered up.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::AtError)
.set(VIFRange::OnTime)
);
addStringFieldWithExtractor(
"meter_datetime",
"Date and time when the meter sent the telegram.",
@ -2768,7 +2794,7 @@ void MeterCommonImplementation::addOptionalCommonFields()
);
addStringFieldWithExtractor(
"meter_error_datetime",
"meter_datetime_at_error",
"Date and time when the meter was in error.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
FieldMatcher::build()
@ -2782,7 +2808,7 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
{
addNumericFieldWithExtractor(
"total",
"The total heating/cooling media volume consumption recorded by this meter.",
"The total media volume consumption recorded by this meter.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
@ -2791,9 +2817,21 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
.set(VIFRange::Volume)
);
addNumericFieldWithExtractor(
"total_backward",
"The total media volume flowing backward.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.add(VIFCombinable::BackwardFlow)
);
addNumericFieldWithExtractor(
"flow_temperature",
"Forward heat/cooling media temperature.",
"Forward media temperature.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::Auto,
@ -2804,7 +2842,7 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
addNumericFieldWithExtractor(
"return_temperature",
"Return heat/cooling media temperature.",
"Return media temperature.",
PrintProperty::JSON | PrintProperty::OPTIONAL,
Quantity::Temperature,
VifScaling::Auto,
@ -2834,4 +2872,6 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
.set(MeasurementType::Instantaneous)
.set(VIFRange::VolumeFlow)
);
}

Wyświetl plik

@ -183,6 +183,7 @@ struct MeterInfo
vector<string> shells;
vector<string> extra_constant_fields; // Additional static fields that are added to each message.
vector<Unit> conversions; // Additional units desired in json.
vector<string> selected_fields; // Usually set to the default fields, but can be override in meter config.
// If this is a meter that needs to be polled.
int poll_interval; // Poll every x seconds.
@ -251,13 +252,14 @@ private:
MeterType type_; // Water, Electricity etc.
function<shared_ptr<Meter>(MeterInfo&,DriverInfo&di)> constructor_; // Invoke this to create an instance of the driver.
vector<DriverDetect> detect_;
vector<string> default_fields_;
public:
DriverInfo() {};
DriverInfo(MeterDriver mt) : driver_(mt) {};
void setName(std::string n) { name_ = n; }
void setMeterType(MeterType t) { type_ = t; }
void setDefaultFields(string f) { default_fields_ = splitString(f, ','); }
void addLinkMode(LinkMode lm) { linkmodes_.addLinkMode(lm); }
void addMfctTPLStatusBits(Translate::Lookup lookup) { mfct_tpl_status_bits_ = lookup; }
void setConstructor(function<shared_ptr<Meter>(MeterInfo&,DriverInfo&)> c) { constructor_ = c; }
@ -267,6 +269,7 @@ public:
MeterDriver driver() { return driver_; }
DriverName name() { return name_; }
MeterType type() { return type_; }
vector<string>& defaultFields() { return default_fields_; }
LinkModeSet linkModes() { return linkmodes_; }
Translate::Lookup &mfctTPLStatusBits() { return mfct_tpl_status_bits_; }
shared_ptr<Meter> construct(MeterInfo& mi) { return constructor_(mi, *this); }
@ -429,6 +432,9 @@ struct Meter
virtual string idsc() = 0;
// This meter can report these fields, like total_m3, temp_c.
virtual vector<FieldInfo> &fieldInfos() = 0;
// Either the default fields specified in the driver, or override fields in the meter configuration file.
virtual vector<string> &selectedFields() = 0;
virtual void setSelectedFields(vector<string> &f) = 0;
virtual string meterDriver() = 0;
virtual string name() = 0;
virtual MeterDriver driver() = 0;

Wyświetl plik

@ -95,6 +95,7 @@ protected:
void setMeterType(MeterType mt);
void addLinkMode(LinkMode lm);
void addMfctTPLStatusBits(Translate::Lookup lookup);
void setDefaultFields(string f);
// Print with the default unit for this quantity.
void addPrint(string vname, Quantity vquantity,
@ -235,6 +236,9 @@ protected:
void addOptionalCommonFields();
void addOptionalFlowRelatedFields();
vector<string> &selectedFields() { return selected_fields_; }
void setSelectedFields(vector<string> &f) { selected_fields_ = f; }
private:
int index_ {};
@ -261,6 +265,9 @@ protected:
vector<Unit> conversions_;
vector<FieldInfo> field_infos_;
vector<string> field_names_;
// Defaults to a setting specified in the driver. Can be overridden in the meter file.
// There is also a global selected_fields that can be set on the command line or in the conf file.
vector<string> selected_fields_;
// Map difvif key to hex values from telegrams.
std::map<std::string,std::pair<int,std::string>> hex_values_;
// Map field name (total_volume) to numeric value.