Merge pull request #5 from weetmuts/Issue-3-Add-Support-for-T1-meters

Issue 3 add support for t1 meters.
pull/22/head
Fredrik Öhrström 2018-11-02 16:21:43 +01:00 zatwierdzone przez GitHub
commit 88444335e5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
18 zmienionych plików z 418 dodań i 42 usunięć

Wyświetl plik

@ -40,6 +40,7 @@ METERS_OBJS:=\
$(BUILD)/meter_multical21.o \
$(BUILD)/meter_multical302.o \
$(BUILD)/meter_omnipower.o \
$(BUILD)/meter_supercom587.o \
$(BUILD)/printer.o \
$(BUILD)/serial.o \
$(BUILD)/shell.o \

Wyświetl plik

@ -1,5 +1,5 @@
# wmbusmeters
The program receives and decodes C1 telegrams
The program receives and decodes C1 or T1 telegrams
(using the wireless mbus protocol) to acquire
utility meter readings.
@ -19,6 +19,7 @@ Add --verbose for detailed debug information.
--meterfiles=dir to create status files below dir,
named dir/meter_name, containing the latest reading.
--meterfiles defaults dir to /tmp.
--c1 or --t1 listen to C1 or T1 messages
--shell=cmd invokes cmd with env variables containing the latest reading.
--shellenvs list the env variables available for the meter.
--oneshot wait for an update from each meter, then quit.
@ -28,8 +29,8 @@ Add --verbose for detailed debug information.
Specifying auto as the device will automatically look for usb
wmbus dongles on /dev/im871a and /dev/amb8465.
The meter types: multical21 and flowiq3100 (water meters) are supported.
The meter types: multical302 (heat) and omnipower (electricity)
The meter types: multical21,flowiq3100 (water meters) are supported.
The meter types: multical302 (heat), omnipower (electricity) and supercom587 (water)
are work in progress.
```
@ -55,7 +56,15 @@ multical-602) might be compatible as well. The same is true for the
omnipower meter type, which might include the electricity meters
Kamstrup-162 Kamstrup-382, Kamstrup-351 etc).
No meter quadruplets means listen for telegram traffic and print any id heard.
No meter quadruplets means listen for telegram traffic and print any id heard,
but you have to specify if you want to listen using radio mode C1 or T1. E.g.
```
./build/wmbusmeters --t1 /dev/ttyUSB0
```
You can listen to multiple meters as long as they all require the same radio mode C1 or T1.
So (currently) you cannot listen to a multical21 and a supercom587 with a single dongle at the same time.
# Usage examples

Wyświetl plik

@ -50,6 +50,24 @@ CommandLine *parseCommandLine(int argc, char **argv) {
i++;
continue;
}
if (!strcmp(argv[i], "--c1")) {
if (c->link_mode_set) {
error("You have already specified a link mode!\n");
}
c->link_mode = LinkModeC1;
c->link_mode_set = true;
i++;
continue;
}
if (!strcmp(argv[i], "--t1")) {
if (c->link_mode_set) {
error("You have already specified a link mode!\n");
}
c->link_mode = LinkModeT1;
c->link_mode_set = true;
i++;
continue;
}
if (!strcmp(argv[i], "--logtelegrams")) {
c->logtelegrams = true;
i++;

Wyświetl plik

@ -55,6 +55,8 @@ struct CommandLine {
bool oneshot {};
int exitafter {}; // Seconds to exit.
char *usb_device {};
LinkMode link_mode {};
bool link_mode_set {};
vector<MeterInfo> meters;
};

Wyświetl plik

@ -21,7 +21,7 @@
// 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...
//#define DEBUG_PARSER(...) fprintf(stderr, __VA_ARGS__)
//#define DEBUG_PARSER(...) fprintf(stdout, __VA_ARGS__)
#define DEBUG_PARSER(...)
using namespace std;
@ -44,6 +44,7 @@ bool parseDV(Telegram *t,
vector<uchar>::iterator data_end = data+data_len;
vector<uchar>::iterator format_end;
bool full_header = false;
bool variable_length = false;
if (format == NULL) {
format = &data;
@ -73,7 +74,7 @@ bool parseDV(Telegram *t,
if (*format == format_end) break;
uchar dif = **format;
DEBUG_PARSER("dif=%02x\n", dif);
DEBUG_PARSER("dif=%02x \"%s\"\n", dif, difType(dif).c_str());
if (dif == 0x2f) {
t->addExplanation(*format, 1, "%02X skip", dif);
continue;
@ -81,6 +82,11 @@ bool parseDV(Telegram *t,
int len = difLenBytes(dif);
DEBUG_PARSER("len=%d\n", len);
if (len == -1) {
variable_length = true;
} else {
variable_length = false;
}
if (len == -2) {
warning("(dvparser) cannot handle dif %2X\n", dif);
return false;
}
@ -94,7 +100,7 @@ bool parseDV(Telegram *t,
if (*format == format_end) { warning("(dvparser) warning: unexpected end of data (vif expected)"); break; }
uchar vif = **format;
DEBUG_PARSER("vif=%02x\n", vif);
DEBUG_PARSER("vif=%02x \"%s\"\n", vif, vifType(vif).c_str());
if (full_header) {
format_bytes.push_back(vif);
t->addExplanation(*format, 1, "%02X vif (%s)", vif, vifType(vif).c_str());
@ -102,14 +108,6 @@ bool parseDV(Telegram *t,
(*format)++;
}
if (overrideDifLen) {
int new_len = overrideDifLen(dif, vif, len);
if (new_len != len) {
DEBUG_PARSER("changing len %d to %d for dif=%02x vif=%02x\n", len, new_len, dif, vif);
len = new_len;
}
}
strprintf(dv, "%02X%02X", dif, vif);
if ((vif&0x80) == 0x80) {
// vif extension
@ -125,15 +123,26 @@ bool parseDV(Telegram *t,
strprintf(dv, "%02X%02X%02X", dif, vif, vife);
}
if (overrideDifLen) {
int new_len = overrideDifLen(dif, vif, len);
if (new_len != len) {
DEBUG_PARSER("changing len %d to %d for dif=%02x vif=%02x\n", len, new_len, dif, vif);
len = new_len;
}
}
int count = ++dv_count[dv];
DEBUG_PARSER("DifVif key is %s and its count is %d\n", dv.c_str(), count);
if (count > 1) {
strprintf(key, "%s_%d", dv.c_str(), count);
} else {
strprintf(key, "%s", dv.c_str());
}
DEBUG_PARSER("DifVif key is %s\n", key.c_str());
int remaining = std::distance(data,data_end);
int remaining = std::distance(data, data_end);
if (variable_length) {
len = remaining;
}
DEBUG_PARSER("remaining data %d len=%d\n", remaining, len);
if (remaining < len) {
warning("(dvparser) warning: unexpected end of data\n");
@ -142,7 +151,9 @@ bool parseDV(Telegram *t,
string value = bin2hex(data, len);
(*values)[key] = { start_parse_here+data-data_start, value };
t->addExplanation(data, len, "%s", value.c_str());
if (value.length() > 0) {
t->addExplanation(data, len, "%s", value.c_str());
}
}
string format_string = bin2hex(format_bytes);

29
main.cc
Wyświetl plik

@ -50,7 +50,7 @@ int main(int argc, char **argv)
" or 10m for ten minutes or 5s for five seconds.\n\n");
printf("Specifying auto as the device will automatically look for usb\n");
printf("wmbus dongles on /dev/im871a and /dev/amb8465\n\n");
printf("The meter types: multical21 and flowiq3100 (water meters) are supported.\n"
printf("The meter types: multical21,flowiq3100,supercom587 (water meters) are supported.\n"
"The meter types: multical302 (heat) and omnipower (electricity)\n"
"are work in progress.\n\n");
exit(0);
@ -101,8 +101,27 @@ int main(int argc, char **argv)
break;
}
wmbus->setLinkMode(LinkModeC1);
if (wmbus->getLinkMode()!=LinkModeC1) error("Could not set link mode to receive C1 telegrams.\n");
if (!cmdline->link_mode_set) {
// The link mode is not explicitly set. Examine the meters to see which
// link mode to use.
for (auto &m : cmdline->meters) {
if (!cmdline->link_mode_set) {
cmdline->link_mode = toMeterLinkMode(m.type);
cmdline->link_mode_set = true;
} else {
if (cmdline->link_mode != toMeterLinkMode(m.type)) {
error("A different link mode has been set already.\n");
}
}
}
}
if (!cmdline->link_mode_set) {
error("If you specify no meters, you have to specify the link mode: --c1 or --t1\n");
}
wmbus->setLinkMode(cmdline->link_mode);
string using_link_mode = linkModeName(wmbus->getLinkMode());
verbose("(cmdline) using link mode: %s\n", using_link_mode.c_str());
Printer *output = new Printer(cmdline->json, cmdline->fields,
cmdline->separator, cmdline->meterfiles, cmdline->meterfiles_dir,
@ -127,6 +146,10 @@ int main(int argc, char **argv)
m.meter = createOmnipower(wmbus, m.name, m.id, m.key);
verbose("(omnipower) configured \"%s\" \"omnipower\" \"%s\" \"%s\"\n", m.name, m.id, m.key);
break;
case SUPERCOM587_METER:
m.meter = createSupercom587(wmbus, m.name, m.id, m.key);
verbose("(supercom587) configured \"%s\" \"supercom587\" \"%s\" \"%s\"\n", m.name, m.id, m.key);
break;
case UNKNOWN_METER:
error("No such meter type \"%s\"\n", m.type);
break;

Wyświetl plik

@ -93,7 +93,7 @@ private:
};
MeterMultical21::MeterMultical21(WMBus *bus, const char *name, const char *id, const char *key, MeterType mt) :
MeterCommonImplementation(bus, name, id, key, mt, MANUFACTURER_KAM, 0x16)
MeterCommonImplementation(bus, name, id, key, mt, MANUFACTURER_KAM, 0x16, LinkModeC1)
{
if (type() == MULTICAL21_METER) {
expected_version_ = 0x1b;

Wyświetl plik

@ -50,7 +50,7 @@ private:
};
MeterMultical302::MeterMultical302(WMBus *bus, const char *name, const char *id, const char *key) :
MeterCommonImplementation(bus, name, id, key, MULTICAL302_METER, MANUFACTURER_KAM, 0x04)
MeterCommonImplementation(bus, name, id, key, MULTICAL302_METER, MANUFACTURER_KAM, 0x04, LinkModeC1)
{
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
}

Wyświetl plik

@ -49,7 +49,7 @@ private:
};
MeterOmnipower::MeterOmnipower(WMBus *bus, const char *name, const char *id, const char *key) :
MeterCommonImplementation(bus, name, id, key, OMNIPOWER_METER, MANUFACTURER_KAM, 0x04)
MeterCommonImplementation(bus, name, id, key, OMNIPOWER_METER, MANUFACTURER_KAM, 0x04, LinkModeC1)
{
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
}

Wyświetl plik

@ -0,0 +1,254 @@
/*
Copyright (C) 2017-2018 Fredrik Öhrström
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include"dvparser.h"
#include"meters.h"
#include"meters_common_implementation.h"
#include"wmbus.h"
#include"wmbus_utils.h"
#include"util.h"
#include<assert.h>
#include<map>
#include<memory.h>
#include<stdio.h>
#include<string>
#include<time.h>
#include<vector>
using namespace std;
struct MeterSupercom587 : public virtual WaterMeter, public virtual MeterCommonImplementation {
MeterSupercom587(WMBus *bus, const char *name, const char *id, const char *key);
// Total water counted through the meter
double totalWaterConsumption();
bool hasTotalWaterConsumption();
double targetWaterConsumption();
bool hasTargetWaterConsumption();
double maxFlow();
bool hasMaxFlow();
string statusHumanReadable();
string status();
string timeDry();
string timeReversed();
string timeLeaking();
string timeBursting();
void printMeter(string *human_readable,
string *fields, char separator,
string *json,
vector<string> *envs);
private:
void handleTelegram(Telegram *t);
void processContent(Telegram *t);
string decodeTime(int time);
double total_water_consumption_ {};
};
MeterSupercom587::MeterSupercom587(WMBus *bus, const char *name, const char *id, const char *key) :
MeterCommonImplementation(bus, name, id, key, SUPERCOM587_METER, MANUFACTURER_SON, 0x16, LinkModeT1)
{
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
}
double MeterSupercom587::totalWaterConsumption()
{
return total_water_consumption_;
}
WaterMeter *createSupercom587(WMBus *bus, const char *name, const char *id, const char *key)
{
return new MeterSupercom587(bus,name,id,key);
}
void MeterSupercom587::handleTelegram(Telegram *t)
{
if (!isTelegramForMe(t)) {
// This telegram is not intended for this meter.
return;
}
verbose("(%s) telegram for %s %02x%02x%02x%02x\n", "supercom587",
name().c_str(),
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
t->a_field_address[3]);
if (t->a_field_device_type != 0x07 && t->a_field_device_type != 0x06) {
warning("(%s) expected telegram for cold or warm water media, but got \"%s\"!\n", "supercom587",
mediaType(t->m_field, t->a_field_device_type).c_str());
}
updateMedia(t->a_field_device_type);
if (t->m_field != manufacturer() ||
t->a_field_version != 0x3c) {
warning("(%s) expected telegram from SON meter with version 0x%02x, "
"but got \"%s\" meter with version 0x%02x !\n", "supercom587",
0x3c,
manufacturerFlag(t->m_field).c_str(),
t->a_field_version);
}
if (useAes()) {
vector<uchar> aeskey = key();
decryptKamstrupC1(t, aeskey);
} else {
t->content = t->payload;
}
char log_prefix[256];
snprintf(log_prefix, 255, "(%s) log", "supercom587");
logTelegram(log_prefix, t->parsed, t->content);
int content_start = t->parsed.size();
processContent(t);
if (isDebugEnabled()) {
snprintf(log_prefix, 255, "(%s)", "supercom587");
t->explainParse(log_prefix, content_start);
}
triggerUpdate(t);
}
void MeterSupercom587::processContent(Telegram *t)
{
// Meter record:
map<string,pair<int,string>> values;
parseDV(t, t->content.begin(), t->content.size(), &values);
int offset;
extractDVdouble(&values, "0C13", &offset, &total_water_consumption_);
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_);
}
void MeterSupercom587::printMeter(string *human_readable,
string *fields, char separator,
string *json,
vector<string> *envs)
{
char buf[65536];
buf[65535] = 0;
snprintf(buf, sizeof(buf)-1,
"%s\t"
"%s\t"
"% 3.3f m3\t"
"%s",
name().c_str(),
id().c_str(),
totalWaterConsumption(),
datetimeOfUpdateHumanReadable().c_str());
*human_readable = buf;
snprintf(buf, sizeof(buf)-1,
"%s%c"
"%s%c"
"%f%c"
"%s",
name().c_str(), separator,
id().c_str(), separator,
totalWaterConsumption(), separator,
datetimeOfUpdateRobot().c_str());
*fields = buf;
#define Q(x,y) "\""#x"\":"#y","
#define QS(x,y) "\""#x"\":\""#y"\","
#define QSE(x,y) "\""#x"\":\""#y"\""
snprintf(buf, sizeof(buf)-1, "{"
QS(media,%s)
QS(meter,supercom587)
QS(name,%s)
QS(id,%s)
Q(total_m3,%f)
QSE(timestamp,%s)
"}",
mediaType(manufacturer(), media()).c_str(),
name().c_str(),
id().c_str(),
totalWaterConsumption(),
datetimeOfUpdateRobot().c_str());
*json = buf;
envs->push_back(string("METER_JSON=")+*json);
envs->push_back(string("METER_TYPE=supercom587"));
envs->push_back(string("METER_ID=")+id());
envs->push_back(string("METER_TOTAL_M3=")+to_string(totalWaterConsumption()));
envs->push_back(string("METER_TIMESTAMP=")+datetimeOfUpdateRobot());
}
bool MeterSupercom587::hasTotalWaterConsumption()
{
return true;
}
double MeterSupercom587::targetWaterConsumption()
{
return 0.0;
}
bool MeterSupercom587::hasTargetWaterConsumption()
{
return false;
}
double MeterSupercom587::maxFlow()
{
return 0.0;
}
bool MeterSupercom587::hasMaxFlow()
{
return false;
}
string MeterSupercom587::statusHumanReadable()
{
return "";
}
string MeterSupercom587::status()
{
return "";
}
string MeterSupercom587::timeDry()
{
return "";
}
string MeterSupercom587::timeReversed()
{
return "";
}
string MeterSupercom587::timeLeaking()
{
return "";
}
string MeterSupercom587::timeBursting()
{
return "";
}

Wyświetl plik

@ -21,8 +21,10 @@
#include<memory.h>
MeterCommonImplementation::MeterCommonImplementation(WMBus *bus, const char *name, const char *id, const char *key,
MeterType type, int manufacturer, int media) :
type_(type), manufacturer_(manufacturer), media_(media), name_(name), bus_(bus)
MeterType type, int manufacturer, int media,
LinkMode required_link_mode) :
type_(type), manufacturer_(manufacturer), media_(media), name_(name), bus_(bus),
required_link_mode_(required_link_mode)
{
use_aes_ = true;
hex2bin(id, &id_);
@ -63,6 +65,11 @@ WMBus *MeterCommonImplementation::bus()
return bus_;
}
LinkMode MeterCommonImplementation::requiredLinkMode()
{
return required_link_mode_;
}
void MeterCommonImplementation::onUpdate(function<void(Meter*)> cb)
{
on_update_.push_back(cb);
@ -96,12 +103,21 @@ MeterType toMeterType(const char *type)
if (!strcmp(type, "flowiq3100")) return FLOWIQ3100_METER;
if (!strcmp(type, "multical302")) return MULTICAL302_METER;
if (!strcmp(type, "omnipower")) return OMNIPOWER_METER;
if (!strcmp(type, "water")) return MULTICAL21_METER;
if (!strcmp(type, "heat")) return MULTICAL302_METER;
if (!strcmp(type, "electricity")) return OMNIPOWER_METER;
if (!strcmp(type, "supercom587")) return SUPERCOM587_METER;
return UNKNOWN_METER;
}
LinkMode toMeterLinkMode(const char *type)
{
if (!strcmp(type, "multical21")) return LinkModeC1;
if (!strcmp(type, "flowiq3100")) return LinkModeC1;
if (!strcmp(type, "multical302")) return LinkModeC1;
if (!strcmp(type, "omnipower")) return LinkModeC1;
if (!strcmp(type, "supercom587")) return LinkModeT1;
return UNKNOWN_LINKMODE;
}
bool MeterCommonImplementation::isTelegramForMe(Telegram *t)
{
@ -149,3 +165,8 @@ void MeterCommonImplementation::triggerUpdate(Telegram *t)
for (auto &cb : on_update_) if (cb) cb(this);
t->handled = true;
}
void MeterCommonImplementation::updateMedia(int media)
{
media_ = media;
}

Wyświetl plik

@ -24,7 +24,7 @@
#include<string>
#include<vector>
#define LIST_OF_METERS X(MULTICAL21_METER)X(FLOWIQ3100_METER)X(MULTICAL302_METER)X(OMNIPOWER_METER)X(UNKNOWN_METER)
#define LIST_OF_METERS X(MULTICAL21_METER)X(FLOWIQ3100_METER)X(MULTICAL302_METER)X(OMNIPOWER_METER)X(SUPERCOM587_METER)X(UNKNOWN_METER)
enum MeterType {
#define X(name) name,
@ -43,6 +43,7 @@ struct Meter {
virtual int manufacturer() = 0;
virtual int media() = 0;
virtual WMBus *bus() = 0;
virtual LinkMode requiredLinkMode() = 0;
virtual string datetimeOfUpdateHumanReadable() = 0;
virtual string datetimeOfUpdateRobot() = 0;
@ -96,9 +97,11 @@ struct GenericMeter : public virtual Meter {
};
MeterType toMeterType(const char *type);
LinkMode toMeterLinkMode(const char *type);
WaterMeter *createMultical21(WMBus *bus, const char *name, const char *id, const char *key, MeterType mt);
HeatMeter *createMultical302(WMBus *bus, const char *name, const char *id, const char *key);
ElectricityMeter *createOmnipower(WMBus *bus, const char *name, const char *id, const char *key);
WaterMeter *createSupercom587(WMBus *bus, const char *name, const char *id, const char *key);
GenericMeter *createGeneric(WMBus *bus, const char *name, const char *id, const char *key);
#endif

Wyświetl plik

@ -29,6 +29,7 @@ struct MeterCommonImplementation : public virtual Meter {
int manufacturer();
int media();
WMBus *bus();
LinkMode requiredLinkMode();
string datetimeOfUpdateHumanReadable();
string datetimeOfUpdateRobot();
@ -45,11 +46,13 @@ struct MeterCommonImplementation : public virtual Meter {
uint16_t getRecordAsUInt16(std::string record);
MeterCommonImplementation(WMBus *bus, const char *name, const char *id, const char *key,
MeterType type, int manufacturer, int media);
MeterType type, int manufacturer, int media,
LinkMode required_link_mode);
protected:
void triggerUpdate(Telegram *t);
void updateMedia(int media);
private:
@ -64,6 +67,7 @@ private:
int num_updates_ {};
bool use_aes_ {};
time_t datetime_of_update_ {};
LinkMode required_link_mode_ {};
protected:
std::map<std::string,std::pair<int,std::string>> values_;

Wyświetl plik

@ -17,8 +17,6 @@ telegram=|25442D2C785634121b048D2093E13CBA20|0000790000000000000000000000000000|
# full telegram
# Test Multical302 T1 telegrams
# Test Omnipower C1 telegrams
telegram=|1E442D2C0771941501027A|B300108504833B08340500|

Wyświetl plik

@ -0,0 +1,7 @@
# Test Supercom587 T1 telegrams
telegram=|A244EE4D785634123C067A73000000|0C1334190000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0113412B03FD6CDE120082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002|
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":6.452000,"timestamp":"1111-11-11T11:11:11Z"}
telegram=|A244EE4D111111113C077A72000000|0C1374140000426CE1F14C130000000082046C21298C0413010000008D04931E3A3CFE0100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000001600000031130000046D0113412B03FD6CDF120082206C5C290BFD0F0200018C4079629885238310FD3100000082106C01018110FD610002FD66020002|
{"media":"water","meter":"supercom587","name":"MyColdWater","id":"11111111","total_m3":5.236000,"timestamp":"1111-11-11T11:11:11Z"}

23
test.sh
Wyświetl plik

@ -2,10 +2,10 @@
PROG="$1"
cat simulation.txt | grep '^{' > test_expected.txt
$PROG --robot=json simulation.txt \
MyTapWater multical21 76348799 "" \
cat simulation_c1.txt | grep '^{' > test_expected.txt
$PROG --robot=json simulation_c1.txt \
MyHeater multical302 12345678 "" \
MyTapWater multical21 76348799 "" \
MyElectricity omnipower 15947107 "" \
> test_output.txt
if [ "$?" == "0" ]
@ -19,3 +19,20 @@ then
else
Failure.
fi
cat simulation_t1.txt | grep '^{' > test_expected.txt
$PROG --robot=json simulation_t1.txt \
MyWarmWater supercom587 12345678 "" \
MyColdWater supercom587 11111111 "" \
> test_output.txt
if [ "$?" == "0" ]
then
cat test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > test_responses.txt
diff test_expected.txt test_responses.txt
if [ "$?" == "0" ]
then
echo OK
fi
else
Failure.
fi

Wyświetl plik

@ -382,11 +382,7 @@ void Telegram::explainParse(string intro, int from)
{
for (auto& p : explanations) {
if (p.first < from) continue;
printf("%s ", intro.c_str());
for (int i=0; i<p.first; ++i) {
printf(" ");
}
printf("%s\n", p.second.c_str());
printf("%s %02x: %s\n", intro.c_str(), p.first, p.second.c_str());
}
string hex = bin2hex(parsed);
printf("%s %s\n", intro.c_str(), hex.c_str());
@ -446,7 +442,7 @@ int difLenBytes(int dif)
case 0xC: return 4; // 8 digit BCD
case 0xD: return -1; // variable length
case 0xE: return 6; // 12 digit BCD
case 0xF: return -1; // Special Functions
case 0xF: return -2; // Special Functions
}
return -1;
}
@ -1261,3 +1257,13 @@ string formatData(int dif, int vif, int vife, string data)
return data;
}
string linkModeName(LinkMode link_mode)
{
switch (link_mode) {
case LinkModeC1: return "C1";
case LinkModeT1: return "T1";
case UNKNOWN_LINKMODE: break;
}
return "UnknownLinkMode";
}

Wyświetl plik

@ -143,4 +143,6 @@ double extract32bitAsDouble(int dif, int vif, int vife, string data);
int difLenBytes(int dif);
string linkModeName(LinkMode link_mode);
#endif