kopia lustrzana https://github.com/weetmuts/wmbusmeters
Merge pull request #5 from weetmuts/Issue-3-Add-Support-for-T1-meters
Issue 3 add support for t1 meters.pull/22/head
commit
88444335e5
1
Makefile
1
Makefile
|
@ -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 \
|
||||
|
|
17
README.md
17
README.md
|
@ -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
|
||||
|
||||
|
|
18
cmdline.cc
18
cmdline.cc
|
@ -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++;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
39
dvparser.cc
39
dvparser.cc
|
@ -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
29
main.cc
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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*));
|
||||
}
|
||||
|
|
|
@ -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*));
|
||||
}
|
||||
|
|
|
@ -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 "";
|
||||
}
|
31
meters.cc
31
meters.cc
|
@ -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;
|
||||
}
|
||||
|
|
5
meters.h
5
meters.h
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -17,8 +17,6 @@ telegram=|25442D2C785634121b048D2093E13CBA20|0000790000000000000000000000000000|
|
|||
|
||||
# full telegram
|
||||
|
||||
# Test Multical302 T1 telegrams
|
||||
|
||||
# Test Omnipower C1 telegrams
|
||||
|
||||
telegram=|1E442D2C0771941501027A|B300108504833B08340500|
|
|
@ -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
23
test.sh
|
@ -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
|
||||
|
|
18
wmbus.cc
18
wmbus.cc
|
@ -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";
|
||||
}
|
||||
|
|
2
wmbus.h
2
wmbus.h
|
@ -143,4 +143,6 @@ double extract32bitAsDouble(int dif, int vif, int vife, string data);
|
|||
|
||||
int difLenBytes(int dif);
|
||||
|
||||
string linkModeName(LinkMode link_mode);
|
||||
|
||||
#endif
|
||||
|
|
Ładowanie…
Reference in New Issue