Merge remote-tracking branch 'origin/master' into fb_add_itron_ultramaxx

pull/1091/head
Andreas Horrer 2023-11-12 20:45:50 +01:00
commit 00039bbdb8
24 zmienionych plików z 1091 dodań i 198 usunięć

Wyświetl plik

@ -1,4 +1,7 @@
ATTENTION! The hydrus driver could report the wrong value for total_at_date_m3
if an at_date had not been reached yet. This is fixed.
Added initial support for drivers that can be loaded from config files. Added initial support for drivers that can be loaded from config files.
Properly receive telegrams from amb8465 which is in command mode. Properly receive telegrams from amb8465 which is in command mode.
Chris Bednarczyk improved the build process for Darwin/MacOS platform. Thanks Chris! Chris Bednarczyk improved the build process for Darwin/MacOS platform. Thanks Chris!

Wyświetl plik

@ -55,4 +55,4 @@ telegram=|494468509494949495377286868686A85CFE07A90030052F2F_0413100000000F52FCF
# Test Zenner Minomess C1 water meter # Test Zenner Minomess C1 water meter
telegram=|6644496A1064035514377251345015496A0007EE0050052F2F_0C1359000000026CBE2B82046CA12B8C0413FFFFFFFF8D0493132CFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FD1700002F2F| telegram=|6644496A1064035514377251345015496A0007EE0050052F2F_0C1359000000026CBE2B82046CA12B8C0413FFFFFFFF8D0493132CFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FD1700002F2F|
{"media":"water","meter":"minomess","name":"Mino","id":"15503451","meter_date":"2021-11-30","total_m3":0.059,"target_m3":244444.442,"target_date":"2021-11-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"} {"media":"water","meter":"minomess","name":"Mino","id":"15503451","meter_date":"2021-11-30","total_m3":0.059,"target_date":"2021-11-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"}

Wyświetl plik

