kopia lustrzana https://github.com/weetmuts/wmbusmeters
Test formulas.
rodzic
aba13bd5d9
commit
ab23cc4212
|
@ -485,6 +485,18 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
|
|||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(argv[i], "--calculate_", 12))
|
||||
{
|
||||
// For example: --calculate_adjusted_kwh='total_kwh + 12345 kwh'
|
||||
string extra_calculated_field = string(argv[i]+12);
|
||||
if (extra_calculated_field == "") {
|
||||
error("The calculated field command cannot be empty.\n");
|
||||
}
|
||||
debug("(cmdline) add calculated field %s\n", extra_calculated_field.c_str());
|
||||
c->extra_calculated_fields.push_back(extra_calculated_field);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(argv[i], "--listenvs=", 11)) {
|
||||
c->list_shell_envs = true;
|
||||
c->list_meter = string(argv[i]+11);
|
||||
|
|
|
@ -59,6 +59,7 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
|||
vector<string> telegram_shells;
|
||||
vector<string> alarm_shells;
|
||||
vector<string> extra_constant_fields;
|
||||
vector<string> extra_calculated_fields;
|
||||
vector<string> selected_fields;
|
||||
|
||||
debug("(config) loading meter file %s\n", file.c_str());
|
||||
|
@ -152,6 +153,12 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
|||
string keyvalue = p.first.substr(off)+"="+p.second;
|
||||
extra_constant_fields.push_back(keyvalue);
|
||||
}
|
||||
else
|
||||
if (startsWith(p.first, "calculate_"))
|
||||
{
|
||||
string keyvalue = p.first.substr(10)+"="+p.second;
|
||||
extra_calculated_fields.push_back(keyvalue);
|
||||
}
|
||||
else
|
||||
warning("Found invalid key \"%s\" in meter config file\n", p.first.c_str());
|
||||
|
||||
|
@ -195,6 +202,7 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
|||
}
|
||||
if (use) {
|
||||
mi.extra_constant_fields = extra_constant_fields;
|
||||
mi.extra_calculated_fields = extra_calculated_fields;
|
||||
mi.shells = telegram_shells;
|
||||
mi.idsc = toIdsCommaSeparated(mi.ids);
|
||||
mi.selected_fields = selected_fields;
|
||||
|
|
|
@ -136,6 +136,7 @@ struct Configuration
|
|||
std::vector<std::string> selected_fields;
|
||||
std::vector<MeterInfo> meters;
|
||||
std::vector<std::string> extra_constant_fields; // Additional constant fields to always add to json.
|
||||
std::vector<std::string> extra_calculated_fields; // Additional calculated fields to always add to json.
|
||||
// These extra constant fields can also be part of selected with selectfields.
|
||||
std::vector<SendBusContent> send_bus_content; // Telegrams used to wake up a meter for reading or mbus read-out requests.
|
||||
std::set<BusDeviceType> probe_for; // Which devices should be probed for? DEVICE_AUTO means all.
|
||||
|
|
|
@ -133,13 +133,15 @@ namespace
|
|||
.set(DifVifKey("04FB82F53C"))
|
||||
);
|
||||
|
||||
/*
|
||||
addNumericFieldWithCalculator(
|
||||
"total_apparent_energy_consumption",
|
||||
"Calculated: the total apparent energy consumption.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
Quantity::Energy,
|
||||
Quantity::Apparent_Energy,
|
||||
"total_energy_consumption_kwh + 99 kwh"
|
||||
);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -454,9 +454,10 @@ void setup_log_file(Configuration *config)
|
|||
|
||||
void setup_meters(Configuration *config, MeterManager *manager)
|
||||
{
|
||||
for (auto &m : config->meters)
|
||||
for (MeterInfo &m : config->meters)
|
||||
{
|
||||
m.conversions = config->conversions;
|
||||
m.extra_calculated_fields = config->extra_calculated_fields;
|
||||
|
||||
if (m.usesPolling() || driverNeedsPolling(m.driver, m.driver_name))
|
||||
{
|
||||
|
|
|
@ -738,10 +738,12 @@ MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi,
|
|||
{
|
||||
hex2bin(mi.key, &meter_keys_.confidentiality_key);
|
||||
}
|
||||
for (auto s : mi.shells) {
|
||||
for (auto s : mi.shells)
|
||||
{
|
||||
addShell(s);
|
||||
}
|
||||
for (auto j : mi.extra_constant_fields) {
|
||||
for (auto j : mi.extra_constant_fields)
|
||||
{
|
||||
addExtraConstantField(j);
|
||||
}
|
||||
}
|
||||
|
@ -765,10 +767,12 @@ MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi,
|
|||
{
|
||||
hex2bin(mi.key, &meter_keys_.confidentiality_key);
|
||||
}
|
||||
for (auto s : mi.shells) {
|
||||
for (auto s : mi.shells)
|
||||
{
|
||||
addShell(s);
|
||||
}
|
||||
for (auto j : mi.extra_constant_fields) {
|
||||
for (auto j : mi.extra_constant_fields)
|
||||
{
|
||||
addExtraConstantField(j);
|
||||
}
|
||||
|
||||
|
@ -794,6 +798,39 @@ void MeterCommonImplementation::addExtraConstantField(string ecf)
|
|||
extra_constant_fields_.push_back(ecf);
|
||||
}
|
||||
|
||||
void MeterCommonImplementation::addExtraCalculatedField(string ecf)
|
||||
{
|
||||
verbose("(meter) Adding calculated field: %s\n", ecf.c_str());
|
||||
|
||||
vector<string> parts = splitString(ecf, '=');
|
||||
|
||||
if (parts.size() != 2)
|
||||
{
|
||||
warning("Invalid formula for calculated field. %s\n", ecf.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
string vname;
|
||||
Unit unit;
|
||||
|
||||
bool ok = extractUnit(parts[0], &vname, &unit);
|
||||
if (!ok)
|
||||
{
|
||||
warning("Could not extract a valid unit from calculated field name %s\n", parts[0].c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Quantity quantity = toQuantity(unit);
|
||||
|
||||
addNumericFieldWithCalculator(
|
||||
vname,
|
||||
"Calculated: "+ecf,
|
||||
PrintProperty::JSON | PrintProperty::FIELD,
|
||||
quantity,
|
||||
parts[1]
|
||||
);
|
||||
}
|
||||
|
||||
vector<string> &MeterCommonImplementation::shellCmdlines()
|
||||
{
|
||||
return shell_cmdlines_;
|
||||
|
@ -965,6 +1002,15 @@ void MeterCommonImplementation::addNumericFieldWithCalculator(string vname,
|
|||
{
|
||||
Formula *f = newFormula();
|
||||
bool ok = f->parse(this, formula);
|
||||
if (!ok)
|
||||
{
|
||||
string err = f->errors();
|
||||
warning("Warning! Ignoring calculated field %s because parse failed:\n%s",
|
||||
vname.c_str(),
|
||||
err.c_str());
|
||||
delete f;
|
||||
return;
|
||||
}
|
||||
assert(ok);
|
||||
|
||||
field_infos_.push_back(
|
||||
|
@ -2417,6 +2463,10 @@ shared_ptr<Meter> createMeter(MeterInfo *mi)
|
|||
{
|
||||
shared_ptr<Meter> newm = di->construct(*mi);
|
||||
newm->addConversions(mi->conversions);
|
||||
for (string &j : mi->extra_calculated_fields)
|
||||
{
|
||||
newm->addExtraCalculatedField(j);
|
||||
}
|
||||
newm->setPollInterval(mi->poll_interval);
|
||||
if (mi->selected_fields.size() > 0)
|
||||
{
|
||||
|
|
|
@ -159,6 +159,7 @@ struct MeterInfo
|
|||
int bps {}; // For mbus communication you need to know the baud rate.
|
||||
vector<string> shells;
|
||||
vector<string> extra_constant_fields; // Additional static fields that are added to each message.
|
||||
vector<string> extra_calculated_fields; // Additional field calculated using formulas.
|
||||
vector<Unit> conversions; // Additional units desired in json.
|
||||
vector<string> selected_fields; // Usually set to the default fields, but can be override in meter config.
|
||||
|
||||
|
@ -172,7 +173,7 @@ struct MeterInfo
|
|||
string str();
|
||||
DriverName driverName();
|
||||
|
||||
MeterInfo(string b, string n, MeterDriver d, string e, vector<string> i, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &j)
|
||||
MeterInfo(string b, string n, MeterDriver d, string e, vector<string> i, string k, LinkModeSet lms, int baud, vector<string> &s, vector<string> &j, vector<string> &calcfs)
|
||||
{
|
||||
bus = b;
|
||||
name = n;
|
||||
|
@ -183,6 +184,7 @@ struct MeterInfo
|
|||
key = k;
|
||||
shells = s;
|
||||
extra_constant_fields = j;
|
||||
extra_calculated_fields = calcfs;
|
||||
link_modes = lms;
|
||||
bps = baud;
|
||||
}
|
||||
|
@ -197,6 +199,7 @@ struct MeterInfo
|
|||
key = "";
|
||||
shells.clear();
|
||||
extra_constant_fields.clear();
|
||||
extra_calculated_fields.clear();
|
||||
link_modes.clear();
|
||||
bps = 0;
|
||||
}
|
||||
|
@ -471,6 +474,7 @@ struct Meter
|
|||
virtual MeterKeys *meterKeys() = 0;
|
||||
|
||||
virtual void addConversions(std::vector<Unit> cs) = 0;
|
||||
virtual void addExtraCalculatedField(std::string ecf) = 0;
|
||||
virtual vector<Unit>& conversions() = 0;
|
||||
virtual void addShell(std::string cmdline) = 0;
|
||||
virtual vector<string> &shellCmdlines() = 0;
|
||||
|
|
|
@ -67,6 +67,7 @@ struct MeterCommonImplementation : public virtual Meter
|
|||
void setPollInterval(time_t interval);
|
||||
time_t pollInterval();
|
||||
bool usesPolling();
|
||||
void addExtraCalculatedField(std::string ef);
|
||||
|
||||
void onUpdate(function<void(Telegram*,Meter*)> cb);
|
||||
int numUpdates();
|
||||
|
|
3
test.sh
3
test.sh
|
@ -84,6 +84,9 @@ if [ "$?" != "0" ]; then RC="1"; fi
|
|||
tests/test_conversions.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
tests/test_formulas.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
tests/test_fields.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/sh
|
||||
|
||||
PROG="$1"
|
||||
|
||||
rm -rf testoutput
|
||||
mkdir -p testoutput
|
||||
TEST=testoutput
|
||||
|
||||
TESTNAME="Test formulas"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
$PROG --format=json \
|
||||
--calculate_sumtemp_c='external_temperature_c+flow_temperature_c' \
|
||||
--calculate_addtemp_c='external_temperature_c + 1100 c' \
|
||||
23442D2C998734761B168D2087D19EAD217F1779EDA86AB6_710008190000081900007F13 \
|
||||
MyTapWater multical21 76348799 "" \
|
||||
> $TEST/test_output.txt
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","status":"DRY","total_m3":6.408,"target_m3":6.408,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","sumtemp_c":146,"addtemp_c":1119,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
EOF
|
||||
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||
diff $TEST/test_expected.txt $TEST/test_responses.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
echo "OK: $TESTNAME"
|
||||
TESTRESULT="OK"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$TESTRESULT" = "ERROR" ]; then echo ERROR: $TESTNAME; exit 1; fi
|
Ładowanie…
Reference in New Issue