kopia lustrzana https://github.com/weetmuts/wmbusmeters
Add formulas and SIUnits.
rodzic
86b497d339
commit
a01cdc8c34
6
Makefile
6
Makefile
|
@ -248,7 +248,11 @@ $(BUILD)/short_manual.h: README.md
|
|||
| grep -v '```' >> $(BUILD)/short_manual.h
|
||||
echo ')MANUAL";' >> $(BUILD)/short_manual.h
|
||||
|
||||
$(BUILD)/testinternals: $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/testinternals.o
|
||||
testinternals: $(BUILD)/testinternals
|
||||
|
||||
$(BUILD)/testinternals.o: $(PROG_OBJS) $(DRIVER_OBJS) $(wildcard src/*.h)
|
||||
|
||||
$(BUILD)/testinternals: $(BUILD)/testinternals.o
|
||||
$(CXX) -o $(BUILD)/testinternals $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/testinternals.o $(LDFLAGS) -lrtlsdr $(USBLIB) -lpthread
|
||||
|
||||
$(BUILD)/fuzz: $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/fuzz.o
|
||||
|
|
|
@ -808,7 +808,7 @@ LinkModeCalculationResult calculateLinkModes(Configuration *config, BusDevice *d
|
|||
if (n == 0) num = "any combination";
|
||||
string dongles = device->supportedLinkModes().hr();
|
||||
string dongle;
|
||||
strprintf(dongle, "%s of %s", num.c_str(), dongles.c_str());
|
||||
strprintf(&dongle, "%s of %s", num.c_str(), dongles.c_str());
|
||||
|
||||
// Calculate the possible listen_to linkmodes for this collection of meters.
|
||||
LinkModeSet meters_union = UNKNOWN_bit;
|
||||
|
@ -825,7 +825,7 @@ LinkModeCalculationResult calculateLinkModes(Configuration *config, BusDevice *d
|
|||
if (link_modes_matter && config->all_device_linkmodes_specified.empty())
|
||||
{
|
||||
string msg;
|
||||
strprintf(msg,"(config) No meters supplied. You must supply which link modes to listen to. 22 Eg. auto:t1");
|
||||
strprintf(&msg,"(config) No meters supplied. You must supply which link modes to listen to. 22 Eg. auto:t1");
|
||||
debug("%s\n", msg.c_str());
|
||||
return { LinkModeCalculationResultType::NoMetersMustSupplyModes , msg};
|
||||
}
|
||||
|
@ -859,7 +859,7 @@ LinkModeCalculationResult calculateLinkModes(Configuration *config, BusDevice *d
|
|||
if (!config->all_device_linkmodes_specified.hasAll(meters_union))
|
||||
{
|
||||
string msg;
|
||||
strprintf(msg, "(config) You have specified to listen to the link modes: %s but the meters might transmit on: %s\n"
|
||||
strprintf(&msg, "(config) You have specified to listen to the link modes: %s but the meters might transmit on: %s\n"
|
||||
"(config) Therefore you might miss telegrams! Please specify the expected transmit mode for the meters, eg: apator162:t1\n"
|
||||
"(config) Or use a dongle that can listen to all the required link modes at the same time.",
|
||||
all_lms.c_str(), metersu.c_str());
|
||||
|
|
|
@ -64,7 +64,7 @@ void MeterApator08::processContent(Telegram *t)
|
|||
map<string,pair<int,DVEntry>> vendor_values;
|
||||
|
||||
string total;
|
||||
strprintf(total, "%02x%02x%02x%02x", content[0], content[1], content[2], content[3]);
|
||||
strprintf(&total, "%02x%02x%02x%02x", content[0], content[1], content[2], content[3]);
|
||||
|
||||
vendor_values["0413"] = { 25, DVEntry(25, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) };
|
||||
int offset;
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace
|
|||
{
|
||||
// We found the register representing the total
|
||||
string total;
|
||||
strprintf(total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]);
|
||||
strprintf(&total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]);
|
||||
int offset = i-1+t->header_size;
|
||||
vendor_values["0413"] = {offset, DVEntry(offset, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) };
|
||||
extractDVdouble(&vendor_values, "0413", &offset, &total_water_consumption_m3_);
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace
|
|||
{
|
||||
// We found the register representing the total
|
||||
string total;
|
||||
strprintf(total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]);
|
||||
strprintf(&total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]);
|
||||
int offset = i-1+t->header_size;
|
||||
vendor_values["0413"] = {offset, DVEntry(offset, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) };
|
||||
double tmp = 0;
|
||||
|
|
|
@ -72,8 +72,8 @@ MeterAventiesHCA::MeterAventiesHCA(MeterInfo &mi, DriverInfo &di) : MeterCommonI
|
|||
for (int i=2; i<=17; ++i)
|
||||
{
|
||||
string key, info;
|
||||
strprintf(key, "consumption_at_set_date_%d", i);
|
||||
strprintf(info, "Heat cost allocation at the %d billing period date.", i);
|
||||
strprintf(&key, "consumption_at_set_date_%d", i);
|
||||
strprintf(&info, "Heat cost allocation at the %d billing period date.", i);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
key,
|
||||
|
|
|
@ -60,8 +60,8 @@ MeterAventiesWM::MeterAventiesWM(MeterInfo &mi, DriverInfo &di) :
|
|||
for (int i=1; i<=14; ++i)
|
||||
{
|
||||
string msg, info;
|
||||
strprintf(msg, "consumption_at_set_date_%d", i);
|
||||
strprintf(info, "Water consumption at the %d billing period date.", i);
|
||||
strprintf(&msg, "consumption_at_set_date_%d", i);
|
||||
strprintf(&info, "Water consumption at the %d billing period date.", i);
|
||||
addNumericFieldWithExtractor(
|
||||
msg,
|
||||
Quantity::Volume,
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace
|
|||
double prev = (256.0*prev_hi+prev_lo);
|
||||
|
||||
string prevs;
|
||||
strprintf(prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
int offset = t->parsed.size()+3;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, prevs) };
|
||||
Explanation pe(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL);
|
||||
|
@ -94,7 +94,7 @@ namespace
|
|||
double curr = (256.0*curr_hi+curr_lo);
|
||||
|
||||
string currs;
|
||||
strprintf(currs, "%02x%02x", curr_lo, curr_hi);
|
||||
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
|
||||
offset = t->parsed.size()+7;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, currs) };
|
||||
Explanation ce(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL);
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
Copyright (C) 2017-2022 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"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Driver : public virtual MeterCommonImplementation
|
||||
{
|
||||
Driver(MeterInfo &mi, DriverInfo &di);
|
||||
};
|
||||
|
||||
static bool ok = registerDriver([](DriverInfo&di)
|
||||
{
|
||||
di.setName("em24");
|
||||
di.setMeterType(MeterType::ElectricityMeter);
|
||||
di.addLinkMode(LinkMode::C1);
|
||||
di.addDetection(MANUFACTURER_KAM, 0x02, 0x33);
|
||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||
});
|
||||
|
||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||
{
|
||||
addStringFieldWithExtractorAndLookup(
|
||||
"status",
|
||||
"Status of meter.",
|
||||
PrintProperty::JSON | PrintProperty::IMPORTANT | PrintProperty::STATUS,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::ErrorFlags),
|
||||
{
|
||||
{
|
||||
{
|
||||
"ERROR_FLAGS",
|
||||
Translate::Type::BitToString,
|
||||
0xff,
|
||||
"OK",
|
||||
{
|
||||
{ 0x01, "V_1_OVERFLOW" },
|
||||
{ 0x02, "V_2_OVERFLOW" },
|
||||
{ 0x04, "V_2_OVERFLOW" },
|
||||
{ 0x08, "I_1_OVERFLOW" },
|
||||
{ 0x10, "I_2_OVERFLOW" },
|
||||
{ 0x20, "I_3_OVERFLOW" },
|
||||
{ 0x40, "FREQUENCY" },
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
addStringFieldWithExtractorAndLookup(
|
||||
"error",
|
||||
"Any errors currently being reported, this field is deprecated and replaced by the status field.",
|
||||
PrintProperty::JSON | PrintProperty::IMPORTANT | PrintProperty::DEPRECATED,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::ErrorFlags),
|
||||
{
|
||||
{
|
||||
{
|
||||
"ERROR_FLAGS",
|
||||
Translate::Type::BitToString,
|
||||
0xff,
|
||||
"",
|
||||
{
|
||||
{ 0x01, "V~1~OVERFLOW" },
|
||||
{ 0x02, "V~2~OVERFLOW" },
|
||||
{ 0x04, "V~2~OVERFLOW" },
|
||||
{ 0x08, "I~1~OVERFLOW" },
|
||||
{ 0x10, "I~2~OVERFLOW" },
|
||||
{ 0x20, "I~3~OVERFLOW" },
|
||||
{ 0x40, "FREQUENCY" },
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"total_energy_consumption",
|
||||
"The total energy consumption recorded by this meter.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
Quantity::Energy,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::AnyEnergyVIF)
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"total_energy_production",
|
||||
"The total energy backward (production) recorded by this meter.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
Quantity::Energy,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::AnyEnergyVIF)
|
||||
.add(VIFCombinable::BackwardFlow)
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"total_reactive_energy_consumption",
|
||||
"The reactive total energy consumption recorded by this meter.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
Quantity::Reactive_Energy,
|
||||
VifScaling::None,
|
||||
FieldMatcher::build()
|
||||
.set(DifVifKey("04FB8275"))
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"total_reactive_energy_production",
|
||||
"The total reactive energy backward (production) recorded by this meter.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
Quantity::Reactive_Energy,
|
||||
VifScaling::None,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(DifVifKey("04FB82F53C"))
|
||||
);
|
||||
|
||||
addNumericFieldWithCalculator(
|
||||
"total_apparent_energy_consumption",
|
||||
"Calculated: the total apparent energy consumption.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
Quantity::Energy,
|
||||
"total_energy_consumption_kwh + 99 kwh"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Test: Elen em24 66666666 NOKEY
|
||||
// telegram=|35442D2C6666666633028D2070806A0520B4D378_0405F208000004FB82753F00000004853C0000000004FB82F53CCA01000001FD1722|
|
||||
// {}
|
||||
// |GURKA
|
|
@ -112,8 +112,8 @@ namespace
|
|||
for (int i = 1; i <= 13; ++i)
|
||||
{
|
||||
string key, info;
|
||||
strprintf(key, "prev_%d_month", i);
|
||||
strprintf(info, "Energy consumption %d months back.", i);
|
||||
strprintf(&key, "prev_%d_month", i);
|
||||
strprintf(&info, "Energy consumption %d months back.", i);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
key,
|
||||
|
|
|
@ -411,9 +411,9 @@ bool parseDV(Telegram *t,
|
|||
|
||||
int count = ++dv_count[dv];
|
||||
if (count > 1) {
|
||||
strprintf(key, "%s_%d", dv.c_str(), count);
|
||||
strprintf(&key, "%s_%d", dv.c_str(), count);
|
||||
} else {
|
||||
strprintf(key, "%s", dv.c_str());
|
||||
strprintf(&key, "%s", dv.c_str());
|
||||
}
|
||||
DEBUG_PARSER("(dvparser debug) DifVif key is %s\n", key.c_str());
|
||||
|
||||
|
|
255
src/formula.cc
255
src/formula.cc
|
@ -39,9 +39,14 @@ NumericFormulaAddition::~NumericFormulaAddition()
|
|||
{
|
||||
}
|
||||
|
||||
NumericFormulaMultiplication::~NumericFormulaMultiplication()
|
||||
{
|
||||
}
|
||||
|
||||
double NumericFormulaConstant::calculate(Unit to)
|
||||
{
|
||||
return convert(constant_, unit(), to);
|
||||
SIUnit tosiunit(to);
|
||||
return convert(constant_, siunit(), tosiunit);
|
||||
}
|
||||
|
||||
double NumericFormulaField::calculate(Unit to)
|
||||
|
@ -59,6 +64,14 @@ double NumericFormulaAddition::calculate(Unit to)
|
|||
return sum;
|
||||
}
|
||||
|
||||
double NumericFormulaMultiplication::calculate(Unit to)
|
||||
{
|
||||
double l = left_->calculate(to);
|
||||
double r = right_->calculate(to);
|
||||
|
||||
return l*r;
|
||||
}
|
||||
|
||||
const char *toString(TokenType tt)
|
||||
{
|
||||
switch (tt) {
|
||||
|
@ -99,7 +112,7 @@ Unit Token::unit(const string &s)
|
|||
void FormulaImplementation::clear()
|
||||
{
|
||||
valid_ = true;
|
||||
while (!ops_.empty()) ops_.pop();
|
||||
op_stack_.clear();
|
||||
tokens_.clear();
|
||||
formula_ = "";
|
||||
meter_ = NULL;
|
||||
|
@ -195,9 +208,9 @@ size_t FormulaImplementation::findUnit(size_t i)
|
|||
// All units start with a lower case a-z, followed by more letters and _ underscores.
|
||||
if (!is_letter(c)) return 0;
|
||||
|
||||
#define X(cname,lcname,hrname,quantity,explanation) \
|
||||
if ( (i+sizeof(#lcname)-1 <= len) && \
|
||||
!is_letter_or_underscore(formula_[i+sizeof(#lcname)-1]) && \
|
||||
#define X(cname,lcname,hrname,quantity,explanation) \
|
||||
if ( (i+sizeof(#lcname)-1 <= len) && \
|
||||
!is_letter_or_underscore(formula_[i+sizeof(#lcname)-1]) && \
|
||||
!strncmp(#lcname, formula_.c_str()+i, sizeof(#lcname)-1)) return sizeof(#lcname)-1;
|
||||
LIST_OF_UNITS
|
||||
#undef X
|
||||
|
@ -279,7 +292,7 @@ size_t FormulaImplementation::parseOps(size_t i)
|
|||
if (tok->type == TokenType::PLUS)
|
||||
{
|
||||
size_t next = parseOps(i+1);
|
||||
handleAddition();
|
||||
handleAddition(tok);
|
||||
return next;
|
||||
}
|
||||
|
||||
|
@ -322,13 +335,15 @@ size_t FormulaImplementation::parsePar(size_t i)
|
|||
|
||||
if (tok == NULL)
|
||||
{
|
||||
warning("No closing parenthesis found!\n");
|
||||
errors_.push_back(tostrprintf("Missing closing parenthesis at end of formula!\n"));
|
||||
valid_ = false;
|
||||
return i;
|
||||
}
|
||||
|
||||
if (tok->type != TokenType::RPAR)
|
||||
{
|
||||
warning("Expected parenthesis but got xx intead.\n");
|
||||
errors_.push_back("Expected closing parenthesis!\n"+tok->withMarker(formula_));
|
||||
valid_ = false;
|
||||
return i;
|
||||
}
|
||||
return i+1;
|
||||
|
@ -341,32 +356,54 @@ void FormulaImplementation::handleConstant(Token *number, Token *unit)
|
|||
|
||||
if (u == Unit::Unknown)
|
||||
{
|
||||
warning("Unknown unit \"%s\" in formula:\n%s\n", unit->vals(formula_).c_str(), formula_.c_str());
|
||||
errors_.push_back(tostrprintf("Unknown unit \"%s\"!\n%s",
|
||||
unit->vals(formula_).c_str(),
|
||||
unit->withMarker(formula_).c_str()));
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
//debug("(formula) push constant %f %s\n", c, unitToStringLowerCase(u).c_str());
|
||||
|
||||
doConstant(u, c);
|
||||
}
|
||||
|
||||
void FormulaImplementation::handleAddition()
|
||||
void FormulaImplementation::handleAddition(Token *tok)
|
||||
{
|
||||
//debug("(formula) push addition\n");
|
||||
SIUnit right_siunit = topOp()->siunit();
|
||||
SIUnit left_siunit = top2Op()->siunit();
|
||||
|
||||
if (!canConvert(left_siunit, right_siunit))
|
||||
{
|
||||
string lsis = left_siunit.str();
|
||||
string rsis = right_siunit.str();
|
||||
errors_.push_back(tostrprintf("Cannot add %s to %s!\n%s",
|
||||
left_siunit.info().c_str(),
|
||||
right_siunit.info().c_str(),
|
||||
tok->withMarker(formula_).c_str()));
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
doAddition();
|
||||
}
|
||||
|
||||
void FormulaImplementation::handleMultiplication(Token *tok)
|
||||
{
|
||||
// Any two units can be multiplied! You might not like the answer thought....
|
||||
doMultiplication();
|
||||
}
|
||||
|
||||
void FormulaImplementation::handleField(Token *field)
|
||||
{
|
||||
string field_name = field->vals(formula_); // Full field: total_m3
|
||||
//debug("(formula) push field %s\n", field_name.c_str());
|
||||
string vname; // Without unit: total
|
||||
Unit unit;
|
||||
string vname; // Without unit: total
|
||||
Unit unit; // The extracted unit: m3
|
||||
bool ok = extractUnit(field_name, &vname, &unit);
|
||||
|
||||
debug("(formula) handle field %s into %s %s\n", field_name.c_str(), vname.c_str(), unitToStringLowerCase(unit).c_str());
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
warning("Could not extract a valid unit from field name \"%s\"\n", field_name.c_str());
|
||||
errors_.push_back("Cannot extra a valid unit from field name \""+field_name+"\"\n"+field->withMarker(formula_));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -375,16 +412,12 @@ void FormulaImplementation::handleField(Token *field)
|
|||
|
||||
if (f == NULL)
|
||||
{
|
||||
warning("No such field found \"%s\" (%s %s)\n", field_name.c_str(), vname.c_str(), toString(q));
|
||||
errors_.push_back("No such field found \""+field_name+"\"\n"+field->withMarker(formula_));
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ok = doField(unit, meter_, f);
|
||||
if (!ok)
|
||||
{
|
||||
warning("Could not use field \"%s\" (%s %s)\n", field_name.c_str(), vname.c_str(), toString(q));
|
||||
return;
|
||||
}
|
||||
doField(unit, meter_, f);
|
||||
}
|
||||
|
||||
bool FormulaImplementation::go()
|
||||
|
@ -436,70 +469,82 @@ bool FormulaImplementation::parse(Meter *m, const string &f)
|
|||
{
|
||||
debug("(formula) %s\n", tree().c_str());
|
||||
}
|
||||
return true;
|
||||
return valid_;
|
||||
}
|
||||
|
||||
bool FormulaImplementation::valid()
|
||||
{
|
||||
return valid_ == true && ops_.size() == 1;
|
||||
return valid_ == true && op_stack_.size() == 1;
|
||||
}
|
||||
|
||||
string FormulaImplementation::errors()
|
||||
{
|
||||
string s;
|
||||
for (string& e : errors_) s += e;
|
||||
return s;
|
||||
}
|
||||
double FormulaImplementation::calculate(Unit to)
|
||||
{
|
||||
if (!valid_)
|
||||
{
|
||||
warning("(formula) not valid returning nan!\n");
|
||||
return std::nan("");
|
||||
}
|
||||
if (ops_.size() != 1)
|
||||
{
|
||||
warning("(formula) does not have a single op (has %zu), not valid returning nan!\n", ops_.size());
|
||||
string t = tree();
|
||||
warning("Warning! Formula is not valid! Returning nan!\n%s\n", t.c_str());
|
||||
return std::nan("");
|
||||
}
|
||||
|
||||
return ops_.top().get()->calculate(to);
|
||||
}
|
||||
|
||||
bool FormulaImplementation::doConstant(Unit u, double c)
|
||||
{
|
||||
ops_.push(unique_ptr<NumericFormula>(new NumericFormulaConstant(u, c)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FormulaImplementation::doAddition()
|
||||
{
|
||||
if (ops_.size() < 2) { valid_ = false; return false;}
|
||||
|
||||
Unit right_unit = ops_.top()->unit();
|
||||
|
||||
unique_ptr<NumericFormula> right_node = std::move(ops_.top());
|
||||
ops_.pop();
|
||||
|
||||
Unit left_unit = ops_.top()->unit();
|
||||
|
||||
unique_ptr<NumericFormula> left_node = std::move(ops_.top());
|
||||
ops_.pop();
|
||||
|
||||
ops_.push(unique_ptr<NumericFormula>(new NumericFormulaAddition(left_unit, left_node, right_node)));
|
||||
|
||||
if (!canConvert(left_unit, right_unit))
|
||||
if (op_stack_.size() != 1)
|
||||
{
|
||||
valid_ = false;
|
||||
return false;
|
||||
string t = tree();
|
||||
warning("Warning! Formula is not valid! Multiple ops on stack! Returning nan!\n%s\n", t.c_str());
|
||||
return std::nan("");
|
||||
}
|
||||
|
||||
return true;
|
||||
return topOp()->calculate(to);
|
||||
}
|
||||
|
||||
bool FormulaImplementation::doField(Unit u, Meter *m, FieldInfo *fi)
|
||||
void FormulaImplementation::doConstant(Unit u, double c)
|
||||
{
|
||||
if (!canConvert(u, fi->defaultUnit()))
|
||||
{
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
ops_.push(unique_ptr<NumericFormula>(new NumericFormulaField(u, m, fi)));
|
||||
return true;
|
||||
pushOp(new NumericFormulaConstant(u, c));
|
||||
}
|
||||
|
||||
void FormulaImplementation::doAddition()
|
||||
{
|
||||
assert(op_stack_.size() >= 2);
|
||||
|
||||
SIUnit right_siunit = topOp()->siunit();
|
||||
|
||||
unique_ptr<NumericFormula> right_node = popOp();
|
||||
|
||||
SIUnit left_siunit = topOp()->siunit();
|
||||
|
||||
unique_ptr<NumericFormula> left_node = popOp();
|
||||
|
||||
pushOp(new NumericFormulaAddition(left_siunit, left_node, right_node));
|
||||
|
||||
assert(canConvert(left_siunit, right_siunit));
|
||||
}
|
||||
|
||||
void FormulaImplementation::doMultiplication()
|
||||
{
|
||||
assert(op_stack_.size() >= 2);
|
||||
|
||||
// SIUnit right_siunit = topOp()->siunit();
|
||||
|
||||
unique_ptr<NumericFormula> right_node = popOp();
|
||||
|
||||
SIUnit left_siunit = topOp()->siunit();
|
||||
|
||||
unique_ptr<NumericFormula> left_node = popOp();
|
||||
|
||||
pushOp(new NumericFormulaMultiplication(left_siunit, left_node, right_node));
|
||||
|
||||
// assert(canConvert(left_siunit, right_siunit));
|
||||
}
|
||||
|
||||
void FormulaImplementation::doField(Unit u, Meter *m, FieldInfo *fi)
|
||||
{
|
||||
assert(canConvert(u, fi->defaultUnit()));
|
||||
pushOp(new NumericFormulaField(u, m, fi));
|
||||
}
|
||||
|
||||
Formula *newFormula()
|
||||
|
@ -517,24 +562,35 @@ FormulaImplementation::~FormulaImplementation()
|
|||
|
||||
string FormulaImplementation::str()
|
||||
{
|
||||
return ops_.top()->str();
|
||||
return topOp()->str();
|
||||
}
|
||||
|
||||
string FormulaImplementation::tree()
|
||||
{
|
||||
string t = ops_.top()->tree();
|
||||
if (t.back() == ' ') t.pop_back();
|
||||
return t;
|
||||
string s;
|
||||
|
||||
for (auto &op : op_stack_)
|
||||
{
|
||||
if (s.length() > 0) s += " | ";
|
||||
s += op->tree();
|
||||
if (s.back() == ' ') s.pop_back();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
string NumericFormulaConstant::str()
|
||||
{
|
||||
return tostrprintf("%.15g %s", constant_, unit());
|
||||
return tostrprintf("%.17g %s", constant_, siunit());
|
||||
}
|
||||
|
||||
string NumericFormulaConstant::tree()
|
||||
{
|
||||
return tostrprintf("<CONST %.15g %s> ", constant_, unitToStringLowerCase(unit()).c_str());
|
||||
Unit u = siunit().asUnit();
|
||||
Quantity q = siunit().quantity();
|
||||
string sis = siunit().str();
|
||||
|
||||
|
||||
return tostrprintf("<CONST %.17g %s[%s]%s> ", constant_, unitToStringLowerCase(u).c_str(), sis.c_str(), toString(q));
|
||||
}
|
||||
|
||||
string NumericFormulaAddition::str()
|
||||
|
@ -551,6 +607,20 @@ string NumericFormulaAddition::tree()
|
|||
return "<ADD "+left+right+"> ";
|
||||
}
|
||||
|
||||
string NumericFormulaMultiplication::str()
|
||||
{
|
||||
string left = left_->tree();
|
||||
string right = right_->tree();
|
||||
return left+" × "+right;
|
||||
}
|
||||
|
||||
string NumericFormulaMultiplication::tree()
|
||||
{
|
||||
string left = left_->tree();
|
||||
string right = right_->tree();
|
||||
return "<MUL "+left+right+"> ";
|
||||
}
|
||||
|
||||
string NumericFormulaField::str()
|
||||
{
|
||||
return field_info_->vname()+"_"+unitToStringLowerCase(field_info_->defaultUnit());
|
||||
|
@ -560,3 +630,40 @@ string NumericFormulaField::tree()
|
|||
{
|
||||
return "<FIELD "+field_info_->vname()+"_"+unitToStringLowerCase(field_info_->defaultUnit())+"> ";
|
||||
}
|
||||
|
||||
void FormulaImplementation::pushOp(NumericFormula *nf)
|
||||
{
|
||||
op_stack_.push_back(unique_ptr<NumericFormula>(nf));
|
||||
}
|
||||
|
||||
unique_ptr<NumericFormula> FormulaImplementation::popOp()
|
||||
{
|
||||
assert(op_stack_.size() > 0);
|
||||
unique_ptr<NumericFormula> nf = std::move(op_stack_.back());
|
||||
op_stack_.pop_back();
|
||||
return nf;
|
||||
}
|
||||
|
||||
NumericFormula * FormulaImplementation::topOp()
|
||||
{
|
||||
assert(op_stack_.size() > 0);
|
||||
return op_stack_.back().get();
|
||||
}
|
||||
|
||||
NumericFormula * FormulaImplementation::top2Op()
|
||||
{
|
||||
assert(op_stack_.size() > 1);
|
||||
return op_stack_[op_stack_.size()-2].get();
|
||||
}
|
||||
|
||||
string Token::withMarker(const string& formula)
|
||||
{
|
||||
string indent;
|
||||
size_t n = start;
|
||||
while (n > 0)
|
||||
{
|
||||
indent += " ";
|
||||
n--;
|
||||
}
|
||||
return formula+"\n"+indent+"^~~~~\n";
|
||||
}
|
||||
|
|
|
@ -28,9 +28,15 @@ struct FieldInfo;
|
|||
|
||||
struct Formula
|
||||
{
|
||||
// Parse and return false if parse failed.
|
||||
virtual bool parse(Meter *m, const std::string &f) = 0;
|
||||
// Returns false if the parse has failed and the formula is invalid.
|
||||
virtual bool valid() = 0;
|
||||
// Returns lines of error messages.
|
||||
virtual std::string errors() = 0;
|
||||
// Calculate the formula. Returns nan if it could not be calculated.
|
||||
virtual double calculate(Unit to) = 0;
|
||||
// Clear the formula, ie drop any parsed tree.
|
||||
virtual void clear() = 0;
|
||||
// Return a regenerated formula string.
|
||||
virtual std::string str() = 0;
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
|
||||
struct NumericFormula
|
||||
{
|
||||
NumericFormula(Unit u) : unit_(u) { }
|
||||
Unit unit() { return unit_; }
|
||||
NumericFormula(SIUnit u) : siunit_(u) { }
|
||||
SIUnit &siunit() { return siunit_; }
|
||||
virtual double calculate(Unit to) = 0;
|
||||
virtual string str() = 0;
|
||||
virtual string tree() = 0;
|
||||
|
@ -34,7 +34,7 @@ struct NumericFormula
|
|||
|
||||
private:
|
||||
|
||||
Unit unit_;
|
||||
SIUnit siunit_;
|
||||
};
|
||||
|
||||
struct NumericFormulaConstant : public NumericFormula
|
||||
|
@ -66,10 +66,10 @@ struct NumericFormulaField : public NumericFormula
|
|||
|
||||
struct NumericFormulaAddition : public NumericFormula
|
||||
{
|
||||
NumericFormulaAddition(Unit u,
|
||||
NumericFormulaAddition(SIUnit siu,
|
||||
unique_ptr<NumericFormula> &a,
|
||||
unique_ptr<NumericFormula> &b)
|
||||
: NumericFormula(u), left_(std::move(a)), right_(std::move(b)) {}
|
||||
: NumericFormula(siu), left_(std::move(a)), right_(std::move(b)) {}
|
||||
|
||||
double calculate(Unit to);
|
||||
string str();
|
||||
|
@ -83,6 +83,25 @@ private:
|
|||
std::unique_ptr<NumericFormula> right_;
|
||||
};
|
||||
|
||||
struct NumericFormulaMultiplication : public NumericFormula
|
||||
{
|
||||
NumericFormulaMultiplication(SIUnit siu,
|
||||
unique_ptr<NumericFormula> &a,
|
||||
unique_ptr<NumericFormula> &b)
|
||||
: NumericFormula(siu), left_(std::move(a)), right_(std::move(b)) {}
|
||||
|
||||
double calculate(Unit to);
|
||||
string str();
|
||||
string tree();
|
||||
|
||||
~NumericFormulaMultiplication();
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<NumericFormula> left_;
|
||||
std::unique_ptr<NumericFormula> right_;
|
||||
};
|
||||
|
||||
enum class TokenType
|
||||
{
|
||||
SPACE,
|
||||
|
@ -108,25 +127,30 @@ struct Token
|
|||
string vals(const string &s);
|
||||
double val(const string &s);
|
||||
Unit unit(const string &s);
|
||||
|
||||
string withMarker(const string &s);
|
||||
};
|
||||
|
||||
struct FormulaImplementation : public Formula
|
||||
{
|
||||
bool parse(Meter *m, const string &f);
|
||||
bool valid();
|
||||
string errors();
|
||||
double calculate(Unit to);
|
||||
void clear();
|
||||
string str();
|
||||
string tree();
|
||||
|
||||
// Pushes a constant on the stack.
|
||||
bool doConstant(Unit u, double c);
|
||||
// Pushes a field read on the stack. Returns false if the field is not found in the meter.
|
||||
bool doField(Unit u, Meter *m, FieldInfo *fi);
|
||||
void doConstant(Unit u, double c);
|
||||
// Pushes a field read on the stack.
|
||||
void doField(Unit u, Meter *m, FieldInfo *fi);
|
||||
// Pops the two top nodes of the stack and pushes an addition (using these members) on the stack.
|
||||
// The target unit will be the first unit of the two operands. If incompatible units, then it will
|
||||
// return false.
|
||||
bool doAddition();
|
||||
// The target unit will be the first unit of the two operands.
|
||||
void doAddition();
|
||||
// Pops the two top nodes of the stack and pushes a multiplication (using these members) on the stack.
|
||||
// The target unit will be multiplication of the SI Units.
|
||||
void doMultiplication();
|
||||
|
||||
~FormulaImplementation();
|
||||
|
||||
|
@ -147,14 +171,23 @@ private:
|
|||
size_t parsePar(size_t i);
|
||||
|
||||
void handleConstant(Token *number, Token *unit);
|
||||
void handleAddition();
|
||||
void handleAddition(Token *add);
|
||||
void handleMultiplication(Token *add);
|
||||
void handleField(Token *field);
|
||||
|
||||
void pushOp(NumericFormula *nf);
|
||||
std::unique_ptr<NumericFormula> popOp();
|
||||
NumericFormula *topOp();
|
||||
NumericFormula *top2Op();
|
||||
|
||||
bool valid_ = true;
|
||||
std::stack<std::unique_ptr<NumericFormula>> ops_;
|
||||
std::vector<std::unique_ptr<NumericFormula>> op_stack_;
|
||||
std::vector<Token> tokens_;
|
||||
std::string formula_; // To be parsed.
|
||||
Meter *meter_; // To be referenced when parsing.
|
||||
|
||||
// Any errors during parsing are store here.
|
||||
std::vector<std::string> errors_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
23
src/main.cc
23
src/main.cc
|
@ -318,8 +318,9 @@ LIST_OF_METERS
|
|||
|
||||
struct TmpUnit
|
||||
{
|
||||
string suff, expl, name, quantity;
|
||||
string suff, expl, name, quantity, si;
|
||||
};
|
||||
|
||||
void list_units()
|
||||
{
|
||||
vector<TmpUnit> units;
|
||||
|
@ -327,12 +328,15 @@ void list_units()
|
|||
|
||||
// X(KVARH,kvarh,"kVARh",Reactive_Energy,"kilo volt amperes reactive hour")
|
||||
int width = 1;
|
||||
int total = 1;
|
||||
int total = 50;
|
||||
int tmp = 0;
|
||||
#define X(upn,lcn,name,quantity,expl) \
|
||||
#define X(upn,lcn,name,quantity,expl) \
|
||||
tmp = strlen(#lcn); if (tmp > width) { width = tmp; } \
|
||||
tmp = strlen(#lcn)+strlen(name)+strlen(expl)+3; if (tmp > total) { total = tmp; } \
|
||||
units.push_back( { #lcn, expl, name, #quantity } ); \
|
||||
{ \
|
||||
SIUnit si(Unit::upn); \
|
||||
string sis = si.str(); \
|
||||
units.push_back( { #lcn, expl, name, #quantity, sis } ); \
|
||||
} \
|
||||
quantities.insert(#quantity);
|
||||
LIST_OF_UNITS
|
||||
#undef X
|
||||
|
@ -354,7 +358,14 @@ LIST_OF_UNITS
|
|||
{
|
||||
if (u.quantity == q)
|
||||
{
|
||||
printf("%-*s %s (%s)\n", width, u.suff.c_str(), u.expl.c_str(), u.name.c_str());
|
||||
string s = tostrprintf("%-*s %s (%s)", width, u.suff.c_str(), u.expl.c_str(), u.name.c_str());
|
||||
int left = strlen_utf8(s.c_str());
|
||||
string si = tostrprintf("%s", u.si.c_str());
|
||||
int right = strlen_utf8(si.c_str());
|
||||
int space = 50-left-right;
|
||||
printf("%s", s.c_str());
|
||||
while (space > 0 && space < 100) { printf(" "); space--; }
|
||||
printf("%s\n", si.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
X(EURISII, MANUFACTURER_INE, 0x08, 0x55) \
|
||||
X(EHZP, MANUFACTURER_EMH, 0x02, 0x02) \
|
||||
X(ESYSWM, MANUFACTURER_ESY, 0x37, 0x30) \
|
||||
X(EM24, MANUFACTURER_KAM, 0x02, 0x33) \
|
||||
X(EVO868, MANUFACTURER_MAD, 0x07, 0x50) \
|
||||
X(FHKVDATAIII,MANUFACTURER_TCH, 0x80, 0x69) \
|
||||
X(FHKVDATAIII,MANUFACTURER_TCH, 0x80, 0x94) \
|
||||
|
|
|
@ -1,215 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2017-2020 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"dvparser.h"
|
||||
#include"meters.h"
|
||||
#include"meters_common_implementation.h"
|
||||
#include"wmbus.h"
|
||||
#include"wmbus_utils.h"
|
||||
#include"util.h"
|
||||
|
||||
#include<cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
constexpr uint8_t ERROR_CODE_VOLTAGE_PHASE_1_OVERFLOW=0x01;
|
||||
constexpr uint8_t ERROR_CODE_VOLTAGE_PHASE_2_OVERFLOW=0x02;
|
||||
constexpr uint8_t ERROR_CODE_VOLTAGE_PHASE_3_OVERFLOW=0x04;
|
||||
|
||||
constexpr uint8_t ERROR_CODE_CURRENT_PHASE_1_OVERFLOW=0x08;
|
||||
constexpr uint8_t ERROR_CODE_CURRENT_PHASE_2_OVERFLOW=0x10;
|
||||
constexpr uint8_t ERROR_CODE_CURRENT_PHASE_3_OVERFLOW=0x20;
|
||||
|
||||
constexpr uint8_t ERROR_CODE_FREQUENCY_OUT_OF_RANGE=0x40;
|
||||
|
||||
|
||||
struct MeterEM24 : public virtual MeterCommonImplementation {
|
||||
MeterEM24(MeterInfo &mi);
|
||||
|
||||
double totalEnergyConsumption(Unit u);
|
||||
double totalEnergyProduction(Unit u);
|
||||
|
||||
double totalReactiveEnergyConsumption(Unit u);
|
||||
double totalReactiveEnergyProduction(Unit u);
|
||||
|
||||
double totalApparentEnergyConsumption(Unit u);
|
||||
double totalApparentEnergyProduction(Unit u);
|
||||
|
||||
string status();
|
||||
|
||||
private:
|
||||
void processContent(Telegram *t);
|
||||
|
||||
double total_true_energy_consumption_kwh_ {};
|
||||
double total_true_energy_production_kwh_ {};
|
||||
|
||||
double total_reactive_energy_consumption_kvarh_ {};
|
||||
double total_reactive_energy_production_kvarh_ {};
|
||||
|
||||
uint8_t error_codes_ {};
|
||||
};
|
||||
|
||||
shared_ptr<Meter> createEM24(MeterInfo &mi)
|
||||
{
|
||||
return shared_ptr<Meter>(new MeterEM24(mi));
|
||||
}
|
||||
|
||||
MeterEM24::MeterEM24(MeterInfo &mi) :
|
||||
MeterCommonImplementation(mi, "em24")
|
||||
{
|
||||
setMeterType(MeterType::ElectricityMeter);
|
||||
|
||||
setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR);
|
||||
|
||||
addLinkMode(LinkMode::C1);
|
||||
|
||||
addPrint("total_energy_consumption", Quantity::Energy,
|
||||
[&](Unit u){ return totalEnergyConsumption(u); },
|
||||
"The total energy consumption recorded by this meter.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("total_energy_production", Quantity::Energy,
|
||||
[&](Unit u){ return totalEnergyProduction(u); },
|
||||
"The total energy production recorded by this meter.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("total_reactive_energy_consumption", Quantity::Reactive_Energy,
|
||||
[&](Unit u){ return totalReactiveEnergyConsumption(u); },
|
||||
"The total reactive energy consumption recorded by this meter.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("total_reactive_energy_production", Quantity::Reactive_Energy,
|
||||
[&](Unit u){ return totalReactiveEnergyProduction(u); },
|
||||
"The total reactive energy production recorded by this meter.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("total_apparent_energy_consumption", Quantity::Apparent_Energy,
|
||||
[&](Unit u){ return totalApparentEnergyConsumption(u); },
|
||||
"The total apparent energy consumption by calculation.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("total_apparent_energy_production", Quantity::Apparent_Energy,
|
||||
[&](Unit u){ return totalApparentEnergyProduction(u); },
|
||||
"The total apparent energy production by calculation.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("errors", Quantity::Text,
|
||||
[&](){ return status(); },
|
||||
"Any errors currently being reported.",
|
||||
PrintProperty::JSON);
|
||||
}
|
||||
|
||||
double MeterEM24::totalEnergyConsumption(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Energy);
|
||||
return convert(total_true_energy_consumption_kwh_, Unit::KWH, u);
|
||||
}
|
||||
|
||||
double MeterEM24::totalEnergyProduction(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Energy);
|
||||
return convert(total_true_energy_production_kwh_, Unit::KWH, u);
|
||||
}
|
||||
|
||||
double MeterEM24::totalReactiveEnergyConsumption(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Reactive_Energy);
|
||||
return convert(total_reactive_energy_consumption_kvarh_, Unit::KVARH, u);
|
||||
}
|
||||
|
||||
double MeterEM24::totalReactiveEnergyProduction(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Reactive_Energy);
|
||||
return convert(total_reactive_energy_production_kvarh_, Unit::KVARH, u);
|
||||
}
|
||||
|
||||
double MeterEM24::totalApparentEnergyConsumption(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Apparent_Energy);
|
||||
return convert(
|
||||
sqrt(
|
||||
pow(total_true_energy_consumption_kwh_, 2) +
|
||||
pow(total_reactive_energy_consumption_kvarh_, 2)
|
||||
)
|
||||
, Unit::KVAH, u);
|
||||
}
|
||||
|
||||
double MeterEM24::totalApparentEnergyProduction(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Apparent_Energy);
|
||||
return convert(
|
||||
sqrt(
|
||||
pow(total_true_energy_production_kwh_, 2) +
|
||||
pow(total_reactive_energy_production_kvarh_, 2)
|
||||
)
|
||||
, Unit::KVAH, u);
|
||||
}
|
||||
|
||||
void MeterEM24::processContent(Telegram *t)
|
||||
{
|
||||
int offset;
|
||||
|
||||
// 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
// 05 vif (Energy 10² Wh)
|
||||
extractDVdouble(&t->dv_entries, "0405", &offset, &total_true_energy_consumption_kwh_);
|
||||
t->addMoreExplanation(offset, " total power (%f kwh)", total_true_energy_consumption_kwh_);
|
||||
|
||||
// 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
// FB vif (First extension of VIF-codes)
|
||||
// 82 vife (Reserved)
|
||||
// 75 vife (Cold / Warm Temperature Limit 10^-2 Celsius)
|
||||
extractDVdouble(&t->dv_entries, "04FB8275", &offset, &total_reactive_energy_consumption_kvarh_);
|
||||
t->addMoreExplanation(offset, " total reactive power (%f kvarh)", total_reactive_energy_consumption_kvarh_);
|
||||
|
||||
// 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
// 85 vif (Energy 10² Wh)
|
||||
// 3C vife (backward flow)
|
||||
extractDVdouble(&t->dv_entries, "04853C", &offset, &total_true_energy_production_kwh_);
|
||||
t->addMoreExplanation(offset, " total power (%f kwh)", total_true_energy_production_kwh_);
|
||||
|
||||
// 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
// FB vif (First extension of VIF-codes)
|
||||
// 82 vife (Reserved)
|
||||
// F5 vife (Cold / Warm Temperature Limit 10^-2 Celsius)
|
||||
// 3C vife (Reserved)
|
||||
extractDVdouble(&t->dv_entries, "04FB82F53C", &offset, &total_reactive_energy_production_kvarh_);
|
||||
t->addMoreExplanation(offset, " total reactive power (%f kvarh)", total_reactive_energy_production_kvarh_);
|
||||
|
||||
// 01 dif (8 Bit Integer/Binary Instantaneous value)
|
||||
// FD vif (Second extension of VIF-codes)
|
||||
// 17 vife (Error flags (binary))
|
||||
extractDVuint8(&t->dv_entries, "01FD17", &offset, &error_codes_);
|
||||
t->addMoreExplanation(offset, " error codes (%s)", status().c_str());
|
||||
}
|
||||
|
||||
string MeterEM24::status()
|
||||
{
|
||||
string s;
|
||||
if (error_codes_ & ERROR_CODE_VOLTAGE_PHASE_1_OVERFLOW) s.append("V 1 OVERFLOW ");
|
||||
if (error_codes_ & ERROR_CODE_VOLTAGE_PHASE_2_OVERFLOW) s.append("V 2 OVERFLOW ");
|
||||
if (error_codes_ & ERROR_CODE_VOLTAGE_PHASE_3_OVERFLOW) s.append("V 3 OVERFLOW ");
|
||||
if (error_codes_ & ERROR_CODE_CURRENT_PHASE_1_OVERFLOW) s.append("I 1 OVERFLOW ");
|
||||
if (error_codes_ & ERROR_CODE_CURRENT_PHASE_2_OVERFLOW) s.append("I 2 OVERFLOW ");
|
||||
if (error_codes_ & ERROR_CODE_CURRENT_PHASE_3_OVERFLOW) s.append("I 3 OVERFLOW ");
|
||||
if (error_codes_ & ERROR_CODE_FREQUENCY_OUT_OF_RANGE) s.append("FREQUENCY ");
|
||||
if (s.length() > 0) {
|
||||
s.pop_back(); // Remove final space
|
||||
return s;
|
||||
}
|
||||
return s;
|
||||
}
|
|
@ -68,8 +68,8 @@ MeterEurisII::MeterEurisII(MeterInfo &mi) :
|
|||
for (int i=1; i<=17; ++i)
|
||||
{
|
||||
string msg, info;
|
||||
strprintf(msg, "consumption_at_set_date_%d", i);
|
||||
strprintf(info, "Heat cost allocation at the %d billing period date.", i);
|
||||
strprintf(&msg, "consumption_at_set_date_%d", i);
|
||||
strprintf(&info, "Heat cost allocation at the %d billing period date.", i);
|
||||
addPrint(msg, Quantity::HCA,
|
||||
[this,i](Unit u){ return consumption_at_set_date_hca_[i-1]; },
|
||||
info,
|
||||
|
@ -119,7 +119,7 @@ string MeterEurisII::errorFlagsHumanReadable()
|
|||
|
||||
if (error_flags_ != 0 && s.length() == 0) {
|
||||
// Some higher bits are set that we do not know about! Fall back to printing the number!
|
||||
strprintf(s, "0x%04X", error_flags_);
|
||||
strprintf(&s, "0x%04X", error_flags_);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ void MeterEurisII::processContent(Telegram *t)
|
|||
if (findKey(MeasurementType::Instantaneous, VIFRange::HeatCostAllocation, i, 0, &key, &t->dv_entries))
|
||||
{
|
||||
string info;
|
||||
strprintf(info, " consumption at set date %d (%%f hca)", i);
|
||||
strprintf(&info, " consumption at set date %d (%%f hca)", i);
|
||||
extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_set_date_hca_[i-1]);
|
||||
t->addMoreExplanation(offset, info.c_str(), consumption_at_set_date_hca_[i-1]);
|
||||
}
|
||||
|
|
|
@ -366,6 +366,6 @@ string MeterEvo868::status()
|
|||
|
||||
// How do we decode these?
|
||||
string info;
|
||||
strprintf(info, "ERROR bits %08x", error_flags_);
|
||||
strprintf(&info, "ERROR bits %08x", error_flags_);
|
||||
return info;
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ void MeterFHKVDataIII::processContent(Telegram *t)
|
|||
prev_energy_hca_ = prev;
|
||||
|
||||
string prevs;
|
||||
strprintf(prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
//t->addMoreExplanation(14, " energy used in previous billing period (%f HCA)", prevs);
|
||||
|
||||
// Previous Date
|
||||
|
@ -172,7 +172,7 @@ void MeterFHKVDataIII::processContent(Telegram *t)
|
|||
curr_energy_hca_ = curr;
|
||||
|
||||
string currs;
|
||||
strprintf(currs, "%02x%02x", curr_lo, curr_hi);
|
||||
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
|
||||
//t->addMoreExplanation(offset, " energy used in current billing period (%f HCA)", currs);
|
||||
|
||||
// Current Date
|
||||
|
@ -216,7 +216,7 @@ void MeterFHKVDataIII::processContent(Telegram *t)
|
|||
temp_room_ = room_t;
|
||||
|
||||
string room_ts;
|
||||
strprintf(room_ts, "%02x%02x", room_tlo, room_thi);
|
||||
strprintf(&room_ts, "%02x%02x", room_tlo, room_thi);
|
||||
// t->addMoreExplanation(offset, " current room temparature (%f °C)", room_ts);
|
||||
|
||||
// Radiator Temperature
|
||||
|
@ -224,7 +224,7 @@ void MeterFHKVDataIII::processContent(Telegram *t)
|
|||
temp_radiator_ = radiator_t;
|
||||
|
||||
string radiator_ts;
|
||||
strprintf(radiator_ts, "%02x%02x", radiator_tlo, radiator_thi);
|
||||
strprintf(&radiator_ts, "%02x%02x", radiator_tlo, radiator_thi);
|
||||
// t->addMoreExplanation(offset, " current radiator temparature (%f °C)", radiator_ts);
|
||||
}
|
||||
|
||||
|
|
|
@ -108,23 +108,23 @@ MeterGransystemsCCx01::MeterGransystemsCCx01(MeterInfo &mi) :
|
|||
"Voltage at phase L3.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("currrent_at_phase_1", Quantity::Current,
|
||||
addPrint("currrent_at_phase_1", Quantity::Amperage,
|
||||
[&](Unit u){ return convert(current_L_[0], Unit::Ampere, u); },
|
||||
"Current at phase L1.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("currrent_at_phase_2", Quantity::Current,
|
||||
addPrint("currrent_at_phase_2", Quantity::Amperage,
|
||||
[&](Unit u){ return convert(current_L_[1], Unit::Ampere, u); },
|
||||
"Current at phase L2.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("currrent_at_phase_3", Quantity::Current,
|
||||
addPrint("currrent_at_phase_3", Quantity::Amperage,
|
||||
[&](Unit u){ return convert(current_L_[2], Unit::Ampere, u); },
|
||||
"Current at phase L3.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("frequency", Quantity::Frequency,
|
||||
[&](Unit u){ return convert(frequency_, Unit::Hz, u); },
|
||||
[&](Unit u){ return convert(frequency_, Unit::HZ, u); },
|
||||
"Frequency.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
|
|
|
@ -159,14 +159,14 @@ double MeterIzar::lastMonthTotalWaterConsumption(Unit u)
|
|||
string MeterIzar::setH0Date()
|
||||
{
|
||||
string date;
|
||||
strprintf(date, "%d-%02d-%02d", h0_year, h0_month%99, h0_day%99);
|
||||
strprintf(&date, "%d-%02d-%02d", h0_year, h0_month%99, h0_day%99);
|
||||
return date;
|
||||
}
|
||||
|
||||
string MeterIzar::serialNumber()
|
||||
{
|
||||
string result;
|
||||
strprintf(result, "%06d", serial_number);
|
||||
strprintf(&result, "%06d", serial_number);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -263,7 +263,7 @@ void MeterIzar::processContent(Telegram *t)
|
|||
uchar meter_type = '@' + ((origin[8] & 0x7C) >> 2);
|
||||
uchar diameter = '@' + (((origin[8] & 0x03) << 3) | (origin[7] >> 5));
|
||||
// build the prefix
|
||||
strprintf(prefix, "%c%02d%c%c", supplier_code, yy, meter_type, diameter);
|
||||
strprintf(&prefix, "%c%02d%c%c", supplier_code, yy, meter_type, diameter);
|
||||
}
|
||||
|
||||
// get the remaining battery life (in year) and transmission period (in seconds)
|
||||
|
|
|
@ -95,10 +95,10 @@ void MKRadio3::processContent(Telegram *t)
|
|||
uint prev_date_day = (prev_date >> 0) & 0x1F;
|
||||
uint prev_date_month = (prev_date >> 5) & 0x0F;
|
||||
uint prev_date_year = (prev_date >> 9) & 0x3F;
|
||||
strprintf(previous_date_, "%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day);
|
||||
strprintf(&previous_date_, "%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day);
|
||||
|
||||
string prev_date_str;
|
||||
strprintf(prev_date_str, "%04x", prev_date);
|
||||
strprintf(&prev_date_str, "%04x", prev_date);
|
||||
uint offset = t->parsed.size() + 1;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x6c, {}, 0, 0, 0, prev_date_str) };
|
||||
t->explanations.push_back(Explanation(offset, 1, prev_date_str, KindOfData::CONTENT, Understanding::FULL));
|
||||
|
@ -110,7 +110,7 @@ void MKRadio3::processContent(Telegram *t)
|
|||
double prev = (256.0*prev_hi+prev_lo)/10.0;
|
||||
|
||||
string prevs;
|
||||
strprintf(prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
offset = t->parsed.size()+3;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, prevs) };
|
||||
t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL));
|
||||
|
@ -120,10 +120,10 @@ void MKRadio3::processContent(Telegram *t)
|
|||
uint16_t current_date = (content[6] << 8) | content[5];
|
||||
uint current_date_day = (current_date >> 4) & 0x1F;
|
||||
uint current_date_month = (current_date >> 9) & 0x0F;
|
||||
strprintf(current_date_, "%s-%02d-%02dT02:00:00Z", currentYear().c_str(), current_date_month, current_date_day);
|
||||
strprintf(¤t_date_, "%s-%02d-%02dT02:00:00Z", currentYear().c_str(), current_date_month, current_date_day);
|
||||
|
||||
string current_date_str;
|
||||
strprintf(current_date_str, "%04x", current_date);
|
||||
strprintf(¤t_date_str, "%04x", current_date);
|
||||
offset = t->parsed.size() + 5;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x6c, {}, 0, 0, 0, current_date_str) };
|
||||
t->explanations.push_back(Explanation(offset, 1, current_date_str, KindOfData::CONTENT, Understanding::FULL));
|
||||
|
@ -135,7 +135,7 @@ void MKRadio3::processContent(Telegram *t)
|
|||
double curr = (256.0*curr_hi+curr_lo)/10.0;
|
||||
|
||||
string currs;
|
||||
strprintf(currs, "%02x%02x", curr_lo, curr_hi);
|
||||
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
|
||||
offset = t->parsed.size()+7;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, currs) };
|
||||
t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL));
|
||||
|
|
|
@ -81,7 +81,7 @@ void MKRadio4::processContent(Telegram *t)
|
|||
double prev = (256.0*prev_hi+prev_lo)/10.0;
|
||||
|
||||
string prevs;
|
||||
strprintf(prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
int offset = t->parsed.size()+3;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, prevs) };
|
||||
t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL));
|
||||
|
@ -92,7 +92,7 @@ void MKRadio4::processContent(Telegram *t)
|
|||
double curr = (256.0*curr_hi+curr_lo)/10.0;
|
||||
|
||||
string currs;
|
||||
strprintf(currs, "%02x%02x", curr_lo, curr_hi);
|
||||
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
|
||||
offset = t->parsed.size()+7;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, currs) };
|
||||
t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL));
|
||||
|
|
|
@ -102,7 +102,7 @@ void MeterRfmTX1::processContent(Telegram *t)
|
|||
int m = bcd2bin(frame[o+1]);
|
||||
int s = bcd2bin(frame[o+0]);
|
||||
|
||||
strprintf(meter_datetime_, "%d-%02d-%02d %02d:%02d:%02d",
|
||||
strprintf(&meter_datetime_, "%d-%02d-%02d %02d:%02d:%02d",
|
||||
y, M%99, d%99, H%99, m%99, s%99);
|
||||
|
||||
return;
|
||||
|
|
|
@ -161,7 +161,7 @@ void MeterTopasEsKr::processContent(Telegram *t)
|
|||
|
||||
uint16_t tmp16;
|
||||
extractDVuint16(&t->dv_entries, "02FD74", &offset, &tmp16);
|
||||
strprintf(battery_life_days_remaining_, "%u", (unsigned int)tmp16);
|
||||
strprintf(&battery_life_days_remaining_, "%u", (unsigned int)tmp16);
|
||||
t->addMoreExplanation(offset, " battery life (%s days remaining)", battery_life_days_remaining_.c_str());
|
||||
|
||||
vector<uchar> data;
|
||||
|
|
|
@ -92,10 +92,10 @@ void MeterTSD2::processContent(Telegram *t)
|
|||
uint prev_date_day = (prev_date >> 0) & 0x1F;
|
||||
uint prev_date_month = (prev_date >> 5) & 0x0F;
|
||||
uint prev_date_year = (prev_date >> 9) & 0x3F;
|
||||
strprintf(previous_date_, "%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day);
|
||||
strprintf(&previous_date_, "%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day);
|
||||
|
||||
string prev_date_str;
|
||||
strprintf(prev_date_str, "%04x", prev_date);
|
||||
strprintf(&prev_date_str, "%04x", prev_date);
|
||||
uint offset = t->parsed.size() + 1;
|
||||
t->explanations.push_back(Explanation(offset, 1, prev_date_str, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " previous date (%s)", previous_date_.c_str());
|
||||
|
|
|
@ -107,7 +107,7 @@ void MeterVario451::processContent(Telegram *t)
|
|||
double prev = (256.0*prev_hi+prev_lo)/1000;
|
||||
|
||||
string prevs;
|
||||
strprintf(prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
int offset = t->parsed.size()+3;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, prevs) };
|
||||
t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL));
|
||||
|
@ -118,7 +118,7 @@ void MeterVario451::processContent(Telegram *t)
|
|||
double curr = (256.0*curr_hi+curr_lo)/1000;
|
||||
|
||||
string currs;
|
||||
strprintf(currs, "%02x%02x", curr_lo, curr_hi);
|
||||
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
|
||||
offset = t->parsed.size()+7;
|
||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, currs) };
|
||||
t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL));
|
||||
|
|
|
@ -148,7 +148,7 @@ bool registerDriver(function<void(DriverInfo&)> setup)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool lookupDriverInfo(string& driver, DriverInfo *out_di)
|
||||
bool lookupDriverInfo(const string& driver, DriverInfo *out_di)
|
||||
{
|
||||
DriverInfo *di = lookupDriver(driver);
|
||||
if (di == NULL)
|
||||
|
@ -156,7 +156,10 @@ bool lookupDriverInfo(string& driver, DriverInfo *out_di)
|
|||
return false;
|
||||
}
|
||||
|
||||
*out_di = *di;
|
||||
if (out_di != NULL)
|
||||
{
|
||||
*out_di = *di;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1393,7 +1396,7 @@ string toString(DriverInfo &di)
|
|||
return di.name().str();
|
||||
}
|
||||
|
||||
MeterDriver toMeterDriver(string& t)
|
||||
MeterDriver toMeterDriver(const string& t)
|
||||
{
|
||||
#define X(mname,linkmodes,info,type,cname) if (t == #mname) return MeterDriver::type;
|
||||
LIST_OF_METERS
|
||||
|
@ -1401,7 +1404,7 @@ LIST_OF_METERS
|
|||
return MeterDriver::UNKNOWN;
|
||||
}
|
||||
|
||||
LinkModeSet toMeterLinkModeSet(string& t)
|
||||
LinkModeSet toMeterLinkModeSet(const string& t)
|
||||
{
|
||||
#define X(mname,linkmodes,info,type,cname) if (t == #mname) return LinkModeSet(linkmodes);
|
||||
LIST_OF_METERS
|
||||
|
@ -1999,7 +2002,6 @@ string MeterCommonImplementation::getStringValue(FieldInfo *fi)
|
|||
{
|
||||
string more = getStringValue(&f);
|
||||
string joined = joinStatusStrings(value, more);
|
||||
//printf("JOINING >%s< >%s< into >%s<\n", value.c_str(), more.c_str(), joined.c_str());
|
||||
value = joined;
|
||||
}
|
||||
}
|
||||
|
@ -2451,7 +2453,7 @@ LIST_OF_METERS
|
|||
return newm;
|
||||
}
|
||||
|
||||
bool is_driver_and_extras(string t, MeterDriver *out_driver, DriverName *out_driver_name, string *out_extras)
|
||||
bool is_driver_and_extras(const string& t, MeterDriver *out_driver, DriverName *out_driver_name, string *out_extras)
|
||||
{
|
||||
// piigth(jump=foo)
|
||||
// multical21
|
||||
|
@ -2596,11 +2598,10 @@ bool MeterInfo::usesPolling()
|
|||
link_modes.has(LinkMode::S2);
|
||||
}
|
||||
|
||||
bool isValidKey(string& key, MeterDriver mt)
|
||||
bool isValidKey(const string& key, MeterDriver mt)
|
||||
{
|
||||
if (key.length() == 0) return true;
|
||||
if (key == "NOKEY") {
|
||||
key = "";
|
||||
return true;
|
||||
}
|
||||
if (mt == MeterDriver::IZAR ||
|
||||
|
|
|
@ -64,7 +64,6 @@ LIST_OF_METER_TYPES
|
|||
X(eurisii, T1_bit, HeatCostAllocationMeter, EURISII, EurisII) \
|
||||
X(ehzp, T1_bit, ElectricityMeter, EHZP, EHZP) \
|
||||
X(esyswm, T1_bit, ElectricityMeter, ESYSWM, ESYSWM) \
|
||||
X(em24, C1_bit, ElectricityMeter, EM24, EM24) \
|
||||
X(evo868, T1_bit, WaterMeter, EVO868, EVO868) \
|
||||
X(fhkvdataiii,T1_bit, HeatCostAllocationMeter, FHKVDATAIII, FHKVDataIII) \
|
||||
X(fhkvdataiv, T1_bit, HeatCostAllocationMeter, FHKVDATAIV, FHKVDataIV) \
|
||||
|
@ -123,7 +122,7 @@ bool isMeterDriverValid(MeterDriver type, int manufacturer, int media, int versi
|
|||
// Ie. do not try to decode a door sensor telegram with a water meter driver.
|
||||
bool isMeterDriverReasonableForMedia(MeterDriver type, string driver_name, int media);
|
||||
|
||||
bool isValidKey(string& key, MeterDriver mt);
|
||||
bool isValidKey(const string& key, MeterDriver mt);
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -263,7 +262,7 @@ public:
|
|||
};
|
||||
|
||||
bool registerDriver(function<void(DriverInfo&di)> setup);
|
||||
bool lookupDriverInfo(string& driver, DriverInfo *di);
|
||||
bool lookupDriverInfo(const string& driver, DriverInfo *di = NULL);
|
||||
// Return the best driver match for a telegram.
|
||||
DriverInfo pickMeterDriver(Telegram *t);
|
||||
// Return true for mbus and S2/C2/T2 drivers.
|
||||
|
@ -507,8 +506,8 @@ shared_ptr<MeterManager> createMeterManager(bool daemon);
|
|||
const char *toString(MeterType type);
|
||||
string toString(MeterDriver driver);
|
||||
string toString(DriverInfo &driver);
|
||||
MeterDriver toMeterDriver(string& driver);
|
||||
LinkModeSet toMeterLinkModeSet(string& driver);
|
||||
MeterDriver toMeterDriver(const string& driver);
|
||||
LinkModeSet toMeterLinkModeSet(const string& driver);
|
||||
LinkModeSet toMeterLinkModeSet(MeterDriver driver);
|
||||
|
||||
#define X(mname,linkmode,info,type,cname) shared_ptr<Meter> create##cname(MeterInfo &m);
|
||||
|
|
|
@ -1544,7 +1544,7 @@ string showSpecialChars(struct termios *tios)
|
|||
if (c != 0)
|
||||
{
|
||||
string cc;
|
||||
strprintf(cc, "%u", c);
|
||||
strprintf(&cc, "%u", c);
|
||||
s += cc+",";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include"dvparser.h"
|
||||
|
||||
#include<string.h>
|
||||
#include<set>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -53,9 +54,13 @@ void test_ascii_detection();
|
|||
void test_status_join();
|
||||
void test_status_sort();
|
||||
void test_field_matcher();
|
||||
void test_units();
|
||||
void test_units_extraction();
|
||||
void test_si_units_basic();
|
||||
void test_si_units_conversion();
|
||||
void test_formulas_building();
|
||||
void test_formulas_parsing();
|
||||
void test_formulas_parsing_1();
|
||||
void test_formulas_parsing_2();
|
||||
void test_formulas_parsing_3();
|
||||
|
||||
bool test(const char *test_name, const char *pattern)
|
||||
{
|
||||
|
@ -110,9 +115,13 @@ int main(int argc, char **argv)
|
|||
if (test("status_join", pattern)) test_status_join();
|
||||
if (test("status_sort", pattern)) test_status_sort();
|
||||
if (test("field_matcher", pattern)) test_field_matcher();
|
||||
if (test("units", pattern)) test_units();
|
||||
if (test("units_extraction", pattern)) test_units_extraction();
|
||||
if (test("si_units_basic", pattern)) test_si_units_basic();
|
||||
if (test("si_units_conversion", pattern)) test_si_units_conversion();
|
||||
if (test("formulas_building", pattern)) test_formulas_building();
|
||||
if (test("formulas_parsing", pattern)) test_formulas_parsing();
|
||||
if (test("formulas_parsing_1", pattern)) test_formulas_parsing_1();
|
||||
if (test("formulas_parsing_2", pattern)) test_formulas_parsing_2();
|
||||
if (test("formulas_parsing_3", pattern)) test_formulas_parsing_3();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1579,7 +1588,7 @@ void test_unit(string in, bool expected_ok, string expected_vname, Unit expected
|
|||
}
|
||||
}
|
||||
|
||||
void test_units()
|
||||
void test_units_extraction()
|
||||
{
|
||||
test_unit("total_kwh", true, "total", Unit::KWH);
|
||||
test_unit("total_", false, "", Unit::Unknown);
|
||||
|
@ -1597,14 +1606,425 @@ void test_units()
|
|||
|
||||
}
|
||||
|
||||
void test_expected_failed_si_convert(Unit from_unit,
|
||||
Unit to_unit,
|
||||
Quantity q)
|
||||
{
|
||||
SIUnit from_si_unit(from_unit);
|
||||
SIUnit to_si_unit(to_unit);
|
||||
string fu = unitToStringLowerCase(from_si_unit.asUnit());
|
||||
string tu = unitToStringLowerCase(to_si_unit.asUnit());
|
||||
|
||||
if (q != from_si_unit.quantity() || q != to_si_unit.quantity())
|
||||
{
|
||||
printf("ERROR! Not the expected quantities!\n");
|
||||
}
|
||||
if (canConvert(from_si_unit, to_si_unit))
|
||||
{
|
||||
printf("ERROR! Should not be able to convert from %s to %s !\n", fu.c_str(), tu.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void test_si_convert(double from_value, double expected_value,
|
||||
Unit from_unit,
|
||||
string expected_from_unit,
|
||||
Unit to_unit,
|
||||
string expected_to_unit,
|
||||
Quantity q,
|
||||
set<Unit> *from_set,
|
||||
set<Unit> *to_set)
|
||||
{
|
||||
string evs = tostrprintf("%.15g", expected_value);
|
||||
|
||||
SIUnit from_si_unit(from_unit);
|
||||
SIUnit to_si_unit(to_unit);
|
||||
string fu = unitToStringLowerCase(from_si_unit.asUnit(q));
|
||||
string tu = unitToStringLowerCase(to_si_unit.asUnit(q));
|
||||
|
||||
from_set->erase(from_unit);
|
||||
to_set->erase(to_unit);
|
||||
|
||||
double e = from_si_unit.convert(from_value, to_si_unit);
|
||||
string es = tostrprintf("%.15g", e);
|
||||
|
||||
if (canConvert(from_unit, to_unit))
|
||||
{
|
||||
// Test if conversion was the same using 16 significant digits.
|
||||
// I.e. slightly less than the maximum 17 significant digits.
|
||||
// Takes up the slack between the old style conversion and the new style conversion
|
||||
// which can introduce minor changes in the final digit.
|
||||
double ee = convert(from_value, from_unit, to_unit);
|
||||
string ees = tostrprintf("%.15g", ee);
|
||||
if (es != ees)
|
||||
{
|
||||
printf("ERROR! SI unit conversion %.15g (%s) from %.15g differs from unit conversion %.15g (%s)! \n",
|
||||
e, es.c_str(), from_value, ee, ees.c_str());
|
||||
}
|
||||
}
|
||||
if (fu != expected_from_unit)
|
||||
{
|
||||
printf("ERROR! Expected from unit %s (but got %s) when converting si unit %s\n",
|
||||
expected_from_unit.c_str(), fu.c_str(), from_si_unit.str().c_str());
|
||||
}
|
||||
if (tu != expected_to_unit)
|
||||
{
|
||||
printf("ERROR! Expected to unit %s (but got %s) when converting si unit %s\n",
|
||||
expected_to_unit.c_str(), tu.c_str(), to_si_unit.str().c_str());
|
||||
}
|
||||
if (es != evs)
|
||||
{
|
||||
printf("ERROR! Expected %.17g [%s] (but got %.17g [%s]) when converting %.17g from %s (%s) to %s (%s)\n",
|
||||
expected_value, evs.c_str(), e, es.c_str(), from_value,
|
||||
from_si_unit.str().c_str(),
|
||||
fu.c_str(),
|
||||
to_si_unit.str().c_str(),
|
||||
tu.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void test_si_units_basic()
|
||||
{
|
||||
// A kilowatt unit generated from scratch:
|
||||
SIUnit kwh(Quantity::Energy, 3.6E6, 0, SI_KG(1)|SI_M(2)|SI_S(-2));
|
||||
|
||||
string expected = "3.6×10⁶kgm²s⁻²";
|
||||
if (kwh.str() != expected) printf("ERROR expected kwh to be %s but got %s\n", expected.c_str(), kwh.str().c_str());
|
||||
|
||||
// A kilowatt unit from the unit lookup table.
|
||||
SIUnit kwh2(Unit::KWH);
|
||||
|
||||
if (kwh2.str() != expected) printf("ERROR expected second kwh to be %s but got %s\n", expected.c_str(), kwh2.str().c_str());
|
||||
|
||||
// A Celsius unit generated from scratch:
|
||||
SIUnit celsius(Quantity::Temperature, 1, 273.15, SI_K(1));
|
||||
|
||||
expected = "1k+273.15";
|
||||
if (celsius.str() != expected) printf("ERROR expected celsius to be %s but got %s\n", expected.c_str(), celsius.str().c_str());
|
||||
|
||||
// A celsius unit from the Unit.
|
||||
SIUnit celsius2(Unit::C);
|
||||
|
||||
if (celsius2.str() != expected) printf("ERROR expected second celsius to be %s but got %s\n", expected.c_str(), celsius2.str().c_str());
|
||||
}
|
||||
|
||||
void fill_with_units_from(Quantity q, set<Unit> *s)
|
||||
{
|
||||
s->clear();
|
||||
#define X(cname,lcname,hrname,quantity,explanation) if (q == Quantity::quantity) s->insert(Unit::cname);
|
||||
LIST_OF_UNITS
|
||||
#undef X
|
||||
}
|
||||
|
||||
void check_units_tested(set<Unit> &from_set, set<Unit> &to_set, Quantity q)
|
||||
{
|
||||
if (from_set.size() > 0)
|
||||
{
|
||||
printf("ERROR not all units as source in quantity %s tested! Remaining: ", toString(q));
|
||||
for (Unit u : from_set) printf("%s ", unitToStringLowerCase(u).c_str());
|
||||
printf("\n");
|
||||
}
|
||||
if (to_set.size() > 0)
|
||||
{
|
||||
printf("ERROR not all units as targets in quantity %s tested! Remaining: ", toString(q));
|
||||
for (Unit u : to_set) printf("%s ", unitToStringLowerCase(u).c_str());
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void check_quantities_tested(set<Quantity> &s)
|
||||
{
|
||||
if (s.size() > 0)
|
||||
{
|
||||
printf("ERROR not all quantities tested! Remaining: ");
|
||||
for (Quantity q : s) printf("%s ", toString(q));
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void test_si_units_conversion()
|
||||
{
|
||||
set<Quantity> q_set;
|
||||
set<Unit> from_set;
|
||||
set<Unit> to_set;
|
||||
|
||||
#define X(quantity,default_unit) q_set.insert(Quantity::quantity);
|
||||
LIST_OF_QUANTITIES
|
||||
#undef X
|
||||
|
||||
// Test time units: s, min, h, d, y
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Time);
|
||||
fill_with_units_from(Quantity::Time, &from_set);
|
||||
fill_with_units_from(Quantity::Time, &to_set);
|
||||
|
||||
// 60 seconds is one minute.
|
||||
test_si_convert(60.0, 1.0, Unit::Second, "s", Unit::Minute, "min", Quantity::Time, &from_set, &to_set);
|
||||
// 3600 seconds is one hour.
|
||||
test_si_convert(3600.0, 1.0, Unit::Second, "s", Unit::Hour, "h", Quantity::Time, &from_set, &to_set);
|
||||
// 3600 seconds is 1/24 of a day which is 0.041666666666666664.
|
||||
test_si_convert(3600.0, 0.041666666666666664, Unit::Second, "s", Unit::Day, "d", Quantity::Time, &from_set, &to_set);
|
||||
// Same test again.
|
||||
test_si_convert(3600.0, 1.0/24.0, Unit::Second, "s", Unit::Day, "d", Quantity::Time, &from_set, &to_set);
|
||||
// 1 min is 60 seconds.
|
||||
test_si_convert(1.0, 60.0, Unit::Minute, "min", Unit::Second, "s", Quantity::Time, &from_set, &to_set);
|
||||
// 1 day is 1/365.2425 year
|
||||
test_si_convert(1.0, 1.0/365.2425, Unit::Day, "d", Unit::Year, "y", Quantity::Time, &from_set, &to_set);
|
||||
// 100 hours is 100/24 days.
|
||||
test_si_convert(100.0, 100.0/24.0, Unit::Hour, "h", Unit::Day, "d", Quantity::Time, &from_set, &to_set);
|
||||
// 1 year is 365.2425 days.
|
||||
test_si_convert(1.0, 365.2425, Unit::Year, "y", Unit::Day, "d", Quantity::Time, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Time);
|
||||
|
||||
// Test length units: m
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Length);
|
||||
fill_with_units_from(Quantity::Length, &from_set);
|
||||
fill_with_units_from(Quantity::Length, &to_set);
|
||||
|
||||
test_si_convert(111.1, 111.1, Unit::M, "m", Unit::M, "m", Quantity::Length, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Length);
|
||||
|
||||
// Test mass units: kg
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Mass);
|
||||
fill_with_units_from(Quantity::Mass, &from_set);
|
||||
fill_with_units_from(Quantity::Mass, &to_set);
|
||||
|
||||
test_si_convert(222.1, 222.1, Unit::KG, "kg", Unit::KG, "kg", Quantity::Mass, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Mass);
|
||||
|
||||
// Test electrical current units: a
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Amperage);
|
||||
fill_with_units_from(Quantity::Amperage, &from_set);
|
||||
fill_with_units_from(Quantity::Amperage, &to_set);
|
||||
|
||||
test_si_convert(999.9, 999.9, Unit::Ampere, "a", Unit::Ampere, "a", Quantity::Amperage, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Amperage);
|
||||
|
||||
// Test temperature units: c k f
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Temperature);
|
||||
fill_with_units_from(Quantity::Temperature, &from_set);
|
||||
fill_with_units_from(Quantity::Temperature, &to_set);
|
||||
|
||||
test_si_convert(10.85, 284.0, Unit::C, "c", Unit::K, "k", Quantity::Temperature, &from_set, &to_set);
|
||||
test_si_convert(100.0, -173.15, Unit::K, "k", Unit::C, "c", Quantity::Temperature, &from_set, &to_set);
|
||||
test_si_convert(100.0, -279.67, Unit::K, "k", Unit::F, "f", Quantity::Temperature, &from_set, &to_set);
|
||||
test_si_convert(100.0, 37.77777777777777, Unit::F, "f", Unit::C, "c", Quantity::Temperature, &from_set, &to_set);
|
||||
test_si_convert(0.0, -17.7777777777778, Unit::F, "f", Unit::C, "c", Quantity::Temperature, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Temperature);
|
||||
|
||||
// Test energy units: kwh, mj, gj
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Energy);
|
||||
fill_with_units_from(Quantity::Energy, &from_set);
|
||||
fill_with_units_from(Quantity::Energy, &to_set);
|
||||
|
||||
// 1 kwh is 3.6 mj
|
||||
test_si_convert(1.0, 3.6, Unit::KWH, "kwh", Unit::MJ, "mj", Quantity::Energy, &from_set, &to_set);
|
||||
// 1 kwh is 0.0036 gj
|
||||
test_si_convert(1.0, 0.0036, Unit::KWH, "kwh", Unit::GJ, "gj", Quantity::Energy, &from_set, &to_set);
|
||||
// 1 gj is 1000 mj
|
||||
test_si_convert(1.0, 1000.0, Unit::GJ, "gj", Unit::MJ, "mj", Quantity::Energy, &from_set, &to_set);
|
||||
// 10 mj is 2.77777 kwh
|
||||
test_si_convert(10, 2.7777777777777777, Unit::MJ, "mj", Unit::KWH, "kwh", Quantity::Energy, &from_set, &to_set);
|
||||
// 1 ws = 1/3600000 kwh is 1 j = 0.000001 MJ
|
||||
test_si_convert(1.0/3600000.0, 0.000001, Unit::KWH, "kwh", Unit::MJ, "mj", Quantity::Energy, &from_set, &to_set);
|
||||
|
||||
// 99 m3c = 99 m3c this is the only test we can do with the m3c energy unit,
|
||||
// which cannot be converted into other energy units since we lack the density of the water etc.
|
||||
test_si_convert(99.0, 99.0, Unit::M3C, "m3c", Unit::M3C, "m3c", Quantity::Energy, &from_set, &to_set);
|
||||
|
||||
test_expected_failed_si_convert(Unit::M3C, Unit::KWH, Quantity::Energy);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Energy);
|
||||
|
||||
// Test reactive energy kvarh unit: kvarh
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Reactive_Energy);
|
||||
fill_with_units_from(Quantity::Reactive_Energy, &from_set);
|
||||
fill_with_units_from(Quantity::Reactive_Energy, &to_set);
|
||||
|
||||
// 1 kvarh is 1kwh
|
||||
test_si_convert(1.0, 1.0, Unit::KVARH, "kvarh", Unit::KWH, "kvarh", Quantity::Reactive_Energy, &from_set, &to_set);
|
||||
test_si_convert(1.0, 1.0, Unit::KWH, "kvarh", Unit::KVARH, "kvarh", Quantity::Reactive_Energy, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Reactive_Energy);
|
||||
|
||||
// Test apparent energy kvah unit: kvah
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Apparent_Energy);
|
||||
fill_with_units_from(Quantity::Apparent_Energy, &from_set);
|
||||
fill_with_units_from(Quantity::Apparent_Energy, &to_set);
|
||||
|
||||
// 1 kvah is 1kwh
|
||||
test_si_convert(1.0, 1.0, Unit::KVAH, "kvah", Unit::KWH, "kvah", Quantity::Apparent_Energy, &from_set, &to_set);
|
||||
test_si_convert(1.0, 1.0, Unit::KWH, "kvah", Unit::KVAH, "kvah", Quantity::Apparent_Energy, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Apparent_Energy);
|
||||
|
||||
// Test volume units: m3 l
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Volume);
|
||||
fill_with_units_from(Quantity::Volume, &from_set);
|
||||
fill_with_units_from(Quantity::Volume, &to_set);
|
||||
|
||||
test_si_convert(1, 1000.0, Unit::M3, "m3", Unit::L, "l", Quantity::Volume, &from_set, &to_set);
|
||||
test_si_convert(1, 1.0/1000.0, Unit::L, "l", Unit::M3, "m3", Quantity::Volume, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Volume);
|
||||
|
||||
// Test voltage unit: v
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Voltage);
|
||||
fill_with_units_from(Quantity::Voltage, &from_set);
|
||||
fill_with_units_from(Quantity::Voltage, &to_set);
|
||||
|
||||
test_si_convert(1, 1, Unit::Volt, "v", Unit::Volt, "v", Quantity::Voltage, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Voltage);
|
||||
|
||||
// Test power unit: kw
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Power);
|
||||
fill_with_units_from(Quantity::Power, &from_set);
|
||||
fill_with_units_from(Quantity::Power, &to_set);
|
||||
|
||||
test_si_convert(1, 1, Unit::KW, "kw", Unit::KW, "kw", Quantity::Power, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Power);
|
||||
|
||||
// Test volume flow units: m3h lh
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Flow);
|
||||
fill_with_units_from(Quantity::Flow, &from_set);
|
||||
fill_with_units_from(Quantity::Flow, &to_set);
|
||||
|
||||
test_si_convert(1, 1000.0, Unit::M3H, "m3h", Unit::LH, "lh", Quantity::Flow, &from_set, &to_set);
|
||||
test_si_convert(1000.0, 1.0, Unit::LH, "lh", Unit::M3H, "m3h", Quantity::Flow, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Flow);
|
||||
|
||||
// Test amount of substance: mol
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::AmountOfSubstance);
|
||||
fill_with_units_from(Quantity::AmountOfSubstance, &from_set);
|
||||
fill_with_units_from(Quantity::AmountOfSubstance, &to_set);
|
||||
|
||||
test_si_convert(1.1717, 1.1717, Unit::MOL, "mol", Unit::MOL, "mol", Quantity::AmountOfSubstance, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::AmountOfSubstance);
|
||||
|
||||
// Test luminous intensity: cd
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::LuminousIntensity);
|
||||
fill_with_units_from(Quantity::LuminousIntensity, &from_set);
|
||||
fill_with_units_from(Quantity::LuminousIntensity, &to_set);
|
||||
|
||||
test_si_convert(1.1717, 1.1717, Unit::CD, "cd", Unit::CD, "cd", Quantity::LuminousIntensity, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::LuminousIntensity);
|
||||
|
||||
// Test relative humidity: rh
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::RelativeHumidity);
|
||||
fill_with_units_from(Quantity::RelativeHumidity, &from_set);
|
||||
fill_with_units_from(Quantity::RelativeHumidity, &to_set);
|
||||
|
||||
test_si_convert(1.1717, 1.1717, Unit::RH, "rh", Unit::RH, "rh", Quantity::RelativeHumidity, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::RelativeHumidity);
|
||||
|
||||
// Test heat cost allocation: hca
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::HCA);
|
||||
fill_with_units_from(Quantity::HCA, &from_set);
|
||||
fill_with_units_from(Quantity::HCA, &to_set);
|
||||
|
||||
test_si_convert(11717, 11717, Unit::HCA, "hca", Unit::HCA, "hca", Quantity::HCA, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::HCA);
|
||||
|
||||
// Test pressure: bar pa
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Pressure);
|
||||
fill_with_units_from(Quantity::Pressure, &from_set);
|
||||
fill_with_units_from(Quantity::Pressure, &to_set);
|
||||
|
||||
test_si_convert(1.1717, 117170, Unit::BAR, "bar", Unit::PA, "pa", Quantity::Pressure, &from_set, &to_set);
|
||||
test_si_convert(1.1717, 1.1717e-05, Unit::PA, "pa", Unit::BAR, "bar", Quantity::Pressure, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Pressure);
|
||||
|
||||
// Test frequency: hz
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Frequency);
|
||||
fill_with_units_from(Quantity::Frequency, &from_set);
|
||||
fill_with_units_from(Quantity::Frequency, &to_set);
|
||||
|
||||
test_si_convert(440, 440, Unit::HZ, "hz", Unit::HZ, "hz", Quantity::Frequency, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Frequency);
|
||||
|
||||
// Test counter: counter
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
q_set.erase(Quantity::Counter);
|
||||
fill_with_units_from(Quantity::Counter, &from_set);
|
||||
fill_with_units_from(Quantity::Counter, &to_set);
|
||||
|
||||
test_si_convert(2211717, 2211717, Unit::COUNTER, "counter", Unit::COUNTER, "counter", Quantity::Counter, &from_set, &to_set);
|
||||
|
||||
check_units_tested(from_set, to_set, Quantity::Counter);
|
||||
|
||||
// Test point in time units: ut utc lt
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// I do not know how to handle the point in time units yet.
|
||||
// Mark them as tested....
|
||||
q_set.erase(Quantity::PointInTime);
|
||||
|
||||
// Test text unit: text
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// I do not know how to handle the text unit yet.
|
||||
// Mark it as tested....
|
||||
q_set.erase(Quantity::Text);
|
||||
|
||||
check_quantities_tested(q_set);
|
||||
}
|
||||
|
||||
void test_formulas_building()
|
||||
{
|
||||
unique_ptr<FormulaImplementation> f = unique_ptr<FormulaImplementation>(new FormulaImplementation());
|
||||
|
||||
assert(f->doConstant(Unit::KWH, 17));
|
||||
assert(f->doConstant(Unit::KWH, 1));
|
||||
assert(f->doAddition());
|
||||
f->doConstant(Unit::KWH, 17);
|
||||
f->doConstant(Unit::KWH, 1);
|
||||
f->doAddition();
|
||||
double v = f->calculate(Unit::KWH);
|
||||
if (v != 18.0)
|
||||
{
|
||||
|
@ -1612,7 +2032,7 @@ void test_formulas_building()
|
|||
}
|
||||
|
||||
f->clear();
|
||||
assert(f->doConstant(Unit::KWH, 10));
|
||||
f->doConstant(Unit::KWH, 10);
|
||||
v = f->calculate(Unit::MJ);
|
||||
if (v != 36.0)
|
||||
{
|
||||
|
@ -1620,9 +2040,9 @@ void test_formulas_building()
|
|||
}
|
||||
|
||||
f->clear();
|
||||
assert(f->doConstant(Unit::GJ, 10));
|
||||
assert(f->doConstant(Unit::MJ, 10));
|
||||
assert(f->doAddition());
|
||||
f->doConstant(Unit::GJ, 10);
|
||||
f->doConstant(Unit::MJ, 10);
|
||||
f->doAddition();
|
||||
v = f->calculate(Unit::GJ);
|
||||
if (v != 10.01)
|
||||
{
|
||||
|
@ -1630,11 +2050,11 @@ void test_formulas_building()
|
|||
}
|
||||
|
||||
f->clear();
|
||||
assert(f->doConstant(Unit::C, 10));
|
||||
assert(f->doConstant(Unit::C, 20));
|
||||
assert(f->doAddition());
|
||||
assert(f->doConstant(Unit::C, 22));
|
||||
assert(f->doAddition());
|
||||
f->doConstant(Unit::C, 10);
|
||||
f->doConstant(Unit::C, 20);
|
||||
f->doAddition();
|
||||
f->doConstant(Unit::C, 22);
|
||||
f->doAddition();
|
||||
v = f->calculate(Unit::C);
|
||||
if (v != 52)
|
||||
{
|
||||
|
@ -1645,6 +2065,7 @@ void test_formulas_building()
|
|||
|
||||
{
|
||||
MeterInfo mi;
|
||||
assert(lookupDriverInfo("multical21"));
|
||||
mi.parse("testur", "multical21", "12345678", "");
|
||||
shared_ptr<Meter> meter = createMeter(&mi);
|
||||
FieldInfo *fi_flow = meter->findFieldInfo("flow_temperature", Quantity::Temperature);
|
||||
|
@ -1665,7 +2086,7 @@ void test_formulas_building()
|
|||
|
||||
f->clear();
|
||||
|
||||
assert(f->doField(Unit::C, meter.get(), fi_flow));
|
||||
f->doField(Unit::C, meter.get(), fi_flow);
|
||||
v = f->calculate(Unit::C);
|
||||
if (v != 31)
|
||||
{
|
||||
|
@ -1674,9 +2095,9 @@ void test_formulas_building()
|
|||
|
||||
f->clear();
|
||||
|
||||
assert(f->doField(Unit::C, meter.get(), fi_flow));
|
||||
assert(f->doField(Unit::C, meter.get(), fi_ext));
|
||||
assert(f->doAddition());
|
||||
f->doField(Unit::C, meter.get(), fi_flow);
|
||||
f->doField(Unit::C, meter.get(), fi_ext);
|
||||
f->doAddition();
|
||||
v = f->calculate(Unit::C);
|
||||
if (v != 50)
|
||||
{
|
||||
|
@ -1686,7 +2107,7 @@ void test_formulas_building()
|
|||
|
||||
// Check that trying to add a field reference expecting a non-convertible unit, will fail!
|
||||
f->clear();
|
||||
assert(false == f->doField(Unit::M3, meter.get(), fi_flow));
|
||||
// assert(false == f->doField(Unit::M3, meter.get(), fi_flow));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1717,11 +2138,11 @@ void test_formulas_building()
|
|||
|
||||
f->clear();
|
||||
|
||||
assert(f->doField(Unit::KW, meter.get(), fi_p1));
|
||||
assert(f->doField(Unit::KW, meter.get(), fi_p2));
|
||||
assert(f->doAddition());
|
||||
assert(f->doField(Unit::KW, meter.get(), fi_p3));
|
||||
assert(f->doAddition());
|
||||
f->doField(Unit::KW, meter.get(), fi_p1);
|
||||
f->doField(Unit::KW, meter.get(), fi_p2);
|
||||
f->doAddition();
|
||||
f->doField(Unit::KW, meter.get(), fi_p3);
|
||||
f->doAddition();
|
||||
|
||||
v = f->calculate(Unit::KW);
|
||||
if (v != 0.21679)
|
||||
|
@ -1759,7 +2180,23 @@ void test_formula_value(FormulaImplementation *f, Meter *m, string formula, doub
|
|||
}
|
||||
}
|
||||
|
||||
void test_formulas_parsing()
|
||||
void test_formula_error(FormulaImplementation *f, Meter *m, string formula, Unit unit, string errors)
|
||||
{
|
||||
f->clear();
|
||||
|
||||
bool ok = f->parse(m, formula);
|
||||
string es = f->errors();
|
||||
if (es != errors)
|
||||
{
|
||||
printf("ERROR when parsing \"%s\"\nExpected errors:\n%sBut got errors:\n%s",
|
||||
formula.c_str(),
|
||||
errors.c_str(),
|
||||
f->errors().c_str());
|
||||
}
|
||||
assert(!ok);
|
||||
}
|
||||
|
||||
void test_formulas_parsing_1()
|
||||
{
|
||||
{
|
||||
MeterInfo mi;
|
||||
|
@ -1794,14 +2231,61 @@ void test_formulas_parsing()
|
|||
|
||||
test_formula_tree(f.get(), meter.get(),
|
||||
"5 c + 7 c + 10 c",
|
||||
"<ADD <ADD <CONST 5 c> <CONST 7 c> > <CONST 10 c> >");
|
||||
"<ADD <ADD <CONST 5 c[1k+273.15]Temperature> <CONST 7 c[1k+273.15]Temperature> > <CONST 10 c[1k+273.15]Temperature> >");
|
||||
|
||||
test_formula_tree(f.get(), meter.get(),
|
||||
"(5 c + 7 c) + 10 c",
|
||||
"<ADD <ADD <CONST 5 c> <CONST 7 c> > <CONST 10 c> >");
|
||||
"<ADD <ADD <CONST 5 c[1k+273.15]Temperature> <CONST 7 c[1k+273.15]Temperature> > <CONST 10 c[1k+273.15]Temperature> >");
|
||||
|
||||
test_formula_tree(f.get(), meter.get(),
|
||||
"5 c + (7 c + 10 c)",
|
||||
"<ADD <CONST 5 c> <ADD <CONST 7 c> <CONST 10 c> > >");
|
||||
"<ADD <CONST 5 c[1k+273.15]Temperature> <ADD <CONST 7 c[1k+273.15]Temperature> <CONST 10 c[1k+273.15]Temperature> > >");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void test_formulas_parsing_2()
|
||||
{
|
||||
{
|
||||
MeterInfo mi;
|
||||
mi.parse("testur", "em24", "66666666", "");
|
||||
shared_ptr<Meter> meter = createMeter(&mi);
|
||||
|
||||
vector<uchar> frame;
|
||||
hex2bin("35442D2C6666666633028D2070806A0520B4D378_0405F208000004FB82753F00000004853C0000000004FB82F53CCA01000001FD1722", &frame);
|
||||
|
||||
Telegram t;
|
||||
MeterKeys mk;
|
||||
t.parse(frame, &mk, true);
|
||||
|
||||
string id;
|
||||
bool match;
|
||||
meter->handleTelegram(t.about, frame, true, &id, &match, &t);
|
||||
|
||||
unique_ptr<FormulaImplementation> f = unique_ptr<FormulaImplementation>(new FormulaImplementation());
|
||||
|
||||
test_formula_value(f.get(), meter.get(),
|
||||
"total_energy_consumption_kwh + 18 kwh",
|
||||
247,
|
||||
Unit::KWH);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void test_formulas_parsing_3()
|
||||
{
|
||||
{
|
||||
MeterInfo mi;
|
||||
mi.parse("testur", "em24", "66666666", "");
|
||||
|
||||
auto meter = createMeter(&mi);
|
||||
auto formula = unique_ptr<FormulaImplementation>(new FormulaImplementation());
|
||||
|
||||
test_formula_error(formula.get(), meter.get(),
|
||||
"10 kwh + 20 kw", Unit::KWH,
|
||||
"Cannot add kwh[3.6×10⁶kgm²s⁻²]Energy to kw[1000kgm²s⁻³]Power!\n"
|
||||
"10 kwh + 20 kw\n"
|
||||
" ^~~~~\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ void handleBitToString(Rule& rule, string &out_s, uint64_t bits)
|
|||
{
|
||||
// Oups, there are set bits that we have not handled....
|
||||
string tmp;
|
||||
strprintf(tmp, "%s_%X", rule.name.c_str(), bits);
|
||||
strprintf(&tmp, "%s_%X", rule.name.c_str(), bits);
|
||||
s += tmp+" ";
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ void handleIndexToString(Rule& rule, string &out_s, uint64_t bits)
|
|||
if ((~rule.mask & m.from) != 0)
|
||||
{
|
||||
string tmp;
|
||||
strprintf(tmp, "BAD_RULE_%s(from=0x%x mask=0x%x)", rule.name.c_str(), m.from, rule.mask);
|
||||
strprintf(&tmp, "BAD_RULE_%s(from=0x%x mask=0x%x)", rule.name.c_str(), m.from, rule.mask);
|
||||
s += tmp+" ";
|
||||
}
|
||||
uint64_t from = m.from & rule.mask; // Better safe than sorry.
|
||||
|
@ -104,7 +104,7 @@ void handleIndexToString(Rule& rule, string &out_s, uint64_t bits)
|
|||
{
|
||||
// Oups, this index has not been found.
|
||||
string tmp;
|
||||
strprintf(tmp, "%s_%X", rule.name.c_str(), bits);
|
||||
strprintf(&tmp, "%s_%X", rule.name.c_str(), bits);
|
||||
s += tmp+" ";
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ void handleDecimalsToString(Rule& rule, string &out_s, uint64_t bits)
|
|||
if ((m.from - (m.from % rule.mask)) != 0)
|
||||
{
|
||||
string tmp;
|
||||
strprintf(tmp, "BAD_RULE_%s(from=%d modulomask=%d)", rule.name.c_str(), m.from, rule.mask);
|
||||
strprintf(&tmp, "BAD_RULE_%s(from=%d modulomask=%d)", rule.name.c_str(), m.from, rule.mask);
|
||||
s += tmp+" ";
|
||||
}
|
||||
int num = m.from % rule.mask; // Better safe than sorry.
|
||||
|
@ -142,7 +142,7 @@ void handleDecimalsToString(Rule& rule, string &out_s, uint64_t bits)
|
|||
{
|
||||
// Oups, this number has not been fully understood.
|
||||
string tmp;
|
||||
strprintf(tmp, "%s_%d", rule.name.c_str(), number);
|
||||
strprintf(&tmp, "%s_%d", rule.name.c_str(), number);
|
||||
s += tmp+" ";
|
||||
}
|
||||
|
||||
|
|
227
src/units.cc
227
src/units.cc
|
@ -54,8 +54,47 @@ using namespace std;
|
|||
X(K, C, {vto=vfrom-273.15;}) \
|
||||
X(C, F, {vto=(vfrom*9.0/5.0)+32.0;}) \
|
||||
X(F, C, {vto=(vfrom-32)*5.0/9.0;}) \
|
||||
X(PA, BAR,{vto=vfrom/100000.0;}) \
|
||||
X(BAR, PA, {vto=vfrom*100000.0;}) \
|
||||
|
||||
|
||||
#define LIST_OF_SI_CONVERSIONS \
|
||||
X(Second,1.0,0,SI_S(1)) \
|
||||
X(M,1.0,0,SI_M(1)) \
|
||||
X(KG,1.0,0,SI_KG(1)) \
|
||||
X(Ampere,1.0,0,SI_A(1)) \
|
||||
X(K,1.0,0,SI_K(1)) \
|
||||
X(MOL,1.0,0,SI_MOL(1)) \
|
||||
X(CD,1.0,0,SI_CD(1)) \
|
||||
X(KWH, 3.6e+06, 0, SI_KG(1)|SI_M(2)|SI_S(-2)) \
|
||||
X(MJ, 1.0e+06, 0, SI_KG(1)|SI_M(2)|SI_S(-2)) \
|
||||
X(GJ, 1.0e+09, 0, SI_KG(1)|SI_M(2)|SI_S(-2)) \
|
||||
X(KVARH,3.6e+06, 0, SI_KG(1)|SI_M(2)|SI_S(-2)) \
|
||||
X(KVAH, 3.6e+06, 0, SI_KG(1)|SI_M(2)|SI_S(-2)) \
|
||||
X(M3C, 1.0, 0, SI_M(3)|SI_K(1)) \
|
||||
X(M3, 1.0, 0, SI_M(3)) \
|
||||
X(L, 1.0/1000.0, 0, SI_M(3)) \
|
||||
X(KW, 1000.0, 0, SI_KG(1)|SI_M(2)|SI_S(-3)) \
|
||||
X(M3H, 3600.0, 0, SI_M(3)|SI_S(-1)) \
|
||||
X(LH, 3.600, 0, SI_M(3)|SI_S(-1)) \
|
||||
X(C, 1.0, 273.15, SI_K(1)) \
|
||||
X(F, (5.0/9.0), (-32.0+(273.15*9.0/5.0)), SI_K(1)) \
|
||||
X(RH, 1.0,0,0) \
|
||||
X(HCA, 1.0,0,0) \
|
||||
X(TXT, 1.0,0,0) \
|
||||
X(COUNTER, 1.0,0,0) \
|
||||
X(Minute, 60.0, 0, SI_S(1)) \
|
||||
X(Hour, 3600.0, 0, SI_S(1)) \
|
||||
X(Day, 3600.0*24, 0, SI_S(1)) \
|
||||
X(Year, 3600.0*24*365.2425, 0, SI_S(1)) \
|
||||
X(DateTimeUT,1.0,0,SI_S(1)) \
|
||||
X(DateTimeUTC,1.0,0,SI_S(1)) \
|
||||
X(DateTimeLT,1.0,0,SI_S(1)) \
|
||||
X(Volt, 1.0, 0, SI_KG(1)|SI_M(2)|SI_S(-3)|SI_A(-1)) \
|
||||
X(HZ, 1.0, 0, SI_S(-1)) \
|
||||
X(PA, 1.0, 0, SI_KG(1)|SI_M(-1)|SI_S(-2)) \
|
||||
X(BAR, 100000.0, 0, SI_KG(1)|SI_M(-1)|SI_S(-2))
|
||||
|
||||
bool canConvert(Unit ufrom, Unit uto)
|
||||
{
|
||||
if (ufrom == uto) return true;
|
||||
|
@ -82,12 +121,33 @@ LIST_OF_CONVERSIONS
|
|||
return 0;
|
||||
}
|
||||
|
||||
Unit whenMultiplied(Unit left, Unit right)
|
||||
bool canConvert(SIUnit &ufrom, SIUnit &uto)
|
||||
{
|
||||
return ufrom.sameExponents(uto);
|
||||
}
|
||||
|
||||
double convert(double vfrom, SIUnit &ufrom, SIUnit &uto)
|
||||
{
|
||||
assert(canConvert(ufrom, uto));
|
||||
|
||||
return ufrom.convert(vfrom, uto);
|
||||
}
|
||||
|
||||
double SIUnit::convert(double val, SIUnit &to)
|
||||
{
|
||||
return ((val+offset_)*scale_)/to.scale_-to.offset_;
|
||||
}
|
||||
|
||||
void SIUnit::mul(SIUnit &m)
|
||||
{
|
||||
}
|
||||
|
||||
SIUnit whenMultiplied(SIUnit left, SIUnit right)
|
||||
{
|
||||
return Unit::Unknown;
|
||||
}
|
||||
|
||||
double multiply(double l, Unit left, double r, Unit right)
|
||||
double multiply(double l, SIUnit left, double r, SIUnit right)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -103,10 +163,14 @@ LIST_OF_UNITS
|
|||
|
||||
Quantity toQuantity(Unit u)
|
||||
{
|
||||
#define X(cname,lcname,hrname,quantity,explanation) if (u == Unit::cname) return Quantity::quantity;
|
||||
switch(u)
|
||||
{
|
||||
#define X(cname,lcname,hrname,quantity,explanation) case Unit::cname: return Quantity::quantity;
|
||||
LIST_OF_UNITS
|
||||
#undef X
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return Quantity::Unknown;
|
||||
}
|
||||
|
||||
|
@ -237,3 +301,158 @@ err:
|
|||
*u = Unit::Unknown;
|
||||
return false;
|
||||
}
|
||||
|
||||
SIUnit::SIUnit(Unit u)
|
||||
{
|
||||
quantity_ = toQuantity(u);
|
||||
|
||||
switch (u)
|
||||
{
|
||||
#define X(cname,si_scale,si_offset,si_exponents) \
|
||||
case Unit::cname: scale_ = si_scale; offset_ = si_offset; exponents_ = si_exponents; break;
|
||||
LIST_OF_SI_CONVERSIONS
|
||||
#undef X
|
||||
default:
|
||||
quantity_ = Quantity::Unknown;
|
||||
scale_ = 0;
|
||||
offset_ = 0;
|
||||
exponents_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SIUnit::SIUnit(string s)
|
||||
{
|
||||
}
|
||||
|
||||
Unit SIUnit::asUnit()
|
||||
{
|
||||
#define X(cname,si_scale,si_offset,si_exponents) \
|
||||
if ((scale_ == si_scale) && (offset_ == si_offset) && (exponents_ == (si_exponents)) && quantity_ == toQuantity(Unit::cname)) return Unit::cname;
|
||||
LIST_OF_SI_CONVERSIONS
|
||||
#undef X
|
||||
|
||||
return Unit::Unknown;
|
||||
}
|
||||
|
||||
Unit SIUnit::asUnit(Quantity q)
|
||||
{
|
||||
#define X(cname,si_scale,si_offset,si_exponents) \
|
||||
if ((scale_ == si_scale) && (offset_ == si_offset) && (exponents_ == (si_exponents)) && q == toQuantity(Unit::cname)) return Unit::cname;
|
||||
LIST_OF_SI_CONVERSIONS
|
||||
#undef X
|
||||
|
||||
return Unit::Unknown;
|
||||
}
|
||||
|
||||
string super(uchar c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '-': return "⁻";
|
||||
case '+': return "⁺";
|
||||
case '0': return "⁰";
|
||||
case '1': return "¹";
|
||||
case '2': return "²";
|
||||
case '3': return "³";
|
||||
case '4': return "⁴";
|
||||
case '5': return "⁵";
|
||||
case '6': return "⁶";
|
||||
case '7': return "⁷";
|
||||
case '8': return "⁸";
|
||||
case '9': return "⁹";
|
||||
}
|
||||
assert(false);
|
||||
return "?";
|
||||
}
|
||||
|
||||
string to_superscript(int8_t n)
|
||||
{
|
||||
string out;
|
||||
|
||||
char buf[5];
|
||||
sprintf(buf, "%d", n);
|
||||
|
||||
for (int i=0; i<5; ++i)
|
||||
{
|
||||
if (buf[i] == 0) break;
|
||||
out += super(buf[i]);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
string to_superscript(string &s)
|
||||
{
|
||||
string out;
|
||||
|
||||
size_t i = 0;
|
||||
size_t len = s.length();
|
||||
|
||||
// Skip non-superscript number.
|
||||
while (i<len && (s[i] == '-' || s[i] == '.' || (s[i] >= '0' && s[i] <= '9')))
|
||||
{
|
||||
out += s[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
while (i<len && (s[i] == 'e' || s[i] == 'E'))
|
||||
{
|
||||
i++;
|
||||
out += "×10";
|
||||
}
|
||||
|
||||
bool found_start = false;
|
||||
while (i<len)
|
||||
{
|
||||
// Remove leading +0
|
||||
if (!found_start && s[i] != '+' && s[i] != '0') found_start = true;
|
||||
|
||||
if (found_start)
|
||||
{
|
||||
out += super(s[i]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
#define DO_UNIT(u) if (u != 0) { if (r.length()>0) { } r += #u; if (u != 1) { r += to_superscript(u); } }
|
||||
|
||||
string SIUnit::str()
|
||||
{
|
||||
string r;
|
||||
int8_t s = SI_GET_S(exponents_);
|
||||
int8_t m = SI_GET_M(exponents_);
|
||||
int8_t kg = SI_GET_KG(exponents_);
|
||||
int8_t a = SI_GET_A(exponents_);
|
||||
int8_t k = SI_GET_K(exponents_);
|
||||
int8_t mol = SI_GET_MOL(exponents_);
|
||||
int8_t cd = SI_GET_CD(exponents_);
|
||||
|
||||
DO_UNIT(mol);
|
||||
DO_UNIT(cd);
|
||||
DO_UNIT(kg);
|
||||
DO_UNIT(k);
|
||||
DO_UNIT(m);
|
||||
DO_UNIT(s);
|
||||
DO_UNIT(a);
|
||||
|
||||
string num = tostrprintf("%g", scale_);
|
||||
string offset;
|
||||
if (offset_ > 0) offset = tostrprintf("+%g", offset_);
|
||||
if (offset_ < 0) offset = tostrprintf("%g", offset_);
|
||||
|
||||
num = to_superscript(num);
|
||||
return num+r+offset;
|
||||
}
|
||||
|
||||
|
||||
string SIUnit::info()
|
||||
{
|
||||
Unit u = asUnit();
|
||||
return tostrprintf("%s[%s]%s",
|
||||
unitToStringLowerCase(u).c_str(),
|
||||
str().c_str(),
|
||||
toString(quantity_));
|
||||
}
|
||||
|
|
142
src/units.h
142
src/units.h
|
@ -22,54 +22,63 @@
|
|||
#include<vector>
|
||||
|
||||
#define LIST_OF_QUANTITIES \
|
||||
X(Time,Hour) \
|
||||
X(Length,M) \
|
||||
X(Mass,KG) \
|
||||
X(Amperage,Ampere) \
|
||||
X(Temperature,C) \
|
||||
X(AmountOfSubstance,MOL) \
|
||||
X(LuminousIntensity,CD) \
|
||||
X(Energy,KWH) \
|
||||
X(Reactive_Energy,KVARH) \
|
||||
X(Apparent_Energy,KVAH) \
|
||||
X(Power,KW) \
|
||||
X(Volume,M3) \
|
||||
X(Flow,M3H) \
|
||||
X(Temperature,C) \
|
||||
X(RelativeHumidity,RH) \
|
||||
X(HCA,HCA) \
|
||||
X(Text,TXT) \
|
||||
X(Counter,COUNTER) \
|
||||
X(Time,Hour) \
|
||||
X(PointInTime,DateTimeLT) \
|
||||
X(Voltage,Volt) \
|
||||
X(Current,Ampere) \
|
||||
X(Frequency,Hz) \
|
||||
X(Frequency,HZ) \
|
||||
X(Pressure,BAR)
|
||||
|
||||
#define LIST_OF_UNITS \
|
||||
X(KWH,kwh,"kWh",Energy,"kilo Watt hour") \
|
||||
X(MJ,mj,"MJ",Energy,"Mega Joule") \
|
||||
X(GJ,gj,"GJ",Energy,"Giga Joule") \
|
||||
X(KVARH,kvarh,"kVARh",Reactive_Energy,"kilo volt amperes reactive hour") \
|
||||
X(KVAH,kvah,"kVAh",Apparent_Energy,"kilo volt amperes hour") \
|
||||
X(M3C,m3c,"m³°C",Energy,"cubic meter celsius") \
|
||||
X(M3,m3,"m³",Volume,"cubic meter") \
|
||||
X(L,l,"l",Volume,"litre") \
|
||||
X(KW,kw,"kW",Power,"kilo Watt") \
|
||||
X(M3H,m3h,"m³/h",Flow,"cubic meters per hour") \
|
||||
X(LH,lh,"l/h",Flow,"liters per hour") \
|
||||
X(C,c,"°C",Temperature,"celsius") \
|
||||
X(F,f,"°F",Temperature,"fahrenheit") \
|
||||
X(K,k,"K",Temperature,"kelvin") \
|
||||
X(RH,rh,"RH",RelativeHumidity,"relative humidity") \
|
||||
X(HCA,hca,"hca",HCA,"heat cost allocation") \
|
||||
X(TXT,txt,"txt",Text,"text") \
|
||||
X(COUNTER,counter,"counter",Counter,"counter") \
|
||||
X(Second,s,"s",Time,"second") \
|
||||
X(Minute,m,"m",Time,"minute") \
|
||||
X(Hour,h,"h",Time,"hour") \
|
||||
X(Day,d,"d",Time,"day") \
|
||||
X(Year,y,"y",Time,"year") \
|
||||
X(DateTimeUT,ut,"ut",PointInTime,"unix timestamp") \
|
||||
X(DateTimeUTC,utc,"utc",PointInTime,"coordinated universal time") \
|
||||
X(DateTimeLT,lt,"lt",PointInTime,"local time") \
|
||||
X(Volt,v,"V",Voltage,"volt") \
|
||||
X(Ampere,a,"A",Current,"ampere") \
|
||||
X(Hz,hz,"Hz",Frequency,"hz") \
|
||||
X(Second,s,"s",Time,"second") \
|
||||
X(M,m,"m",Length,"meter") \
|
||||
X(KG,kg,"kg",Mass,"kilogram") \
|
||||
X(Ampere,a,"A",Amperage,"ampere") \
|
||||
X(K,k,"K",Temperature,"kelvin") \
|
||||
X(MOL,mol,"mol",AmountOfSubstance,"mole") \
|
||||
X(CD,cd,"cd",LuminousIntensity,"candela") \
|
||||
X(KWH,kwh,"kWh",Energy,"kilo Watt hour") \
|
||||
X(MJ,mj,"MJ",Energy,"Mega Joule") \
|
||||
X(GJ,gj,"GJ",Energy,"Giga Joule") \
|
||||
X(KVARH,kvarh,"kVARh",Reactive_Energy,"kilo volt amperes reactive hour") \
|
||||
X(KVAH,kvah,"kVAh",Apparent_Energy,"kilo volt amperes hour") \
|
||||
X(M3C,m3c,"m³°C",Energy,"cubic meter celsius") \
|
||||
X(M3,m3,"m³",Volume,"cubic meter") \
|
||||
X(L,l,"l",Volume,"litre") \
|
||||
X(KW,kw,"kW",Power,"kilo Watt") \
|
||||
X(M3H,m3h,"m³/h",Flow,"cubic meters per hour") \
|
||||
X(LH,lh,"l/h",Flow,"liters per hour") \
|
||||
X(C,c,"°C",Temperature,"celsius") \
|
||||
X(F,f,"°F",Temperature,"fahrenheit") \
|
||||
X(RH,rh,"RH",RelativeHumidity,"relative humidity") \
|
||||
X(HCA,hca,"hca",HCA,"heat cost allocation") \
|
||||
X(TXT,txt,"txt",Text,"text") \
|
||||
X(COUNTER,counter,"counter",Counter,"counter") \
|
||||
X(Minute,min,"min",Time,"minute") \
|
||||
X(Hour,h,"h",Time,"hour") \
|
||||
X(Day,d,"d",Time,"day") \
|
||||
X(Year,y,"y",Time,"year") \
|
||||
X(DateTimeUT,ut,"ut",PointInTime,"unix timestamp") \
|
||||
X(DateTimeUTC,utc,"utc",PointInTime,"coordinated universal time") \
|
||||
X(DateTimeLT,lt,"lt",PointInTime,"local time") \
|
||||
X(Volt,v,"V",Voltage,"volt") \
|
||||
X(HZ,hz,"Hz",Frequency,"hz") \
|
||||
X(PA,pa,"pa",Pressure,"pascal") \
|
||||
X(BAR,bar,"bar",Pressure,"bar")
|
||||
|
||||
enum class Unit
|
||||
|
@ -80,6 +89,35 @@ LIST_OF_UNITS
|
|||
Unknown
|
||||
};
|
||||
|
||||
// The SIUnit is used inside formulas to verify the end result.
|
||||
// https://en.wikipedia.org/wiki/SI_derived_unit
|
||||
|
||||
#define SI_S_OFFSET 0
|
||||
#define SI_M_OFFSET 8
|
||||
#define SI_KG_OFFSET 16
|
||||
#define SI_A_OFFSET 24
|
||||
#define SI_K_OFFSET 32
|
||||
#define SI_MOL_OFFSET 40
|
||||
#define SI_CD_OFFSET 48
|
||||
|
||||
#define SI_X(x,y) (((uint64_t)(x & 0xff))<<y)
|
||||
#define SI_S(x) SI_X(x,SI_S_OFFSET)
|
||||
#define SI_M(x) SI_X(x,SI_M_OFFSET)
|
||||
#define SI_KG(x) SI_X(x,SI_KG_OFFSET)
|
||||
#define SI_A(x) SI_X(x,SI_A_OFFSET)
|
||||
#define SI_K(x) SI_X(x,SI_K_OFFSET)
|
||||
#define SI_MOL(x) SI_X(x,SI_MOL_OFFSET)
|
||||
#define SI_CD(x) SI_X(x,SI_CD_OFFSET)
|
||||
|
||||
#define SI_GET_X(x,y) ((int8_t)(x>>y))
|
||||
#define SI_GET_S(x) SI_GET_X(x,SI_S_OFFSET)
|
||||
#define SI_GET_M(x) SI_GET_X(x,SI_M_OFFSET)
|
||||
#define SI_GET_KG(x) SI_GET_X(x,SI_KG_OFFSET)
|
||||
#define SI_GET_A(x) SI_GET_X(x,SI_A_OFFSET)
|
||||
#define SI_GET_K(x) SI_GET_X(x,SI_K_OFFSET)
|
||||
#define SI_GET_MOL(x) SI_GET_X(x,SI_MOL_OFFSET)
|
||||
#define SI_GET_CD(x) SI_GET_X(x,SI_CD_OFFSET)
|
||||
|
||||
enum class Quantity
|
||||
{
|
||||
#define X(quantity,default_unit) quantity,
|
||||
|
@ -88,6 +126,41 @@ LIST_OF_QUANTITIES
|
|||
Unknown
|
||||
};
|
||||
|
||||
struct SIUnit
|
||||
{
|
||||
// Transform a double,double,uint64_t into an SIUnit.
|
||||
// The exp can be created compile time like this: SIUNIT(3.6E6, 0, SI_KG(1)|SI_M(2)|SI_S(-2)) which is kwh.
|
||||
SIUnit(Quantity q, double scale, double offset, uint64_t exponents) :
|
||||
quantity_(q), scale_(scale), offset_(offset), exponents_(exponents) {}
|
||||
// Transform a named unit into an SIUnit.
|
||||
SIUnit(Unit);
|
||||
// Parse string like: 3.6E106*kg*m^2*s^-3*a^−1 (ie volt)
|
||||
SIUnit(std::string s);
|
||||
// Return the known unit that best matches the SIUnit, or Unit::Unknown if none.
|
||||
Unit asUnit();
|
||||
// Return the known unit that best matches the SIUnit and the quantity, or Unit::Unknown if none.
|
||||
Unit asUnit(Quantity q);
|
||||
// Return the quantity that this unit is used for.
|
||||
Quantity quantity() { return quantity_; }
|
||||
// Return a string like 3.6⁶s⁻²m²kg
|
||||
std::string str();
|
||||
// Return a detailed string like: kwh[3.6⁶s⁻²m²kg]Energy
|
||||
std::string info();
|
||||
// Check if the exponents (ie units) are the same.
|
||||
bool sameExponents(SIUnit &to) { return exponents_ == to.exponents_; }
|
||||
// Convert value from this unit to another unit.
|
||||
double convert(double val, SIUnit &to);
|
||||
// Multiply this unit with another unit.
|
||||
void mul(SIUnit &m);
|
||||
|
||||
private:
|
||||
|
||||
Quantity quantity_;
|
||||
double scale_;
|
||||
double offset_; // The offset is in the same scale_ factor.
|
||||
uint64_t exponents_;
|
||||
};
|
||||
|
||||
bool canConvert(Unit from, Unit to);
|
||||
double convert(double v, Unit from, Unit to);
|
||||
Unit whenMultiplied(Unit left, Unit right);
|
||||
|
@ -109,4 +182,7 @@ Unit replaceWithConversionUnit(Unit u, std::vector<Unit> cs);
|
|||
|
||||
bool extractUnit(const std::string &s, std::string *vname, Unit *u);
|
||||
|
||||
bool canConvert(SIUnit &from, SIUnit &to);
|
||||
double convert(double v, SIUnit &from, SIUnit &to);
|
||||
|
||||
#endif
|
||||
|
|
154
src/util.cc
154
src/util.cc
|
@ -197,7 +197,7 @@ bool isHexStringFlex(const char* txt, bool *invalid)
|
|||
return isHexString(txt, invalid, false);
|
||||
}
|
||||
|
||||
bool isHexStringFlex(const std::string &txt, bool *invalid)
|
||||
bool isHexStringFlex(const string &txt, bool *invalid)
|
||||
{
|
||||
return isHexString(txt.c_str(), invalid, false);
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ bool isHexStringStrict(const char* txt, bool *invalid)
|
|||
return isHexString(txt, invalid, true);
|
||||
}
|
||||
|
||||
bool isHexStringStrict(const std::string &txt, bool *invalid)
|
||||
bool isHexStringStrict(const string &txt, bool *invalid)
|
||||
{
|
||||
return isHexString(txt.c_str(), invalid, true);
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ bool hex2bin(const char* src, vector<uchar> *target)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool hex2bin(string &src, vector<uchar> *target)
|
||||
bool hex2bin(const string &src, vector<uchar> *target)
|
||||
{
|
||||
return hex2bin(src.c_str(), target);
|
||||
}
|
||||
|
@ -251,8 +251,8 @@ bool hex2bin(vector<uchar> &src, vector<uchar> *target)
|
|||
|
||||
char const hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A','B','C','D','E','F'};
|
||||
|
||||
std::string bin2hex(const vector<uchar> &target) {
|
||||
std::string str;
|
||||
string bin2hex(const vector<uchar> &target) {
|
||||
string str;
|
||||
for (size_t i = 0; i < target.size(); ++i) {
|
||||
const char ch = target[i];
|
||||
str.append(&hex[(ch & 0xF0) >> 4], 1);
|
||||
|
@ -261,8 +261,8 @@ std::string bin2hex(const vector<uchar> &target) {
|
|||
return str;
|
||||
}
|
||||
|
||||
std::string bin2hex(vector<uchar>::iterator data, vector<uchar>::iterator end, int len) {
|
||||
std::string str;
|
||||
string bin2hex(vector<uchar>::iterator data, vector<uchar>::iterator end, int len) {
|
||||
string str;
|
||||
while (data != end && len-- > 0) {
|
||||
const char ch = *data;
|
||||
data++;
|
||||
|
@ -272,8 +272,8 @@ std::string bin2hex(vector<uchar>::iterator data, vector<uchar>::iterator end, i
|
|||
return str;
|
||||
}
|
||||
|
||||
std::string bin2hex(vector<uchar> &data, int offset, int len) {
|
||||
std::string str;
|
||||
string bin2hex(vector<uchar> &data, int offset, int len) {
|
||||
string str;
|
||||
vector<uchar>::iterator i = data.begin();
|
||||
i += offset;
|
||||
while (i != data.end() && len-- > 0) {
|
||||
|
@ -285,8 +285,8 @@ std::string bin2hex(vector<uchar> &data, int offset, int len) {
|
|||
return str;
|
||||
}
|
||||
|
||||
std::string safeString(vector<uchar> &target) {
|
||||
std::string str;
|
||||
string safeString(vector<uchar> &target) {
|
||||
string str;
|
||||
for (size_t i = 0; i < target.size(); ++i) {
|
||||
const char ch = target[i];
|
||||
if (ch >= 32 && ch < 127 && ch != '<' && ch != '>') {
|
||||
|
@ -307,20 +307,35 @@ string tostrprintf(const char* fmt, ...)
|
|||
char buf[4096];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, 4095, fmt, args);
|
||||
size_t n = vsnprintf(buf, 4096, fmt, args);
|
||||
assert(n < 4096);
|
||||
va_end(args);
|
||||
s = buf;
|
||||
return s;
|
||||
}
|
||||
|
||||
void strprintf(std::string &s, const char* fmt, ...)
|
||||
string tostrprintf(const string& fmt, ...)
|
||||
{
|
||||
string s;
|
||||
char buf[4096];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
size_t n = vsnprintf(buf, 4096, fmt.c_str(), args);
|
||||
assert(n < 4096);
|
||||
va_end(args);
|
||||
s = buf;
|
||||
return s;
|
||||
}
|
||||
|
||||
void strprintf(string *s, const char* fmt, ...)
|
||||
{
|
||||
char buf[4096];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, 4095, fmt, args);
|
||||
size_t n = vsnprintf(buf, 4096, fmt, args);
|
||||
assert(n < 4096);
|
||||
va_end(args);
|
||||
s = buf;
|
||||
*s = buf;
|
||||
}
|
||||
|
||||
void xorit(uchar *srca, uchar *srcb, uchar *dest, int len)
|
||||
|
@ -344,7 +359,7 @@ void shiftLeft(uchar *srca, uchar *srcb, int len)
|
|||
string format3fdot3f(double v)
|
||||
{
|
||||
string r;
|
||||
strprintf(r, "%3.3f", v);
|
||||
strprintf(&r, "%3.3f", v);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -370,7 +385,7 @@ void enableSyslog() {
|
|||
syslog_enabled_ = true;
|
||||
}
|
||||
|
||||
bool enableLogfile(string logfile, bool daemon)
|
||||
bool enableLogfile(const string& logfile, bool daemon)
|
||||
{
|
||||
log_file_ = logfile;
|
||||
logfile_enabled_ = true;
|
||||
|
@ -613,7 +628,7 @@ bool is_ascii(char c)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool isValidAlias(string alias)
|
||||
bool isValidAlias(const string& alias)
|
||||
{
|
||||
if (alias.length() == 0) return false;
|
||||
|
||||
|
@ -627,8 +642,10 @@ bool isValidAlias(string alias)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool isValidMatchExpression(string me, bool non_compliant)
|
||||
bool isValidMatchExpression(const string& s, bool non_compliant)
|
||||
{
|
||||
string me = s;
|
||||
|
||||
// Examples of valid match expressions:
|
||||
// 12345678
|
||||
// *
|
||||
|
@ -690,7 +707,7 @@ bool isValidMatchExpression(string me, bool non_compliant)
|
|||
return count <= 7;
|
||||
}
|
||||
|
||||
bool isValidMatchExpressions(string mes, bool non_compliant)
|
||||
bool isValidMatchExpressions(const string& mes, bool non_compliant)
|
||||
{
|
||||
vector<string> v = splitMatchExpressions(mes);
|
||||
|
||||
|
@ -701,7 +718,7 @@ bool isValidMatchExpressions(string mes, bool non_compliant)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool isValidId(string id, bool accept_non_compliant)
|
||||
bool isValidId(const string& id, bool accept_non_compliant)
|
||||
{
|
||||
|
||||
for (size_t i=0; i<id.length(); ++i)
|
||||
|
@ -717,8 +734,9 @@ bool isValidId(string id, bool accept_non_compliant)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool doesIdMatchExpression(string id, string match)
|
||||
bool doesIdMatchExpression(const string& s, string match)
|
||||
{
|
||||
string id = s;
|
||||
if (id.length() == 0) return false;
|
||||
|
||||
// Here we assume that the match expression has been
|
||||
|
@ -763,7 +781,7 @@ bool doesIdMatchExpression(string id, string match)
|
|||
return can_match;
|
||||
}
|
||||
|
||||
bool hasWildCard(string &mes)
|
||||
bool hasWildCard(const string& mes)
|
||||
{
|
||||
return mes.find('*') != string::npos;
|
||||
}
|
||||
|
@ -783,7 +801,7 @@ bool doesIdsMatchExpressions(vector<string> &ids, vector<string>& mes, bool *use
|
|||
return match;
|
||||
}
|
||||
|
||||
bool doesIdMatchExpressions(string id, vector<string>& mes, bool *used_wildcard)
|
||||
bool doesIdMatchExpressions(const string& id, vector<string>& mes, bool *used_wildcard)
|
||||
{
|
||||
bool found_match = false;
|
||||
bool found_negative_match = false;
|
||||
|
@ -850,7 +868,7 @@ bool doesIdMatchExpressions(string id, vector<string>& mes, bool *used_wildcard)
|
|||
return false;
|
||||
}
|
||||
|
||||
string toIdsCommaSeparated(std::vector<std::string> &ids)
|
||||
string toIdsCommaSeparated(vector<string> &ids)
|
||||
{
|
||||
string cs;
|
||||
for (string& s: ids)
|
||||
|
@ -862,7 +880,7 @@ string toIdsCommaSeparated(std::vector<std::string> &ids)
|
|||
return cs;
|
||||
}
|
||||
|
||||
bool isFrequency(std::string& fq)
|
||||
bool isFrequency(const string& fq)
|
||||
{
|
||||
int len = fq.length();
|
||||
if (len == 0) return false;
|
||||
|
@ -874,7 +892,7 @@ bool isFrequency(std::string& fq)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool isNumber(std::string& fq)
|
||||
bool isNumber(const string& fq)
|
||||
{
|
||||
int len = fq.length();
|
||||
if (len == 0) return false;
|
||||
|
@ -884,7 +902,7 @@ bool isNumber(std::string& fq)
|
|||
return true;
|
||||
}
|
||||
|
||||
vector<string> splitMatchExpressions(string& mes)
|
||||
vector<string> splitMatchExpressions(const string& mes)
|
||||
{
|
||||
vector<string> r;
|
||||
bool eof, err;
|
||||
|
@ -991,7 +1009,7 @@ bool checkIfDirExists(const char *dir)
|
|||
return false;
|
||||
}
|
||||
|
||||
void debugPayload(string intro, vector<uchar> &payload)
|
||||
void debugPayload(const string& intro, vector<uchar> &payload)
|
||||
{
|
||||
if (isDebugEnabled())
|
||||
{
|
||||
|
@ -1000,7 +1018,7 @@ void debugPayload(string intro, vector<uchar> &payload)
|
|||
}
|
||||
}
|
||||
|
||||
void debugPayload(string intro, vector<uchar> &payload, vector<uchar>::iterator &pos)
|
||||
void debugPayload(const string& intro, vector<uchar> &payload, vector<uchar>::iterator &pos)
|
||||
{
|
||||
if (isDebugEnabled())
|
||||
{
|
||||
|
@ -1082,7 +1100,7 @@ void padWithZeroesTo(vector<uchar> *content, size_t len, vector<uchar> *full_con
|
|||
}
|
||||
|
||||
static string space = " ";
|
||||
string padLeft(string input, int width)
|
||||
string padLeft(const string& input, int width)
|
||||
{
|
||||
int w = width-input.size();
|
||||
if (w < 0) return input;
|
||||
|
@ -1090,7 +1108,9 @@ string padLeft(string input, int width)
|
|||
return space.substr(0, w)+input;
|
||||
}
|
||||
|
||||
int parseTime(string time) {
|
||||
int parseTime(const string& s)
|
||||
{
|
||||
string time = s;
|
||||
int mul = 1;
|
||||
if (time.back() == 'h') {
|
||||
time.pop_back();
|
||||
|
@ -1174,7 +1194,7 @@ bool crc16_CCITT_check(uchar *data, uint16_t length)
|
|||
return crc == CRC16_GOOD_VALUE;
|
||||
}
|
||||
|
||||
bool listFiles(string dir, vector<string> *files)
|
||||
bool listFiles(const string& dir, vector<string> *files)
|
||||
{
|
||||
DIR *dp = NULL;
|
||||
struct dirent *dptr = NULL;
|
||||
|
@ -1204,7 +1224,7 @@ bool listFiles(string dir, vector<string> *files)
|
|||
return true;
|
||||
}
|
||||
|
||||
int loadFile(string file, vector<string> *lines)
|
||||
int loadFile(const string& file, vector<string> *lines)
|
||||
{
|
||||
char block[32768+1];
|
||||
vector<uchar> buf;
|
||||
|
@ -1246,7 +1266,7 @@ int loadFile(string file, vector<string> *lines)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool loadFile(string file, vector<char> *buf)
|
||||
bool loadFile(const string& file, vector<char> *buf)
|
||||
{
|
||||
int blocksize = 1024;
|
||||
char block[blocksize];
|
||||
|
@ -1412,7 +1432,7 @@ void addMonths(struct tm *date, int months)
|
|||
}
|
||||
else
|
||||
{
|
||||
day = std::min(date->tm_mday, get_days_in_month(year, month));
|
||||
day = min(date->tm_mday, get_days_in_month(year, month));
|
||||
}
|
||||
|
||||
date->tm_year = year;
|
||||
|
@ -1433,7 +1453,7 @@ const char* toString(AccessCheck ac)
|
|||
return "?";
|
||||
}
|
||||
|
||||
AccessCheck checkIfExistsAndHasAccess(string device)
|
||||
AccessCheck checkIfExistsAndHasAccess(const string& device)
|
||||
{
|
||||
struct stat device_sb;
|
||||
|
||||
|
@ -1498,12 +1518,12 @@ int countSetBits(int v)
|
|||
return n;
|
||||
}
|
||||
|
||||
bool startsWith(string &s, string &prefix)
|
||||
bool startsWith(const string& s, string &prefix)
|
||||
{
|
||||
return startsWith(s, prefix.c_str());
|
||||
}
|
||||
|
||||
bool startsWith(string &s, const char *prefix)
|
||||
bool startsWith(const string& s, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
if (s.length() < len) return false;
|
||||
|
@ -1511,7 +1531,7 @@ bool startsWith(string &s, const char *prefix)
|
|||
return !strncmp(&s[0], prefix, len);
|
||||
}
|
||||
|
||||
string makeQuotedJson(string &s)
|
||||
string makeQuotedJson(const string& s)
|
||||
{
|
||||
size_t p = s.find('=');
|
||||
string key, value;
|
||||
|
@ -1608,7 +1628,7 @@ bool hasBytes(int n, vector<uchar>::iterator &pos, vector<uchar> &frame)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool startsWith(string s, std::vector<uchar> &data)
|
||||
bool startsWith(const string& s, vector<uchar> &data)
|
||||
{
|
||||
if (s.length() > data.size()) return false;
|
||||
|
||||
|
@ -1719,7 +1739,7 @@ bool extract_single_period(char *tok, TimePeriod *tp)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool extract_periods(string periods, vector<TimePeriod> *period_structs)
|
||||
bool extract_periods(const string& periods, vector<TimePeriod> *period_structs)
|
||||
{
|
||||
if (periods.length() == 0) return false;
|
||||
|
||||
|
@ -1750,14 +1770,14 @@ bool extract_periods(string periods, vector<TimePeriod> *period_structs)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool isValidTimePeriod(std::string periods)
|
||||
bool isValidTimePeriod(const string& periods)
|
||||
{
|
||||
vector<TimePeriod> period_structs;
|
||||
bool ok = extract_periods(periods, &period_structs);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool isInsideTimePeriod(time_t now, std::string periods)
|
||||
bool isInsideTimePeriod(time_t now, string periods)
|
||||
{
|
||||
struct tm nowt {};
|
||||
localtime_r(&now, &nowt);
|
||||
|
@ -1819,13 +1839,15 @@ void setAlarmShells(vector<string> &alarm_shells)
|
|||
alarm_shells_ = alarm_shells;
|
||||
}
|
||||
|
||||
bool stringFoundCaseIgnored(string haystack, string needle)
|
||||
bool stringFoundCaseIgnored(const string& h, const string& n)
|
||||
{
|
||||
string haystack = h;
|
||||
string needle = n;
|
||||
// Modify haystack and needle, in place, to become lowercase.
|
||||
std::for_each(haystack.begin(), haystack.end(), [](char & c) {
|
||||
for_each(haystack.begin(), haystack.end(), [](char & c) {
|
||||
c = ::tolower(c);
|
||||
});
|
||||
std::for_each(needle.begin(), needle.end(), [](char & c) {
|
||||
for_each(needle.begin(), needle.end(), [](char & c) {
|
||||
c = ::tolower(c);
|
||||
});
|
||||
|
||||
|
@ -1833,12 +1855,12 @@ bool stringFoundCaseIgnored(string haystack, string needle)
|
|||
return haystack.find(needle) != string::npos;
|
||||
}
|
||||
|
||||
vector<string> splitString(string &s, char c)
|
||||
vector<string> splitString(const string &s, char c)
|
||||
{
|
||||
auto end = s.cend();
|
||||
auto start = end;
|
||||
|
||||
std::vector<std::string> v;
|
||||
vector<string> v;
|
||||
for (auto i = s.cbegin(); i != end; ++i)
|
||||
{
|
||||
if (*i != c)
|
||||
|
@ -1862,8 +1884,9 @@ vector<string> splitString(string &s, char c)
|
|||
return v;
|
||||
}
|
||||
|
||||
vector<string> splitDeviceString(string &s)
|
||||
vector<string> splitDeviceString(const string& ds)
|
||||
{
|
||||
string s = ds;
|
||||
string cmd;
|
||||
|
||||
// The CMD(...) might have colons inside.
|
||||
|
@ -1886,7 +1909,7 @@ vector<string> splitDeviceString(string &s)
|
|||
return r;
|
||||
}
|
||||
|
||||
uint32_t indexFromRtlSdrName(string &s)
|
||||
uint32_t indexFromRtlSdrName(const string& s)
|
||||
{
|
||||
size_t p = s.find('_');
|
||||
if (p == string::npos) return -1;
|
||||
|
@ -1981,7 +2004,7 @@ bool check_if_rtlsdr_exists_in_path()
|
|||
return found;
|
||||
}
|
||||
|
||||
std::string currentProcessExe()
|
||||
string currentProcessExe()
|
||||
{
|
||||
char buf[1024];
|
||||
memset(buf, 0, 1024);
|
||||
|
@ -2011,14 +2034,14 @@ std::string currentProcessExe()
|
|||
#endif
|
||||
}
|
||||
|
||||
string dirname(string p)
|
||||
string dirname(const string& p)
|
||||
{
|
||||
size_t s = p.rfind('/');
|
||||
if (s == string::npos) return "";
|
||||
return p.substr(0, s);
|
||||
}
|
||||
|
||||
string lookForExecutable(string prog, string bin_dir, string default_dir)
|
||||
string lookForExecutable(const string& prog, string bin_dir, string default_dir)
|
||||
{
|
||||
string tmp = bin_dir+"/"+prog;
|
||||
if (checkFileExists(tmp.c_str()))
|
||||
|
@ -2033,7 +2056,7 @@ string lookForExecutable(string prog, string bin_dir, string default_dir)
|
|||
return "";
|
||||
}
|
||||
|
||||
bool parseExtras(string s, map<string,string> *extras)
|
||||
bool parseExtras(const string& s, map<string,string> *extras)
|
||||
{
|
||||
vector<string> parts = splitString(s, ' ');
|
||||
|
||||
|
@ -2071,7 +2094,7 @@ void checkIfMultipleWmbusMetersRunning()
|
|||
}
|
||||
}
|
||||
|
||||
bool isValidBps(string b)
|
||||
bool isValidBps(const string& b)
|
||||
{
|
||||
if (b == "300") return true;
|
||||
if (b == "600") return true;
|
||||
|
@ -2103,7 +2126,7 @@ size_t findBytes(vector<uchar> &v, uchar a, uchar b, uchar c)
|
|||
return (size_t)-1;
|
||||
}
|
||||
|
||||
string reverseBCD(string v)
|
||||
string reverseBCD(const string& v)
|
||||
{
|
||||
int vlen = v.length();
|
||||
if (vlen % 2 != 0)
|
||||
|
@ -2120,7 +2143,7 @@ string reverseBCD(string v)
|
|||
return n;
|
||||
}
|
||||
|
||||
string reverseBinaryAsciiSafeToString(string v)
|
||||
string reverseBinaryAsciiSafeToString(const string& v)
|
||||
{
|
||||
vector<uchar> bytes;
|
||||
bool ok = hex2bin(v, &bytes);
|
||||
|
@ -2207,7 +2230,7 @@ void removeSlipFraming(vector<uchar>& from, size_t *frame_length, vector<uchar>
|
|||
}
|
||||
|
||||
// Check if hex string is likely to be ascii
|
||||
bool isLikelyAscii(std::string v)
|
||||
bool isLikelyAscii(const string& v)
|
||||
{
|
||||
vector<uchar> val;
|
||||
bool ok = hex2bin(v, &val);
|
||||
|
@ -2291,11 +2314,22 @@ string sortStatusString(const string &a)
|
|||
|
||||
while (result.size() > 0 && result.back() == ' ') result.pop_back();
|
||||
|
||||
// This feature is only used for the em24 deprecated backwards compatible error field.
|
||||
// This should go away in the future.
|
||||
replace(result.begin(), result.end(), '~', ' ');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uchar *safeButUnsafeVectorPtr(std::vector<uchar> &v)
|
||||
uchar *safeButUnsafeVectorPtr(vector<uchar> &v)
|
||||
{
|
||||
if (v.size() == 0) return NULL;
|
||||
return &v[0];
|
||||
}
|
||||
|
||||
int strlen_utf8(const char *s)
|
||||
{
|
||||
int len = 0;
|
||||
for (; *s; ++s) if ((*s & 0xC0) != 0x80) ++len;
|
||||
return len;
|
||||
}
|
||||
|
|
76
src/util.h
76
src/util.h
|
@ -46,11 +46,11 @@ uchar bcd2bin(uchar c);
|
|||
uchar revbcd2bin(uchar c);
|
||||
uchar reverse(uchar c);
|
||||
// A BCD string 102030405060 is reversed to 605040302010
|
||||
std::string reverseBCD(std::string v);
|
||||
std::string reverseBCD(const std::string& v);
|
||||
// A hex string encoding ascii chars is reversed and safely translated into a readble string.
|
||||
std::string reverseBinaryAsciiSafeToString(std::string v);
|
||||
std::string reverseBinaryAsciiSafeToString(const std::string& v);
|
||||
// Check if hex string is likely to be ascii
|
||||
bool isLikelyAscii(std::string v);
|
||||
bool isLikelyAscii(const std::string& v);
|
||||
|
||||
bool isHexChar(uchar c);
|
||||
|
||||
|
@ -62,14 +62,15 @@ bool isHexStringStrict(const char* txt, bool *invalid);
|
|||
bool isHexStringStrict(const std::string &txt, bool *invalid);
|
||||
int char2int(char input);
|
||||
bool hex2bin(const char* src, std::vector<uchar> *target);
|
||||
bool hex2bin(std::string &src, std::vector<uchar> *target);
|
||||
bool hex2bin(const std::string &src, std::vector<uchar> *target);
|
||||
bool hex2bin(std::vector<uchar> &src, std::vector<uchar> *target);
|
||||
std::string bin2hex(const std::vector<uchar> &target);
|
||||
std::string bin2hex(std::vector<uchar>::iterator data, std::vector<uchar>::iterator end, int len);
|
||||
std::string bin2hex(std::vector<uchar> &data, int offset, int len);
|
||||
std::string safeString(std::vector<uchar> &target);
|
||||
void strprintf(std::string &s, const char* fmt, ...);
|
||||
void strprintf(std::string *s, const char* fmt, ...);
|
||||
std::string tostrprintf(const char* fmt, ...);
|
||||
std::string tostrprintf(const std::string &fmt, ...);
|
||||
|
||||
// Return for example: 2010-03-21
|
||||
std::string strdate(struct tm *date);
|
||||
|
@ -79,12 +80,12 @@ std::string strdatetime(struct tm *date);
|
|||
std::string strdatetimesec(struct tm *date);
|
||||
void addMonths(struct tm* date, int m);
|
||||
|
||||
bool stringFoundCaseIgnored(std::string haystack, std::string needle);
|
||||
bool stringFoundCaseIgnored(const std::string& haystack, const std::string& needle);
|
||||
|
||||
void xorit(uchar *srca, uchar *srcb, uchar *dest, int len);
|
||||
void shiftLeft(uchar *srca, uchar *srcb, int len);
|
||||
std::string format3fdot3f(double v);
|
||||
bool enableLogfile(std::string logfile, bool daemon);
|
||||
bool enableLogfile(const std::string& logfile, bool daemon);
|
||||
void disableLogfile();
|
||||
void enableSyslog();
|
||||
void error(const char* fmt, ...);
|
||||
|
@ -118,8 +119,8 @@ bool isDebugEnabled();
|
|||
bool isTraceEnabled();
|
||||
bool isLogTelegramsEnabled();
|
||||
|
||||
void debugPayload(std::string intro, std::vector<uchar> &payload);
|
||||
void debugPayload(std::string intro, std::vector<uchar> &payload, std::vector<uchar>::iterator &pos);
|
||||
void debugPayload(const std::string& intro, std::vector<uchar> &payload);
|
||||
void debugPayload(const std::string& intro, std::vector<uchar> &payload, std::vector<uchar>::iterator &pos);
|
||||
void logTelegram(std::vector<uchar> &original, std::vector<uchar> &parsed, int header_size, int suffix_size);
|
||||
|
||||
enum class Alarm
|
||||
|
@ -134,26 +135,26 @@ const char* toString(Alarm type);
|
|||
void logAlarm(Alarm type, std::string info);
|
||||
void setAlarmShells(std::vector<std::string> &alarm_shells);
|
||||
|
||||
bool isValidAlias(std::string alias);
|
||||
bool isValidBps(std::string b);
|
||||
bool isValidMatchExpression(std::string id, bool non_compliant);
|
||||
bool isValidMatchExpressions(std::string ids, bool non_compliant);
|
||||
bool doesIdMatchExpression(std::string id, std::string match_rule);
|
||||
bool doesIdMatchExpressions(std::string id, std::vector<std::string>& match_rules, bool *used_wildcard);
|
||||
bool isValidAlias(const std::string& alias);
|
||||
bool isValidBps(const std::string& b);
|
||||
bool isValidMatchExpression(const std::string& s, bool non_compliant);
|
||||
bool isValidMatchExpressions(const std::string& s, bool non_compliant);
|
||||
bool doesIdMatchExpression(const std::string& id, std::string match_rule);
|
||||
bool doesIdMatchExpressions(const std::string& id, std::vector<std::string>& match_rules, bool *used_wildcard);
|
||||
bool doesIdsMatchExpressions(std::vector<std::string> &ids, std::vector<std::string>& match_rules, bool *used_wildcard);
|
||||
std::string toIdsCommaSeparated(std::vector<std::string> &ids);
|
||||
|
||||
bool isValidId(std::string id, bool accept_non_compliant);
|
||||
bool isValidId(const std::string& id, bool accept_non_compliant);
|
||||
|
||||
bool isFrequency(std::string& fq);
|
||||
bool isNumber(std::string& fq);
|
||||
bool isFrequency(const std::string& fq);
|
||||
bool isNumber(const std::string& fq);
|
||||
|
||||
std::vector<std::string> splitMatchExpressions(std::string& mes);
|
||||
std::vector<std::string> splitMatchExpressions(const std::string& mes);
|
||||
// Split s into strings separated by c.
|
||||
std::vector<std::string> splitString(std::string &s, char c);
|
||||
std::vector<std::string> splitString(const std::string &s, char c);
|
||||
// Split device string cul:c1:CMD(bar 1:2) into cul c1 CMD(bar 1:2)
|
||||
// I.e. the : colon inside CMD is not used for splitting.
|
||||
std::vector<std::string> splitDeviceString(std::string &s);
|
||||
std::vector<std::string> splitDeviceString(const std::string &s);
|
||||
|
||||
void incrementIV(uchar *iv, size_t len);
|
||||
|
||||
|
@ -161,24 +162,24 @@ bool checkCharacterDeviceExists(const char *tty, bool fail_if_not);
|
|||
bool checkFileExists(const char *file);
|
||||
bool checkIfSimulationFile(const char *file);
|
||||
bool checkIfDirExists(const char *dir);
|
||||
bool listFiles(std::string dir, std::vector<std::string> *files);
|
||||
int loadFile(std::string file, std::vector<std::string> *lines);
|
||||
bool loadFile(std::string file, std::vector<char> *buf);
|
||||
bool listFiles(const std::string& dir, std::vector<std::string> *files);
|
||||
int loadFile(const std::string& file, std::vector<std::string> *lines);
|
||||
bool loadFile(const std::string& file, std::vector<char> *buf);
|
||||
|
||||
std::string eatTo(std::vector<uchar> &v, std::vector<uchar>::iterator &i, int c, size_t max, bool *eof, bool *err);
|
||||
|
||||
void padWithZeroesTo(std::vector<uchar> *content, size_t len, std::vector<uchar> *full_content);
|
||||
std::string padLeft(std::string input, int width);
|
||||
std::string padLeft(const std::string& input, int width);
|
||||
|
||||
// Parse text string into seconds, 5h = (3600*5) 2m = (60*2) 1s = 1
|
||||
int parseTime(std::string time);
|
||||
int parseTime(const std::string& time);
|
||||
|
||||
// Test if current time is inside any of the specified periods.
|
||||
// For example: mon-sun(00-24) is always true!
|
||||
// mon-fri(08-20) is true monday to friday from 08.00 to 19.59
|
||||
// tue(09-10),sat(00-24) is true tuesday 09.00 to 09.59 and whole of saturday.
|
||||
bool isInsideTimePeriod(time_t now, std::string periods);
|
||||
bool isValidTimePeriod(std::string periods);
|
||||
bool isValidTimePeriod(const std::string& periods);
|
||||
|
||||
uint16_t crc16_EN13757(uchar *data, size_t len);
|
||||
|
||||
|
@ -209,15 +210,15 @@ void trimWhitespace(std::string *s);
|
|||
// NoProperResponse means that we talked to something, but we do not know what it is.
|
||||
enum class AccessCheck { NoSuchDevice, NoProperResponse, NoPermission, NotSameGroup, AccessOK };
|
||||
const char* toString(AccessCheck ac);
|
||||
AccessCheck checkIfExistsAndHasAccess(std::string device);
|
||||
AccessCheck checkIfExistsAndHasAccess(const std::string& device);
|
||||
// Count the number of 1:s in the binary number v.
|
||||
int countSetBits(int v);
|
||||
|
||||
bool startsWith(std::string &s, const char *prefix);
|
||||
bool startsWith(std::string &s, std::string &prefix);
|
||||
bool startsWith(const std::string &s, const char *prefix);
|
||||
bool startsWith(const std::string &s, std::string &prefix);
|
||||
|
||||
// Given alfa=beta it returns "alfa":"beta"
|
||||
std::string makeQuotedJson(std::string &s);
|
||||
std::string makeQuotedJson(const std::string &s);
|
||||
|
||||
std::string currentYear();
|
||||
std::string currentDay();
|
||||
|
@ -230,14 +231,14 @@ std::string currentMicros();
|
|||
|
||||
bool hasBytes(int n, std::vector<uchar>::iterator &pos, std::vector<uchar> &frame);
|
||||
|
||||
bool startsWith(std::string s, std::vector<uchar> &data);
|
||||
bool startsWith(const std::string& s, std::vector<uchar> &data);
|
||||
|
||||
// Sum the memory used by the heap and stack.
|
||||
size_t memoryUsage();
|
||||
|
||||
std::string humanReadableTwoDecimals(size_t s);
|
||||
|
||||
uint32_t indexFromRtlSdrName(std::string &s);
|
||||
uint32_t indexFromRtlSdrName(const std::string& s);
|
||||
|
||||
bool check_if_rtlwmbus_exists_in_path();
|
||||
bool check_if_rtlsdr_exists_in_path();
|
||||
|
@ -245,12 +246,12 @@ bool check_if_rtlsdr_exists_in_path();
|
|||
// Return the actual executable binary that is running.
|
||||
std::string currentProcessExe();
|
||||
|
||||
std::string dirname(std::string p);
|
||||
std::string dirname(const std::string& p);
|
||||
|
||||
std::string lookForExecutable(std::string prog, std::string bin_dir, std::string default_dir);
|
||||
std::string lookForExecutable(const std::string& prog, std::string bin_dir, std::string default_dir);
|
||||
|
||||
// Extract from "ppm=5 radix=7" and put key values into map.
|
||||
bool parseExtras(std::string s, std::map<std::string,std::string> *extras);
|
||||
bool parseExtras(const std::string& s, std::map<std::string,std::string> *extras);
|
||||
void checkIfMultipleWmbusMetersRunning();
|
||||
|
||||
size_t findBytes(std::vector<uchar> &v, uchar a, uchar b, uchar c);
|
||||
|
@ -272,6 +273,7 @@ std::string joinStatusStrings(const std::string &a, const std::string &b);
|
|||
|
||||
// Sort the words in a status string: "GAMMA BETA ALFA" --> "ALFA BETA GAMMA"
|
||||
// Also identical flags are merged: "BETA ALFA ALFA" --> "ALFA BETA"
|
||||
// Finally ~ is replaced with a space, this is only used for backwards compatibilty for deprecated fields.
|
||||
std::string sortStatusString(const std::string &a);
|
||||
|
||||
// If a vector is empty, then there will be an assert (with some compiler flags) if we use &v[0]
|
||||
|
@ -279,6 +281,8 @@ std::string sortStatusString(const std::string &a);
|
|||
// So provide safeVectorPtr which will return NULL instead of assert-crashing.
|
||||
uchar *safeButUnsafeVectorPtr(std::vector<uchar> &v);
|
||||
|
||||
// Count utf8 unicode code points.
|
||||
int strlen_utf8(const char *s);
|
||||
|
||||
#ifndef FUZZING
|
||||
#define FUZZING false
|
||||
|
|
60
src/wmbus.cc
60
src/wmbus.cc
|
@ -3223,7 +3223,7 @@ string vif_7B_FirstExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7e) == 0x00) {
|
||||
int n = vife & 0x01;
|
||||
string s;
|
||||
strprintf(s, "10^%d MWh", n-1);
|
||||
strprintf(&s, "10^%d MWh", n-1);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3235,7 +3235,7 @@ string vif_7B_FirstExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7e) == 0x08) {
|
||||
int n = vife & 0x01;
|
||||
string s;
|
||||
strprintf(s, "10^%d GJ", n-1);
|
||||
strprintf(&s, "10^%d GJ", n-1);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3247,7 +3247,7 @@ string vif_7B_FirstExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7e) == 0x10) {
|
||||
int n = vife & 0x01;
|
||||
string s;
|
||||
strprintf(s, "10^%d m3", n+2);
|
||||
strprintf(&s, "10^%d m3", n+2);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3259,14 +3259,14 @@ string vif_7B_FirstExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7e) == 0x18) {
|
||||
int n = vife & 0x01;
|
||||
string s;
|
||||
strprintf(s, "10^%d ton", n+2);
|
||||
strprintf(&s, "10^%d ton", n+2);
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x7e) == 0x1a) {
|
||||
int n = vife & 0x01;
|
||||
string s;
|
||||
strprintf(s, "Relative Humidity 10^%d %%", n-1);
|
||||
strprintf(&s, "Relative Humidity 10^%d %%", n-1);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3315,7 +3315,7 @@ string vif_7B_FirstExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
// output from a nuclear power plant?
|
||||
int n = vife & 0x01;
|
||||
string s;
|
||||
strprintf(s, "10^%d MW", n-1);
|
||||
strprintf(&s, "10^%d MW", n-1);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3327,7 +3327,7 @@ string vif_7B_FirstExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7e) == 0x30) {
|
||||
int n = vife & 0x01;
|
||||
string s;
|
||||
strprintf(s, "10^%d GJ/h", n-1);
|
||||
strprintf(&s, "10^%d GJ/h", n-1);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3338,28 +3338,28 @@ string vif_7B_FirstExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7c) == 0x58) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Flow temperature 10^%d Fahrenheit", nn-3);
|
||||
strprintf(&s, "Flow temperature 10^%d Fahrenheit", nn-3);
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x7c) == 0x5c) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Return temperature 10^%d Fahrenheit", nn-3);
|
||||
strprintf(&s, "Return temperature 10^%d Fahrenheit", nn-3);
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x7c) == 0x60) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Temperature difference 10^%d Fahrenheit", nn-3);
|
||||
strprintf(&s, "Temperature difference 10^%d Fahrenheit", nn-3);
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x7c) == 0x64) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "External temperature 10^%d Fahrenheit", nn-3);
|
||||
strprintf(&s, "External temperature 10^%d Fahrenheit", nn-3);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3370,21 +3370,21 @@ string vif_7B_FirstExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7c) == 0x70) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Cold / Warm Temperature Limit 10^%d Fahrenheit", nn-3);
|
||||
strprintf(&s, "Cold / Warm Temperature Limit 10^%d Fahrenheit", nn-3);
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x7c) == 0x74) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Cold / Warm Temperature Limit 10^%d Celsius", nn-3);
|
||||
strprintf(&s, "Cold / Warm Temperature Limit 10^%d Celsius", nn-3);
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x78) == 0x78) {
|
||||
int nnn = vife & 0x07;
|
||||
string s;
|
||||
strprintf(s, "Cumulative count max power 10^%d W", nnn-3);
|
||||
strprintf(&s, "Cumulative count max power 10^%d W", nnn-3);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3398,14 +3398,14 @@ string vif_7D_SecondExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7c) == 0x00) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Credit of 10^%d of the nominal local legal currency units", nn-3);
|
||||
strprintf(&s, "Credit of 10^%d of the nominal local legal currency units", nn-3);
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x7c) == 0x04) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Debit of 10^%d of the nominal local legal currency units", nn-3);
|
||||
strprintf(&s, "Debit of 10^%d of the nominal local legal currency units", nn-3);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3524,7 +3524,7 @@ string vif_7D_SecondExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7c) == 0x24) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Storage interval [%s]", timeNN(nn));
|
||||
strprintf(&s, "Storage interval [%s]", timeNN(nn));
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3547,7 +3547,7 @@ string vif_7D_SecondExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7c) == 0x2c) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Duration since last readout [%s]", timeNN(nn));
|
||||
strprintf(&s, "Duration since last readout [%s]", timeNN(nn));
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3559,14 +3559,14 @@ string vif_7D_SecondExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
int nn = vife & 0x03;
|
||||
string s;
|
||||
// nn == 0 (seconds) is not expected here! According to m-bus spec.
|
||||
strprintf(s, "Duration of tariff [%s]", timeNN(nn));
|
||||
strprintf(&s, "Duration of tariff [%s]", timeNN(nn));
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x7c) == 0x34) {
|
||||
int nn = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Period of tariff [%s]", timeNN(nn));
|
||||
strprintf(&s, "Period of tariff [%s]", timeNN(nn));
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3594,14 +3594,14 @@ string vif_7D_SecondExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x70) == 0x40) {
|
||||
int nnnn = vife & 0x0f;
|
||||
string s;
|
||||
strprintf(s, "10^%d Volts", nnnn-9);
|
||||
strprintf(&s, "10^%d Volts", nnnn-9);
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x70) == 0x50) {
|
||||
int nnnn = vife & 0x0f;
|
||||
string s;
|
||||
strprintf(s, "10^%d Ampere", nnnn-12);
|
||||
strprintf(&s, "10^%d Ampere", nnnn-12);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3640,14 +3640,14 @@ string vif_7D_SecondExtensionType(uchar dif, uchar vif, uchar vife)
|
|||
if ((vife & 0x7c) == 0x68) {
|
||||
int pp = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Duration since last cumulation [%s]", timePP(pp));
|
||||
strprintf(&s, "Duration since last cumulation [%s]", timePP(pp));
|
||||
return s;
|
||||
}
|
||||
|
||||
if ((vife & 0x7c) == 0x6c) {
|
||||
int pp = vife & 0x03;
|
||||
string s;
|
||||
strprintf(s, "Operating time battery [%s]", timePP(pp));
|
||||
strprintf(&s, "Operating time battery [%s]", timePP(pp));
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -3969,7 +3969,7 @@ string formatData(int dif, int vif, int vife, string data)
|
|||
if (t >= 0 && t <= 0x77 && !(t >= 0x6c && t<=0x6f)) {
|
||||
// These are vif codes with an understandable key and unit.
|
||||
double val = dataAsDouble(dif, vif, vife, data);
|
||||
strprintf(r, "%d", val);
|
||||
strprintf(&r, "%d", val);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -4313,7 +4313,7 @@ void BusDeviceCommonImplementation::checkStatus()
|
|||
bool ok = reset();
|
||||
if (ok) return;
|
||||
string msg;
|
||||
strprintf(msg, "failed regular reset of %s %s", device().c_str(), toString(type()));
|
||||
strprintf(&msg, "failed regular reset of %s %s", device().c_str(), toString(type()));
|
||||
logAlarm(Alarm::RegularResetFailure, msg);
|
||||
return;
|
||||
}
|
||||
|
@ -4321,7 +4321,7 @@ void BusDeviceCommonImplementation::checkStatus()
|
|||
if (protocol_error_count_ >= 20)
|
||||
{
|
||||
string msg;
|
||||
strprintf(msg, "too many protocol errors(%d) resetting %s %s", protocol_error_count_, device().c_str(), toString(type()));
|
||||
strprintf(&msg, "too many protocol errors(%d) resetting %s %s", protocol_error_count_, device().c_str(), toString(type()));
|
||||
logAlarm(Alarm::DeviceFailure, msg);
|
||||
bool ok = reset();
|
||||
if (ok)
|
||||
|
@ -4331,7 +4331,7 @@ void BusDeviceCommonImplementation::checkStatus()
|
|||
return;
|
||||
}
|
||||
|
||||
strprintf(msg, "failed to reset wmbus device %s %s exiting wmbusmeters", device().c_str(), toString(type()));
|
||||
strprintf(&msg, "failed to reset wmbus device %s %s exiting wmbusmeters", device().c_str(), toString(type()));
|
||||
logAlarm(Alarm::DeviceFailure, msg);
|
||||
manager_->stop();
|
||||
return;
|
||||
|
@ -4370,7 +4370,7 @@ void BusDeviceCommonImplementation::checkStatus()
|
|||
string nowtxt = strdatetime(&nowtm);
|
||||
|
||||
string msg;
|
||||
strprintf(msg, "%d seconds of inactivity resetting %s %s "
|
||||
strprintf(&msg, "%d seconds of inactivity resetting %s %s "
|
||||
"(timeout %ds expected %s now %s)",
|
||||
since, device().c_str(), toString(type()),
|
||||
timeout_, expected_activity_.c_str(), nowtxt.c_str());
|
||||
|
@ -4384,7 +4384,7 @@ void BusDeviceCommonImplementation::checkStatus()
|
|||
}
|
||||
else
|
||||
{
|
||||
strprintf(msg, "failed to reset wmbus device %s %s exiting wmbusmeters", device().c_str(), toString(type()));
|
||||
strprintf(&msg, "failed to reset wmbus device %s %s exiting wmbusmeters", device().c_str(), toString(type()));
|
||||
logAlarm(Alarm::DeviceFailure, msg);
|
||||
manager_->stop();
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ struct IM871ADeviceInfo
|
|||
else s+="unknown_mode("+to_string(device_mode)+") ";
|
||||
|
||||
string ss;
|
||||
strprintf(ss, "firmware=%02x hci=%02x uid=%08x", firmware_version, hci_version, uid);
|
||||
strprintf(&ss, "firmware=%02x hci=%02x uid=%08x", firmware_version, hci_version, uid);
|
||||
return s+ss;
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ struct ConfigIM871AIM170A
|
|||
string dongleId()
|
||||
{
|
||||
string s;
|
||||
strprintf(s, "%08x", id);
|
||||
strprintf(&s, "%08x", id);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ struct ConfigIM871AIM170A
|
|||
s += "link_mode="+toString(LinkModeIM871A(link_mode));
|
||||
|
||||
string ids;
|
||||
strprintf(ids, " id=%08x media=%02x version=%02x c_field=%02x auto_rssi=%02x", id, media, version, c_field, auto_rssi);
|
||||
strprintf(&ids, " id=%08x media=%02x version=%02x c_field=%02x auto_rssi=%02x", id, media, version, c_field, auto_rssi);
|
||||
|
||||
return s+ids;
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue