wmbusmeters/src/driver_dynamic.cc

1022 wiersze
30 KiB
C++

/*
Copyright (C) 2023-2024 Fredrik Öhrström (gpl-3.0-or-later)
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include"meters_common_implementation.h"
#include"driver_dynamic.h"
#include"xmq.h"
#include<string.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);
DifSignedness check_dif_signedness(const char *dif_signedness_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);
bool checked_set_difvifkey(const char *difvifkey_s, FieldMatcher *fm, DriverDynamic *dd);
void checked_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd);
void checked_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd);
void checked_set_storagenr_range(const char *storagenr_range_s, FieldMatcher *fm, DriverDynamic *dd);
void checked_set_tariffnr_range(const char *tariffnr_range_s, FieldMatcher *fm, DriverDynamic *dd);
void checked_set_subunitnr_range(const char *subunitnr_range_s, FieldMatcher *fm, DriverDynamic *dd);
Translate::MapType checked_map_type(const char *map_type_s, DriverDynamic *dd);
uint64_t checked_mask_bits(const char *mask_bits_s, DriverDynamic *dd);
uint64_t checked_value(const char *value_s, DriverDynamic *dd);
TestBit checked_test_type(const char *test_s, DriverDynamic *dd);
void checked_add_vif_combinable(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd);
const char *line = "-------------------------------------------------------------------------------";
bool DriverDynamic::load(DriverInfo *di, const string &file_name, const char *content)
{
if (!content)
{
if (!endsWith(file_name, ".xmq")) return false;
if (!checkFileExists(file_name.c_str())) return false;
}
string file = file_name;
XMQDoc *doc = xmqNewDoc();
bool ok = false;
if (!content)
{
ok = xmqParseFile(doc, file.c_str(), NULL, 0);
}
else
{
file = "builtin";
ok = xmqParseBuffer(doc, content, content+strlen(content), NULL, 0);
}
if (!ok) {
warning("(driver) error loading wmbusmeters driver file %s\n%s\n%s\n",
file.c_str(),
xmqDocError(doc),
line);
return false;
}
try
{
string name = check_driver_name(xmqGetString(doc, NULL, "/driver/name"), file);
di->setName(name);
MeterType meter_type = check_meter_type(xmqGetString(doc, NULL, "/driver/meter_type"), file);
di->setMeterType(meter_type);
string default_fields = check_default_fields(xmqGetString(doc, NULL, "/driver/default_fields"), file);
di->setDefaultFields(default_fields);
if (!content)
{
verbose("(driver) loading driver %s from file %s\n", name.c_str(), file.c_str());
}
di->setDynamic(file, doc);
xmqForeach(doc, NULL, "/driver/detect/mvt", (XMQNodeCallback)add_detect, di);
check_detection_triplets(di, file);
di->setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new DriverDynamic(mi, di)); });
return true;
}
catch (...)
{
xmqFreeDoc(doc);
di->setDynamic(file, NULL);
return false;
}
}
DriverDynamic::DriverDynamic(MeterInfo &mi, DriverInfo &di) :
MeterCommonImplementation(mi, di), file_name_(di.getDynamicFileName())
{
XMQDoc *doc = NULL;
try
{
doc = di.getDynamicDriver();
assert(doc);
verbose("(driver) constructing driver %s from already loaded file %s\n",
di.name().str().c_str(),
fileName().c_str());
xmqForeach(doc, NULL, "/driver/use", (XMQNodeCallback)add_use, this);
xmqForeach(doc, NULL, "/driver/field", (XMQNodeCallback)add_field, this);
}
catch(...)
{
xmqFreeDoc(doc);
}
}
DriverDynamic::~DriverDynamic()
{
}
XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *di)
{
string mvt = xmqGetString(doc, detect, ".");
auto fields = splitString(mvt, ',');
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);
return XMQ_CONTINUE;
}
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];
char b = mfct[1];
char c = mfct[2];
if (!(a >= 'A' && a <= 'Z' &&
b >= 'A' && b <= 'Z' &&
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);
return XMQ_CONTINUE;
}
mfct_code = toMfctCode(a, b, c);
}
else
{
char *eptr;
mfct_code = strtol(mfct.c_str(), &eptr, 16);
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);
return XMQ_CONTINUE;
}
}
if (version > 255 || version < 0)
{
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);
return XMQ_CONTINUE;
}
if (type > 255 || type < 0)
{
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);
return XMQ_CONTINUE;
}
string mfct_flag = manufacturerFlag(mfct_code);
debug("(driver) register detection %s %s %2x %02x\n",
di->getDynamicFileName().c_str(),
mfct_flag.c_str(),
version,
type);
di->addDetection(mfct_code, type, version);
return XMQ_CONTINUE;
}
XMQProceed DriverDynamic::add_use(XMQDoc *doc, XMQNode *field, DriverDynamic *dd)
{
string name = xmqGetString(doc, field, ".");
bool ok = dd->addOptionalLibraryFields(name);
if (!ok)
{
warning("(driver) error in %s, unknown library field: %s \n",
dd->fileName().c_str(),
name.c_str());
}
return XMQ_CONTINUE;
}
XMQProceed DriverDynamic::add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *dd)
{
// 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);
// 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);
// 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;
// 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);
// The dif signedness is by default Signed but can be overriden for pesky fields.
DifSignedness dif_signedness = check_dif_signedness(xmqGetString(doc, field, "dif_signedness"), dd);
// The properties are by default empty but can be specified for specific fields.
PrintProperties properties = check_print_properties(xmqGetString(doc, field, "attributes"), dd);
// 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());
// 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;
// Now find all matchers.
Translate::Lookup lookup = Translate::Lookup();
/*
.add(Translate::Rule("ERROR_FLAGS", Translate::Type::BitToString)
.set(MaskBits(0x000f))
.set(DefaultMessage("OK"))
.add(Translate::Map(0x01 ,"DRY", TestBit::Set))
.add(Translate::Map(0x02 ,"REVERSE", TestBit::Set))
.add(Translate::Map(0x04 ,"LEAK", TestBit::Set))
.add(Translate::Map(0x08 ,"BURST", TestBit::Set))
));
*/
dd->tmp_lookup_ = &lookup;
int num_lookups = xmqForeach(doc, field, "lookup", (XMQNodeCallback)add_lookup, dd);
if (is_numeric)
{
if (calculate == "")
{
dd->addNumericFieldWithExtractor(
name,
info,
properties,
quantity,
vif_scaling,
dif_signedness,
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
{
if (num_lookups > 0)
{
dd->addStringFieldWithExtractorAndLookup(
name,
info,
properties,
match,
lookup
);
}
else
{
dd->addStringFieldWithExtractor(
name,
info,
properties,
match
);
}
}
return XMQ_CONTINUE;
}
XMQProceed DriverDynamic::add_match(XMQDoc *doc, XMQNode *match, DriverDynamic *dd)
{
FieldMatcher *fm = dd->tmp_matcher_;
if (checked_set_difvifkey(xmqGetString(doc, match, "difvifkey"), fm, dd)) return XMQ_CONTINUE;
checked_set_measurement_type(xmqGetString(doc, match, "measurement_type"), fm, dd);
checked_set_vif_range(xmqGetString(doc, match, "vif_range"), fm, dd);
checked_set_storagenr_range(xmqGetString(doc, match, "storage_nr"), fm, dd);
xmqForeach(doc, match, "add_combinable", (XMQNodeCallback)add_combinable, dd);
return XMQ_CONTINUE;
}
XMQProceed DriverDynamic::add_combinable(XMQDoc *doc, XMQNode *match, DriverDynamic *dd)
{
FieldMatcher *fm = dd->tmp_matcher_;
checked_add_vif_combinable(xmqGetString(doc, match, "."), fm, dd);
return XMQ_CONTINUE;
}
/**
add_map:
Add a mapping from a value (bits,index,decimal) to a string name.
map {
name = SURGE
info = 'Unexpected increase in pressure in relation to average pressure.'
value = 0x02
test = set
}
*/
XMQProceed DriverDynamic::add_map(XMQDoc *doc, XMQNode *map, DriverDynamic *dd)
{
const char *name = xmqGetString(doc, map, "name");
uint64_t value = checked_value(xmqGetString(doc, map, "value"), dd);
TestBit test_type = checked_test_type(xmqGetString(doc, map, "test"), dd);
dd->tmp_rule_->add(Translate::Map(value, name, test_type));
return XMQ_CONTINUE;
}
/**
add_lookup:
Add a lookup from bits,index or decimal to a sequence of string tokens.
Or fallback to the name (ERROR_FLAGS_8) suffixed by the untranslateable bits.
lookup {
name = ERROR_FLAGS
map_type = BitToString
mask_bits = 0xffff
default_message = OK
map { } map {}
}
*/
XMQProceed DriverDynamic::add_lookup(XMQDoc *doc, XMQNode *lookup, DriverDynamic *dd)
{
const char *name = xmqGetString(doc, lookup, "name");
Translate::MapType map_type = checked_map_type(xmqGetString(doc, lookup, "map_type"), dd);
uint64_t mask_bits = checked_mask_bits(xmqGetString(doc, lookup, "mask_bits"), dd);
const char *default_message = xmqGetString(doc, lookup, "default_message");
Translate::Rule rule = Translate::Rule(name, map_type);
dd->tmp_rule_ = &rule;
rule.set(MaskBits(mask_bits));
rule.set(DefaultMessage(default_message));
xmqForeach(doc, lookup, "map", (XMQNodeCallback)add_map, dd);
dd->tmp_lookup_->add(rule);
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"
"Auto\n"
"None\n"
"%s\n",
dd->fileName().c_str(),
vif_scaling_s,
line,
line);
throw 1;
}
return vif_scaling;
}
DifSignedness check_dif_signedness(const char *dif_signedness_s, DriverDynamic *dd)
{
if (!dif_signedness_s)
{
return DifSignedness::Signed;
}
DifSignedness dif_signedness = toDifSignedness(dif_signedness_s);
if (dif_signedness == DifSignedness::Unknown)
{
warning("(driver) error in %s, bad dif signedness: %s\n",
"%s\n"
"Available dif signedness:\n"
"Signed\n"
"Unsigned\n"
"%s\n",
dd->fileName().c_str(),
dif_signedness_s,
line,
line);
throw 1;
}
return dif_signedness;
}
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;
}
bool checked_set_difvifkey(const char *difvifkey_s, FieldMatcher *fm, DriverDynamic *dd)
{
if (!difvifkey_s) return false;
bool invalid_hex = false;
bool hex = isHexStringStrict(difvifkey_s, &invalid_hex);
if (!hex || invalid_hex)
{
warning("(driver) error in %s, bad divfikey: %s\n"
"%s\n"
"Should be all hex.\n"
"%s\n",
dd->fileName().c_str(),
difvifkey_s,
line,
line);
throw 1;
}
fm->set(DifVifKey(difvifkey_s));
return true;
}
void checked_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 checked_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);
}
void checked_set_storagenr_range(const char *storagenr_range_s, FieldMatcher *fm, DriverDynamic *dd)
{
if (!storagenr_range_s) return;
auto fields = splitString(storagenr_range_s, ',');
bool ok = isNumber(fields[0]);
if (fields.size() > 1)
{
ok &= isNumber(fields[1]);
}
if (!ok || fields.size() > 2)
{
warning("(driver) error in %s, bad storagenr_range: %s\n"
"%s\n",
dd->fileName().c_str(),
storagenr_range_s,
line);
throw 1;
}
if (fields.size() == 1)
{
fm->set(StorageNr(atoi(fields[0].c_str())));
}
else
{
fm->set(StorageNr(atoi(fields[0].c_str())),
StorageNr(atoi(fields[1].c_str())));
}
}
void checked_add_vif_combinable(const char *vif_combinable_s, FieldMatcher *fm, DriverDynamic *dd)
{
if (!vif_combinable_s) return;
VIFCombinable vif_combinable = toVIFCombinable(vif_combinable_s);
if (vif_combinable == VIFCombinable::None)
{
warning("(driver) error in %s, bad vif_combinable: %s\n"
"%s\n"
"Available vif combinables:\n"
"%s\n"
"%s\n",
dd->fileName().c_str(),
vif_combinable_s,
line,
availableVIFCombinables().c_str(),
line);
throw 1;
}
fm->add(vif_combinable);
}
Translate::MapType checked_map_type(const char *map_type_s, DriverDynamic *dd)
{
if (!map_type_s)
{
warning("(driver) error in %s, cannot find: driver/field/lookup/map_type\n"
"%s\n"
"Remember to add for example: lookup { map_type = BitToString ... }\n"
"Available map types:\n"
"BitToString\n"
"IndexToString\n"
"DecimalsToString\n"
"%s\n",
dd->fileName().c_str(),
line,
line);
throw 1;
}
Translate::MapType map_type = toMapType(map_type_s);
if (map_type == Translate::MapType::Unknown)
{
warning("(driver) error in %s, bad map_type: %s\n"
"%s\n"
"Available map types:\n"
"BitToString\n"
"IndexToString\n"
"DecimalToString\n"
"%s\n",
dd->fileName().c_str(),
map_type_s,
line,
line);
throw 1;
}
return map_type;
}
uint64_t checked_mask_bits(const char *mask_bits_s, DriverDynamic *dd)
{
if (!mask_bits_s)
{
warning("(driver) error in %s, cannot find: driver/field/lookup/mask_bitse\n"
"%s\n"
"Remember to add for example: lookup { mask_bits = 0x00ff ... }\n"
"%s\n",
dd->fileName().c_str(),
line,
line);
throw 1;
}
uint64_t mask = strtol(mask_bits_s, NULL, 16);
return mask;
}
uint64_t checked_value(const char *value_s, DriverDynamic *dd)
{
if (!value_s)
{
warning("(driver) error in %s, cannot find: driver/field/lookup/map/value\n"
"%s\n"
"Remember to add for example: lookup { map { ... value = 0x01 ... }}\n"
"%s\n",
dd->fileName().c_str(),
line,
line);
throw 1;
}
uint64_t value = strtol(value_s, NULL, 16);
return value;
}
TestBit checked_test_type(const char *test_s, DriverDynamic *dd)
{
if (!test_s)
{
warning("(driver) error in %s, cannot find: driver/field/lookup/map/test\n"
"%s\n"
"Remember to add for example: lookup { map { test = Set } }\n"
"Available test types:\n"
"Set\n"
"NotSet\n"
"%s\n",
dd->fileName().c_str(),
line,
line);
throw 1;
}
TestBit test_type = toTestBit(test_s);
if (test_type == TestBit::Unknown)
{
warning("(driver) error in %s, bad test: %s\n"
"%s\n"
"Available test types:\n"
"Set\n"
"NotSet\n"
"%s\n",
dd->fileName().c_str(),
test_s,
line,
line);
throw 1;
}
return test_type;
}