kopia lustrzana https://github.com/weetmuts/wmbusmeters
Add more loadable driver features.
rodzic
a385737984
commit
c5dc2ada51
|
@ -20,6 +20,24 @@
|
|||
#include"driver_dynamic.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)
|
||||
{
|
||||
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");
|
||||
|
||||
if (!ok) {
|
||||
error("Error loading wmbusmeters driver file %s\n%s",
|
||||
file.c_str(),
|
||||
xmqDocError(doc));
|
||||
warning("(driver) error loading wmbusmeters driver file %s\n%s\n%s\n",
|
||||
file.c_str(),
|
||||
xmqDocError(doc),
|
||||
line);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *name = xmqGetString(doc, NULL, "/driver/name");
|
||||
if (!name) error("(dynamic) Error in %s cannot find driver/name\n"
|
||||
" A driver file looks like: driver { name = abc123 ... }\n", file.c_str());
|
||||
try
|
||||
{
|
||||
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"
|
||||
" The driver name must consist of lower case ascii a-z and digits 0-9.\n",
|
||||
file.c_str(), name);
|
||||
MeterType meter_type = check_meter_type(xmqGetString(doc, NULL, "/driver/meter_type"), file);
|
||||
di->setMeterType(meter_type);
|
||||
|
||||
const char *meter_type_s = xmqGetString(doc, NULL, "/driver/meter_type");
|
||||
if (!meter_type_s) error("(dynamic) Error in %s cannot find driver/meter_type\n"
|
||||
" Remember to add: meter_type = ...\n", file.c_str());
|
||||
string default_fields = check_default_fields(xmqGetString(doc, NULL, "/driver/default_fields"), file);
|
||||
di->setDefaultFields(default_fields);
|
||||
|
||||
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"
|
||||
"Available meter types are:\n%s\n"
|
||||
,
|
||||
file.c_str(), meter_type_s, availableMeterTypes());
|
||||
di->setDynamic(file, doc);
|
||||
|
||||
const char *default_fields = xmqGetString(doc, NULL, "/driver/default_fields");
|
||||
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());
|
||||
xmqForeach(doc, NULL, "/driver/detect/mvt", (XMQNodeCallback)add_detect, di);
|
||||
|
||||
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);
|
||||
di->setDefaultFields(default_fields);
|
||||
di->setMeterType(meter_type);
|
||||
di->setDynamic(file, doc);
|
||||
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
verbose("(dynamic) Constructing driver %s from file %s\n",
|
||||
verbose("(driver) constructing driver %s from file %s\n",
|
||||
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, ".");
|
||||
|
||||
auto fields = splitString(mvt, ',');
|
||||
if (fields.size() != 3) error("(dynamic) Error in %s, wrong number of fields in mvt triple: mvt = %s\n"
|
||||
" There should be three fields, for example: mvt = AAA,07,05\n",
|
||||
di->getDynamicFileName().c_str(),
|
||||
mvt.c_str());
|
||||
if (fields.size() != 3)
|
||||
{
|
||||
warning("(driver) error in %s, wrong number of fields in mvt triple: mvt = %s\n"
|
||||
"%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];
|
||||
int mfct_code = 0;
|
||||
long version = strtol(fields[1].c_str(), NULL, 16);
|
||||
long type = strtol(fields[2].c_str(), NULL, 16);
|
||||
|
||||
if (mfct.length() == 3)
|
||||
{
|
||||
char a = mfct[0];
|
||||
|
@ -116,29 +133,67 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, void *dd)
|
|||
char c = mfct[2];
|
||||
if (!(a >= 'A' && a < '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",
|
||||
di->name().str().c_str(),
|
||||
mfct.c_str());
|
||||
c >= 'A' && c < 'Z'))
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *eptr;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
debug("(dynamic) register detection %s %s %2x %02x\n",
|
||||
di->name().str().c_str(),
|
||||
debug("(driver) register detection %s %s %2x %02x\n",
|
||||
di->getDynamicFileName().c_str(),
|
||||
mfct_flag.c_str(),
|
||||
version,
|
||||
type);
|
||||
|
@ -147,70 +202,456 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, void *dd)
|
|||
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;
|
||||
const char *driver_name = dd->name().c_str();
|
||||
// The field name must be supplied without a unit ie total (not total_m3) since units are managed by wmbusmeters.
|
||||
string name = check_field_name(xmqGetString(doc, field, "name"), dd);
|
||||
|
||||
const char *name = xmqGetString(doc, field, "name");
|
||||
const char *quantity_s = xmqGetString(doc, field, "quantity");
|
||||
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");
|
||||
// The quantity ie Volume, gives the default unit (m3) for the field. The unit can be overriden with display_unit.
|
||||
Quantity quantity = check_field_quantity(xmqGetString(doc, field, "quantity"), dd);
|
||||
|
||||
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);
|
||||
if (type == FieldType::Unknown) error("(dynamic) Error in %s unknown field type %s\n", driver_name, type_s);
|
||||
// The vif scaling is by default Auto but can be overriden for pesky fields.
|
||||
VifScaling vif_scaling = check_vif_scaling(xmqGetString(doc, field, "vif_scaling"), dd);
|
||||
|
||||
Quantity quantity = toQuantity(quantity_s);
|
||||
if (quantity == Quantity::Unknown) error("(dynamic) Error in %s unknown quantity %s\n", driver_name, quantity);
|
||||
// The properties are by default empty but can be specified for specific fields.
|
||||
PrintProperties properties = check_print_properties(xmqGetString(doc, field, "attributes"), dd);
|
||||
|
||||
VifScaling vif_scaling = toVifScaling(vif_scaling_s);
|
||||
if (vif_scaling == VifScaling::Unknown) error("(dynamic) Error in %s unknown vif scaling %s\n", driver_name, vif_scaling_s);
|
||||
// The about fields explains what the value is for. Ie. is storage 1 the previous day or month value etc.
|
||||
string info = get_translation(doc, field, "about", language());
|
||||
|
||||
PrintProperties properties = toPrintProperties(attributes_s);
|
||||
if (properties.hasUnknown()) error("(dynamic) Error in %s unknown attributes %s\n", driver_name, attributes_s);
|
||||
// The calculate formula is optional.
|
||||
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();
|
||||
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);
|
||||
|
||||
dd->addNumericFieldWithExtractor(
|
||||
if (is_numeric)
|
||||
{
|
||||
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,
|
||||
info_s,
|
||||
info,
|
||||
properties,
|
||||
quantity,
|
||||
vif_scaling,
|
||||
match
|
||||
);
|
||||
|
||||
}
|
||||
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");
|
||||
|
||||
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)
|
||||
{
|
||||
VIFRange vif_range = toVIFRange(vif_range_s);
|
||||
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);
|
||||
}
|
||||
|
||||
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/measurement_type\n"
|
||||
"%s\n"
|
||||
"Remember to add for example: field { ... 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/vif_range\n"
|
||||
"%s\n"
|
||||
"Remember to add for example: field { ... 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);
|
||||
}
|
||||
|
|
|
@ -24,9 +24,16 @@ struct DriverDynamic : public virtual MeterCommonImplementation
|
|||
{
|
||||
DriverDynamic(MeterInfo &mi, DriverInfo &di);
|
||||
static bool load(DriverInfo *di, const string &name);
|
||||
static XMQProceed add_detect(XMQDoc *doc, XMQNode *detect, void *dd);
|
||||
static XMQProceed add_field(XMQDoc *doc, XMQNode *field, void *dd);
|
||||
static XMQProceed add_match(XMQDoc *doc, XMQNode *match, void *dd);
|
||||
static XMQProceed add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *di);
|
||||
static XMQProceed add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *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
|
||||
|
|
|
@ -1441,3 +1441,18 @@ const char *toString(DVEntryCounterType ct)
|
|||
|
||||
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_;
|
||||
}
|
||||
|
|
|
@ -584,4 +584,6 @@ bool extractDVdate(std::map<std::string,std::pair<int,DVEntry>> *values,
|
|||
struct tm *value);
|
||||
|
||||
|
||||
const std::string &availableVIFRanges();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3016,36 +3016,6 @@ VifScaling toVifScaling(const char *s)
|
|||
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)
|
||||
{
|
||||
switch(p)
|
||||
|
|
|
@ -54,21 +54,6 @@ struct StringField
|
|||
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
|
||||
{
|
||||
int index();
|
||||
|
|
34
src/units.cc
34
src/units.cc
|
@ -805,3 +805,37 @@ string SIExp::str() const
|
|||
|
||||
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_;
|
||||
}
|
||||
|
|
|
@ -295,4 +295,7 @@ bool extractUnit(const std::string &s, std::string *vname, Unit *u);
|
|||
LIST_OF_UNITS
|
||||
#undef X
|
||||
|
||||
const char *availableQuantities();
|
||||
const char *availableUnits();
|
||||
|
||||
#endif
|
||||
|
|
26
src/util.cc
26
src/util.cc
|
@ -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);
|
||||
}
|
||||
|
||||
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_;
|
||||
}
|
||||
|
|
|
@ -304,6 +304,9 @@ int toMfctCode(char a, char b, char c);
|
|||
|
||||
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
|
||||
#define FUZZING false
|
||||
#endif
|
||||
|
|
|
@ -6369,7 +6369,7 @@ bool has_attributes(xmlNodePtr 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);
|
||||
xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
|
||||
|
|
|
@ -344,12 +344,12 @@ typedef enum
|
|||
} 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.
|
||||
@node: The node triggering the callback.
|
||||
@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.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
|
||||
|
|
|
@ -37,8 +37,11 @@ ddriver { name = Iffo }
|
|||
EOF
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
(dynamic) Error in testoutput/driver.xmq cannot find driver/name
|
||||
A driver file looks like: driver { name = abc123 ... }
|
||||
(driver) error in testoutput/driver.xmq, cannot find: driver/name
|
||||
-------------------------------------------------------------------------------
|
||||
A driver file looks like this: driver { name = abc123 ... }
|
||||
-------------------------------------------------------------------------------
|
||||
Failed to load driver from file: testoutput/driver.xmq
|
||||
EOF
|
||||
|
||||
$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"
|
||||
TESTRESULT="ERROR"
|
||||
cat > $TEST/driver.xmq <<EOF
|
||||
driver { name = Iffo }
|
||||
driver { name = 'a b' }
|
||||
EOF
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
(dynamic) Error in testoutput/driver.xmq invalid driver name "Iffo"
|
||||
The driver name must consist of lower case ascii a-z and digits 0-9.
|
||||
(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.
|
||||
-------------------------------------------------------------------------------
|
||||
Failed to load driver from file: testoutput/driver.xmq
|
||||
EOF
|
||||
|
||||
$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
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
(dynamic) Error in testoutput/driver.xmq cannot find driver/meter_type
|
||||
Remember to add: meter_type = ...
|
||||
(driver) error in testoutput/driver.xmq, cannot find: driver/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
|
||||
|
||||
$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
|
||||
|
||||
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:
|
||||
DoorWindowDetector
|
||||
ElectricityMeter
|
||||
|
@ -95,6 +117,8 @@ SmokeDetector
|
|||
TempHygroMeter
|
||||
WaterMeter
|
||||
PressureSensor
|
||||
-------------------------------------------------------------------------------
|
||||
Failed to load driver from file: testoutput/driver.xmq
|
||||
EOF
|
||||
|
||||
$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
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
(dynamic) 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.
|
||||
(driver) error in testoutput/driver.xmq, cannot find: driver/default_fields
|
||||
-------------------------------------------------------------------------------
|
||||
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
|
||||
|
||||
$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
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
(dynamic) Error in testoutput/driver.xmq cannot find any driver/detect/mvt triplets
|
||||
Remember to add: detect { mvt = AAA,05,07 mvt = AAA,06,07 ... }
|
||||
The triplets contain MANUFACTURER,VERSION,TYPE
|
||||
and you can see these values when listening to all meters.
|
||||
(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 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
|
||||
|
||||
$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
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
(dynamic) 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
|
||||
(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
|
||||
-------------------------------------------------------------------------------
|
||||
Failed to load driver from file: testoutput/driver.xmq
|
||||
EOF
|
||||
|
||||
$PROG 1844AE4C4455223399077A55000000_041389E20100023B0000 Hej $TEST/driver.xmq 33225544 NO_KEY > $TEST/test_output.txt 2>&1 || true
|
||||
|
||||
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/measurement_type
|
||||
-------------------------------------------------------------------------------
|
||||
Remember to add for example: field { ... 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/vif_range
|
||||
-------------------------------------------------------------------------------
|
||||
Remember to add for example: field { ... 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 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 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
|
||||
|
|
|
@ -37,33 +37,30 @@ driver {
|
|||
name = iporl
|
||||
meter_type = WaterMeter
|
||||
default_fields = name,id,total_m3,max_flow_m3h,timestamp
|
||||
link_modes = T1
|
||||
detect {
|
||||
mvt = SEN,99,07
|
||||
}
|
||||
field {
|
||||
name = totalitator
|
||||
quantity = Volume
|
||||
type = NumericFieldWithExtractor
|
||||
info = 'The total water consumption recorded by this meter.'
|
||||
vif_scaling = Auto
|
||||
attributes = ''
|
||||
match {
|
||||
measurement_type = Instantaneous
|
||||
vif_range = Volume
|
||||
}
|
||||
about {
|
||||
en = 'The total water consumption recorded by this meter.'
|
||||
}
|
||||
}
|
||||
field {
|
||||
name = max_flowwor
|
||||
quantity = Flow
|
||||
type = NumericFieldWithExtractor
|
||||
info = 'The maximum flow recorded during previous period.'
|
||||
vif_scaling = Auto
|
||||
attributes = ''
|
||||
match {
|
||||
measurement_type = Instantaneous
|
||||
vif_range = VolumeFlow
|
||||
}
|
||||
about {
|
||||
en = 'The maximum flow recorded during previous period.'
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
|
Ładowanie…
Reference in New Issue