kopia lustrzana https://github.com/weetmuts/wmbusmeters
New function --analyze now works but is not yet complete.
rodzic
ae6e10a291
commit
2ecb3e90d9
|
@ -20,6 +20,7 @@
|
|||
#include"util.h"
|
||||
|
||||
#include<string>
|
||||
#include<unistd.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -104,9 +105,27 @@ shared_ptr<Configuration> parseCommandLine(int argc, char **argv) {
|
|||
}
|
||||
if (!strcmp(argv[i], "--analyze")) {
|
||||
c->analyze = true;
|
||||
if (isatty(1))
|
||||
{
|
||||
c->analyze_format = OutputFormat::TERMINAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->analyze_format = OutputFormat::PLAIN;
|
||||
}
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(argv[i], "--analyze=", 10)) {
|
||||
c->analyze = true;
|
||||
string format = string(argv[i]+10);
|
||||
if (format == "plain") c->analyze_format = OutputFormat::PLAIN;
|
||||
else if (format == "terminal") c->analyze_format = OutputFormat::TERMINAL;
|
||||
else if (format == "json") c->analyze_format = OutputFormat::JSON;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "--debug")) {
|
||||
c->debug = true;
|
||||
i++;
|
||||
|
|
|
@ -59,6 +59,7 @@ struct Configuration
|
|||
bool version {};
|
||||
bool license {};
|
||||
bool analyze {};
|
||||
OutputFormat analyze_format {};
|
||||
bool debug {};
|
||||
bool trace {};
|
||||
AddLogTimestamps addtimestamps {};
|
||||
|
|
|
@ -144,14 +144,14 @@ bool parseDV(Telegram *t,
|
|||
string value = bin2hex(data+1, data_end, datalen-1);
|
||||
t->mfct_0f_index = 1+std::distance(data_start, data);
|
||||
assert(t->mfct_0f_index >= 0);
|
||||
t->addExplanationAndIncrementPos(data, datalen, "%02X manufacturer specific data %s", dif, value.c_str());
|
||||
t->addExplanationAndIncrementPos(data, datalen, KindOfData::PROTOCOL, Understanding::NONE, "%02X manufacturer specific data %s", dif, value.c_str());
|
||||
break;
|
||||
}
|
||||
debug("(dvparser) cannot handle dif %02X ignoring rest of telegram.\n", dif);
|
||||
break;
|
||||
}
|
||||
if (dif == 0x2f) {
|
||||
t->addExplanationAndIncrementPos(*format, 1, "%02X skip", dif);
|
||||
t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02X skip", dif);
|
||||
DEBUG_PARSER("\n");
|
||||
continue;
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ bool parseDV(Telegram *t,
|
|||
if (data_has_difvifs) {
|
||||
format_bytes.push_back(dif);
|
||||
id_bytes.push_back(dif);
|
||||
t->addExplanationAndIncrementPos(*format, 1, "%02X dif (%s)", dif, difType(dif).c_str());
|
||||
t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02X dif (%s)", dif, difType(dif).c_str());
|
||||
} else {
|
||||
id_bytes.push_back(**format);
|
||||
(*format)++;
|
||||
|
@ -193,8 +193,9 @@ bool parseDV(Telegram *t,
|
|||
if (data_has_difvifs) {
|
||||
format_bytes.push_back(dife);
|
||||
id_bytes.push_back(dife);
|
||||
t->addExplanationAndIncrementPos(*format, 1, "%02X dife (subunit=%d tariff=%d storagenr=%d)",
|
||||
dife, subunit, tariff, storage_nr);
|
||||
t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02X dife (subunit=%d tariff=%d storagenr=%d)",
|
||||
dife, subunit, tariff, storage_nr);
|
||||
} else {
|
||||
id_bytes.push_back(**format);
|
||||
(*format)++;
|
||||
|
@ -211,7 +212,8 @@ bool parseDV(Telegram *t,
|
|||
if (data_has_difvifs) {
|
||||
format_bytes.push_back(vif);
|
||||
id_bytes.push_back(vif);
|
||||
t->addExplanationAndIncrementPos(*format, 1, "%02X vif (%s)", vif, vifType(vif).c_str());
|
||||
t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02X vif (%s)", vif, vifType(vif).c_str());
|
||||
} else {
|
||||
id_bytes.push_back(**format);
|
||||
(*format)++;
|
||||
|
@ -224,13 +226,15 @@ bool parseDV(Telegram *t,
|
|||
DEBUG_PARSER("(dvparser debug) variable length vif found\n");
|
||||
if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vif varlen expected)\n"); break; }
|
||||
uchar viflen = **format;
|
||||
t->addExplanationAndIncrementPos(*format, 1, "%02X viflen (%d)", viflen, viflen);
|
||||
t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02X viflen (%d)", viflen, viflen);
|
||||
for (uchar i = 0; i < viflen; ++i)
|
||||
{
|
||||
if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vif varlen byte %d/%d expected)\n",
|
||||
i+1, viflen); break; }
|
||||
uchar v = **format;
|
||||
t->addExplanationAndIncrementPos(*format, 1, "%02X vif (%c)", v, v);
|
||||
t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02X vif (%c)", v, v);
|
||||
id_bytes.push_back(v);
|
||||
}
|
||||
}
|
||||
|
@ -243,7 +247,8 @@ bool parseDV(Telegram *t,
|
|||
if (data_has_difvifs) {
|
||||
format_bytes.push_back(vife);
|
||||
id_bytes.push_back(vife);
|
||||
t->addExplanationAndIncrementPos(*format, 1, "%02X vife (%s)", vife, vifeType(dif, vif, vife).c_str());
|
||||
t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02X vife (%s)", vife, vifeType(dif, vif, vife).c_str());
|
||||
} else {
|
||||
id_bytes.push_back(**format);
|
||||
(*format)++;
|
||||
|
@ -281,14 +286,14 @@ bool parseDV(Telegram *t,
|
|||
|
||||
// Skip the length byte in the variable length data.
|
||||
if (variable_length) {
|
||||
t->addExplanationAndIncrementPos(data, 1, "%02X varlen=%d", *(data+0), datalen);
|
||||
t->addExplanationAndIncrementPos(data, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02X varlen=%d", *(data+0), datalen);
|
||||
}
|
||||
string value = bin2hex(data, data_end, datalen);
|
||||
int offset = start_parse_here+data-data_start;
|
||||
(*values)[key] = { offset, DVEntry(mt, vif&0x7f, storage_nr, tariff, subunit, value) };
|
||||
if (value.length() > 0) {
|
||||
// This call increments data with datalen.
|
||||
t->addExplanationAndIncrementPos(data, datalen, "%s", value.c_str());
|
||||
t->addExplanationAndIncrementPos(data, datalen, KindOfData::CONTENT, Understanding::NONE, "%s", value.c_str());
|
||||
DEBUG_PARSER("(dvparser debug) data \"%s\"\n\n", value.c_str());
|
||||
}
|
||||
if (remaining == datalen || data == databytes.end()) {
|
||||
|
|
|
@ -451,7 +451,7 @@ bool start(Configuration *config)
|
|||
// and creates meters on demand when the telegram arrives
|
||||
// or on startup for 2-way communication meters like mbus or T2.
|
||||
meter_manager_ = createMeterManager(config->daemon);
|
||||
meter_manager_->analyzeEnabled(config->analyze);
|
||||
meter_manager_->analyzeEnabled(config->analyze, config->analyze_format);
|
||||
|
||||
// The bus manager detects new/lost wmbus devices and
|
||||
// configures the devices according to the specification.
|
||||
|
|
|
@ -109,9 +109,13 @@ void MeterApator162::processContent(Telegram *t)
|
|||
vector<uchar> frame;
|
||||
t->extractFrame(&frame);
|
||||
string hex = bin2hex(frame);
|
||||
warning("(apator162) telegram contains a register (%02x) with unknown size.\n"
|
||||
"Please open an issue at https://github.com/weetmuts/wmbusmeters/\n"
|
||||
"and report this telegram: %s\n", c, hex.c_str());
|
||||
|
||||
if (t->beingAnalyzed() == false)
|
||||
{
|
||||
warning("(apator162) telegram contains a register (%02x) with unknown size.\n"
|
||||
"Please open an issue at https://github.com/weetmuts/wmbusmeters/\n"
|
||||
"and report this telegram: %s\n", c, hex.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (c == 0x10 && size == 4 && i+size < content.size())
|
||||
|
@ -123,13 +127,13 @@ void MeterApator162::processContent(Telegram *t)
|
|||
int offset;
|
||||
extractDVdouble(&vendor_values, "0413", &offset, &total_water_consumption_m3_);
|
||||
total = "*** 10|"+total+" total consumption (%f m3)";
|
||||
t->addSpecialExplanation(offset, total.c_str(), total_water_consumption_m3_);
|
||||
t->addSpecialExplanation(offset, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3_);
|
||||
}
|
||||
else
|
||||
{
|
||||
string msg = "*** ";
|
||||
msg += bin2hex(content, i-1, 1)+"|"+bin2hex(content, i, size);
|
||||
t->addSpecialExplanation(i-1+t->header_size, msg.c_str());
|
||||
t->addSpecialExplanation(i-1+t->header_size, KindOfData::CONTENT, Understanding::NONE, msg.c_str());
|
||||
}
|
||||
i += size;
|
||||
}
|
||||
|
|
|
@ -106,7 +106,8 @@ void MeterCompact5::processContent(Telegram *t)
|
|||
strprintf(prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
int offset = t->parsed.size()+3;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Instantaneous, 0x15, 0, 0, 0, prevs) };
|
||||
t->explanations.push_back({ offset, prevs });
|
||||
Explanation pe(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL);
|
||||
t->explanations.push_back(pe);
|
||||
t->addMoreExplanation(offset, " energy used in previous billing period (%f KWH)", prev);
|
||||
|
||||
uchar curr_lo = content[7];
|
||||
|
@ -117,7 +118,8 @@ void MeterCompact5::processContent(Telegram *t)
|
|||
strprintf(currs, "%02x%02x", curr_lo, curr_hi);
|
||||
offset = t->parsed.size()+7;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Instantaneous, 0x15, 0, 0, 0, currs) };
|
||||
t->explanations.push_back({ offset, currs });
|
||||
Explanation ce(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL);
|
||||
t->explanations.push_back(ce);
|
||||
t->addMoreExplanation(offset, " energy used in current billing period (%f KWH)", curr);
|
||||
|
||||
total_energy_kwh_ = prev+curr;
|
||||
|
|
|
@ -134,6 +134,12 @@ void MeterFHKVDataIII::processContent(Telegram *t)
|
|||
|
||||
t->extractPayload(&content);
|
||||
|
||||
if (content.size() < 14)
|
||||
{
|
||||
// Not enough data.
|
||||
debugPayload("(fhkvdataiii) not enough data", content);
|
||||
return;
|
||||
}
|
||||
// Consumption
|
||||
// Previous Consumption
|
||||
uchar prev_lo = content[3];
|
||||
|
|
|
@ -241,7 +241,10 @@ void MeterIzar::processContent(Telegram *t)
|
|||
|
||||
if (decoded_content.empty())
|
||||
{
|
||||
warning("(izar) Decoding PRIOS data failed. Ignoring telegram.\n");
|
||||
if (t->beingAnalyzed() == false)
|
||||
{
|
||||
warning("(izar) Decoding PRIOS data failed. Ignoring telegram.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,9 @@ void MeterIzar3::processContent(Telegram *t)
|
|||
vector<uchar> frame;
|
||||
t->extractFrame(&frame);
|
||||
|
||||
warning("(izar3) cannot decode content of telegram!\n");
|
||||
if (t->beingAnalyzed() == false)
|
||||
{
|
||||
warning("(izar3) cannot decode content of telegram!\n");
|
||||
}
|
||||
total_water_consumption_l_ = 123456789;
|
||||
}
|
||||
|
|
|
@ -108,8 +108,10 @@ void MeterLansenDW::processContent(Telegram *t)
|
|||
*/
|
||||
int offset;
|
||||
|
||||
extractDVuint16(&t->values, "02FD1B", &offset, &info_codes_);
|
||||
t->addMoreExplanation(offset, " info codes (%s)", status().c_str());
|
||||
if (extractDVuint16(&t->values, "02FD1B", &offset, &info_codes_))
|
||||
{
|
||||
t->addMoreExplanation(offset, " info codes (%s)", status().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
string MeterLansenDW::status()
|
||||
|
|
|
@ -99,7 +99,7 @@ void MKRadio3::processContent(Telegram *t)
|
|||
strprintf(prev_date_str, "%04x", prev_date);
|
||||
uint offset = t->parsed.size() + 1;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Unknown, 0x6c, 0, 0, 0, prev_date_str) };
|
||||
t->explanations.push_back({ offset, prev_date_str });
|
||||
t->explanations.push_back(Explanation(offset, 1, prev_date_str, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " previous date (%s)", previous_date_.c_str());
|
||||
|
||||
// Previous consumption
|
||||
|
@ -111,7 +111,7 @@ void MKRadio3::processContent(Telegram *t)
|
|||
strprintf(prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
offset = t->parsed.size()+3;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Instantaneous, 0x15, 0, 0, 0, prevs) };
|
||||
t->explanations.push_back({ offset, prevs });
|
||||
t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " prev consumption (%f m3)", prev);
|
||||
|
||||
// Current date
|
||||
|
@ -124,7 +124,7 @@ void MKRadio3::processContent(Telegram *t)
|
|||
strprintf(current_date_str, "%04x", current_date);
|
||||
offset = t->parsed.size() + 5;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Unknown, 0x6c, 0, 0, 0, current_date_str) };
|
||||
t->explanations.push_back({ offset, current_date_str });
|
||||
t->explanations.push_back(Explanation(offset, 1, current_date_str, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " current date (%s)", current_date_.c_str());
|
||||
|
||||
// Current consumption
|
||||
|
@ -136,7 +136,7 @@ void MKRadio3::processContent(Telegram *t)
|
|||
strprintf(currs, "%02x%02x", curr_lo, curr_hi);
|
||||
offset = t->parsed.size()+7;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Instantaneous, 0x15, 0, 0, 0, currs) };
|
||||
t->explanations.push_back({ offset, currs });
|
||||
t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " curr consumption (%f m3)", curr);
|
||||
|
||||
total_water_consumption_m3_ = prev+curr;
|
||||
|
|
|
@ -82,7 +82,7 @@ void MKRadio4::processContent(Telegram *t)
|
|||
strprintf(prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
int offset = t->parsed.size()+3;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Instantaneous, 0x15, 0, 0, 0, prevs) };
|
||||
t->explanations.push_back({ offset, prevs });
|
||||
t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " prev consumption (%f m3)", prev);
|
||||
|
||||
uchar curr_lo = content[7];
|
||||
|
@ -93,7 +93,7 @@ void MKRadio4::processContent(Telegram *t)
|
|||
strprintf(currs, "%02x%02x", curr_lo, curr_hi);
|
||||
offset = t->parsed.size()+7;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Instantaneous, 0x15, 0, 0, 0, currs) };
|
||||
t->explanations.push_back({ offset, currs });
|
||||
t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " curr consumption (%f m3)", curr);
|
||||
|
||||
total_water_consumption_m3_ = prev+curr;
|
||||
|
|
|
@ -95,7 +95,7 @@ void MeterTSD2::processContent(Telegram *t)
|
|||
string prev_date_str;
|
||||
strprintf(prev_date_str, "%04x", prev_date);
|
||||
uint offset = t->parsed.size() + 1;
|
||||
t->explanations.push_back({ offset, prev_date_str });
|
||||
t->explanations.push_back(Explanation(offset, 1, prev_date_str, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " previous date (%s)", previous_date_.c_str());
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ void MeterVario451::processContent(Telegram *t)
|
|||
strprintf(prevs, "%02x%02x", prev_lo, prev_hi);
|
||||
int offset = t->parsed.size()+3;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Instantaneous, 0x15, 0, 0, 0, prevs) };
|
||||
t->explanations.push_back({ offset, prevs });
|
||||
t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " energy used in previous billing period (%f GJ)", prev);
|
||||
|
||||
uchar curr_lo = content[7];
|
||||
|
@ -117,7 +117,7 @@ void MeterVario451::processContent(Telegram *t)
|
|||
strprintf(currs, "%02x%02x", curr_lo, curr_hi);
|
||||
offset = t->parsed.size()+7;
|
||||
vendor_values["0215"] = { offset, DVEntry(MeasurementType::Instantaneous, 0x15, 0, 0, 0, currs) };
|
||||
t->explanations.push_back({ offset, currs });
|
||||
t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL));
|
||||
t->addMoreExplanation(offset, " energy used in current billing period (%f GJ)", curr);
|
||||
|
||||
total_energy_gj_ = prev+curr;
|
||||
|
|
140
src/meters.cc
140
src/meters.cc
|
@ -34,6 +34,7 @@ struct MeterManagerImplementation : public virtual MeterManager
|
|||
private:
|
||||
bool is_daemon_ {};
|
||||
bool should_analyze_ {};
|
||||
OutputFormat analyze_format_ {};
|
||||
vector<MeterInfo> meter_templates_;
|
||||
vector<shared_ptr<Meter>> meters_;
|
||||
function<void(AboutTelegram&,vector<uchar>)> on_telegram_;
|
||||
|
@ -179,7 +180,11 @@ public:
|
|||
tmp.driver = pickMeterDriver(&t);
|
||||
if (tmp.driver == MeterDriver::UNKNOWN)
|
||||
{
|
||||
warnForUnknownDriver(mi.name, &t);
|
||||
if (should_analyze_ == false)
|
||||
{
|
||||
// We are not analyzing, so warn here.
|
||||
warnForUnknownDriver(mi.name, &t);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now build a meter object with for this exact id.
|
||||
|
@ -260,18 +265,109 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void analyzeEnabled(bool b)
|
||||
void analyzeEnabled(bool b, OutputFormat f)
|
||||
{
|
||||
should_analyze_ = b;
|
||||
analyze_format_ = f;
|
||||
}
|
||||
|
||||
void analyzeTelegram(AboutTelegram &about, vector<uchar> &input_frame, bool simulated)
|
||||
{
|
||||
Telegram t;
|
||||
t.about = about;
|
||||
bool ok = t.parseHeader(input_frame);
|
||||
if (simulated) t.markAsSimulated();
|
||||
t.markAsBeingAnalyzed();
|
||||
|
||||
printf("Analyzing...\n");
|
||||
if (!ok)
|
||||
{
|
||||
printf("Could not even analyze header, giving up.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
vector<MeterDriver> drivers;
|
||||
#define X(mname,linkmode,info,type,cname) drivers.push_back(MeterDriver::type);
|
||||
LIST_OF_METERS
|
||||
#undef X
|
||||
|
||||
MeterInfo mi;
|
||||
if (meter_templates_.size() > 0)
|
||||
{
|
||||
if (meter_templates_.size() > 1)
|
||||
{
|
||||
error("When analyzing you can only specify a single meter quadruple.\n");
|
||||
}
|
||||
if (meter_templates_[0].driver != MeterDriver::AUTO)
|
||||
{
|
||||
drivers.clear();
|
||||
drivers.push_back(meter_templates_[0].driver);
|
||||
mi = meter_templates_[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite the id with the id from the telegram to be analyzed.
|
||||
mi.ids.clear();
|
||||
mi.ids.push_back(t.ids.back());
|
||||
mi.idsc = t.ids.back();
|
||||
|
||||
bool hide_output = drivers.size() > 1;
|
||||
bool handled = false;
|
||||
MeterDriver best_driver {};
|
||||
// For the best driver we have:
|
||||
int best_content_length = 0;
|
||||
int best_understood_content_length = 0;
|
||||
|
||||
|
||||
for (MeterDriver dr : drivers)
|
||||
{
|
||||
if (dr == MeterDriver::AUTO) continue;
|
||||
if (dr == MeterDriver::UNKNOWN) continue;
|
||||
string s = toString(dr);
|
||||
debug("Testing driver %s...\n", s.c_str());
|
||||
mi.driver = dr;
|
||||
auto meter = createMeter(&mi);
|
||||
bool match = false;
|
||||
string id;
|
||||
bool h = meter->handleTelegram(about, input_frame, simulated, &id, &match, &t);
|
||||
if (!match)
|
||||
{
|
||||
|
||||
}
|
||||
else if (!h)
|
||||
{
|
||||
// Oups, we added a new meter object tailored for this telegram
|
||||
// but it still did not handle it! This can happen if the wrong
|
||||
// decryption key was used.
|
||||
warning("(meter) newly created meter (%s %s %s) did not handle telegram!\n",
|
||||
meter->name().c_str(), meter->idsc().c_str(), toString(meter->driver()).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
handled = true;
|
||||
int l = 0;
|
||||
int u = 0;
|
||||
OutputFormat of = analyze_format_;
|
||||
if (hide_output) of = OutputFormat::NONE;
|
||||
t.analyzeParse(of, &l, &u);
|
||||
if (u > best_understood_content_length)
|
||||
{
|
||||
// Understood so many bytes
|
||||
best_understood_content_length = u;
|
||||
// Out of this many bytes of content total.
|
||||
best_content_length = l;
|
||||
best_driver = dr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handled)
|
||||
{
|
||||
string s = toString(best_driver);
|
||||
printf("Best driver %s %d/%d\n", s.c_str(), best_understood_content_length, best_content_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("No suitable driver found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
MeterManagerImplementation(bool daemon) : is_daemon_(daemon) {}
|
||||
|
@ -533,22 +629,25 @@ bool MeterCommonImplementation::isTelegramForMeter(Telegram *t, Meter *meter, Me
|
|||
if (isVerboseEnabled() || isDebugEnabled() || !warned_for_telegram_before(t, t->dll_a))
|
||||
{
|
||||
string possible_drivers = t->autoDetectPossibleDrivers();
|
||||
warning("(meter) %s: meter detection did not match the selected driver %s! correct driver is: %s\n"
|
||||
"(meter) Not printing this warning again for id: %02x%02x%02x%02x mfct: (%s) %s (0x%02x) type: %s (0x%02x) ver: 0x%02x\n",
|
||||
name.c_str(),
|
||||
toString(driver).c_str(),
|
||||
possible_drivers.c_str(),
|
||||
t->dll_id_b[3], t->dll_id_b[2], t->dll_id_b[1], t->dll_id_b[0],
|
||||
manufacturerFlag(t->dll_mfct).c_str(),
|
||||
manufacturer(t->dll_mfct).c_str(),
|
||||
t->dll_mfct,
|
||||
mediaType(t->dll_type, t->dll_mfct).c_str(), t->dll_type,
|
||||
t->dll_version);
|
||||
|
||||
if (possible_drivers == "unknown!")
|
||||
if (t->beingAnalyzed() == false)
|
||||
{
|
||||
warning("(meter) please consider opening an issue at https://github.com/weetmuts/wmbusmeters/\n");
|
||||
warning("(meter) to add support for this unknown mfct,media,version combination\n");
|
||||
warning("(meter) %s: meter detection did not match the selected driver %s! correct driver is: %s\n"
|
||||
"(meter) Not printing this warning again for id: %02x%02x%02x%02x mfct: (%s) %s (0x%02x) type: %s (0x%02x) ver: 0x%02x\n",
|
||||
name.c_str(),
|
||||
toString(driver).c_str(),
|
||||
possible_drivers.c_str(),
|
||||
t->dll_id_b[3], t->dll_id_b[2], t->dll_id_b[1], t->dll_id_b[0],
|
||||
manufacturerFlag(t->dll_mfct).c_str(),
|
||||
manufacturer(t->dll_mfct).c_str(),
|
||||
t->dll_mfct,
|
||||
mediaType(t->dll_type, t->dll_mfct).c_str(), t->dll_type,
|
||||
t->dll_version);
|
||||
|
||||
if (possible_drivers == "unknown!")
|
||||
{
|
||||
warning("(meter) please consider opening an issue at https://github.com/weetmuts/wmbusmeters/\n");
|
||||
warning("(meter) to add support for this unknown mfct,media,version combination\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -796,13 +895,15 @@ string concatFields(Meter *m, Telegram *t, char c, vector<Print> &prints, vector
|
|||
return buf;
|
||||
}
|
||||
|
||||
bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<uchar> input_frame, bool simulated, string *ids, bool *id_match)
|
||||
bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<uchar> input_frame,
|
||||
bool simulated, string *ids, bool *id_match, Telegram *out_analyzed)
|
||||
{
|
||||
Telegram t;
|
||||
t.about = about;
|
||||
bool ok = t.parseHeader(input_frame);
|
||||
|
||||
if (simulated) t.markAsSimulated();
|
||||
if (out_analyzed != NULL) t.markAsBeingAnalyzed();
|
||||
|
||||
*ids = t.idsc;
|
||||
|
||||
|
@ -843,6 +944,7 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector<ucha
|
|||
t.explainParse(log_prefix, 0);
|
||||
}
|
||||
triggerUpdate(&t);
|
||||
if (out_analyzed != NULL) *out_analyzed = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -241,7 +241,8 @@ struct Meter
|
|||
// The handleTelegram expects an input_frame where the DLL crcs have been removed.
|
||||
// Returns true of this meter handled this telegram!
|
||||
// Sets id_match to true, if there was an id match, even though the telegram could not be properly handled.
|
||||
virtual bool handleTelegram(AboutTelegram &about, vector<uchar> input_frame, bool simulated, string *id, bool *id_match) = 0;
|
||||
virtual bool handleTelegram(AboutTelegram &about, vector<uchar> input_frame,
|
||||
bool simulated, string *id, bool *id_match, Telegram *out_t = NULL) = 0;
|
||||
virtual MeterKeys *meterKeys() = 0;
|
||||
|
||||
// Dynamically access all data received for the meter.
|
||||
|
@ -270,7 +271,7 @@ struct MeterManager
|
|||
virtual void onTelegram(function<void(AboutTelegram&,vector<uchar>)> cb) = 0;
|
||||
virtual void whenMeterUpdated(std::function<void(Telegram*t,Meter*)> cb) = 0;
|
||||
virtual void pollMeters(shared_ptr<BusManager> bus) = 0;
|
||||
virtual void analyzeEnabled(bool b) = 0;
|
||||
virtual void analyzeEnabled(bool b, OutputFormat f) = 0;
|
||||
virtual void analyzeTelegram(AboutTelegram &about, vector<uchar> &input_frame, bool simulated) = 0;
|
||||
|
||||
virtual ~MeterManager() = default;
|
||||
|
|
|
@ -82,7 +82,8 @@ protected:
|
|||
// The default implementation of poll does nothing.
|
||||
// Override for mbus meters that need to be queried and likewise for C2/T2 wmbus-meters.
|
||||
void poll(shared_ptr<BusManager> bus);
|
||||
bool handleTelegram(AboutTelegram &about, vector<uchar> frame, bool simulated, string *id, bool *id_match);
|
||||
bool handleTelegram(AboutTelegram &about, vector<uchar> frame,
|
||||
bool simulated, string *id, bool *id_match, Telegram *out_analyzed = NULL);
|
||||
void printMeter(Telegram *t,
|
||||
string *human_readable,
|
||||
string *fields, char separator,
|
||||
|
|
|
@ -228,6 +228,11 @@ void checkIfMultipleWmbusMetersRunning();
|
|||
|
||||
size_t findBytes(std::vector<uchar> &v, uchar a, uchar b, uchar c);
|
||||
|
||||
enum class OutputFormat
|
||||
{
|
||||
NONE, PLAIN, TERMINAL, JSON
|
||||
};
|
||||
|
||||
#ifndef FUZZING
|
||||
#define FUZZING false
|
||||
#endif
|
||||
|
|
235
src/wmbus.cc
235
src/wmbus.cc
|
@ -739,7 +739,7 @@ string ciType(int ci_field)
|
|||
return "?";
|
||||
}
|
||||
|
||||
void Telegram::addExplanationAndIncrementPos(vector<uchar>::iterator &pos, int len, const char* fmt, ...)
|
||||
void Telegram::addExplanationAndIncrementPos(vector<uchar>::iterator &pos, int len, KindOfData k, Understanding u, const char* fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
buf[1023] = 0;
|
||||
|
@ -749,7 +749,9 @@ void Telegram::addExplanationAndIncrementPos(vector<uchar>::iterator &pos, int l
|
|||
vsnprintf(buf, 1023, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
explanations.push_back({parsed.size(), buf});
|
||||
|
||||
Explanation e(parsed.size(), len, buf, k, u);
|
||||
explanations.push_back(e);
|
||||
parsed.insert(parsed.end(), pos, pos+len);
|
||||
pos += len;
|
||||
}
|
||||
|
@ -767,11 +769,12 @@ void Telegram::addMoreExplanation(int pos, const char* fmt, ...)
|
|||
|
||||
bool found = false;
|
||||
for (auto& p : explanations) {
|
||||
if (p.first == pos) {
|
||||
if (p.second[0] == '*') {
|
||||
debug("(wmbus) warning: already added more explanations to offset %d!\n");
|
||||
}
|
||||
p.second = string("* ")+p.second+buf;
|
||||
if (p.pos == pos)
|
||||
{
|
||||
// Append more information.
|
||||
p.info = p.info+buf;
|
||||
// Since we are adding more information, we assume that we have a full understanding.
|
||||
p.understanding = Understanding::FULL;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
@ -781,7 +784,7 @@ void Telegram::addMoreExplanation(int pos, const char* fmt, ...)
|
|||
}
|
||||
}
|
||||
|
||||
void Telegram::addSpecialExplanation(int offset, const char* fmt, ...)
|
||||
void Telegram::addSpecialExplanation(int offset, KindOfData k, Understanding u, const char* fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
buf[1023] = 0;
|
||||
|
@ -791,7 +794,7 @@ void Telegram::addSpecialExplanation(int offset, const char* fmt, ...)
|
|||
vsnprintf(buf, 1023, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
explanations.push_back({offset, buf});
|
||||
explanations.push_back({offset, 1, buf, k, u});
|
||||
}
|
||||
|
||||
bool expectedMore(int line)
|
||||
|
@ -808,13 +811,13 @@ bool Telegram::parseMBusDLL(vector<uchar>::iterator &pos)
|
|||
debug("(wmbus) parse MBUS DLL @%d %d\n", distance(frame.begin(), pos), remaining);
|
||||
dll_len = *pos;
|
||||
if (remaining < dll_len) return expectedMore(__LINE__);
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x length (%d bytes)", dll_len, dll_len);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x length (%d bytes)", dll_len, dll_len);
|
||||
|
||||
dll_c = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x dll-c (%s)", dll_c, mbusCField(dll_c).c_str());
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-c (%s)", dll_c, mbusCField(dll_c).c_str());
|
||||
|
||||
mbus_primary_address = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x dll-a primary (%d)", mbus_primary_address, mbus_primary_address);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-a primary (%d)", mbus_primary_address, mbus_primary_address);
|
||||
|
||||
// Add dll_id to ids.
|
||||
string id = tostrprintf("%02x", dll_a[0]);
|
||||
|
@ -832,16 +835,16 @@ bool Telegram::parseDLL(vector<uchar>::iterator &pos)
|
|||
debug("(wmbus) parseDLL @%d %d\n", distance(frame.begin(), pos), remaining);
|
||||
dll_len = *pos;
|
||||
if (remaining < dll_len) return expectedMore(__LINE__);
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x length (%d bytes)", dll_len, dll_len);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x length (%d bytes)", dll_len, dll_len);
|
||||
|
||||
dll_c = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x dll-c (%s)", dll_c, cType(dll_c).c_str());
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-c (%s)", dll_c, cType(dll_c).c_str());
|
||||
|
||||
dll_mfct_b[0] = *(pos+0);
|
||||
dll_mfct_b[1] = *(pos+1);
|
||||
dll_mfct = dll_mfct_b[1] <<8 | dll_mfct_b[0];
|
||||
string man = manufacturerFlag(dll_mfct);
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x dll-mfct (%s)",
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x dll-mfct (%s)",
|
||||
dll_mfct_b[0], dll_mfct_b[1], man.c_str());
|
||||
|
||||
dll_a.resize(6);
|
||||
|
@ -859,13 +862,13 @@ bool Telegram::parseDLL(vector<uchar>::iterator &pos)
|
|||
string id = tostrprintf("%02x%02x%02x%02x", *(pos+3), *(pos+2), *(pos+1), *(pos+0));
|
||||
ids.push_back(id);
|
||||
idsc = id;
|
||||
addExplanationAndIncrementPos(pos, 4, "%02x%02x%02x%02x dll-id (%s)",
|
||||
addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x%02x%02x dll-id (%s)",
|
||||
*(pos+0), *(pos+1), *(pos+2), *(pos+3), ids.back().c_str());
|
||||
|
||||
dll_version = *(pos+0);
|
||||
dll_type = *(pos+1);
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x dll-version", dll_version);
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x dll-type (%s)", dll_type,
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-version", dll_version);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-type (%s)", dll_type,
|
||||
mediaType(dll_type, dll_mfct).c_str());
|
||||
|
||||
return true;
|
||||
|
@ -894,7 +897,7 @@ bool Telegram::parseELL(vector<uchar>::iterator &pos)
|
|||
debug("(wmbus) parseELL @%d %d\n", distance(frame.begin(), pos), remaining);
|
||||
int ci_field = *pos;
|
||||
if (!isCiFieldOfType(ci_field, CI_TYPE::ELL)) return true;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x ell-ci-field (%s)",
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x ell-ci-field (%s)",
|
||||
ci_field, ciType(ci_field).c_str());
|
||||
ell_ci = ci_field;
|
||||
int len = ciFieldLength(ell_ci);
|
||||
|
@ -904,10 +907,10 @@ bool Telegram::parseELL(vector<uchar>::iterator &pos)
|
|||
// All ELL:s (including ELL I) start with cc,acc.
|
||||
|
||||
ell_cc = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x ell-cc (%s)", ell_cc, ccType(ell_cc).c_str());
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x ell-cc (%s)", ell_cc, ccType(ell_cc).c_str());
|
||||
|
||||
ell_acc = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x ell-acc", ell_acc);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x ell-acc", ell_acc);
|
||||
|
||||
bool has_target_mft_address = false;
|
||||
bool has_session_number_pl_crc = false;
|
||||
|
@ -938,7 +941,7 @@ bool Telegram::parseELL(vector<uchar>::iterator &pos)
|
|||
ell_mfct_b[1] = *(pos+1);
|
||||
ell_mfct = ell_mfct_b[1] << 8 | ell_mfct_b[0];
|
||||
string man = manufacturerFlag(ell_mfct);
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x ell-mfct (%s)",
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x ell-mfct (%s)",
|
||||
ell_mfct_b[0], ell_mfct_b[1], man.c_str());
|
||||
|
||||
ell_id_found = true;
|
||||
|
@ -951,14 +954,14 @@ bool Telegram::parseELL(vector<uchar>::iterator &pos)
|
|||
string id = tostrprintf("%02x%02x%02x%02x", *(pos+3), *(pos+2), *(pos+1), *(pos+0));
|
||||
ids.push_back(id);
|
||||
idsc = idsc+","+id;
|
||||
addExplanationAndIncrementPos(pos, 4, "%02x%02x%02x%02x ell-id",
|
||||
addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x%02x%02x ell-id",
|
||||
ell_id_b[0], ell_id_b[1], ell_id_b[2], ell_id_b[3]);
|
||||
|
||||
ell_version = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x ell-version", ell_version);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x ell-version", ell_version);
|
||||
|
||||
ell_type = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x ell-type");
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x ell-type");
|
||||
}
|
||||
|
||||
if (has_session_number_pl_crc)
|
||||
|
@ -975,7 +978,7 @@ bool Telegram::parseELL(vector<uchar>::iterator &pos)
|
|||
ell_sn_sec = (ell_sn >> 29) & 0x7; // next 3 bits.
|
||||
ell_sec_mode = fromIntToELLSecurityMode(ell_sn_sec);
|
||||
string info = toString(ell_sec_mode);
|
||||
addExplanationAndIncrementPos(pos, 4, "%02x%02x%02x%02x sn (%s)",
|
||||
addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x%02x%02x sn (%s)",
|
||||
ell_sn_b[0], ell_sn_b[1], ell_sn_b[2], ell_sn_b[3], info.c_str());
|
||||
|
||||
if (ell_sec_mode == ELLSecurityMode::AES_CTR)
|
||||
|
@ -996,7 +999,7 @@ bool Telegram::parseELL(vector<uchar>::iterator &pos)
|
|||
int len = distance(pos+2, frame.end());
|
||||
uint16_t check = crc16_EN13757(&(frame[dist]), len);
|
||||
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x payload crc (calculated %02x%02x %s)",
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x payload crc (calculated %02x%02x %s)",
|
||||
ell_pl_crc_b[0], ell_pl_crc_b[1],
|
||||
check & 0xff, check >> 8, (ell_pl_crc==check?"OK":"ERROR"));
|
||||
|
||||
|
@ -1037,7 +1040,7 @@ bool Telegram::parseNWL(vector<uchar>::iterator &pos)
|
|||
debug("(wmbus) parseNWL @%d %d\n", distance(frame.begin(), pos), remaining);
|
||||
int ci_field = *pos;
|
||||
if (!isCiFieldOfType(ci_field, CI_TYPE::NWL)) return true;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x nwl-ci-field (%s)",
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x nwl-ci-field (%s)",
|
||||
ci_field, ciType(ci_field).c_str());
|
||||
nwl_ci = ci_field;
|
||||
// We have only seen 0x81 0x1d so far.
|
||||
|
@ -1046,7 +1049,7 @@ bool Telegram::parseNWL(vector<uchar>::iterator &pos)
|
|||
if (remaining < len+1) return expectedMore(__LINE__);
|
||||
|
||||
uchar nwl = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x nwl?", nwl);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x nwl?", nwl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1063,12 +1066,12 @@ bool Telegram::parseAFL(vector<uchar>::iterator &pos)
|
|||
|
||||
int ci_field = *pos;
|
||||
if (!isCiFieldOfType(ci_field, CI_TYPE::AFL)) return true;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x afl-ci-field (%s)",
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x afl-ci-field (%s)",
|
||||
ci_field, ciType(ci_field).c_str());
|
||||
afl_ci = ci_field;
|
||||
|
||||
afl_len = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x afl-len (%d)",
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x afl-len (%d)",
|
||||
afl_len, afl_len);
|
||||
|
||||
int len = ciFieldLength(afl_ci);
|
||||
|
@ -1078,7 +1081,7 @@ bool Telegram::parseAFL(vector<uchar>::iterator &pos)
|
|||
afl_fc_b[1] = *(pos+1);
|
||||
afl_fc = afl_fc_b[1] << 8 | afl_fc_b[0];
|
||||
string afl_fc_info = toStringFromAFLFC(afl_fc);
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x afl-fc (%s)",
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x afl-fc (%s)",
|
||||
afl_fc_b[0], afl_fc_b[1], afl_fc_info.c_str());
|
||||
|
||||
bool has_key_info = afl_fc & 0x0200;
|
||||
|
@ -1092,7 +1095,7 @@ bool Telegram::parseAFL(vector<uchar>::iterator &pos)
|
|||
{
|
||||
afl_mcl = *pos;
|
||||
string afl_mcl_info = toStringFromAFLMC(afl_mcl);
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x afl-mcl (%s)",
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x afl-mcl (%s)",
|
||||
afl_mcl, afl_mcl_info.c_str());
|
||||
}
|
||||
|
||||
|
@ -1102,7 +1105,7 @@ bool Telegram::parseAFL(vector<uchar>::iterator &pos)
|
|||
afl_ki_b[1] = *(pos+1);
|
||||
afl_ki = afl_ki_b[1] << 8 | afl_ki_b[0];
|
||||
string afl_ki_info = "";
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x afl-ki (%s)",
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x afl-ki (%s)",
|
||||
afl_ki_b[0], afl_ki_b[1], afl_ki_info.c_str());
|
||||
}
|
||||
|
||||
|
@ -1117,7 +1120,7 @@ bool Telegram::parseAFL(vector<uchar>::iterator &pos)
|
|||
afl_counter_b[1] << 8 |
|
||||
afl_counter_b[0];
|
||||
|
||||
addExplanationAndIncrementPos(pos, 4, "%02x%02x%02x%02x afl-counter (%u)",
|
||||
addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x%02x%02x afl-counter (%u)",
|
||||
afl_counter_b[0],afl_counter_b[1],
|
||||
afl_counter_b[2],afl_counter_b[3],
|
||||
afl_counter);
|
||||
|
@ -1143,7 +1146,7 @@ bool Telegram::parseAFL(vector<uchar>::iterator &pos)
|
|||
afl_mac_b.insert(afl_mac_b.end(), *(pos+i));
|
||||
}
|
||||
string s = bin2hex(afl_mac_b);
|
||||
addExplanationAndIncrementPos(pos, len, "%s afl-mac %d bytes", s.c_str(), len);
|
||||
addExplanationAndIncrementPos(pos, len, KindOfData::PROTOCOL, Understanding::FULL, "%s afl-mac %d bytes", s.c_str(), len);
|
||||
must_check_mac = true;
|
||||
}
|
||||
|
||||
|
@ -1234,7 +1237,8 @@ bool Telegram::parseTPLConfig(std::vector<uchar>::iterator &pos)
|
|||
tpl_num_encr_blocks = (tpl_cfg >> 4) & 0x0f;
|
||||
has_cfg_ext = true;
|
||||
}
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x tpl-cfg %04x (%s)", cfg1, cfg2, tpl_cfg, info.c_str());
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x%02x tpl-cfg %04x (%s)", cfg1, cfg2, tpl_cfg, info.c_str());
|
||||
|
||||
if (has_cfg_ext)
|
||||
{
|
||||
|
@ -1242,7 +1246,8 @@ bool Telegram::parseTPLConfig(std::vector<uchar>::iterator &pos)
|
|||
tpl_cfg_ext = *(pos+0);
|
||||
tpl_kdf_selection = (tpl_cfg_ext >> 4) & 3;
|
||||
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x tpl-cfg-ext (KDFS=%d)", tpl_cfg_ext, tpl_kdf_selection);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x tpl-cfg-ext (KDFS=%d)", tpl_cfg_ext, tpl_kdf_selection);
|
||||
|
||||
if (tpl_kdf_selection == 1)
|
||||
{
|
||||
|
@ -1309,11 +1314,13 @@ bool Telegram::parseShortTPL(std::vector<uchar>::iterator &pos)
|
|||
CHECK(1);
|
||||
|
||||
tpl_acc = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x tpl-acc-field", tpl_acc);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x tpl-acc-field", tpl_acc);
|
||||
|
||||
CHECK(1);
|
||||
tpl_sts = *pos;
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x tpl-sts-field (%s)", tpl_sts, decodeTPLStatusByte(tpl_sts, NULL).c_str());
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x tpl-sts-field (%s)", tpl_sts, decodeTPLStatusByte(tpl_sts, NULL).c_str());
|
||||
|
||||
bool ok = parseTPLConfig(pos);
|
||||
if (!ok) return false;
|
||||
|
@ -1340,7 +1347,9 @@ bool Telegram::parseLongTPL(std::vector<uchar>::iterator &pos)
|
|||
string id = tostrprintf("%02x%02x%02x%02x", *(pos+3), *(pos+2), *(pos+1), *(pos+0));
|
||||
ids.push_back(id);
|
||||
idsc = idsc+","+id;
|
||||
addExplanationAndIncrementPos(pos, 4, "%02x%02x%02x%02x tpl-id (%02x%02x%02x%02x)", tpl_id_b[0], tpl_id_b[1], tpl_id_b[2], tpl_id_b[3],
|
||||
addExplanationAndIncrementPos(pos, 4, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x%02x%02x%02x tpl-id (%02x%02x%02x%02x)",
|
||||
tpl_id_b[0], tpl_id_b[1], tpl_id_b[2], tpl_id_b[3],
|
||||
tpl_id_b[3], tpl_id_b[2], tpl_id_b[1], tpl_id_b[0]);
|
||||
|
||||
CHECK(2);
|
||||
|
@ -1348,18 +1357,18 @@ bool Telegram::parseLongTPL(std::vector<uchar>::iterator &pos)
|
|||
tpl_mfct_b[1] = *(pos+1);
|
||||
tpl_mfct = tpl_mfct_b[1] << 8 | tpl_mfct_b[0];
|
||||
string man = manufacturerFlag(tpl_mfct);
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x tpl-mfct (%s)", tpl_mfct_b[0], tpl_mfct_b[1], man.c_str());
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x tpl-mfct (%s)", tpl_mfct_b[0], tpl_mfct_b[1], man.c_str());
|
||||
|
||||
CHECK(1);
|
||||
tpl_version = *(pos+0);
|
||||
tpl_a[4] = *(pos+0);
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x tpl-version", tpl_version);
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x tpl-version", tpl_version);
|
||||
|
||||
CHECK(1);
|
||||
tpl_type = *(pos+0);
|
||||
tpl_a[5] = *(pos+0);
|
||||
string info = mediaType(tpl_type, tpl_mfct);
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x tpl-type (%s)", tpl_type, info.c_str());
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x tpl-type (%s)", tpl_type, info.c_str());
|
||||
|
||||
bool ok = parseShortTPL(pos);
|
||||
|
||||
|
@ -1407,7 +1416,7 @@ bool loadFormatBytesFromSignature(uint16_t format_signature, vector<uchar> *form
|
|||
bool Telegram::alreadyDecryptedCBC(vector<uchar>::iterator &pos)
|
||||
{
|
||||
if (*(pos+0) != 0x2f || *(pos+1) != 0x2f) return false;
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x decrypt check bytes", *(pos+0), *(pos+1));
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL, "%02x%02x decrypt check bytes", *(pos+0), *(pos+1));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1463,7 +1472,8 @@ bool Telegram::potentiallyDecrypt(vector<uchar>::iterator &pos)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x decrypt check bytes", *(pos+0), *(pos+1));
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x%02x decrypt check bytes", *(pos+0), *(pos+1));
|
||||
}
|
||||
else if (tpl_sec_mode == TPLSecurityMode::AES_CBC_NO_IV)
|
||||
{
|
||||
|
@ -1471,7 +1481,8 @@ bool Telegram::potentiallyDecrypt(vector<uchar>::iterator &pos)
|
|||
if (meter_keys == NULL || (!meter_keys->hasConfidentialityKey() && isSimulated()))
|
||||
{
|
||||
CHECK(2);
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x (already) decrypted check bytes", *(pos+0), *(pos+1));
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x%02x (already) decrypted check bytes", *(pos+0), *(pos+1));
|
||||
return true;
|
||||
}
|
||||
bool mac_ok = checkMAC(frame, tpl_start, frame.end(), afl_mac_b, tpl_generated_mac_key);
|
||||
|
@ -1521,7 +1532,8 @@ bool Telegram::potentiallyDecrypt(vector<uchar>::iterator &pos)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x decrypt check bytes", *(pos+0), *(pos+1));
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x%02x decrypt check bytes", *(pos+0), *(pos+1));
|
||||
}
|
||||
else if (tpl_sec_mode == TPLSecurityMode::SPECIFIC_16_31)
|
||||
{
|
||||
|
@ -1597,7 +1609,8 @@ bool Telegram::parse_TPL_79(vector<uchar>::iterator &pos)
|
|||
CHECK(2);
|
||||
uchar ecrc0 = *(pos+0);
|
||||
uchar ecrc1 = *(pos+1);
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x format signature", ecrc0, ecrc1);
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x%02x format signature", ecrc0, ecrc1);
|
||||
format_signature = ecrc1<<8 | ecrc0;
|
||||
|
||||
vector<uchar> format_bytes;
|
||||
|
@ -1622,7 +1635,8 @@ bool Telegram::parse_TPL_79(vector<uchar>::iterator &pos)
|
|||
CHECK(2);
|
||||
int ecrc2 = *(pos+0);
|
||||
int ecrc3 = *(pos+1);
|
||||
addExplanationAndIncrementPos(pos, 2, "%02x%02x data crc", ecrc2, ecrc3);
|
||||
addExplanationAndIncrementPos(pos, 2, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x%02x data crc", ecrc2, ecrc3);
|
||||
|
||||
header_size = distance(frame.begin(), pos);
|
||||
int remaining = distance(pos, frame.end());
|
||||
|
@ -1671,7 +1685,8 @@ bool Telegram::parseTPL(vector<uchar>::iterator &pos)
|
|||
tpl_ci = ci_field;
|
||||
tpl_start = pos;
|
||||
|
||||
addExplanationAndIncrementPos(pos, 1, "%02x tpl-ci-field (%s)",
|
||||
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL,
|
||||
"%02x tpl-ci-field (%s)",
|
||||
tpl_ci, ciType(tpl_ci).c_str());
|
||||
int len = ciFieldLength(tpl_ci);
|
||||
|
||||
|
@ -1923,8 +1938,122 @@ bool Telegram::parseHAN(vector<uchar> &input_frame, MeterKeys *mk, bool warn)
|
|||
|
||||
void Telegram::explainParse(string intro, int from)
|
||||
{
|
||||
for (auto& p : explanations) {
|
||||
debug("%s %02x: %s\n", intro.c_str(), p.first, p.second.c_str());
|
||||
for (auto& p : explanations)
|
||||
{
|
||||
// Protocol or content?
|
||||
const char *c = p.kind == KindOfData::PROTOCOL ? " " : "C";
|
||||
const char *u = "?";
|
||||
if (p.understanding == Understanding::FULL) u = "!";
|
||||
if (p.understanding == Understanding::PARTIAL) u = "p";
|
||||
|
||||
// Do not print ok for understood protocol, it is implicit.
|
||||
// However if a protocol is not full understood then print p or ?.
|
||||
if (p.kind == KindOfData::PROTOCOL && p.understanding == Understanding::FULL) u = " ";
|
||||
|
||||
debug("%s %03d %s%s: %s\n", intro.c_str(), p.pos, c, u, p.info.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void printAnalysisAsText(vector<Explanation> &explanations, bool use_ansi)
|
||||
{
|
||||
const char *green;
|
||||
const char *yellow;
|
||||
const char *red;
|
||||
const char *reset;
|
||||
|
||||
if (use_ansi)
|
||||
{
|
||||
green = "\033[0;97m\033[0;42m";
|
||||
yellow = "\033[0;97m\033[0;43m";
|
||||
red = "\033[0;97m\033[0;41m";
|
||||
reset = "\033[0m";
|
||||
}
|
||||
else
|
||||
{
|
||||
green = "";
|
||||
yellow = "";
|
||||
red = "";
|
||||
reset = "";
|
||||
}
|
||||
|
||||
for (auto& p : explanations)
|
||||
{
|
||||
// Protocol or content?
|
||||
const char *c = p.kind == KindOfData::PROTOCOL ? " " : "C";
|
||||
const char *u = "?";
|
||||
if (p.understanding == Understanding::FULL) u = "!";
|
||||
if (p.understanding == Understanding::PARTIAL) u = "p";
|
||||
|
||||
// Do not print ok for understood protocol, it is implicit.
|
||||
// However if a protocol is not full understood then print p or ?.
|
||||
if (p.kind == KindOfData::PROTOCOL && p.understanding == Understanding::FULL) u = " ";
|
||||
|
||||
const char *pre;
|
||||
const char *post = reset;
|
||||
|
||||
if (*u == '!')
|
||||
{
|
||||
pre = green;
|
||||
}
|
||||
else if (*u == 'p')
|
||||
{
|
||||
pre = yellow;
|
||||
}
|
||||
else if (*u == ' ')
|
||||
{
|
||||
pre = "";
|
||||
post = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
pre = red;
|
||||
}
|
||||
|
||||
printf("%03d %s%s: %s%s%s\n", p.pos, c, u, pre, p.info.c_str(), post);
|
||||
}
|
||||
}
|
||||
|
||||
void printAnalysisAsJson(vector<Explanation> &explanations)
|
||||
{
|
||||
printf("{ \"TODO\": true }\n");
|
||||
}
|
||||
|
||||
void Telegram::analyzeParse(OutputFormat format, int *content_length, int *understood_content_length)
|
||||
{
|
||||
int u = 0;
|
||||
int l = 0;
|
||||
|
||||
// Calculate how much is understood.
|
||||
for (auto& e : explanations)
|
||||
{
|
||||
if (e.kind == KindOfData::CONTENT)
|
||||
{
|
||||
l += e.len;
|
||||
if (e.understanding != Understanding::NONE)
|
||||
{
|
||||
// Its content and we have at least some understanding.
|
||||
u += e.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
*content_length = l;
|
||||
*understood_content_length = u;
|
||||
|
||||
switch(format)
|
||||
{
|
||||
case OutputFormat::PLAIN :
|
||||
case OutputFormat::TERMINAL:
|
||||
{
|
||||
bool use_ansi = format == OutputFormat::TERMINAL;
|
||||
printAnalysisAsText(explanations, use_ansi);
|
||||
break;
|
||||
}
|
||||
case OutputFormat::JSON:
|
||||
printAnalysisAsJson(explanations);
|
||||
break;
|
||||
case OutputFormat::NONE:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
33
src/wmbus.h
33
src/wmbus.h
|
@ -353,6 +353,32 @@ struct AboutTelegram
|
|||
AboutTelegram() {}
|
||||
};
|
||||
|
||||
// Mark understood bytes as either PROTOCOL, ie dif vif, acc and other header bytes.
|
||||
// Or CONTENT, ie the value fields found inside the transport layer.
|
||||
enum class KindOfData
|
||||
{
|
||||
PROTOCOL, CONTENT
|
||||
};
|
||||
|
||||
// Content can be not understood at all NONE, partially understood PARTIAL when typically bitsets have
|
||||
// been partially decoded, or FULL when the volume or energy field is by itself complete.
|
||||
enum class Understanding
|
||||
{
|
||||
NONE, PARTIAL, FULL
|
||||
};
|
||||
|
||||
struct Explanation
|
||||
{
|
||||
int pos {};
|
||||
int len {};
|
||||
string info;
|
||||
KindOfData kind {};
|
||||
Understanding understanding {};
|
||||
|
||||
Explanation(int p, int l, const string &i, KindOfData k, Understanding u) :
|
||||
pos(p), len(l), info(i), kind(k), understanding(u) {}
|
||||
};
|
||||
|
||||
struct Telegram
|
||||
{
|
||||
AboutTelegram about;
|
||||
|
@ -482,12 +508,13 @@ struct Telegram
|
|||
|
||||
// A vector of indentations and explanations, to be printed
|
||||
// below the raw data bytes to explain the telegram content.
|
||||
vector<pair<int,string>> explanations;
|
||||
void addExplanationAndIncrementPos(vector<uchar>::iterator &pos, int len, const char* fmt, ...);
|
||||
vector<Explanation> explanations;
|
||||
void addExplanationAndIncrementPos(vector<uchar>::iterator &pos, int len, KindOfData k, Understanding u, const char* fmt, ...);
|
||||
void addMoreExplanation(int pos, const char* fmt, ...);
|
||||
// Add an explanation of data inside manufacturer specific data.
|
||||
void addSpecialExplanation(int offset, const char* fmt, ...);
|
||||
void addSpecialExplanation(int offset, KindOfData k, Understanding u, const char* fmt, ...);
|
||||
void explainParse(string intro, int from);
|
||||
void analyzeParse(OutputFormat o, int *content_length, int *understood_content_length);
|
||||
|
||||
bool parserWarns() { return parser_warns_; }
|
||||
bool isSimulated() { return is_simulated_; }
|
||||
|
|
3
test.sh
3
test.sh
|
@ -120,6 +120,9 @@ if [ "$?" != "0" ]; then RC="1"; fi
|
|||
./tests/test_broken.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
./tests/test_analyze.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
if [ -x ../additional_tests.sh ]
|
||||
then
|
||||
(cd ..; ./additional_tests.sh $PROG)
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
#!/bin/sh
|
||||
|
||||
PROG="$1"
|
||||
TEST=testoutput
|
||||
mkdir -p $TEST
|
||||
|
||||
TESTNAME="Test analyze compact telegram"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
000 : 23 length (35 bytes)
|
||||
001 : 44 dll-c (from meter SND_NR)
|
||||
002 : 2d2c dll-mfct (KAM)
|
||||
004 : 99873476 dll-id (76348799)
|
||||
008 : 1b dll-version
|
||||
009 : 16 dll-type (Cold water meter)
|
||||
010 : 8d ell-ci-field (ELL: Extended Link Layer II (8 Byte))
|
||||
011 : 20 ell-cc (slow_resp sync)
|
||||
012 : 87 ell-acc
|
||||
013 : d19ead21 sn (AES_CTR)
|
||||
017 : 7f17 payload crc (calculated 7f17 OK)
|
||||
019 : 79 tpl-ci-field (EN 13757-3 Application Layer with Compact frame (no tplh))
|
||||
020 : eda8 format signature
|
||||
022 : 6ab6 data crc
|
||||
024 C!: 7100 info codes (DRY(dry 22-31 days))
|
||||
026 C!: 08190000 total consumption (6.408000 m3)
|
||||
030 C!: 08190000 target consumption (6.408000 m3)
|
||||
034 C!: 7F flow temperature (127.000000 °C)
|
||||
035 C!: 13 external temperature (19.000000 °C)
|
||||
Best driver flowiq3100 12/12
|
||||
EOF
|
||||
|
||||
$PROG --analyze=plain 23442D2C998734761B168D2087D19EAD217F1779EDA86AB6710008190000081900007F13 Foof flowiq3100 11111111 NOKEY 2> $TEST/test_output.txt 1>&2
|
||||
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
diff $TEST/test_expected.txt $TEST/test_output.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
echo OK: $TESTNAME
|
||||
TESTRESULT="OK"
|
||||
fi
|
||||
else
|
||||
echo ERROR: $TESTNAME
|
||||
echo "wmbusmeters returned error code: $?"
|
||||
cat $TEST/test_output.txt
|
||||
fi
|
||||
|
||||
TESTNAME="Test analyze find best driver"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
Best driver flowiq3100 12/12
|
||||
EOF
|
||||
|
||||
$PROG --analyze=plain 23442D2C998734761B168D2087D19EAD217F1779EDA86AB6710008190000081900007F13 2> $TEST/test_output.txt 1>&2
|
||||
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
diff $TEST/test_expected.txt $TEST/test_output.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
echo OK: $TESTNAME
|
||||
TESTRESULT="OK"
|
||||
fi
|
||||
else
|
||||
echo ERROR: $TESTNAME
|
||||
echo "wmbusmeters returned error code: $?"
|
||||
cat $TEST/test_output.txt
|
||||
fi
|
||||
|
||||
TESTNAME="Test analyze normal telegram"
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
cat > $TEST/test_expected.txt <<EOF
|
||||
000 : a2 length (162 bytes)
|
||||
001 : 44 dll-c (from meter SND_NR)
|
||||
002 : ee4d dll-mfct (SON)
|
||||
004 : 78563412 dll-id (12345678)
|
||||
008 : 3c dll-version
|
||||
009 : 06 dll-type (Warm Water (30°C-90°C) meter)
|
||||
010 : 7a tpl-ci-field (EN 13757-3 Application Layer (short tplh))
|
||||
011 : 8f tpl-acc-field
|
||||
012 : 00 tpl-sts-field (OK)
|
||||
013 : 0000 tpl-cfg 0000 ( )
|
||||
015 : 0C dif (8 digit BCD Instantaneous value)
|
||||
016 : 13 vif (Volume l)
|
||||
017 C!: 48550000 total consumption (5.548000 m3)
|
||||
021 : 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
||||
022 : 6C vif (Date type G)
|
||||
023 C!: E1F1 set date (2127-01-01)
|
||||
025 : 4C dif (8 digit BCD Instantaneous value storagenr=1)
|
||||
026 : 13 vif (Volume l)
|
||||
027 C!: 00000000 consumption at set date (0.000000 m3)
|
||||
031 : 82 dif (16 Bit Integer/Binary Instantaneous value)
|
||||
032 : 04 dife (subunit=0 tariff=0 storagenr=8)
|
||||
033 : 6C vif (Date type G)
|
||||
034 C!: 2129 history starts with date (2017-09-01)
|
||||
036 : 8C dif (8 digit BCD Instantaneous value)
|
||||
037 : 04 dife (subunit=0 tariff=0 storagenr=8)
|
||||
038 : 13 vif (Volume l)
|
||||
039 C!: 33000000 consumption at history 1 (0.033000 m3)
|
||||
043 : 8D dif (variable length Instantaneous value)
|
||||
044 : 04 dife (subunit=0 tariff=0 storagenr=8)
|
||||
045 : 93 vif (Volume l)
|
||||
046 : 1E vife (Compact profile with register)
|
||||
047 : 3A varlen=58
|
||||
048 C?: 3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000
|
||||
106 : 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
107 : 6D vif (Date and time type)
|
||||
108 C!: 0D0B5C2B device datetime (2018-11-28 11:13)
|
||||
112 : 03 dif (24 Bit Integer/Binary Instantaneous value)
|
||||
113 : FD vif (Second extension FD of VIF-codes)
|
||||
114 : 6C vife (Operating time battery [hour(s)])
|
||||
115 C?: 5E1500
|
||||
118 : 82 dif (16 Bit Integer/Binary Instantaneous value)
|
||||
119 : 20 dife (subunit=0 tariff=2 storagenr=0)
|
||||
120 : 6C vif (Date type G)
|
||||
121 C?: 5C29
|
||||
123 : 0B dif (6 digit BCD Instantaneous value)
|
||||
124 : FD vif (Second extension FD of VIF-codes)
|
||||
125 : 0F vife (Software version #)
|
||||
126 C?: 020001
|
||||
129 : 8C dif (8 digit BCD Instantaneous value)
|
||||
130 : 40 dife (subunit=1 tariff=0 storagenr=0)
|
||||
131 : 79 vif (Enhanced identification)
|
||||
132 C?: 67888523
|
||||
136 : 83 dif (24 Bit Integer/Binary Instantaneous value)
|
||||
137 : 10 dife (subunit=0 tariff=1 storagenr=0)
|
||||
138 : FD vif (Second extension FD of VIF-codes)
|
||||
139 : 31 vife (Duration of tariff [minute(s)])
|
||||
140 C?: 000000
|
||||
143 : 82 dif (16 Bit Integer/Binary Instantaneous value)
|
||||
144 : 10 dife (subunit=0 tariff=1 storagenr=0)
|
||||
145 : 6C vif (Date type G)
|
||||
146 C?: 0101
|
||||
148 : 81 dif (8 Bit Integer/Binary Instantaneous value)
|
||||
149 : 10 dife (subunit=0 tariff=1 storagenr=0)
|
||||
150 : FD vif (Second extension FD of VIF-codes)
|
||||
151 : 61 vife (Cumulation counter)
|
||||
152 C?: 00
|
||||
153 : 02 dif (16 Bit Integer/Binary Instantaneous value)
|
||||
154 : FD vif (Second extension FD of VIF-codes)
|
||||
155 : 66 vife (State of parameter activation)
|
||||
156 C?: 0200
|
||||
158 : 02 dif (16 Bit Integer/Binary Instantaneous value)
|
||||
159 : FD vif (Second extension FD of VIF-codes)
|
||||
160 : 17 vife (Error flags (binary))
|
||||
161 C?: 0000
|
||||
Best driver evo868 20/100
|
||||
EOF
|
||||
|
||||
$PROG --analyze=plain A244EE4D785634123C067A8F0000000C1348550000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0D0B5C2B03FD6C5E150082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002FD170000 Water evo868 11111111 NOKEY 2> $TEST/test_output.txt 1>&2
|
||||
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
diff $TEST/test_expected.txt $TEST/test_output.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
echo OK: $TESTNAME
|
||||
TESTRESULT="OK"
|
||||
fi
|
||||
else
|
||||
echo "wmbusmeters returned error code: $?"
|
||||
cat $TEST/test_output.txt
|
||||
fi
|
Ładowanie…
Reference in New Issue