2018-04-01 06:53:37 +00:00
|
|
|
/*
|
2020-01-27 08:29:40 +00:00
|
|
|
Copyright (C) 2018-2020 Fredrik Öhrström
|
2018-04-01 06:53:37 +00:00
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include"dvparser.h"
|
|
|
|
#include"util.h"
|
|
|
|
|
2018-12-28 17:35:32 +00:00
|
|
|
#include<assert.h>
|
2019-01-01 20:04:06 +00:00
|
|
|
#include<memory.h>
|
2018-12-28 17:35:32 +00:00
|
|
|
|
2018-04-16 18:47:27 +00:00
|
|
|
// The parser should not crash on invalid data, but yeah, when I
|
|
|
|
// need to debug it because it crashes on invalid data, then
|
|
|
|
// I enable the following define...
|
2018-11-01 18:56:42 +00:00
|
|
|
//#define DEBUG_PARSER(...) fprintf(stdout, __VA_ARGS__)
|
2018-04-16 18:47:27 +00:00
|
|
|
#define DEBUG_PARSER(...)
|
|
|
|
|
2018-04-01 06:53:37 +00:00
|
|
|
using namespace std;
|
|
|
|
|
2020-01-28 18:09:39 +00:00
|
|
|
const char *toString(ValueInformation v)
|
2019-01-27 23:03:25 +00:00
|
|
|
{
|
|
|
|
switch (v) {
|
|
|
|
#define X(name,from,to) case ValueInformation::name: return #name;
|
|
|
|
LIST_OF_VALUETYPES
|
|
|
|
#undef X
|
|
|
|
}
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
2020-01-28 18:09:39 +00:00
|
|
|
ValueInformation toValueInformation(int i)
|
|
|
|
{
|
|
|
|
switch (i) {
|
|
|
|
#define X(name,from,to) if (from >= i && i <= to) return ValueInformation::name;
|
|
|
|
LIST_OF_VALUETYPES
|
|
|
|
#undef X
|
|
|
|
}
|
|
|
|
return ValueInformation::None;
|
|
|
|
}
|
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
map<uint16_t,string> hash_to_format_;
|
|
|
|
|
|
|
|
bool loadFormatBytesFromSignature(uint16_t format_signature, vector<uchar> *format_bytes)
|
|
|
|
{
|
|
|
|
if (hash_to_format_.count(format_signature) > 0) {
|
|
|
|
debug("(dvparser) found remembered format for hash %x\n", format_signature);
|
|
|
|
// Return the proper hash!
|
|
|
|
hex2bin(hash_to_format_[format_signature], format_bytes);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Unknown format signature.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-04-01 06:53:37 +00:00
|
|
|
bool parseDV(Telegram *t,
|
2018-12-28 17:35:32 +00:00
|
|
|
vector<uchar> &databytes,
|
2018-04-01 06:53:37 +00:00
|
|
|
vector<uchar>::iterator data,
|
|
|
|
size_t data_len,
|
2019-01-27 23:03:25 +00:00
|
|
|
map<string,pair<int,DVEntry>> *values,
|
2018-04-01 06:53:37 +00:00
|
|
|
vector<uchar>::iterator *format,
|
|
|
|
size_t format_len,
|
2019-08-12 09:47:39 +00:00
|
|
|
uint16_t *format_hash)
|
2018-04-01 06:53:37 +00:00
|
|
|
{
|
|
|
|
map<string,int> dv_count;
|
|
|
|
vector<uchar> format_bytes;
|
2019-01-01 20:04:06 +00:00
|
|
|
vector<uchar> id_bytes;
|
2018-04-01 06:53:37 +00:00
|
|
|
vector<uchar> data_bytes;
|
|
|
|
string dv, key;
|
2018-04-16 18:47:27 +00:00
|
|
|
size_t start_parse_here = t->parsed.size();
|
2018-04-01 06:53:37 +00:00
|
|
|
vector<uchar>::iterator data_start = data;
|
|
|
|
vector<uchar>::iterator data_end = data+data_len;
|
|
|
|
vector<uchar>::iterator format_end;
|
2019-01-01 20:04:06 +00:00
|
|
|
bool data_has_difvifs = true;
|
2018-11-01 18:56:42 +00:00
|
|
|
bool variable_length = false;
|
2018-04-01 06:53:37 +00:00
|
|
|
|
|
|
|
if (format == NULL) {
|
2019-01-01 20:04:06 +00:00
|
|
|
// No format string was supplied, we therefore assume
|
|
|
|
// that the difvifs necessary to parse the data is
|
|
|
|
// part of the data! This is the default.
|
2018-04-01 06:53:37 +00:00
|
|
|
format = &data;
|
|
|
|
format_end = data_end;
|
|
|
|
} else {
|
2019-01-01 20:04:06 +00:00
|
|
|
// A format string has been supplied. The data is compressed,
|
|
|
|
// and can only be decoded using the supplied difvifs.
|
|
|
|
// Since the data does not have the difvifs.
|
|
|
|
data_has_difvifs = false;
|
2018-04-01 06:53:37 +00:00
|
|
|
format_end = *format+format_len;
|
2019-03-15 13:21:50 +00:00
|
|
|
string s = bin2hex(*format, format_end, format_len);
|
2018-04-01 06:53:37 +00:00
|
|
|
debug("(dvparser) using format \"%s\"\n", s.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Data format is:
|
2019-01-01 20:04:06 +00:00
|
|
|
|
2018-04-01 06:53:37 +00:00
|
|
|
// DIF byte (defines how the binary data bits should be decoded and how man data bytes there are)
|
2019-01-01 20:04:06 +00:00
|
|
|
// Sometimes followed by one or more dife bytes, if the 0x80 high bit is set.
|
|
|
|
// The last dife byte does not have the 0x80 bit set.
|
|
|
|
|
2018-04-01 06:53:37 +00:00
|
|
|
// VIF byte (defines what the decoded value means, water,energy,power,etc.)
|
2019-01-01 20:04:06 +00:00
|
|
|
// Sometimes followed by one or more vife bytes, if the 0x80 high bit is set.
|
|
|
|
// The last vife byte does not have the 0x80 bit set.
|
|
|
|
|
|
|
|
// Data bytes, the number of data bytes are defined by the dif format.
|
|
|
|
// Or if the dif says variable length, then the first data byte specifies the number of data bytes.
|
|
|
|
|
2018-04-01 06:53:37 +00:00
|
|
|
// DIF again...
|
|
|
|
|
2019-01-01 20:04:06 +00:00
|
|
|
// A Dif(Difes)Vif(Vifes) identifier can be for example be the 02FF20 for the Multical21
|
|
|
|
// vendor specific status bits. The parser then uses this identifier as a key to store the
|
2019-03-20 21:16:45 +00:00
|
|
|
// data bytes in a map. The same identifier could occur several times in a telegram,
|
2019-01-01 20:04:06 +00:00
|
|
|
// even though it often don't. Since the first occurence is stored under 02FF20,
|
|
|
|
// the second identical identifier stores its data under the key "02FF20_2" etc for 3 and forth...
|
2019-03-20 21:16:45 +00:00
|
|
|
// A proper meter would use storagenr etc to differentiate between different measurements of
|
|
|
|
// the same value.
|
2018-04-01 06:53:37 +00:00
|
|
|
|
|
|
|
format_bytes.clear();
|
2019-01-01 20:04:06 +00:00
|
|
|
id_bytes.clear();
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
id_bytes.clear();
|
2018-11-25 11:33:14 +00:00
|
|
|
DEBUG_PARSER("(dvparser debug) Remaining format data %ju\n", std::distance(*format,format_end));
|
2018-04-01 06:53:37 +00:00
|
|
|
if (*format == format_end) break;
|
|
|
|
uchar dif = **format;
|
2018-04-02 14:51:17 +00:00
|
|
|
|
2019-10-14 18:26:31 +00:00
|
|
|
MeasurementType mt = difMeasurementType(dif);
|
2018-11-02 17:06:48 +00:00
|
|
|
int datalen = difLenBytes(dif);
|
2019-10-14 18:26:31 +00:00
|
|
|
DEBUG_PARSER("(dvparser debug) dif=%02x datalen=%d \"%s\" type=%s\n", dif, datalen, difType(dif).c_str(),
|
|
|
|
measurementTypeName(mt).c_str());
|
2018-11-25 11:33:14 +00:00
|
|
|
if (datalen == -2) {
|
2020-01-27 08:29:40 +00:00
|
|
|
debug("(dvparser) cannot handle dif %02X ignoring rest of telegram.\n", dif);
|
2019-01-01 20:04:06 +00:00
|
|
|
break;
|
2018-11-25 11:33:14 +00:00
|
|
|
}
|
2018-04-02 14:51:17 +00:00
|
|
|
if (dif == 0x2f) {
|
2020-01-20 19:41:33 +00:00
|
|
|
t->addExplanationAndIncrementPos(*format, 1, "%02X skip", dif);
|
2019-01-01 20:04:06 +00:00
|
|
|
DEBUG_PARSER("\n");
|
2018-04-02 14:51:17 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-11-02 17:06:48 +00:00
|
|
|
if (datalen == -1) {
|
2018-11-01 18:56:42 +00:00
|
|
|
variable_length = true;
|
|
|
|
} else {
|
|
|
|
variable_length = false;
|
|
|
|
}
|
2019-01-01 20:04:06 +00:00
|
|
|
if (data_has_difvifs) {
|
2018-04-01 06:53:37 +00:00
|
|
|
format_bytes.push_back(dif);
|
2019-01-01 20:04:06 +00:00
|
|
|
id_bytes.push_back(dif);
|
2020-01-20 19:41:33 +00:00
|
|
|
t->addExplanationAndIncrementPos(*format, 1, "%02X dif (%s)", dif, difType(dif).c_str());
|
2018-04-01 06:53:37 +00:00
|
|
|
} else {
|
2019-01-01 20:04:06 +00:00
|
|
|
id_bytes.push_back(**format);
|
2018-04-01 06:53:37 +00:00
|
|
|
(*format)++;
|
|
|
|
}
|
|
|
|
|
2019-01-01 23:20:10 +00:00
|
|
|
|
|
|
|
int difenr = 0;
|
|
|
|
int subunit = 0;
|
|
|
|
int tariff = 0;
|
2019-01-27 23:03:25 +00:00
|
|
|
int lsb_of_storage_nr = (dif & 0x40) >> 6;
|
|
|
|
int storage_nr = lsb_of_storage_nr;
|
2019-01-01 23:20:10 +00:00
|
|
|
|
2019-01-01 20:04:06 +00:00
|
|
|
bool has_another_dife = (dif & 0x80) == 0x80;
|
2019-01-01 23:20:10 +00:00
|
|
|
|
2019-01-01 20:04:06 +00:00
|
|
|
while (has_another_dife) {
|
2019-03-15 13:27:09 +00:00
|
|
|
if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (dife expected)\n"); break; }
|
2019-01-01 20:04:06 +00:00
|
|
|
uchar dife = **format;
|
2019-01-01 23:20:10 +00:00
|
|
|
int subunit_bit = (dife & 0x40) >> 6;
|
|
|
|
subunit |= subunit_bit << difenr;
|
|
|
|
int tariff_bits = (dife & 0x30) >> 4;
|
|
|
|
tariff |= tariff_bits << (difenr*2);
|
|
|
|
int storage_nr_bits = (dife & 0x0f);
|
2019-01-27 23:03:25 +00:00
|
|
|
storage_nr |= storage_nr_bits << (1+difenr*4);
|
2019-01-01 23:20:10 +00:00
|
|
|
|
|
|
|
DEBUG_PARSER("(dvparser debug) dife=%02x (subunit=%d tariff=%d storagenr=%d)\n",
|
|
|
|
dife, subunit, tariff, storage_nr);
|
2019-01-01 20:04:06 +00:00
|
|
|
if (data_has_difvifs) {
|
|
|
|
format_bytes.push_back(dife);
|
|
|
|
id_bytes.push_back(dife);
|
2020-01-20 19:41:33 +00:00
|
|
|
t->addExplanationAndIncrementPos(*format, 1, "%02X dife (subunit=%d tariff=%d storagenr=%d)",
|
2019-01-01 23:20:10 +00:00
|
|
|
dife, subunit, tariff, storage_nr);
|
2019-01-01 20:04:06 +00:00
|
|
|
} else {
|
|
|
|
id_bytes.push_back(**format);
|
|
|
|
(*format)++;
|
|
|
|
}
|
2019-01-01 23:20:10 +00:00
|
|
|
|
2019-01-01 20:04:06 +00:00
|
|
|
has_another_dife = (dife & 0x80) == 0x80;
|
2019-01-01 23:20:10 +00:00
|
|
|
difenr++;
|
2019-01-01 20:04:06 +00:00
|
|
|
}
|
|
|
|
|
2019-03-15 13:27:09 +00:00
|
|
|
if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vif expected)\n"); break; }
|
2018-04-01 06:53:37 +00:00
|
|
|
|
|
|
|
uchar vif = **format;
|
2018-11-25 11:33:14 +00:00
|
|
|
DEBUG_PARSER("(dvparser debug) vif=%02x \"%s\"\n", vif, vifType(vif).c_str());
|
2019-01-01 20:04:06 +00:00
|
|
|
if (data_has_difvifs) {
|
2018-04-01 06:53:37 +00:00
|
|
|
format_bytes.push_back(vif);
|
2019-01-01 20:04:06 +00:00
|
|
|
id_bytes.push_back(vif);
|
2020-01-20 19:41:33 +00:00
|
|
|
t->addExplanationAndIncrementPos(*format, 1, "%02X vif (%s)", vif, vifType(vif).c_str());
|
2018-04-01 06:53:37 +00:00
|
|
|
} else {
|
2019-01-01 20:04:06 +00:00
|
|
|
id_bytes.push_back(**format);
|
2018-04-01 06:53:37 +00:00
|
|
|
(*format)++;
|
|
|
|
}
|
|
|
|
|
2019-01-01 20:04:06 +00:00
|
|
|
bool has_another_vife = (vif & 0x80) == 0x80;
|
|
|
|
while (has_another_vife) {
|
2019-03-15 13:27:09 +00:00
|
|
|
if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vife expected)\n"); break; }
|
2018-04-01 06:53:37 +00:00
|
|
|
uchar vife = **format;
|
2019-01-01 23:20:10 +00:00
|
|
|
DEBUG_PARSER("(dvparser debug) vife=%02x (%s)\n", vife, vifeType(dif, vif, vife).c_str());
|
2019-01-01 20:04:06 +00:00
|
|
|
if (data_has_difvifs) {
|
2018-04-01 06:53:37 +00:00
|
|
|
format_bytes.push_back(vife);
|
2019-01-01 20:04:06 +00:00
|
|
|
id_bytes.push_back(vife);
|
2020-01-20 19:41:33 +00:00
|
|
|
t->addExplanationAndIncrementPos(*format, 1, "%02X vife (%s)", vife, vifeType(dif, vif, vife).c_str());
|
2018-04-01 06:53:37 +00:00
|
|
|
} else {
|
2019-01-01 20:04:06 +00:00
|
|
|
id_bytes.push_back(**format);
|
2018-04-01 06:53:37 +00:00
|
|
|
(*format)++;
|
|
|
|
}
|
2019-01-01 20:04:06 +00:00
|
|
|
has_another_vife = (vife & 0x80) == 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
dv = "";
|
|
|
|
for (uchar c : id_bytes) {
|
|
|
|
char hex[3];
|
|
|
|
hex[2] = 0;
|
|
|
|
snprintf(hex, 3, "%02X", c);
|
|
|
|
dv.append(hex);
|
2018-04-01 06:53:37 +00:00
|
|
|
}
|
2019-01-01 20:04:06 +00:00
|
|
|
DEBUG_PARSER("(dvparser debug) key \"%s\"\n", dv.c_str());
|
2018-04-01 06:53:37 +00:00
|
|
|
|
2018-04-16 18:47:27 +00:00
|
|
|
int count = ++dv_count[dv];
|
2018-04-01 06:53:37 +00:00
|
|
|
if (count > 1) {
|
|
|
|
strprintf(key, "%s_%d", dv.c_str(), count);
|
|
|
|
} else {
|
|
|
|
strprintf(key, "%s", dv.c_str());
|
|
|
|
}
|
2018-11-25 11:33:14 +00:00
|
|
|
DEBUG_PARSER("(dvparser debug) DifVif key is %s\n", key.c_str());
|
2018-04-01 06:53:37 +00:00
|
|
|
|
2018-11-01 18:56:42 +00:00
|
|
|
int remaining = std::distance(data, data_end);
|
|
|
|
if (variable_length) {
|
2018-12-28 17:35:32 +00:00
|
|
|
DEBUG_PARSER("(dvparser debug) varlen %02x\n", *(data+0));
|
2018-11-25 11:33:14 +00:00
|
|
|
if (remaining > 2) {
|
|
|
|
datalen = *(data);
|
|
|
|
} else {
|
|
|
|
datalen = remaining;
|
|
|
|
}
|
2018-11-01 18:56:42 +00:00
|
|
|
}
|
2018-11-25 11:33:14 +00:00
|
|
|
DEBUG_PARSER("(dvparser debug) remaining data %d len=%d\n", remaining, datalen);
|
2018-11-02 17:06:48 +00:00
|
|
|
if (remaining < datalen) {
|
2018-11-29 21:25:50 +00:00
|
|
|
debug("(dvparser) warning: unexpected end of data\n");
|
2019-03-15 13:21:50 +00:00
|
|
|
datalen = remaining-1;
|
2018-04-16 18:47:27 +00:00
|
|
|
}
|
|
|
|
|
2019-01-01 20:04:06 +00:00
|
|
|
// Skip the length byte in the variable length data.
|
|
|
|
if (variable_length) {
|
2020-01-20 19:41:33 +00:00
|
|
|
t->addExplanationAndIncrementPos(data, 1, "%02X varlen=%d", datalen, datalen);
|
2019-01-01 20:04:06 +00:00
|
|
|
}
|
2019-03-15 13:21:50 +00:00
|
|
|
string value = bin2hex(data, data_end, datalen);
|
2019-03-01 14:41:11 +00:00
|
|
|
int offset = start_parse_here+data-data_start;
|
2019-10-14 18:26:31 +00:00
|
|
|
(*values)[key] = { offset, DVEntry(mt, vif&0x7f, storage_nr, tariff, subunit, value) };
|
2018-11-02 15:20:46 +00:00
|
|
|
if (value.length() > 0) {
|
2018-11-25 11:33:14 +00:00
|
|
|
// This call increments data with datalen.
|
2020-01-20 19:41:33 +00:00
|
|
|
t->addExplanationAndIncrementPos(data, datalen, "%s", value.c_str());
|
2019-01-01 20:04:06 +00:00
|
|
|
DEBUG_PARSER("(dvparser debug) data \"%s\"\n\n", value.c_str());
|
2018-11-02 15:20:46 +00:00
|
|
|
}
|
2019-03-15 13:21:50 +00:00
|
|
|
if (remaining == datalen || data == databytes.end()) {
|
2018-11-29 21:25:50 +00:00
|
|
|
// We are done here!
|
|
|
|
break;
|
|
|
|
}
|
2018-04-01 06:53:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
string format_string = bin2hex(format_bytes);
|
|
|
|
uint16_t hash = crc16_EN13757(&format_bytes[0], format_bytes.size());
|
|
|
|
|
2019-01-01 20:04:06 +00:00
|
|
|
if (data_has_difvifs) {
|
2019-01-27 23:03:25 +00:00
|
|
|
if (hash_to_format_.count(hash) == 0) {
|
|
|
|
hash_to_format_[hash] = format_string;
|
|
|
|
debug("(dvparser) found new format \"%s\" with hash %x, remembering!\n", format_string.c_str(), hash);
|
|
|
|
}
|
2018-04-01 06:53:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
void valueInfoRange(ValueInformation v, int *low, int *hi)
|
|
|
|
{
|
|
|
|
switch (v) {
|
2020-01-28 18:09:39 +00:00
|
|
|
#define X(name,from,to) case ValueInformation::name: *low = from; *hi = to; return;
|
2019-01-27 23:03:25 +00:00
|
|
|
LIST_OF_VALUETYPES
|
|
|
|
#undef X
|
|
|
|
}
|
2020-01-28 18:09:39 +00:00
|
|
|
assert(0);
|
2019-01-27 23:03:25 +00:00
|
|
|
}
|
|
|
|
|
2019-03-01 14:41:11 +00:00
|
|
|
bool hasKey(std::map<std::string,std::pair<int,DVEntry>> *values, std::string key)
|
|
|
|
{
|
|
|
|
return values->count(key) > 0;
|
|
|
|
}
|
|
|
|
|
2020-01-28 18:09:39 +00:00
|
|
|
bool findKey(MeasurementType mit, ValueInformation vif, int storagenr,
|
|
|
|
std::string *key, std::map<std::string,std::pair<int,DVEntry>> *values)
|
2019-01-27 23:03:25 +00:00
|
|
|
{
|
|
|
|
int low, hi;
|
|
|
|
valueInfoRange(vif, &low, &hi);
|
|
|
|
|
2020-01-28 18:09:39 +00:00
|
|
|
debug("(dvparser) looking for type=%s vif=%s storagenr=%d value_ran_low=%02x value_ran_hi=%02x\n",
|
|
|
|
measurementTypeName(mit).c_str(), toString(vif), storagenr,
|
|
|
|
low, hi);
|
2019-10-14 18:26:31 +00:00
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
for (auto& v : *values)
|
|
|
|
{
|
2019-10-14 18:26:31 +00:00
|
|
|
MeasurementType ty = v.second.second.type;
|
2019-01-27 23:03:25 +00:00
|
|
|
int vi = v.second.second.value_information;
|
|
|
|
int sn = v.second.second.storagenr;
|
2020-01-28 18:09:39 +00:00
|
|
|
debug("(dvparser) match? %s type=%s vif=%02x (%s) and storagenr=%d\n",
|
|
|
|
v.first.c_str(),
|
|
|
|
measurementTypeName(ty).c_str(), vi, toString(toValueInformation(vi)), storagenr, sn);
|
2019-10-14 18:26:31 +00:00
|
|
|
|
|
|
|
if (vi >= low && vi <= hi
|
|
|
|
&& (mit == MeasurementType::Unknown || mit == ty)
|
|
|
|
&& (storagenr == ANY_STORAGENR || storagenr == sn))
|
|
|
|
{
|
2019-01-27 23:03:25 +00:00
|
|
|
*key = v.first;
|
2020-01-28 18:09:39 +00:00
|
|
|
debug("(dvparser) found key %s for type=%s vif=%02x (%s) storagenr=%d\n",
|
|
|
|
v.first.c_str(), measurementTypeName(ty).c_str(),
|
|
|
|
vi, toString(toValueInformation(vi)), storagenr);
|
2019-01-27 23:03:25 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-01-01 20:04:06 +00:00
|
|
|
void extractDV(string &s, uchar *dif, uchar *vif)
|
2018-04-01 06:53:37 +00:00
|
|
|
{
|
|
|
|
vector<uchar> bytes;
|
|
|
|
hex2bin(s, &bytes);
|
|
|
|
*dif = bytes[0];
|
2019-01-01 20:04:06 +00:00
|
|
|
bool has_another_dife = (*dif & 0x80) == 0x80;
|
|
|
|
size_t i=1;
|
|
|
|
while (has_another_dife) {
|
|
|
|
if (i >= bytes.size()) {
|
|
|
|
debug("(dvparser) Invalid key \"%s\" used. Settinf vif to zero.\n", s.c_str());
|
|
|
|
*vif = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
uchar dife = bytes[i];
|
|
|
|
has_another_dife = (dife & 0x80) == 0x80;
|
|
|
|
i++;
|
2018-04-01 06:53:37 +00:00
|
|
|
}
|
2019-01-01 20:04:06 +00:00
|
|
|
|
|
|
|
*vif = bytes[i];
|
2018-04-01 06:53:37 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 15:12:32 +00:00
|
|
|
bool extractDVuint8(map<string,pair<int,DVEntry>> *values,
|
|
|
|
string key,
|
|
|
|
int *offset,
|
|
|
|
uchar *value)
|
|
|
|
{
|
|
|
|
if ((*values).count(key) == 0) {
|
|
|
|
verbose("(dvparser) warning: cannot extract uint16 from non-existant key \"%s\"\n", key.c_str());
|
|
|
|
*offset = -1;
|
|
|
|
*value = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
uchar dif, vif;
|
|
|
|
extractDV(key, &dif, &vif);
|
|
|
|
|
|
|
|
pair<int,DVEntry>& p = (*values)[key];
|
|
|
|
*offset = p.first;
|
|
|
|
vector<uchar> v;
|
|
|
|
hex2bin(p.second.value, &v);
|
|
|
|
|
|
|
|
*value = v[0];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
bool extractDVuint16(map<string,pair<int,DVEntry>> *values,
|
2018-04-01 06:53:37 +00:00
|
|
|
string key,
|
|
|
|
int *offset,
|
|
|
|
uint16_t *value)
|
|
|
|
{
|
|
|
|
if ((*values).count(key) == 0) {
|
2018-12-01 10:26:40 +00:00
|
|
|
verbose("(dvparser) warning: cannot extract uint16 from non-existant key \"%s\"\n", key.c_str());
|
2018-04-01 06:53:37 +00:00
|
|
|
*offset = -1;
|
|
|
|
*value = 0;
|
|
|
|
return false;
|
|
|
|
}
|
2019-01-01 20:04:06 +00:00
|
|
|
uchar dif, vif;
|
|
|
|
extractDV(key, &dif, &vif);
|
2018-04-01 06:53:37 +00:00
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
pair<int,DVEntry>& p = (*values)[key];
|
2018-04-01 06:53:37 +00:00
|
|
|
*offset = p.first;
|
|
|
|
vector<uchar> v;
|
2019-01-27 23:03:25 +00:00
|
|
|
hex2bin(p.second.value, &v);
|
2018-04-01 06:53:37 +00:00
|
|
|
|
|
|
|
*value = v[1]<<8 | v[0];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
bool extractDVdouble(map<string,pair<int,DVEntry>> *values,
|
2018-11-02 17:57:56 +00:00
|
|
|
string key,
|
|
|
|
int *offset,
|
2019-10-14 14:53:02 +00:00
|
|
|
double *value,
|
|
|
|
bool auto_scale)
|
2018-04-01 06:53:37 +00:00
|
|
|
{
|
|
|
|
if ((*values).count(key) == 0) {
|
2018-12-01 10:26:40 +00:00
|
|
|
verbose("(dvparser) warning: cannot extract double from non-existant key \"%s\"\n", key.c_str());
|
2018-04-01 06:53:37 +00:00
|
|
|
*offset = 0;
|
|
|
|
*value = 0;
|
|
|
|
return false;
|
|
|
|
}
|
2019-01-01 20:04:06 +00:00
|
|
|
uchar dif, vif;
|
|
|
|
extractDV(key, &dif, &vif);
|
2018-04-01 06:53:37 +00:00
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
pair<int,DVEntry>& p = (*values)[key];
|
2018-04-01 06:53:37 +00:00
|
|
|
*offset = p.first;
|
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
if (p.second.value.length() == 0) {
|
2018-12-01 10:26:40 +00:00
|
|
|
verbose("(dvparser) warning: key found but no data \"%s\"\n", key.c_str());
|
|
|
|
*offset = 0;
|
|
|
|
*value = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-11-02 17:57:56 +00:00
|
|
|
int t = dif&0xf;
|
|
|
|
if (t == 0x1 || // 8 Bit Integer/Binary
|
|
|
|
t == 0x2 || // 16 Bit Integer/Binary
|
|
|
|
t == 0x3 || // 24 Bit Integer/Binary
|
|
|
|
t == 0x4 || // 32 Bit Integer/Binary
|
|
|
|
t == 0x6 || // 48 Bit Integer/Binary
|
|
|
|
t == 0x7) // 64 Bit Integer/Binary
|
|
|
|
{
|
|
|
|
vector<uchar> v;
|
2019-01-27 23:03:25 +00:00
|
|
|
hex2bin(p.second.value, &v);
|
2018-11-02 17:57:56 +00:00
|
|
|
unsigned int raw = 0;
|
|
|
|
if (t == 0x1) {
|
2019-01-27 23:03:25 +00:00
|
|
|
assert(v.size() == 1);
|
2018-11-02 17:57:56 +00:00
|
|
|
raw = v[0];
|
|
|
|
} else if (t == 0x2) {
|
2019-01-27 23:03:25 +00:00
|
|
|
assert(v.size() == 2);
|
2018-11-02 17:57:56 +00:00
|
|
|
raw = v[1]*256 + v[0];
|
|
|
|
} else if (t == 0x3) {
|
2019-01-27 23:03:25 +00:00
|
|
|
assert(v.size() == 3);
|
2018-11-02 17:57:56 +00:00
|
|
|
raw = v[2]*256*256 + v[1]*256 + v[0];
|
|
|
|
} else if (t == 0x4) {
|
2019-01-27 23:03:25 +00:00
|
|
|
assert(v.size() == 4);
|
2018-11-02 17:57:56 +00:00
|
|
|
raw = ((unsigned int)v[3])*256*256*256
|
|
|
|
+ ((unsigned int)v[2])*256*256
|
|
|
|
+ ((unsigned int)v[1])*256
|
|
|
|
+ ((unsigned int)v[0]);
|
2020-01-28 18:09:39 +00:00
|
|
|
} else if (t == 0x6) {
|
|
|
|
assert(v.size() == 6);
|
|
|
|
raw = ((uint64_t)v[5])*256*256*256*256*256
|
|
|
|
+ ((uint64_t)v[4])*256*256*256*256
|
|
|
|
+ ((uint64_t)v[3])*256*256*256
|
|
|
|
+ ((uint64_t)v[2])*256*256
|
|
|
|
+ ((uint64_t)v[1])*256
|
|
|
|
+ ((uint64_t)v[0]);
|
|
|
|
} else if (t == 0x7) {
|
|
|
|
assert(v.size() == 8);
|
|
|
|
raw = ((uint64_t)v[7])*256*256*256*256*256*256*256
|
|
|
|
+ ((uint64_t)v[6])*256*256*256*256*256*256
|
|
|
|
+ ((uint64_t)v[5])*256*256*256*256*256
|
|
|
|
+ ((uint64_t)v[4])*256*256*256*256
|
|
|
|
+ ((uint64_t)v[3])*256*256*256
|
|
|
|
+ ((uint64_t)v[2])*256*256
|
|
|
|
+ ((uint64_t)v[1])*256
|
|
|
|
+ ((uint64_t)v[0]);
|
2018-11-02 17:57:56 +00:00
|
|
|
}
|
2019-10-14 14:53:02 +00:00
|
|
|
double scale = 1.0;
|
|
|
|
if (auto_scale) scale = vifScale(vif);
|
2018-11-02 17:57:56 +00:00
|
|
|
*value = ((double)raw) / scale;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (t == 0x9 || // 2 digit BCD
|
|
|
|
t == 0xA || // 4 digit BCD
|
|
|
|
t == 0xB || // 6 digit BCD
|
|
|
|
t == 0xC || // 8 digit BCD
|
|
|
|
t == 0xE) // 12 digit BCD
|
|
|
|
{
|
|
|
|
// 74140000 -> 00001474
|
2019-01-27 23:03:25 +00:00
|
|
|
string& v = p.second.value;
|
2018-11-02 17:57:56 +00:00
|
|
|
unsigned int raw = 0;
|
|
|
|
if (t == 0x9) {
|
2019-01-27 23:03:25 +00:00
|
|
|
assert(v.size() == 2);
|
2018-11-02 17:57:56 +00:00
|
|
|
raw = (v[0]-'0')*10 + (v[1]-'0');
|
|
|
|
} else if (t == 0xA) {
|
2019-01-27 23:03:25 +00:00
|
|
|
assert(v.size() == 4);
|
2018-11-02 17:57:56 +00:00
|
|
|
raw = (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
|
|
|
|
+ (v[0]-'0')*10 + (v[1]-'0');
|
|
|
|
} else if (t == 0xB) {
|
2019-01-27 23:03:25 +00:00
|
|
|
assert(v.size() == 6);
|
2018-11-02 17:57:56 +00:00
|
|
|
raw = (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
|
|
|
|
+ (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
|
|
|
|
+ (v[0]-'0')*10 + (v[1]-'0');
|
|
|
|
} else if (t == 0xC) {
|
2019-01-27 23:03:25 +00:00
|
|
|
assert(v.size() == 8);
|
2018-11-02 17:57:56 +00:00
|
|
|
raw = (v[6]-'0')*10*10*10*10*10*10*10 + (v[7]-'0')*10*10*10*10*10*10
|
|
|
|
+ (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
|
|
|
|
+ (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
|
|
|
|
+ (v[0]-'0')*10 + (v[1]-'0');
|
|
|
|
} else if (t == 0xE) {
|
2019-01-27 23:03:25 +00:00
|
|
|
assert(v.size() == 12);
|
2018-11-02 17:57:56 +00:00
|
|
|
raw =(v[10]-'0')*10*10*10*10*10*10*10*10*10*10*10 + (v[11]-'0')*10*10*10*10*10*10*10*10*10*10
|
|
|
|
+ (v[8]-'0')*10*10*10*10*10*10*10*10*10 + (v[9]-'0')*10*10*10*10*10*10*10*10
|
|
|
|
+ (v[6]-'0')*10*10*10*10*10*10*10 + (v[7]-'0')*10*10*10*10*10*10
|
|
|
|
+ (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
|
|
|
|
+ (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
|
|
|
|
+ (v[0]-'0')*10 + (v[1]-'0');
|
|
|
|
}
|
|
|
|
|
2019-10-14 14:53:02 +00:00
|
|
|
double scale = 1.0;
|
|
|
|
if (auto_scale) scale = vifScale(vif);
|
2018-11-02 17:57:56 +00:00
|
|
|
*value = ((double)raw) / scale;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error("Unsupported dif format for extraction to double! dif=%02x\n", dif);
|
|
|
|
}
|
2018-04-01 06:53:37 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
bool extractDVstring(map<string,pair<int,DVEntry>> *values,
|
2019-01-01 20:04:06 +00:00
|
|
|
string key,
|
|
|
|
int *offset,
|
|
|
|
string *value)
|
|
|
|
{
|
|
|
|
if ((*values).count(key) == 0) {
|
|
|
|
verbose("(dvparser) warning: cannot extract string from non-existant key \"%s\"\n", key.c_str());
|
|
|
|
*offset = -1;
|
|
|
|
*value = "";
|
|
|
|
return false;
|
|
|
|
}
|
2019-01-27 23:03:25 +00:00
|
|
|
pair<int,DVEntry>& p = (*values)[key];
|
2019-01-01 20:04:06 +00:00
|
|
|
*offset = p.first;
|
2019-01-27 23:03:25 +00:00
|
|
|
*value = p.second.value;
|
2019-01-01 20:04:06 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-01 14:41:11 +00:00
|
|
|
bool extractDate(uchar hi, uchar lo, struct tm *date)
|
|
|
|
{
|
|
|
|
// | hi | lo |
|
|
|
|
// | YYYY MMMM | YYY DDDDD |
|
|
|
|
|
|
|
|
int day = (0x1f) & lo;
|
|
|
|
int year1 = ((0xe0) & lo) >> 5;
|
|
|
|
int month = (0x0f) & hi;
|
|
|
|
int year2 = ((0xf0) & hi) >> 1;
|
|
|
|
int year = (2000 + year1 + year2);
|
|
|
|
|
|
|
|
date->tm_mday = day; /* Day of the month (1-31) */
|
|
|
|
date->tm_mon = month - 1; /* Month (0-11) */
|
|
|
|
date->tm_year = year - 1900; /* Year - 1900 */
|
|
|
|
|
|
|
|
if (month > 12) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool extractTime(uchar hi, uchar lo, struct tm *date)
|
|
|
|
{
|
|
|
|
// | hi | lo |
|
|
|
|
// | ...hhhhh | ..mmmmmm |
|
|
|
|
int min = (0x3f) & lo;
|
|
|
|
int hour = (0x1f) & hi;
|
|
|
|
|
|
|
|
date->tm_min = min;
|
|
|
|
date->tm_hour = hour;
|
|
|
|
|
|
|
|
if (min > 59) return false;
|
|
|
|
if (hour > 23) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
bool extractDVdate(map<string,pair<int,DVEntry>> *values,
|
2019-01-01 20:04:06 +00:00
|
|
|
string key,
|
|
|
|
int *offset,
|
2019-03-01 14:41:11 +00:00
|
|
|
struct tm *value)
|
2019-01-01 20:04:06 +00:00
|
|
|
{
|
2019-03-01 14:41:11 +00:00
|
|
|
if ((*values).count(key) == 0)
|
|
|
|
{
|
2019-01-01 20:04:06 +00:00
|
|
|
verbose("(dvparser) warning: cannot extract date from non-existant key \"%s\"\n", key.c_str());
|
|
|
|
*offset = -1;
|
2019-03-01 14:41:11 +00:00
|
|
|
memset(value, 0, sizeof(struct tm));
|
2019-01-01 20:04:06 +00:00
|
|
|
return false;
|
|
|
|
}
|
2019-03-01 14:41:11 +00:00
|
|
|
// This will install the correct timezone
|
|
|
|
// offset tm_gmtoff into the timestamp.
|
|
|
|
time_t t = time(NULL);
|
|
|
|
localtime_r(&t, value);
|
|
|
|
value->tm_hour = 0;
|
|
|
|
value->tm_min = 0;
|
|
|
|
value->tm_sec = 0;
|
|
|
|
value->tm_mday = 0;
|
|
|
|
value->tm_mon = 0;
|
|
|
|
value->tm_year = 0;
|
|
|
|
|
2019-01-01 20:04:06 +00:00
|
|
|
uchar dif, vif;
|
|
|
|
extractDV(key, &dif, &vif);
|
|
|
|
|
2019-01-27 23:03:25 +00:00
|
|
|
pair<int,DVEntry>& p = (*values)[key];
|
2019-01-01 20:04:06 +00:00
|
|
|
*offset = p.first;
|
|
|
|
vector<uchar> v;
|
2019-01-27 23:03:25 +00:00
|
|
|
hex2bin(p.second.value, &v);
|
2019-01-01 20:04:06 +00:00
|
|
|
|
2019-03-01 14:41:11 +00:00
|
|
|
bool ok = true;
|
|
|
|
if (v.size() == 2) {
|
|
|
|
ok &= extractDate(v[1], v[0], value);
|
|
|
|
}
|
|
|
|
else if (v.size() == 4) {
|
|
|
|
ok &= extractDate(v[3], v[2], value);
|
|
|
|
ok &= extractTime(v[1], v[0], value);
|
|
|
|
}
|
2019-03-20 21:16:45 +00:00
|
|
|
else if (v.size() == 6) {
|
|
|
|
ok &= extractDate(v[4], v[3], value);
|
|
|
|
ok &= extractTime(v[2], v[1], value);
|
|
|
|
// ..ss ssss
|
|
|
|
int sec = (0x3f) & v[0];
|
|
|
|
value->tm_sec = sec;
|
|
|
|
// some daylight saving time decoding needed here....
|
|
|
|
}
|
2019-01-01 20:04:06 +00:00
|
|
|
|
2019-03-01 14:41:11 +00:00
|
|
|
return ok;
|
2019-01-01 20:04:06 +00:00
|
|
|
}
|