New function --analyze now works but is not yet complete.

pull/418/head
Fredrik Öhrström 2021-12-07 23:56:29 +01:00
rodzic ae6e10a291
commit 2ecb3e90d9
22 zmienionych plików z 589 dodań i 110 usunięć

Wyświetl plik

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

Wyświetl plik

@ -59,6 +59,7 @@ struct Configuration
bool version {};
bool license {};
bool analyze {};
OutputFormat analyze_format {};
bool debug {};
bool trace {};
AddLogTimestamps addtimestamps {};

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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