@ -122,13 +122,13 @@ telegram=|5744b40988227711101b7ab20800000265a00842658f088201659f08226589081265a0
# Test Hydrus water meter telegram # Test Hydrus water meter telegram
telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000| telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000|
{"at_datetime": "2019-10-31 23:59","flow_m3h": 0,"flow_temperature_c": 10.4,"id": "64646464","media": "water","meter": "hydrus","name": "HydrusWater","remaining_battery_life_y": 13.686797,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 0.2,"total_m3": 1.174} {"at_datetime": "2019-10-31 23:59","flow_m3h": 0,"flow_temperature_c": 10.4,"id": "64646464","media": "water","meter": "hydrus","name": "HydrusWater","remaining_battery_life_y": 13.686797,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_woot_m3": 0.2,"total_m3": 1.174}
|HydrusWater;64646464;1.174;0.2;OK;1111-11-11 11:11.11 |HydrusWater;64646464;1.174;null;OK;1111-11-11 11:11.11
# Test Hydrus new version water meter telegram # Test Hydrus new version water meter telegram
telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F| telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F|
{"at_datetime": "2020-09-13 23:59","customer": "A20B410178","flow_m3h": 0,"id": "65656565","media": "warm water","meter": "hydrus","name": "HydrusVater","remaining_battery_life_y": 15.321328,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 3.431,"total_m3": 3.45} {"at_datetime": "2020-09-13 23:59","customer": "A20B410178","flow_m3h": 0,"id": "65656565","media": "warm water","meter": "hydrus","name": "HydrusVater","remaining_battery_life_y": 15.321328,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_woot_m3": 3.431,"total_m3": 3.45}
|HydrusVater;65656565;3.45;3.431;OK;1111-11-11 11:11.11 |HydrusVater;65656565;3.45;null;OK;1111-11-11 11:11.11
# Test Hydrus with default AES encryption # Test Hydrus with default AES encryption
telegram=||6644242328001081640E7266567464A51170071F0050052C411A08674048DD6BA82A0DF79FFD401309179A893A1BE3CE8EDC50C2A45CD7AFEC3B4CE765820BE8056C124A17416C3722985FFFF7FCEB7094901AB3A16294B511B9A740C9F9911352B42A72FB3B0C| telegram=||6644242328001081640E7266567464A51170071F0050052C411A08674048DD6BA82A0DF79FFD401309179A893A1BE3CE8EDC50C2A45CD7AFEC3B4CE765820BE8056C124A17416C3722985FFFF7FCEB7094901AB3A16294B511B9A740C9F9911352B42A72FB3B0C|

Wyświetl plik

@ -20,6 +20,24 @@
#include"driver_dynamic.h" #include"driver_dynamic.h"
#include"xmq.h" #include"xmq.h"
string check_driver_name(const char *name, string file);
MeterType check_meter_type(const char *meter_type_s, string file);
string check_default_fields(const char *fields, string file);
void check_detection_triplets(DriverInfo *di, string file);
string check_field_name(const char *name, DriverDynamic *dd);
Quantity check_field_quantity(const char *quantity_s, DriverDynamic *dd);
VifScaling check_vif_scaling(const char *vif_scaling_s, DriverDynamic *dd);
PrintProperties check_print_properties(const char *print_properties_s, DriverDynamic *dd);
string get_translation(XMQDoc *doc, XMQNode *node, string name, string lang);
string check_calculate(const char *formula, DriverDynamic *dd);
Unit check_display_unit(const char *display_unit, DriverDynamic *dd);
void check_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd);
void check_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd);
const char *line = "-------------------------------------------------------------------------------";
bool DriverDynamic::load(DriverInfo *di, const string &file) bool DriverDynamic::load(DriverInfo *di, const string &file)
{ {
if (!endsWith(file, ".xmq")) return false; if (!endsWith(file, ".xmq")) return false;
@ -30,85 +48,84 @@ bool DriverDynamic::load(DriverInfo *di, const string &file)
bool ok = xmqParseFile(doc, file.c_str(), "config"); bool ok = xmqParseFile(doc, file.c_str(), "config");
if (!ok) { if (!ok) {
error("Error loading wmbusmeters driver file %s\n%s", warning("(driver) error loading wmbusmeters driver file %s\n%s\n%s\n",
file.c_str(), file.c_str(),
xmqDocError(doc)); xmqDocError(doc),
line);
return false; return false;
} }
const char *name = xmqGetString(doc, NULL, "/driver/name"); try
if (!name) error("(dynamic) Error in %s cannot find driver/name\n" {
" A driver file looks like: driver { name = abc123 ... }\n", file.c_str()); string name = check_driver_name(xmqGetString(doc, NULL, "/driver/name"), file);
di->setName(name);
if (!is_lowercase_alnum_text(name)) error("(dynamic) Error in %s invalid driver name \"%s\"\n" MeterType meter_type = check_meter_type(xmqGetString(doc, NULL, "/driver/meter_type"), file);
" The driver name must consist of lower case ascii a-z and digits 0-9.\n", di->setMeterType(meter_type);
file.c_str(), name);
const char *meter_type_s = xmqGetString(doc, NULL, "/driver/meter_type"); string default_fields = check_default_fields(xmqGetString(doc, NULL, "/driver/default_fields"), file);
if (!meter_type_s) error("(dynamic) Error in %s cannot find driver/meter_type\n" di->setDefaultFields(default_fields);
" Remember to add: meter_type = ...\n", file.c_str());
MeterType meter_type = toMeterType(meter_type_s); verbose("(driver) loading driver %s\n", name.c_str());
if (meter_type == MeterType::UnknownMeter) error("(dynamic) Error in %s unknown meter type %s\n" di->setDynamic(file, doc);
"Available meter types are:\n%s\n"
,
file.c_str(), meter_type_s, availableMeterTypes());
const char *default_fields = xmqGetString(doc, NULL, "/driver/default_fields"); xmqForeach(doc, NULL, "/driver/detect/mvt", (XMQNodeCallback)add_detect, di);
if (!default_fields) error("(dynamic) Error in %s cannot find driver/default_fields\n"
" Remember to add: default_fields = name,id,total_m3,timestamp\n"
" Where you change total_m3 to your meters most important field.\n", file.c_str());
check_detection_triplets(di, file);
verbose("(dynamic) loading %s %s\n", meter_type_s, name); di->setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new DriverDynamic(mi, di)); });
di->setName(name); return true;
di->setDefaultFields(default_fields); }
di->setMeterType(meter_type); catch (...)
di->setDynamic(file, doc); {
return false;
xmqForeach(doc, NULL, "/driver/detect/mvt", add_detect, di); }
if (di->detect().size() == 0) error("(dynamic) Error in %s cannot find any driver/detect/mvt triplets\n"
" Remember to add: detect { mvt = AAA,05,07 mvt = AAA,06,07 ... }\n"
" The triplets contain MANUFACTURER,VERSION,TYPE\n"
" and you can see these values when listening to all meters.\n", file.c_str());
di->setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new DriverDynamic(mi, di)); });
return true;
} }
DriverDynamic::DriverDynamic(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) DriverDynamic::DriverDynamic(MeterInfo &mi, DriverInfo &di) :
MeterCommonImplementation(mi, di), file_name_(di.getDynamicFileName())
{ {
string file = di.getDynamicFileName();
XMQDoc *doc = di.getDynamicDriver(); XMQDoc *doc = di.getDynamicDriver();
verbose("(dynamic) Constructing driver %s from file %s\n", verbose("(driver) constructing driver %s from file %s\n",
di.name().str().c_str(), di.name().str().c_str(),
file.c_str()); fileName().c_str());
xmqForeach(doc, NULL, "/driver/field", add_field, this); try
{
xmqForeach(doc, NULL, "/driver/field", (XMQNodeCallback)add_field, this);
}
catch (...)
{
}
} }
XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, void *dd) XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *di)
{ {
DriverInfo *di = (DriverInfo*)dd;
string mvt = xmqGetString(doc, detect, "."); string mvt = xmqGetString(doc, detect, ".");
auto fields = splitString(mvt, ','); auto fields = splitString(mvt, ',');
if (fields.size() != 3) error("(dynamic) Error in %s, wrong number of fields in mvt triple: mvt = %s\n" if (fields.size() != 3)
" There should be three fields, for example: mvt = AAA,07,05\n", {
di->getDynamicFileName().c_str(), warning("(driver) error in %s, wrong number of fields in mvt triple: mvt = %s\n"
mvt.c_str()); "%s\n"
"There should be three fields, for example: mvt = AAA,07,05\n"
"%s\n",
di->getDynamicFileName().c_str(),
mvt.c_str(),
line,
line);
throw 1;
}
string mfct = fields[0]; string mfct = fields[0];
int mfct_code = 0; int mfct_code = 0;
long version = strtol(fields[1].c_str(), NULL, 16); long version = strtol(fields[1].c_str(), NULL, 16);
long type = strtol(fields[2].c_str(), NULL, 16); long type = strtol(fields[2].c_str(), NULL, 16);
if (mfct.length() == 3) if (mfct.length() == 3)
{ {
char a = mfct[0]; char a = mfct[0];
@ -116,29 +133,67 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, void *dd)
char c = mfct[2]; char c = mfct[2];
if (!(a >= 'A' && a < 'Z' && if (!(a >= 'A' && a < 'Z' &&
b >= 'A' && b < 'Z' && b >= 'A' && b < 'Z' &&
c >= 'A' && c < 'Z')) error("(dynamic) %s bad mfct \"%s\" in mvt detect: should be 3 uppercase characters A-Z\n", c >= 'A' && c < 'Z'))
di->name().str().c_str(), {
mfct.c_str()); warning("(driver) error in %s, bad manufacturer in mvt triplet: %s\n"
"%s\n"
"Use 3 uppercase characters A-Z or 4 lowercase hex chars.\n"
"%s\n",
di->getDynamicFileName().c_str(),
mfct.c_str(),
line,
line);
throw 1;
}
mfct_code = toMfctCode(a, b, c); mfct_code = toMfctCode(a, b, c);
} }
else else
{ {
char *eptr; char *eptr;
mfct_code = strtol(mfct.c_str(), &eptr, 16); mfct_code = strtol(mfct.c_str(), &eptr, 16);
if (*eptr) error("(dynmic) Error in %s: mfct must be either version must be in range 0..ff\n", di->name().str().c_str()); if (*eptr)
{
warning("(driver) error in %s, bad manufacturer in mvt triplet: %s\n"
"%s\n"
"Use 3 uppercase characters A-Z or 4 lowercase hex chars.\n"
"%s\n",
di->getDynamicFileName().c_str(),
mfct.c_str(),
line,
line);
throw 1;
}
} }
if (version > 255 || version < 0) if (version > 255 || version < 0)
{ {
error("(dynmic) Error in %s: version must be in range 0..ff\n", di->name().str().c_str()); warning("(driver) error in %s, bad version in mvt triplet: %02x\n"
"%s\n"
"The version must be a hex value from 00 to ff.\n"
"%s\n",
di->getDynamicFileName().c_str(),
version,
line,
line);
throw 1;
} }
if (type > 255 || type < 0) if (type > 255 || type < 0)
{ {
error("(dynmic) Error in %s: type must be in range 0..ff\n", di->name().str().c_str()); warning("(driver) error in %s, bad type in mvt triplet: %02x\n"
"%s\n"
"The type must be a hex value from 00 to ff.\n"
"%s\n",
di->getDynamicFileName().c_str(),
type,
line,
line);
throw 1;
} }
string mfct_flag = manufacturerFlag(mfct_code); string mfct_flag = manufacturerFlag(mfct_code);
debug("(dynamic) register detection %s %s %2x %02x\n", debug("(driver) register detection %s %s %2x %02x\n",
di->name().str().c_str(), di->getDynamicFileName().c_str(),
mfct_flag.c_str(), mfct_flag.c_str(),
version, version,
type); type);
@ -147,70 +202,456 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, void *dd)
return XMQ_CONTINUE; return XMQ_CONTINUE;
} }
XMQProceed DriverDynamic::add_field(XMQDoc *doc, XMQNode *field, void *d) XMQProceed DriverDynamic::add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *dd)
{ {
DriverDynamic *dd = (DriverDynamic*)d; // The field name must be supplied without a unit ie total (not total_m3) since units are managed by wmbusmeters.
const char *driver_name = dd->name().c_str(); string name = check_field_name(xmqGetString(doc, field, "name"), dd);
const char *name = xmqGetString(doc, field, "name"); // The quantity ie Volume, gives the default unit (m3) for the field. The unit can be overriden with display_unit.
const char *quantity_s = xmqGetString(doc, field, "quantity"); Quantity quantity = check_field_quantity(xmqGetString(doc, field, "quantity"), dd);
const char *type_s = xmqGetString(doc, field, "type");
const char *info_s = xmqGetString(doc, field, "info");
const char *attributes_s = xmqGetString(doc, field, "attributes");
const char *vif_scaling_s = xmqGetString(doc, field, "vif_scaling");
debug("(dynamic) field %s %s %s %s\n", name, quantity_s, vif_scaling_s, attributes_s); // Text fields are either version strings or lookups from status bits.
// All other fields are numeric, ie they have a unit. This also includes date and datetime.
bool is_numeric = quantity != Quantity::Text;
FieldType type = toFieldType(type_s); // The vif scaling is by default Auto but can be overriden for pesky fields.
if (type == FieldType::Unknown) error("(dynamic) Error in %s unknown field type %s\n", driver_name, type_s); VifScaling vif_scaling = check_vif_scaling(xmqGetString(doc, field, "vif_scaling"), dd);
Quantity quantity = toQuantity(quantity_s); // The properties are by default empty but can be specified for specific fields.
if (quantity == Quantity::Unknown) error("(dynamic) Error in %s unknown quantity %s\n", driver_name, quantity); PrintProperties properties = check_print_properties(xmqGetString(doc, field, "attributes"), dd);
VifScaling vif_scaling = toVifScaling(vif_scaling_s); // The about fields explains what the value is for. Ie. is storage 1 the previous day or month value etc.
if (vif_scaling == VifScaling::Unknown) error("(dynamic) Error in %s unknown vif scaling %s\n", driver_name, vif_scaling_s); string info = get_translation(doc, field, "about", language());
PrintProperties properties = toPrintProperties(attributes_s); // The calculate formula is optional.
if (properties.hasUnknown()) error("(dynamic) Error in %s unknown attributes %s\n", driver_name, attributes_s); string calculate = check_calculate(xmqGetString(doc, field, "calculate"), dd);
// The display unit is usually based on the quantity. But you can override it.
Unit display_unit = check_display_unit(xmqGetString(doc, field, "display_unit"), dd);
// Now find all matchers.
FieldMatcher match = FieldMatcher::build(); FieldMatcher match = FieldMatcher::build();
dd->tmp_matcher_ = &match;
int num_matches = xmqForeach(doc, field, "match", (XMQNodeCallback)add_match, dd);
// Check if there were any matches at all, if not, then disable the matcher.
match.active = num_matches > 0;
xmqForeach(doc, field, "match", add_match, &match); if (is_numeric)
{
dd->addNumericFieldWithExtractor( if (calculate == "")
{
dd->addNumericFieldWithExtractor(
name,
info,
properties,
quantity,
vif_scaling,
match,
display_unit
);
}
else
{
if (match.active)
{
dd->addNumericFieldWithCalculator(
name,
info,
properties,
quantity,
calculate,
display_unit
);
}
else
{
dd->addNumericFieldWithCalculatorAndMatcher(
name,
info,
properties,
quantity,
calculate,
match,
display_unit
);
}
}
}
else
{
dd->addStringFieldWithExtractor(
name, name,
info_s, info,
properties, properties,
quantity,
vif_scaling,
match match
); );
}
return XMQ_CONTINUE; return XMQ_CONTINUE;
} }
XMQProceed DriverDynamic::add_match(XMQDoc *doc, XMQNode *match, void *m) XMQProceed DriverDynamic::add_match(XMQDoc *doc, XMQNode *match, DriverDynamic *dd)
{ {
FieldMatcher *fm = (FieldMatcher*)m; FieldMatcher *fm = dd->tmp_matcher_;
check_set_measurement_type(xmqGetString(doc, match, "measurement_type"), fm, dd);
check_set_vif_range(xmqGetString(doc, match, "vif_range"), fm, dd);
const char *measurement_type_s = xmqGetString(doc, match, "measurement_type");
const char *vif_range_s = xmqGetString(doc, match, "vif_range"); const char *vif_range_s = xmqGetString(doc, match, "vif_range");
if (measurement_type_s)
{
MeasurementType measurement_type = toMeasurementType(measurement_type_s);
if (measurement_type == MeasurementType::Unknown)
error("(dynamic) Error unknown measurement type %s\n", measurement_type_s);
fm->set(measurement_type);
}
if (vif_range_s) if (vif_range_s)
{ {
VIFRange vif_range = toVIFRange(vif_range_s); VIFRange vif_range = toVIFRange(vif_range_s);
if (vif_range == VIFRange::None) if (vif_range == VIFRange::None)
error("(dynamic) Error unknown measurement type %s\n", vif_range_s); {
warning("(driver) error unknown measurement type %s\n", vif_range_s);
throw 1;
}
fm->set(vif_range); fm->set(vif_range);
} }
return XMQ_CONTINUE; return XMQ_CONTINUE;
} }
string check_driver_name(const char *name, string file)
{
if (!name)
{
warning("(driver) error in %s, cannot find: driver/name\n"
"%s\n"
"A driver file looks like this: driver { name = abc123 ... }\n"
"%s\n",
file.c_str(),
line,
line);
throw 1;
}
if (!is_lowercase_alnum_text(name))
{
warning("(driver) error in %s, bad driver name: %s\n"
"%s\n"
"The driver name must consist of lower case ascii a-z and digits 0-9.\n"
"%s\n",
file.c_str(),
name,
line,
line);
throw 1;
}
return name;
}
MeterType check_meter_type(const char *meter_type_s, string file)
{
if (!meter_type_s)
{
warning("(driver) error in %s, cannot find: driver/meter_type\n"
"%s\n"
"Remember to add: meter_type = ...\n"
"Available meter types are:\n%s\n"
"%s\n",
file.c_str(),
line,
availableMeterTypes(),
line);
throw 1;
}
MeterType meter_type = toMeterType(meter_type_s);
if (meter_type == MeterType::UnknownMeter)
{
warning("(driver) error in %s, unknown meter type: %s\n"
"%s\n"
"Available meter types are:\n%s\n"
"%s\n",
file.c_str(),
meter_type_s,
line,
availableMeterTypes(),
line);
throw 1;
}
return meter_type;
}
string check_default_fields(const char *default_fields, string file)
{
if (!default_fields)
{
warning("(driver) error in %s, cannot find: driver/default_fields\n"
"%s\n"
"Remember to add for example: default_fields = name,id,total_m3,timestamp\n"
"Where you change total_m3 to your meters most important field.\n"
"%s\n",
file.c_str(),
line,
line);
throw 1;
}
return default_fields;
}
void check_detection_triplets(DriverInfo *di, string file)
{
if (di->detect().size() == 0)
{
warning("(driver) error in %s, cannot find any detection triplets: driver/detect/mvt\n"
"%s\n"
"Remember to add: detect { mvt = AAA,05,07 mvt = AAA,06,07 ... }\n"
"The triplets consists of MANUFACTURER,VERSION,TYPE\n"
"You can see these values when listening to all meters.\n"
"The manufacturer can be given as three uppercase characters A-Z\n"
"or as 4 lower case hex digits.\n"
"%s\n",
file.c_str(),
line,
line);
throw 1;
}
}
string check_field_name(const char *name, DriverDynamic *dd)
{
if (!name)
{
warning("(driver) error in %s, cannot find: driver/field/name\n"
"%s\n"
"Remember to add for example: field { name = total ... }\n"
"%s\n",
dd->fileName().c_str(),
line,
line);
throw 1;
}
string vname;
Unit u;
if (extractUnit(string(name), &vname, &u))
{
warning("(driver) error in %s, bad field name %s (field names should not have units)\n"
"%s\n"
"The field name should not have a unit since units are added automatically.\n"
"Either indirectly based on the quantity or directly based on the display_unit.\n"
"%s\n",
dd->fileName().c_str(),
name,
line,
line);
throw 1;
}
return name;
}
Quantity check_field_quantity(const char *quantity_s, DriverDynamic *dd)
{
if (!quantity_s)
{
warning("(driver) error in %s, cannot find: driver/field/quantity\n"
"%s\n"
"Remember to add for example: field { quantity = Volume ... }\n"
"Available quantities:\n%s\n"
"%s\n",
dd->fileName().c_str(),
line,
availableQuantities(),
line);
throw 1;
}
Quantity quantity = toQuantity(quantity_s);
if (quantity == Quantity::Unknown)
{
warning("(driver) error in %s, bad quantity: %s\n"
"%s\n"
"Available quantities:\n"
"%s\n"
"%s\n",
dd->fileName().c_str(),
quantity_s,
line,
availableQuantities(),
line);
throw 1;
}
return quantity;
}
VifScaling check_vif_scaling(const char *vif_scaling_s, DriverDynamic *dd)
{
if (!vif_scaling_s)
{
return VifScaling::Auto;
}
VifScaling vif_scaling = toVifScaling(vif_scaling_s);
if (vif_scaling == VifScaling::Unknown)
{
warning("(driver) error in %s, bad vif scaling: %s\n",
"%s\n"
"Available vif scalings:\n"
"%s\n"
"%s\n",
dd->fileName().c_str(),
vif_scaling_s,
line,
"???",
line);
throw 1;
}
return vif_scaling;
}
PrintProperties check_print_properties(const char *print_properties_s, DriverDynamic *dd)
{
if (!print_properties_s)
{
return PrintProperties(0);
}
PrintProperties print_properties = toPrintProperties(print_properties_s);
if (print_properties.hasUnknown())
{
warning("(driver) error in %s, unknown attributes: %s\n",
dd->fileName().c_str(),
print_properties_s);
throw 1;
}
return print_properties;
}
string get_translation(XMQDoc *doc, XMQNode *node, string name, string lang)
{
string xpath = name+"/"+lang;
const char *txt = xmqGetString(doc, node, xpath.c_str());
if (!txt)
{
xpath = name+"/en";
txt = xmqGetString(doc, node, xpath.c_str());
if (!txt)
{
txt = "";
}
}
return txt;
}
string check_calculate(const char *formula, DriverDynamic *dd)
{
if (!formula) return "";
return formula;
}
Unit check_display_unit(const char *display_unit_s, DriverDynamic *dd)
{
if (!display_unit_s)
{
return Unit::Unknown;
}
Unit u = toUnit(display_unit_s);
if (u == Unit::Unknown)
{
warning("(driver) error in %s, unknown display unit: %s\n"
"Available units:\n"
"%s\n",
dd->fileName().c_str(),
display_unit_s,
availableUnits());
throw 1;
}
return u;
}
void check_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd)
{
if (!measurement_type_s)
{
warning("(driver) error in %s, cannot find: driver/field/match/measurement_type\n"
"%s\n"
"Remember to add for example: match { measurement_type = Instantaneous ... }\n"
"Available measurement types:\n"
"Instantaneous\n"
"Minimum\n"
"Maximum\n"
"AtError\n"
"Any\n"
"%s\n",
dd->fileName().c_str(),
line,
line);
throw 1;
}
MeasurementType measurement_type = toMeasurementType(measurement_type_s);
if (measurement_type == MeasurementType::Unknown)
{
warning("(driver) error in %s, bad measurement_type: %s\n"
"%s\n"
"Available measurement types:\n"
"Instantaneous\n"
"Minimum\n"
"Maximum\n"
"AtError\n"
"Any\n"
"%s\n",
dd->fileName().c_str(),
measurement_type_s,
line,
line);
throw 1;
}
fm->set(measurement_type);
}
void check_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd)
{
if (!vif_range_s)
{
warning("(driver) error in %s, cannot find: driver/field/match/vif_range\n"
"%s\n"
"Remember to add for example: match { ... vif_range = ReturnTemperature ... }\n"
"Available vif ranges:\n"
"%s\n"
"%s\n",
dd->fileName().c_str(),
line,
availableVIFRanges().c_str(),
line);
throw 1;
}
VIFRange vif_range = toVIFRange(vif_range_s);
if (vif_range == VIFRange::None)
{
warning("(driver) error in %s, bad vif_range: %s\n"
"%s\n"
"Available vif ranges:\n"
"%s\n"
"%s\n",
dd->fileName().c_str(),
vif_range_s,
line,
availableVIFRanges().c_str(),
line);
throw 1;
}
fm->set(vif_range);
}

