Add formulas and SIUnits.

pull/645/head^2
Fredrik Öhrström 2022-11-01 12:14:48 +01:00
rodzic 86b497d339
commit a01cdc8c34
39 zmienionych plików z 1483 dodań i 572 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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());

Wyświetl plik

@ -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;

Wyświetl plik

@ -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_);

Wyświetl plik

@ -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;

Wyświetl plik

@ -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,

Wyświetl plik

@ -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,

Wyświetl plik

@ -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);

149
src/driver_em24.cc 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -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,

Wyświetl plik

@ -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());

Wyświetl plik

@ -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";
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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

Wyświetl plik

@ -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());
}
}
}

Wyświetl plik

@ -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) \

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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]);
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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)

Wyświetl plik

@ -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(&current_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(&current_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));

Wyświetl plik

@ -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));

Wyświetl plik

@ -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;

Wyświetl plik

@ -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;

Wyświetl plik

@ -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());

Wyświetl plik

@ -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));

Wyświetl plik

@ -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 ||

Wyświetl plik

@ -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);

Wyświetl plik

@ -1544,7 +1544,7 @@ string showSpecialChars(struct termios *tios)
if (c != 0)
{
string cc;
strprintf(cc, "%u", c);
strprintf(&cc, "%u", c);
s += cc+",";
}
}

Wyświetl plik

@ -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");
}
}

Wyświetl plik

@ -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+" ";
}

Wyświetl plik

@ -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_));
}

Wyświetl plik

@ -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,"",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,"",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

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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();
}

Wyświetl plik

@ -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;
}