Wyświetl plik

@ -24,9 +24,16 @@ struct DriverDynamic : public virtual MeterCommonImplementation
{ {
DriverDynamic(MeterInfo &mi, DriverInfo &di); DriverDynamic(MeterInfo &mi, DriverInfo &di);
static bool load(DriverInfo *di, const string &name); static bool load(DriverInfo *di, const string &name);
static XMQProceed add_detect(XMQDoc *doc, XMQNode *detect, void *dd); static XMQProceed add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *di);
static XMQProceed add_field(XMQDoc *doc, XMQNode *field, void *dd); static XMQProceed add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *dd);
static XMQProceed add_match(XMQDoc *doc, XMQNode *match, void *dd); static XMQProceed add_match(XMQDoc *doc, XMQNode *match, DriverDynamic *dd);
const string &fileName() { return file_name_; }
private:
string file_name_;
FieldMatcher *tmp_matcher_;
}; };
#endif #endif

Wyświetl plik

@ -124,7 +124,7 @@ namespace
); );
addNumericFieldWithExtractor( addNumericFieldWithExtractor(
"total_at_date", "total_at_woot",
"Fix this! The total water consumption recorded at last day. Perhaps?", "Fix this! The total water consumption recorded at last day. Perhaps?",
DEFAULT_PRINT_PROPERTIES, DEFAULT_PRINT_PROPERTIES,
Quantity::Volume, Quantity::Volume,
@ -163,14 +163,14 @@ namespace
// Test: HydrusWater hydrus 64646464 NOKEY // Test: HydrusWater hydrus 64646464 NOKEY
// Comment: // Comment:
// telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000| // telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000|
// {"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"flow_m3h":0,"flow_temperature_c":10.4,"remaining_battery_life_y":13.686797,"status":"OK","at_datetime":"2019-10-31 23:59","total_at_date_m3": 0.2,"timestamp":"1111-11-11T11:11:11Z"} // {"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"flow_m3h":0,"flow_temperature_c":10.4,"remaining_battery_life_y":13.686797,"status":"OK","at_datetime":"2019-10-31 23:59","total_at_woot_m3": 0.2,"timestamp":"1111-11-11T11:11:11Z"}
// |HydrusWater;64646464;1.174;0.2;OK;1111-11-11 11:11.11 // |HydrusWater;64646464;1.174;null;OK;1111-11-11 11:11.11
// Test: HydrusVater hydrus 65656565 NOKEY // Test: HydrusVater hydrus 65656565 NOKEY
// Comment: // Comment:
// telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F| // telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F|
// {"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","flow_m3h":0,"customer": "A20B410178","total_m3":3.45,"total_at_date_m3":3.431,"remaining_battery_life_y":15.321328,"at_datetime":"2020-09-13 23:59","total_at_date_m3": 3.431,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} // {"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","flow_m3h":0,"customer": "A20B410178","total_m3":3.45,"remaining_battery_life_y":15.321328,"at_datetime":"2020-09-13 23:59","total_at_woot_m3": 3.431,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |HydrusVater;65656565;3.45;3.431;OK;1111-11-11 11:11.11 // |HydrusVater;65656565;3.45;null;OK;1111-11-11 11:11.11
// Test: HydrusAES hydrus 64745666 NOKEY // Test: HydrusAES hydrus 64745666 NOKEY
// Comment: // Comment:
@ -189,3 +189,9 @@ namespace
// telegram=|1E4424238B06204790607A2A0010D8_0413DDC00000426CBF23441382BB0000| // telegram=|1E4424238B06204790607A2A0010D8_0413DDC00000426CBF23441382BB0000|
// {"media":"warm water","meter":"hydrus","name":"HydrusIzarRSWarm","id":"60904720","total_m3":49.373,"total_at_date_m3":48.002,"at_date":"2021-03-31","status":"OK","timestamp":"1111-11-11T11:11:11Z"} // {"media":"warm water","meter":"hydrus","name":"HydrusIzarRSWarm","id":"60904720","total_m3":49.373,"total_at_date_m3":48.002,"at_date":"2021-03-31","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |HydrusIzarRSWarm;60904720;49.373;48.002;OK;1111-11-11 11:11.11 // |HydrusIzarRSWarm;60904720;49.373;48.002;OK;1111-11-11 11:11.11
// Test: HydrusFoo hydrus 64641820 NOKEY
// Comment: Negative power values.
// telegram=|6344A5112018646470078C00D7900F002C256AB59B00F0F13032019092DE7A6A004007102F2F0C13896729004C1323462400CC101300000000CC201323462400426CDF2C0B3B0200F002FD742F0D025AC100C4016D3B17FE29CC01132841290001FD089F|
// {"at_date": "2022-12-31","at_datetime": "2023-09-30 23:59","flow_m3h": -0.002,"flow_temperature_c": 19.3,"id": "64641820","media": "water","meter": "hydrus","name": "HydrusFoo","remaining_battery_life_y": 9.240436,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3":244.623,"total_at_woot_m3": 294.128,"total_m3": 296.789,"total_tariff1_at_date_m3": 0,"total_tariff2_at_date_m3": 244.623}
// |HydrusFoo;64641820;296.789;244.623;OK;1111-11-11 11:11.11

Wyświetl plik

@ -33,6 +33,7 @@ namespace
di.addDetection(MANUFACTURER_ITW, 0x07, 0x00); di.addDetection(MANUFACTURER_ITW, 0x07, 0x00);
di.addDetection(MANUFACTURER_ITW, 0x07, 0x03); di.addDetection(MANUFACTURER_ITW, 0x07, 0x03);
di.addDetection(MANUFACTURER_ITW, 0x07, 0x33); di.addDetection(MANUFACTURER_ITW, 0x07, 0x33);
di.addDetection(MANUFACTURER_ITW, 0x16, 0x00);
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); }); di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
}); });
@ -149,3 +150,10 @@ namespace
// telegram=|384497265909312000077a930000a0041360B50100066d101295f427004413ac570100426cdf2c047f0000060c027f6c2a0e79000000000000| // telegram=|384497265909312000077a930000a0041360B50100066d101295f427004413ac570100426cdf2c047f0000060c027f6c2a0e79000000000000|
// {"enhanced_id": "000000000000","id": "20310959","media": "water","meter": "itron","meter_datetime": "2023-07-20 21:18:16","name": "AnyWater","status": "OK","target_date": "2022-12-31","target_m3": 87.98,"timestamp": "1111-11-11T11:11:11Z","total_m3": 111.968,"unknown_a": "WOOTA_C060000","unknown_b": "WOOTB_2A6C"} // {"enhanced_id": "000000000000","id": "20310959","media": "water","meter": "itron","meter_datetime": "2023-07-20 21:18:16","name": "AnyWater","status": "OK","target_date": "2022-12-31","target_m3": 87.98,"timestamp": "1111-11-11T11:11:11Z","total_m3": 111.968,"unknown_a": "WOOTA_C060000","unknown_b": "WOOTB_2A6C"}
// |AnyWater;20310959;111.968;87.98;1111-11-11 11:11.11 // |AnyWater;20310959;111.968;87.98;1111-11-11 11:11.11
// Test: ColdWaterMeter itron 23362098 NOKEY
// Comment: Allmess cold water with Itron Module programmed with type 0x16
// telegram=|3A4497269820362300167AF60020A52F2F_04132E100000066D03260DE12B007413FEFEFEFE426C1F01047F1600060C027F9A2A0E79187103002300|
// {"enhanced_id": "002300037118", "id": "23362098", "media": "cold water", "meter": "itron", "meter_datetime": "2023-11-01 13:38:03", "name": "ColdWaterMeter", "status": "OK", "target_date": "2000-01-31", "timestamp": "1111-11-11T11:11:11Z", "total_m3": 4.142,"unknown_a": "WOOTA_C060016","unknown_b": "WOOTB_2A9A" }
// |ColdWaterMeter;23362098;4.142;null;1111-11-11 11:11.11

Wyświetl plik

@ -1,6 +1,6 @@
/* /*
Copyright (C) 2021 Olli Salonen (gpl-3.0-or-later) Copyright (C) 2021 Olli Salonen (gpl-3.0-or-later)
Copyright (C) 2022 Fredrik Öhrström (gpl-3.0-or-later) Copyright (C) 2022-2023 Fredrik Öhrström (gpl-3.0-or-later)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -207,8 +207,8 @@ namespace
// Test: Mino minomess 15503451 NOKEY // Test: Mino minomess 15503451 NOKEY
// telegram=|6644496A1064035514377251345015496A0007EE0050052F2F#0C1359000000026CBE2B82046CA12B8C0413FFFFFFFF8D0493132CFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FD1700002F2F| // telegram=|6644496A1064035514377251345015496A0007EE0050052F2F#0C1359000000026CBE2B82046CA12B8C0413FFFFFFFF8D0493132CFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FD1700002F2F|
// {"media":"water","meter":"minomess","name":"Mino","id":"15503451","meter_date":"2021-11-30","total_m3":0.059,"target_m3":244444.442,"target_date":"2021-11-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"} // {"media":"water","meter":"minomess","name":"Mino","id":"15503451","meter_date":"2021-11-30","total_m3":0.059,"target_date":"2021-11-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |Mino;15503451;0.059;244444.442;OK;1111-11-11 11:11.11 // |Mino;15503451;0.059;null;OK;1111-11-11 11:11.11
// Test: Minowired minomess 57575757 NOKEY // Test: Minowired minomess 57575757 NOKEY
// telegram=|6874746808007257575757496A000712000000_0C7857575757046D2414DE280413000000000C943C000000004413FFFFFFFF426CFFFF840113FFFFFFFF82016CFFFFC40113FFFFFFFFC2016CFFFF840213FFFFFFFF82026CFFFF043B000000000422E62F000004260000000034220000000002FD1700001F5716| // telegram=|6874746808007257575757496A000712000000_0C7857575757046D2414DE280413000000000C943C000000004413FFFFFFFF426CFFFF840113FFFFFFFF82016CFFFFC40113FFFFFFFFC2016CFFFF840213FFFFFFFF82026CFFFF043B000000000422E62F000004260000000034220000000002FD1700001F5716|
@ -223,4 +223,4 @@ namespace
// Test: Zenner_warm minomess 51413121 NOKEY // Test: Zenner_warm minomess 51413121 NOKEY
// telegram=|6644496A8753155518377221314151496A0106300050052F2F_0C1357000000026CEC2182046CE1218C0413000000808D0493132C33FE00008000008000008000008000008000008000008000008000008000008000008000008000008000008002FD1700002F2F| // telegram=|6644496A8753155518377221314151496A0106300050052F2F_0C1357000000026CEC2182046CE1218C0413000000808D0493132C33FE00008000008000008000008000008000008000008000008000008000008000008000008000008000008002FD1700002F2F|
// {"media":"warm water","meter":"minomess","name":"Zenner_warm","id":"51413121","meter_date":"2023-01-12","total_m3":0.057,"target_m3":80000,"target_date":"2023-01-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"} // {"media":"warm water","meter":"minomess","name":"Zenner_warm","id":"51413121","meter_date":"2023-01-12","total_m3":0.057,"target_m3":80000,"target_date":"2023-01-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |Zenner_warm;51413121;0.057;80000;OK;1111-11-11 11:11.11 // |Zenner_warm;51413121;0.057;80000;OK;1111-11-11 11:11.11

Wyświetl plik

@ -222,8 +222,8 @@ namespace
// Test: zenner_heat qcaloric 25932395 NOKEY // Test: zenner_heat qcaloric 25932395 NOKEY
// telegram=|5E44496A95239325FD087A2CC050052F2F_0B6E030100426CDF2C4B6EFFFFFF82046CE1228B046E6200008D04EE132C3BFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F2F2F2F| // telegram=|5E44496A95239325FD087A2CC050052F2F_0B6E030100426CDF2C4B6EFFFFFF82046CE1228B046E6200008D04EE132C3BFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F2F2F2F|
// {"media":"heat cost allocation","meter":"qcaloric","name":"zenner_heat","id":"25932395","status":"UNKNOWN_C0","current_consumption_hca":103,"set_date":"2022-12-31","consumption_at_set_date_hca":2444442,"set_date_1":"2022-12-31","consumption_at_set_date_1_hca":2444442,"set_date_8":"2023-02-01","consumption_at_set_date_8_hca":62,"timestamp":"1111-11-11T11:11:11Z"} // {"media":"heat cost allocation","meter":"qcaloric","name":"zenner_heat","id":"25932395","status":"UNKNOWN_C0","current_consumption_hca":103,"set_date":"2022-12-31","set_date_1":"2022-12-31","set_date_8":"2023-02-01","consumption_at_set_date_8_hca":62,"timestamp":"1111-11-11T11:11:11Z"}
// |zenner_heat;25932395;103;2022-12-31;2444442;1111-11-11 11:11.11 // |zenner_heat;25932395;103;2022-12-31;null;1111-11-11 11:11.11
// Comment: Normal telegram that fills in values. // Comment: Normal telegram that fills in values.
// telegram=|314493449392919034087a520000200b6e9700004b6e700200426c9f2ccb086e970000c2086cbe26326cffff046d2d16a227| // telegram=|314493449392919034087a520000200b6e9700004b6e700200426c9f2ccb086e970000c2086cbe26326cffff046d2d16a227|

Wyświetl plik

@ -20,6 +20,8 @@
#include"util.h" #include"util.h"
#include<assert.h> #include<assert.h>
#include<cmath>
#include<math.h>
#include<memory.h> #include<memory.h>
#include<limits> #include<limits>
@ -760,6 +762,15 @@ bool checkSizeHex(size_t expected_len, DifVifKey &dvk, string &v)
return false; return false;
} }
bool is_all_F(string &v)
{
for (size_t i = 0; i < v.length(); ++i)
{
if (v[i] != 'F') return false;
}
return true;
}
bool DVEntry::extractDouble(double *out, bool auto_scale, bool assume_signed) bool DVEntry::extractDouble(double *out, bool auto_scale, bool assume_signed)
{ {
int t = dif_vif_key.dif() & 0xf; int t = dif_vif_key.dif() & 0xf;
@ -847,10 +858,18 @@ bool DVEntry::extractDouble(double *out, bool auto_scale, bool assume_signed)
t == 0xC || // 8 digit BCD t == 0xC || // 8 digit BCD
t == 0xE) // 12 digit BCD t == 0xE) // 12 digit BCD
{ {
// Signed BCD values are always visible in bcd! Top nybble is f. We can force assume_signed to true.
assume_signed = true;
// 74140000 -> 00001474 // 74140000 -> 00001474
string& v = value; string& v = value;
uint64_t raw = 0; uint64_t raw = 0;
bool negate = false; bool negate = false;
if (is_all_F(v))
{
*out = std::nan("");
return false;
}
if (t == 0x9) { if (t == 0x9) {
if (!checkSizeHex(2, dif_vif_key, v)) return false; if (!checkSizeHex(2, dif_vif_key, v)) return false;
if (assume_signed && v[0] == 'F') { negate = true; v[0] = '0'; } if (assume_signed && v[0] == 'F') { negate = true; v[0] = '0'; }
@ -1009,24 +1028,33 @@ bool DVEntry::extractLong(uint64_t *out)
{ {
// 74140000 -> 00001474 // 74140000 -> 00001474
string& v = value; string& v = value;
if (is_all_F(v))
{
return false;
}
uint64_t raw = 0; uint64_t raw = 0;
bool negate = false;
if (t == 0x9) { if (t == 0x9) {
if (!checkSizeHex(2, dif_vif_key, value)) return false; if (!checkSizeHex(2, dif_vif_key, value)) return false;
if (v[0] == 'F') { negate = true; v[0] = '0'; }
assert(v.size() == 2); assert(v.size() == 2);
raw = (v[0]-'0')*10 + (v[1]-'0'); raw = (v[0]-'0')*10 + (v[1]-'0');
} else if (t == 0xA) { } else if (t == 0xA) {
if (!checkSizeHex(4, dif_vif_key, value)) return false; if (!checkSizeHex(4, dif_vif_key, value)) return false;
if (v[2] == 'F') { negate = true; v[2] = '0'; }
assert(v.size() == 4); assert(v.size() == 4);
raw = (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10 raw = (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
+ (v[0]-'0')*10 + (v[1]-'0'); + (v[0]-'0')*10 + (v[1]-'0');
} else if (t == 0xB) { } else if (t == 0xB) {
if (!checkSizeHex(6, dif_vif_key, value)) return false; if (!checkSizeHex(6, dif_vif_key, value)) return false;
if (v[4] == 'F') { negate = true; v[4] = '0'; }
assert(v.size() == 6); assert(v.size() == 6);
raw = (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10 raw = (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
+ (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10 + (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
+ (v[0]-'0')*10 + (v[1]-'0'); + (v[0]-'0')*10 + (v[1]-'0');
} else if (t == 0xC) { } else if (t == 0xC) {
if (!checkSizeHex(8, dif_vif_key, value)) return false; if (!checkSizeHex(8, dif_vif_key, value)) return false;
if (v[6] == 'F') { negate = true; v[6] = '0'; }
assert(v.size() == 8); assert(v.size() == 8);
raw = (v[6]-'0')*10*10*10*10*10*10*10 + (v[7]-'0')*10*10*10*10*10*10 raw = (v[6]-'0')*10*10*10*10*10*10*10 + (v[7]-'0')*10*10*10*10*10*10
+ (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10 + (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
@ -1034,6 +1062,7 @@ bool DVEntry::extractLong(uint64_t *out)
+ (v[0]-'0')*10 + (v[1]-'0'); + (v[0]-'0')*10 + (v[1]-'0');
} else if (t == 0xE) { } else if (t == 0xE) {
if (!checkSizeHex(12, dif_vif_key, value)) return false; if (!checkSizeHex(12, dif_vif_key, value)) return false;
if (v[10] == 'F') { negate = true; v[10] = '0'; }
assert(v.size() == 12); assert(v.size() == 12);
raw =(v[10]-'0')*10*10*10*10*10*10*10*10*10*10*10 + (v[11]-'0')*10*10*10*10*10*10*10*10*10*10 raw =(v[10]-'0')*10*10*10*10*10*10*10*10*10*10*10 + (v[11]-'0')*10*10*10*10*10*10*10*10*10*10
+ (v[8]-'0')*10*10*10*10*10*10*10*10*10 + (v[9]-'0')*10*10*10*10*10*10*10*10 + (v[8]-'0')*10*10*10*10*10*10*10*10*10 + (v[9]-'0')*10*10*10*10*10*10*10*10
@ -1043,6 +1072,11 @@ bool DVEntry::extractLong(uint64_t *out)
+ (v[0]-'0')*10 + (v[1]-'0'); + (v[0]-'0')*10 + (v[1]-'0');
} }
if (negate)
{
raw = (uint64_t)(((int64_t)raw)*-1);
}
*out = raw; *out = raw;
} }
else else
@ -1441,3 +1475,18 @@ const char *toString(DVEntryCounterType ct)
return "unknown"; return "unknown";
} }
string available_vif_ranges_;
const string &availableVIFRanges()
{
if (available_vif_ranges_ != "") return available_vif_ranges_;
#define X(n,from,to,q,u) available_vif_ranges_ += string(#n) + "\n";
LIST_OF_VIF_RANGES
#undef X
// Remove last newline
available_vif_ranges_.pop_back();
return available_vif_ranges_;
}

Wyświetl plik

@ -584,4 +584,6 @@ bool extractDVdate(std::map<std::string,std::pair<int,DVEntry>> *values,
struct tm *value); struct tm *value);
const std::string &availableVIFRanges();
#endif #endif

Wyświetl plik

@ -298,6 +298,16 @@ public:
{ {
string best_driver = ""; string best_driver = "";
if (only != "")
{
DriverInfo di;
if (!lookupDriverInfo(only, &di))
{
error("No such driver %s\n", only.c_str());
}
only = di.name().str();
}
for (DriverInfo *ndr : allDrivers()) for (DriverInfo *ndr : allDrivers())
{ {
string driver_name = toString(*ndr); string driver_name = toString(*ndr);

Wyświetl plik

@ -195,9 +195,10 @@ string loadDriver(const string &file)
// Check that the driver name has not been registered before! // Check that the driver name has not been registered before!
if (lookupDriver(di.name().str()) != NULL) if (lookupDriver(di.name().str()) != NULL)
{ {
error("Cannot load driver %s %s since it is already registered!\n", debug("Ignoring loaded driver %s %s since it is already registered!\n",
di.name().str().c_str(), di.name().str().c_str(),
file.c_str()); file.c_str());
return di.name().str();
} }
// Check that no other driver also triggers on the same detection values. // Check that no other driver also triggers on the same detection values.
@ -3016,36 +3017,6 @@ VifScaling toVifScaling(const char *s)
return VifScaling::Unknown; return VifScaling::Unknown;
} }
FieldType toFieldType(const char *s)
{
if (!strcmp(s, "NumericFieldWithExtractor")) return FieldType::NumericFieldWithExtractor;
if (!strcmp(s, "NumericFieldWithCalculator")) return FieldType::NumericFieldWithCalculator;
if (!strcmp(s, "NumericFieldWithCalculatorAndMatcher")) return FieldType::NumericFieldWithCalculatorAndMatcher;
if (!strcmp(s, "NumericField")) return FieldType::NumericField;
if (!strcmp(s, "StringFieldWithExtractor")) return FieldType::StringFieldWithExtractor;
if (!strcmp(s, "StringFieldWithExtractorAndLookup")) return FieldType::StringFieldWithExtractorAndLookup;
if (!strcmp(s, "StringField")) return FieldType::StringField;
return FieldType::Unknown;
}
const char *toString(FieldType ft)
{
switch (ft) {
case FieldType::NumericFieldWithExtractor: return "NumericFieldWithExtractor";
case FieldType::NumericFieldWithCalculator: return "NumericFieldWithCalculator";
case FieldType::NumericFieldWithCalculatorAndMatcher: return "NumericFieldWithCalculatorAndMatcher";
case FieldType::NumericField: return "NumericField";
case FieldType::StringFieldWithExtractor: return "StringFieldWithExtractor";
case FieldType::StringFieldWithExtractorAndLookup: return "StringFieldWithExtractorAndLookup";
case FieldType::StringField: return "StringField";
case FieldType::Unknown: return "Unknown";
}
return "Unknown";
}
const char* toString(PrintProperty p) const char* toString(PrintProperty p)
{ {
switch(p) switch(p)

Wyświetl plik

@ -54,21 +54,6 @@ struct StringField
StringField(std::string v, FieldInfo *f) : value(v), field_info(f) {} StringField(std::string v, FieldInfo *f) : value(v), field_info(f) {}
}; };
enum class FieldType
{
NumericFieldWithExtractor,
NumericFieldWithCalculator,
NumericFieldWithCalculatorAndMatcher,
NumericField,
StringFieldWithExtractor,
StringFieldWithExtractorAndLookup,
StringField,
Unknown
};
FieldType toFieldType(const char *s);
const char *toString(FieldType ft);
struct MeterCommonImplementation : public virtual Meter struct MeterCommonImplementation : public virtual Meter
{ {
int index(); int index();

Wyświetl plik

@ -589,7 +589,10 @@ bool SerialDeviceFile::open(bool fail_if_not_ok)
fd_ = ::open(file_.c_str(), O_RDONLY | O_NONBLOCK); fd_ = ::open(file_.c_str(), O_RDONLY | O_NONBLOCK);
if (fd_ == -1) if (fd_ == -1)
{ {
if (fail_if_not_ok) error("Could not open file %s for reading.\n", file_.c_str()); if (fail_if_not_ok)
{
error("Could not open file %s for reading.\n", file_.c_str());
}
verbose("(serialdevicefile) could not open file %s for reading.\n", file_.c_str()); verbose("(serialdevicefile) could not open file %s for reading.\n", file_.c_str());
return false; return false;
} }

Wyświetl plik

@ -805,3 +805,37 @@ string SIExp::str() const
return r; return r;
} }
char available_quantities_[2048];
const char *availableQuantities()
{
if (available_quantities_[0]) return available_quantities_;
#define X(q,u) if (Quantity::q != Quantity::Unknown) { \
strcat(available_quantities_, #q); strcat(available_quantities_, "\n"); \
assert(strlen(available_quantities_) < 1024); }
LIST_OF_QUANTITIES
#undef X
// Remove last newline
available_quantities_[strlen(available_quantities_)-1] = 0;
return available_quantities_;
}
char available_units_[2048];
const char *availableUnits()
{
if (available_units_[0]) return available_units_;
#define X(n,suffix,sn,q,ln) if (Unit::n != Unit::Unknown) { \
strcat(available_units_, #suffix); strcat(available_units_, "\n"); \
assert(strlen(available_units_) < 1024); }
LIST_OF_UNITS
#undef X
// Remove last newline
available_units_[strlen(available_units_)-1] = 0;
return available_units_;
}

Wyświetl plik

@ -295,4 +295,7 @@ bool extractUnit(const std::string &s, std::string *vname, Unit *u);
LIST_OF_UNITS LIST_OF_UNITS
#undef X #undef X
const char *availableQuantities();
const char *availableUnits();
#endif #endif

Wyświetl plik

@ -587,7 +587,7 @@ void warning(const char* fmt, ...) {
} }
} }
void verbose(const char* fmt, ...) { void verbose_int(const char* fmt, ...) {
if (verbose_enabled_) { if (verbose_enabled_) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
@ -596,7 +596,7 @@ void verbose(const char* fmt, ...) {
} }
} }
void debug(const char* fmt, ...) { void debug_int(const char* fmt, ...) {
if (debug_enabled_) { if (debug_enabled_) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
@ -605,7 +605,7 @@ void debug(const char* fmt, ...) {
} }
} }
void trace(const char* fmt, ...) { void trace_int(const char* fmt, ...) {
if (trace_enabled_) { if (trace_enabled_) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
@ -2457,3 +2457,29 @@ bool endsWith(const std::string& str, const std::string& suffix)
{ {
return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix); return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
} }
string lang_;
const std::string &language()
{
if (lang_.length() > 0) return lang_;
const char *la = getenv("LANG");
if (!la || strlen(la) < 2)
{
lang_ = "en";
}
else
{
if (la[2] == '_' || la[2] == 0)
{
lang_ = string(la, la+2);
}
else
{
lang_ = "en";
}
}
return lang_;
}

Wyświetl plik

@ -100,9 +100,16 @@ bool enableLogfile(const std::string& logfile, bool daemon);
void disableLogfile(); void disableLogfile();
void enableSyslog(); void enableSyslog();
void error(const char* fmt, ...); void error(const char* fmt, ...);
void verbose(const char* fmt, ...);
void trace(const char* fmt, ...); #define verbose(...) { if (isVerboseEnabled()) { verbose_int(__VA_ARGS__); } }
void debug(const char* fmt, ...); void verbose_int(const char* fmt, ...);
#define trace(...) { if (isTraceEnabled()) { trace_int(__VA_ARGS__); } }
void trace_int(const char* fmt, ...);
#define debug(...) { if (isDebugEnabled()) { debug_int(__VA_ARGS__); } }
void debug_int(const char* fmt, ...);
void warning(const char* fmt, ...); void warning(const char* fmt, ...);
void info(const char* fmt, ...); void info(const char* fmt, ...);
void notice(const char* fmt, ...); void notice(const char* fmt, ...);
@ -304,6 +311,9 @@ int toMfctCode(char a, char b, char c);
bool is_lowercase_alnum_text(const char *text); bool is_lowercase_alnum_text(const char *text);
// The language that the user expects driver and other messages in.
const std::string &language();
#ifndef FUZZING #ifndef FUZZING
#define FUZZING false #define FUZZING false
#endif #endif

Wyświetl plik

@ -1532,8 +1532,12 @@ bool Telegram::checkMAC(std::vector<uchar> &frame,
debug("(wmbus) received mac %s\n", received.c_str()); debug("(wmbus) received mac %s\n", received.c_str());
string truncated = calculated.substr(0, received.length()); string truncated = calculated.substr(0, received.length());
bool ok = truncated == received; bool ok = truncated == received;
if (ok) debug("(wmbus) mac ok!\n"); if (ok)
else { {
debug("(wmbus) mac ok!\n");
}
else
{
debug("(wmbus) mac NOT ok!\n"); debug("(wmbus) mac NOT ok!\n");
explainParse("BADMAC", 0); explainParse("BADMAC", 0);
} }

Wyświetl plik

@ -6369,7 +6369,7 @@ bool has_attributes(xmlNodePtr node)
return NULL == xml_first_attribute(node); return NULL == xml_first_attribute(node);
} }
int xmqForeach(XMQDoc *doq, XMQNode *xmq_node, const char *xpath, NodeCallback cb, void *user_data) int xmqForeach(XMQDoc *doq, XMQNode *xmq_node, const char *xpath, XMQNodeCallback cb, void *user_data)
{ {
xmlDocPtr doc = (xmlDocPtr)xmqGetImplementationDoc(doq); xmlDocPtr doc = (xmlDocPtr)xmqGetImplementationDoc(doq);
xmlXPathContextPtr ctx = xmlXPathNewContext(doc); xmlXPathContextPtr ctx = xmlXPathNewContext(doc);

Wyświetl plik

@ -344,12 +344,12 @@ typedef enum
} XMQProceed; } XMQProceed;
/** /**
NodeCallback: The function type which is called by foreach functions. XMQNodeCallback: The function type which is called by foreach functions.
@doc: The document being processed. @doc: The document being processed.
@node: The node triggering the callback. @node: The node triggering the callback.
@user_data: The user data supplied to for_each. @user_data: The user data supplied to for_each.
*/ */
typedef XMQProceed (*NodeCallback)(XMQDoc *doc, XMQNode *node, void *user_data); typedef XMQProceed (*XMQNodeCallback)(XMQDoc *doc, XMQNode *node, void *user_data);
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -660,7 +660,7 @@ const char *xmqGetString(XMQDoc *doc, XMQNode *node, const char *xpath);
Returns the number of hits. Returns the number of hits.
*/ */
int xmqForeach(XMQDoc *doq, XMQNode *node, const char *xpath, NodeCallback cb, void *user_data); int xmqForeach(XMQDoc *doq, XMQNode *node, const char *xpath, XMQNodeCallback cb, void *user_data);
/** /**

Wyświetl plik

@ -37,8 +37,11 @@ ddriver { name = Iffo }
EOF EOF
cat > $TEST/test_expected.txt <<EOF cat > $TEST/test_expected.txt <<EOF
(dynamic) Error in testoutput/driver.xmq cannot find driver/name (driver) error in testoutput/driver.xmq, cannot find: driver/name
A driver file looks like: driver { name = abc123 ... } -------------------------------------------------------------------------------
A driver file looks like this: driver { name = abc123 ... }
-------------------------------------------------------------------------------
Failed to load driver from file: testoutput/driver.xmq
EOF EOF
$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true $PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
@ -48,12 +51,15 @@ performCheck
TESTNAME="Test bad driver name" TESTNAME="Test bad driver name"
TESTRESULT="ERROR" TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF cat > $TEST/driver.xmq <<EOF
driver { name = Iffo } driver { name = 'a b' }
EOF EOF
cat > $TEST/test_expected.txt <<EOF cat > $TEST/test_expected.txt <<EOF
(dynamic) Error in testoutput/driver.xmq invalid driver name "Iffo" (driver) error in testoutput/driver.xmq, bad driver name: a b
The driver name must consist of lower case ascii a-z and digits 0-9. -------------------------------------------------------------------------------
The driver name must consist of lower case ascii a-z and digits 0-9.
-------------------------------------------------------------------------------
Failed to load driver from file: testoutput/driver.xmq
EOF EOF
$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true $PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
@ -67,8 +73,23 @@ driver { name = iffo }
EOF EOF
cat > $TEST/test_expected.txt <<EOF cat > $TEST/test_expected.txt <<EOF
(dynamic) Error in testoutput/driver.xmq cannot find driver/meter_type (driver) error in testoutput/driver.xmq, cannot find: driver/meter_type
Remember to add: meter_type = ... -------------------------------------------------------------------------------
Remember to add: meter_type = ...
Available meter types are:
DoorWindowDetector
ElectricityMeter
GasMeter
HeatCostAllocationMeter
HeatMeter
HeatCoolingMeter
PulseCounter
SmokeDetector
TempHygroMeter
WaterMeter
PressureSensor
-------------------------------------------------------------------------------
Failed to load driver from file: testoutput/driver.xmq
EOF EOF
$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true $PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
@ -82,7 +103,8 @@ driver { name = iffo meter_type = gurka }
EOF EOF
cat > $TEST/test_expected.txt <<EOF cat > $TEST/test_expected.txt <<EOF
(dynamic) Error in testoutput/driver.xmq unknown meter type gurka (driver) error in testoutput/driver.xmq, unknown meter type: gurka
-------------------------------------------------------------------------------
Available meter types are: Available meter types are:
DoorWindowDetector DoorWindowDetector
ElectricityMeter ElectricityMeter
@ -95,6 +117,8 @@ SmokeDetector
TempHygroMeter TempHygroMeter
WaterMeter WaterMeter
PressureSensor PressureSensor
-------------------------------------------------------------------------------
Failed to load driver from file: testoutput/driver.xmq
EOF EOF
$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true $PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
@ -108,9 +132,12 @@ driver { name = iffo meter_type = WaterMeter }
EOF EOF
cat > $TEST/test_expected.txt <<EOF cat > $TEST/test_expected.txt <<EOF
(dynamic) Error in testoutput/driver.xmq cannot find driver/default_fields (driver) error in testoutput/driver.xmq, cannot find: driver/default_fields
Remember to add: default_fields = name,id,total_m3,timestamp -------------------------------------------------------------------------------
Where you change total_m3 to your meters most important field. Remember to add for example: default_fields = name,id,total_m3,timestamp
Where you change total_m3 to your meters most important field.
-------------------------------------------------------------------------------
Failed to load driver from file: testoutput/driver.xmq
EOF EOF
$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true $PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
@ -124,10 +151,15 @@ driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,t
EOF EOF
cat > $TEST/test_expected.txt <<EOF cat > $TEST/test_expected.txt <<EOF
(dynamic) Error in testoutput/driver.xmq cannot find any driver/detect/mvt triplets (driver) error in testoutput/driver.xmq, cannot find any detection triplets: driver/detect/mvt
Remember to add: detect { mvt = AAA,05,07 mvt = AAA,06,07 ... } -------------------------------------------------------------------------------
The triplets contain MANUFACTURER,VERSION,TYPE Remember to add: detect { mvt = AAA,05,07 mvt = AAA,06,07 ... }
and you can see these values when listening to all meters. The triplets consists of MANUFACTURER,VERSION,TYPE
You can see these values when listening to all meters.
The manufacturer can be given as three uppercase characters A-Z
or as 4 lower case hex digits.
-------------------------------------------------------------------------------
Failed to load driver from file: testoutput/driver.xmq
EOF EOF
$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true $PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
@ -143,10 +175,312 @@ driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,t
EOF EOF
cat > $TEST/test_expected.txt <<EOF cat > $TEST/test_expected.txt <<EOF
(dynamic) Error in testoutput/driver.xmq, wrong number of fields in mvt triple: mvt = alfa (driver) error in testoutput/driver.xmq, wrong number of fields in mvt triple: mvt = alfa
There should be three fields, for example: mvt = AAA,07,05 -------------------------------------------------------------------------------
There should be three fields, for example: mvt = AAA,07,05
-------------------------------------------------------------------------------
Failed to load driver from file: testoutput/driver.xmq
EOF EOF
$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true $PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck performCheck
TESTNAME="Test no field name"
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { namee = total }
}
EOF
cat > $TEST/test_expected.txt <<EOF
(driver) error in testoutput/driver.xmq, cannot find: driver/field/name
-------------------------------------------------------------------------------
Remember to add for example: field { name = total ... }
-------------------------------------------------------------------------------
Hej;?total_m3?
EOF
$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck
TESTNAME="Test bad field name with unit"
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { name = total_m3 }
}
EOF
cat > $TEST/test_expected.txt <<EOF
(driver) error in testoutput/driver.xmq, bad field name total_m3 (field names should not have units)
-------------------------------------------------------------------------------
The field name should not have a unit since units are added automatically.
Either indirectly based on the quantity or directly based on the display_unit.
-------------------------------------------------------------------------------
Hej;?total_m3?
EOF
$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck
TESTNAME="Test field missing quantity"
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { name = total }
}
EOF
cat > $TEST/test_expected.txt <<EOF
(driver) error in testoutput/driver.xmq, cannot find: driver/field/quantity
-------------------------------------------------------------------------------
Remember to add for example: field { quantity = Volume ... }
Available quantities:
Time
Length
Mass
Amperage
Temperature
AmountOfSubstance
LuminousIntensity
Energy
Reactive_Energy
Apparent_Energy
Power
Volume
Flow
Voltage
Frequency
Pressure
PointInTime
RelativeHumidity
HCA
Text
Angle
Dimensionless
-------------------------------------------------------------------------------
Hej;?total_m3?
EOF
$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck
TESTNAME="Test field bad quantity"
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { name = total quantity = gurka }
}
EOF
cat > $TEST/test_expected.txt <<EOF
(driver) error in testoutput/driver.xmq, bad quantity: gurka
-------------------------------------------------------------------------------
Available quantities:
Time
Length
Mass
Amperage
Temperature
AmountOfSubstance
LuminousIntensity
Energy
Reactive_Energy
Apparent_Energy
Power
Volume
Flow
Voltage
Frequency
Pressure
PointInTime
RelativeHumidity
HCA
Text
Angle
Dimensionless
-------------------------------------------------------------------------------
Hej;?total_m3?
EOF
$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck
TESTNAME="Test field with no matcher and null output"
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { name = total quantity = Volume }
}
EOF
# There is no calculate nor match in the driver. Thus no value is inserted to it remains null.
cat > $TEST/test_expected.txt <<EOF
Hej;null
EOF
$PROG --format=fields --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck
TESTNAME="Test field with calculate"
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { name = total quantity = Volume calculate = '4711.23m3 + 0.9m3' }
}
EOF
cat > $TEST/test_expected.txt <<EOF
Hej 4712.13 m³
EOF
$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck
TESTNAME="Test field matcher without measurement_type"
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { name = total quantity = Volume match { } }
}
EOF
cat > $TEST/test_expected.txt <<EOF
(driver) error in testoutput/driver.xmq, cannot find: driver/field/match/measurement_type
-------------------------------------------------------------------------------
Remember to add for example: match { measurement_type = Instantaneous ... }
Available measurement types:
Instantaneous
Minimum
Maximum
AtError
Any
-------------------------------------------------------------------------------
Hej ?total_m3?
EOF
$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck
TESTNAME="Test field matcher with bad measurement_type "
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { name = total quantity = Volume match { measurement_type = sdfInstantaneous } }
}
EOF
cat > $TEST/test_expected.txt <<EOF
(driver) error in testoutput/driver.xmq, bad measurement_type: sdfInstantaneous
-------------------------------------------------------------------------------
Available measurement types:
Instantaneous
Minimum
Maximum
AtError
Any
-------------------------------------------------------------------------------
Hej ?total_m3?
EOF
$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck
TESTNAME="Test field matcher without vif_range"
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { name = total quantity = Volume match { measurement_type = Instantaneous } }
}
EOF
cat > $TEST/test_expected.txt <<EOF
(driver) error in testoutput/driver.xmq, cannot find: driver/field/match/vif_range
-------------------------------------------------------------------------------
Remember to add for example: match { ... vif_range = ReturnTemperature ... }
Available vif ranges:
Volume
OnTime
OperatingTime
VolumeFlow
FlowTemperature
ReturnTemperature
TemperatureDifference
ExternalTemperature
Pressure
HeatCostAllocation
Date
DateTime
EnergyMJ
EnergyWh
PowerW
ActualityDuration
FabricationNo
EnhancedIdentification
RelativeHumidity
AccessNumber
ParameterSet
ModelVersion
HardwareVersion
FirmwareVersion
SoftwareVersion
Location
Customer
ErrorFlags
DigitalOutput
DigitalInput
DurationSinceReadout
DurationOfTariff
Dimensionless
Voltage
Amperage
ResetCounter
CumulationCounter
SpecialSupplierInformation
RemainingBattery
AnyVolumeVIF
AnyEnergyVIF
AnyPowerVIF
-------------------------------------------------------------------------------
Hej ?total_m3?
EOF
$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck
TESTNAME="Test proper field matcher"
TESTRESULT="ERROR"
cat > $TEST/driver.xmq <<EOF
driver { name = iffo meter_type = WaterMeter default_fields = name,id,total_m3,timestamp
detect { mvt = SEN,99,07 }
field { name = total quantity = Volume match { measurement_type = Instantaneous vif_range = Volume } }
}
EOF
cat > $TEST/test_expected.txt <<EOF
Hej 123.529 m³
EOF
$PROG --format=hr --selectfields=name,total_m3 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
performCheck

Wyświetl plik

@ -37,33 +37,30 @@ driver {
name = iporl name = iporl
meter_type = WaterMeter meter_type = WaterMeter
default_fields = name,id,total_m3,max_flow_m3h,timestamp default_fields = name,id,total_m3,max_flow_m3h,timestamp
link_modes = T1
detect { detect {
mvt = SEN,99,07 mvt = SEN,99,07
} }
field { field {
name = totalitator name = totalitator
quantity = Volume quantity = Volume
type = NumericFieldWithExtractor
info = 'The total water consumption recorded by this meter.'
vif_scaling = Auto
attributes = ''
match { match {
measurement_type = Instantaneous measurement_type = Instantaneous
vif_range = Volume vif_range = Volume
} }
about {
en = 'The total water consumption recorded by this meter.'
}
} }
field { field {
name = max_flowwor name = max_flowwor
quantity = Flow quantity = Flow
type = NumericFieldWithExtractor
info = 'The maximum flow recorded during previous period.'
vif_scaling = Auto
attributes = ''
match { match {
measurement_type = Instantaneous measurement_type = Instantaneous
vif_range = VolumeFlow vif_range = VolumeFlow
} }
about {
en = 'The maximum flow recorded during previous period.'
}
} }
} }
EOF EOF