Added mbus driver ultraheat.

pull/451/head
Fredrik Öhrström 2022-01-13 09:51:08 +01:00
rodzic c20a63e5b7
commit b322838b59
14 zmienionych plików z 404 dodań i 84 usunięć

Wyświetl plik

@ -135,8 +135,12 @@ PROG_OBJS:=\
$(BUILD)/wmbus_rc1180.o \
$(BUILD)/wmbus_utils.o \
DRIVER_OBJS:=$(wildcard src/meter_*.cc)
ifeq ($(DRIVER),)
DRIVER_OBJS:=$(wildcard src/meter_*.cc)
else
$(info Building a single driver $(DRIVER))
DRIVER_OBJS:=src/meter_auto.cc src/meter_unknown.cc src/meter_$(DRIVER).cc
endif
DRIVER_OBJS:=$(patsubst src/%.cc,$(BUILD)/%.o,$(DRIVER_OBJS))
all: $(BUILD)/wmbusmeters $(BUILD)/wmbusmetersd $(BUILD)/wmbusmeters.g $(BUILD)/wmbusmeters-admin $(BUILD)/testinternals

Wyświetl plik

@ -92,7 +92,7 @@ telegram=|27442D2C5768663230028D20E900C91C2011BA79138CCCFB|1A0300000000000003000
{"media":"electricity","meter":"omnipower","name":"myomnipower","id":"32666857","total_energy_consumption_kwh":7.94,"total_energy_production_kwh":0,"current_power_consumption_kw":0.003,"current_power_production_kw":0,"timestamp":"1111-11-11T11:11:11Z"}
# Test EI Electronics smoke detector
telegram=|5E462515112801000C1A7A370050252F2F|0BFD0F060101046D300CAB2202FD17000082206CAB22426C01018440FF2C000F11008250FD61000082506C01018260FD6100008360FD3100000082606C01018270FD61010082706CAB222F2F2F2F|
telegram=|5E442515112801000C1A7A370050252F2F#0BFD0F060101046D300CAB2202FD17000082206CAB22426C01018440FF2C000F11008250FD61000082506C01018260FD6100008360FD3100000082606C01018270FD61010082706CAB222F2F2F2F|
{"media":"smoke detector","meter":"ei6500","name":"Smokey","id":"00012811","software_version":"1.1.6","message_datetime":"2021-02-11 12:48","last_alarm_date":"2000-01-01","smoke_alarm_counter":"0","total_remove_duration":"0 minutes","last_remove_date":"2000-01-01","removed_counter":"0","test_button_last_date":"2021-02-11","test_button_counter":"1","status":"NOT_INSTALLED","timestamp":"1111-11-11T11:11:11Z"}
# Test Techem radio convert + Wehrle water meter combo.

Wyświetl plik

@ -34,7 +34,21 @@ const char *toString(ValueInformation v)
switch (v) {
case ValueInformation::None: return "None";
case ValueInformation::Any: return "Any";
#define X(name,from,to,quantity) case ValueInformation::name: return #name;
#define X(name,from,to,quantity,unit) case ValueInformation::name: return #name;
LIST_OF_VALUETYPES
#undef X
}
assert(0);
}
Unit toDefaultUnit(ValueInformation v)
{
switch (v) {
case ValueInformation::Any:
case ValueInformation::None:
assert(0);
break;
#define X(name,from,to,quantity,unit) case ValueInformation::name: return unit;
LIST_OF_VALUETYPES
#undef X
}
@ -43,7 +57,7 @@ LIST_OF_VALUETYPES
ValueInformation toValueInformation(int i)
{
#define X(name,from,to,quantity) if (from <= i && i <= to) return ValueInformation::name;
#define X(name,from,to,quantity,unit) if (from <= i && i <= to) return ValueInformation::name;
LIST_OF_VALUETYPES
#undef X
return ValueInformation::None;
@ -321,7 +335,7 @@ void valueInfoRange(ValueInformation v, int *low, int *hi)
switch (v) {
case ValueInformation::Any:
case ValueInformation::None: *low = 0; *hi = 0; return;
#define X(name,from,to,quantity) case ValueInformation::name: *low = from; *hi = to; return;
#define X(name,from,to,quantity,unit) case ValueInformation::name: *low = from; *hi = to; return;
LIST_OF_VALUETYPES
#undef X
}

Wyświetl plik

@ -19,6 +19,7 @@
#define DVPARSER_H
#include"util.h"
#include"units.h"
#include"wmbus.h"
#include<map>
@ -28,31 +29,32 @@
#include<vector>
#define LIST_OF_VALUETYPES \
X(Volume,0x10,0x17,Quantity::Volume) \
X(OperatingTime,0x24,0x27, Quantity::Time) \
X(VolumeFlow,0x38,0x3F, Quantity::Flow) \
X(FlowTemperature,0x58,0x5B, Quantity::Temperature) \
X(ReturnTemperature,0x5C,0x5F, Quantity::Temperature) \
X(TemperatureDifference,0x60,0x63, Quantity::Temperature) \
X(ExternalTemperature,0x64,0x67, Quantity::Temperature) \
X(HeatCostAllocation,0x6E,0x6E, Quantity::HCA) \
X(Date,0x6C,0x6C, Quantity::PointInTime) \
X(DateTime,0x6D,0x6D, Quantity::PointInTime) \
X(EnergyMJ,0x0E,0x0F, Quantity::Energy) \
X(EnergyWh,0x00,0x07, Quantity::Energy) \
X(PowerW,0x28,0x2f, Quantity::Power) \
X(ActualityDuration,0x74,0x77, Quantity::Time) \
X(Volume,0x10,0x17,Quantity::Volume,Unit::M3) \
X(OperatingTime,0x24,0x27, Quantity::Time, Unit::Second) \
X(VolumeFlow,0x38,0x3F, Quantity::Flow, Unit::M3H) \
X(FlowTemperature,0x58,0x5B, Quantity::Temperature, Unit::C) \
X(ReturnTemperature,0x5C,0x5F, Quantity::Temperature, Unit::C) \
X(TemperatureDifference,0x60,0x63, Quantity::Temperature, Unit::C) \
X(ExternalTemperature,0x64,0x67, Quantity::Temperature, Unit::C) \
X(HeatCostAllocation,0x6E,0x6E, Quantity::HCA, Unit::HCA) \
X(Date,0x6C,0x6C, Quantity::PointInTime, Unit::DateTimeLT) \
X(DateTime,0x6D,0x6D, Quantity::PointInTime, Unit::DateTimeLT) \
X(EnergyMJ,0x0E,0x0F, Quantity::Energy, Unit::MJ) \
X(EnergyWh,0x00,0x07, Quantity::Energy, Unit::KWH) \
X(PowerW,0x28,0x2f, Quantity::Power, Unit::KW) \
X(ActualityDuration,0x74,0x77, Quantity::Time, Unit::Second) \
enum class ValueInformation
{
None,
Any,
#define X(name,from,to,quantity) name,
#define X(name,from,to,quantity,unit) name,
LIST_OF_VALUETYPES
#undef X
};
const char *toString(ValueInformation v);
Unit toDefaultUnit(ValueInformation v);
ValueInformation toValueInformation(int i);
struct DifVifKey

Wyświetl plik

@ -120,7 +120,7 @@ void MBusRawTTY::processSerialData()
for (;;)
{
FrameStatus status = checkMBusFrame(read_buffer_, &frame_length, &payload_len, &payload_offset);
FrameStatus status = checkMBusFrame(read_buffer_, &frame_length, &payload_len, &payload_offset, false);
if (status == PartialFrame)
{

Wyświetl plik

@ -90,6 +90,7 @@
X(FLOWIQ3100,MANUFACTURER_KAM, 0x16, 0x1d) \
X(MULTICAL302,MANUFACTURER_KAM, 0x04, 0x30) \
X(MULTICAL302,MANUFACTURER_KAM, 0x0d, 0x30) \
X(MULTICAL302,MANUFACTURER_KAM, 0x0c, 0x30) \
X(MULTICAL403,MANUFACTURER_KAM, 0x0a, 0x34) \
X(MULTICAL403,MANUFACTURER_KAM, 0x0b, 0x34) \
X(MULTICAL403,MANUFACTURER_KAM, 0x0c, 0x34) \

Wyświetl plik

@ -0,0 +1,146 @@
/*
Copyright (C) 2022 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"
using namespace std;
struct MeterUltraHeat : public virtual MeterCommonImplementation
{
MeterUltraHeat(MeterInfo &mi, DriverInfo &di);
private:
double heat_mj_ {};
double volume_m3_ {};
double power_kw_ {};
double flow_m3h_ {};
double flow_c_ {};
double return_c_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
{
di.setName("ultraheat");
di.setMeterType(MeterType::HeatMeter);
di.addDetection(MANUFACTURER_LUG, 0x04, 0x04);
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new MeterUltraHeat(mi, di)); });
});
MeterUltraHeat::MeterUltraHeat(MeterInfo &mi, DriverInfo &di) :
MeterCommonImplementation(mi, di)
{
addFieldWithExtractor(
"heat",
Quantity::Energy,
NoDifVifKey,
VifScaling::Auto,
MeasurementType::Instantaneous,
ValueInformation::EnergyMJ,
StorageNr(0),
TariffNr(0),
IndexNr(1),
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
"The total heat energy consumption recorded by this meter.",
SET_FUNC(heat_mj_, Unit::MJ),
GET_FUNC(heat_mj_, Unit::MJ));
addFieldWithExtractor(
"volume",
Quantity::Volume,
NoDifVifKey,
VifScaling::Auto,
MeasurementType::Instantaneous,
ValueInformation::Volume,
StorageNr(0),
TariffNr(0),
IndexNr(1),
PrintProperty::JSON ,
"The total heating media volume recorded by this meter.",
SET_FUNC(volume_m3_, Unit::M3),
GET_FUNC(volume_m3_, Unit::M3));
addFieldWithExtractor(
"power",
Quantity::Power,
NoDifVifKey,
VifScaling::Auto,
MeasurementType::Instantaneous,
ValueInformation::PowerW,
StorageNr(0),
TariffNr(0),
IndexNr(1),
PrintProperty::JSON ,
"The current power consumption.",
SET_FUNC(power_kw_, Unit::KW),
GET_FUNC(power_kw_, Unit::KW));
addFieldWithExtractor(
"flow",
Quantity::Flow,
NoDifVifKey,
VifScaling::Auto,
MeasurementType::Instantaneous,
ValueInformation::VolumeFlow,
StorageNr(0),
TariffNr(0),
IndexNr(1),
PrintProperty::JSON ,
"The current heat media volume flow.",
SET_FUNC(flow_m3h_, Unit::M3H),
GET_FUNC(flow_m3h_, Unit::M3H));
addFieldWithExtractor(
"flow",
Quantity::Temperature,
NoDifVifKey,
VifScaling::Auto,
MeasurementType::Instantaneous,
ValueInformation::FlowTemperature,
StorageNr(0),
TariffNr(0),
IndexNr(1),
PrintProperty::JSON ,
"The current forward heat media temperature.",
SET_FUNC(flow_c_, Unit::C),
GET_FUNC(flow_c_, Unit::C));
addFieldWithExtractor(
"return",
Quantity::Temperature,
NoDifVifKey,
VifScaling::Auto,
MeasurementType::Instantaneous,
ValueInformation::ReturnTemperature,
StorageNr(0),
TariffNr(0),
IndexNr(1),
PrintProperty::JSON ,
"The current return heat media temperature.",
SET_FUNC(return_c_, Unit::C),
GET_FUNC(return_c_, Unit::C));
}
// Test: MyUltra ultraheat 70444600 NOKEY
// telegram=|68F8F86808007200464470A7320404270000000974040970040C0E082303000C14079519000B2D0500000B3B0808000A5B52000A5F51000A6206004C14061818004C0E490603000C7800464470891071609B102D020100DB102D0201009B103B6009009A105B78009A105F74000C22726701003C22000000007C2200000000426C01018C2006000000008C3006000000008C80100600000000CC200600000000CC300600000000CC801006000000009A115B64009A115F63009B113B5208009B112D020100BC0122000000008C010E490603008C2106000000008C3106000000008C811006000000008C011406181800046D310ACA210F21040010A0C116|
// {"media":"heat","meter":"ultraheat","name":"MyUltra","id":"70444600","heat_kwh":8974.444444,"volume_m3":1995.07,"power_kw":0.5,"flow_m3h":0.808,"flow_c":52,"return_c":51,"timestamp":"1111-11-11T11:11:11Z"}
// |MyUltra;70444600;8974.444444;1111-11-11 11:11.11

Wyświetl plik

@ -719,7 +719,13 @@ void MeterCommonImplementation::addFieldWithExtractor(
&extracted_double_value,
fi->vifScaling() == VifScaling::Auto))
{
fi->setValueDouble(fi->defaultUnit(), extracted_double_value);
Unit decoded_unit = fi->defaultUnit();
if (fi->valueInformation() != ValueInformation::Any &&
fi->valueInformation() != ValueInformation::None)
{
decoded_unit = toDefaultUnit(fi->valueInformation());
}
fi->setValueDouble(decoded_unit, extracted_double_value);
t->addMoreExplanation(offset, fi->renderJson(&m->conversions()));
found = true;
}

Wyświetl plik

@ -268,10 +268,12 @@ void Telegram::print()
void Telegram::printDLL()
{
string possible_drivers = autoDetectPossibleDrivers();
if (about.type == FrameType::WMBUS)
{
string possible_drivers = autoDetectPossibleDrivers();
string man = manufacturerFlag(dll_mfct);
verbose("(telegram) DLL L=%02x C=%02x (%s) M=%04x (%s) A=%02x%02x%02x%02x VER=%02x TYPE=%02x (%s) (driver %s) DEV=%s RSSI=%d\n",
string man = manufacturerFlag(dll_mfct);
verbose("(telegram) DLL L=%02x C=%02x (%s) M=%04x (%s) A=%02x%02x%02x%02x VER=%02x TYPE=%02x (%s) (driver %s) DEV=%s RSSI=%d\n",
dll_len,
dll_c, cType(dll_c).c_str(),
dll_mfct,
@ -283,6 +285,16 @@ void Telegram::printDLL()
possible_drivers.c_str(),
about.device.c_str(),
about.rssi_dbm);
}
if (about.type == FrameType::MBUS)
{
verbose("(telegram) DLL L=%02x C=%02x (%s) A=%02x\n",
dll_len,
dll_c, cType(dll_c).c_str(),
mbus_primary_address);
}
}
void Telegram::printELL()
@ -818,25 +830,46 @@ bool expectedMore(int line)
bool Telegram::parseMBusDLL(vector<uchar>::iterator &pos)
{
int remaining = distance(pos, frame.end());
if (remaining == 0) return expectedMore(__LINE__);
if (remaining < 5) return expectedMore(__LINE__);
debug("(mbus) parse MBUS DLL @%d %d\n", distance(frame.begin(), pos), remaining);
debugPayload("(mbus) ", frame);
if (*pos != 0x68) return false;
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "68 start");
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, KindOfData::PROTOCOL, Understanding::FULL, "%02x length (%d bytes)", dll_len, dll_len);
// Two identical length bytes are expected!
if (*pos != dll_len) return false;
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x length again (%d bytes)", dll_len, dll_len);
if (*pos != 0x68) return false;
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "68 start");
if (remaining < dll_len) return expectedMore(__LINE__);
dll_c = *pos;
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%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));
mbus_primary_address = *pos;
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]);
string id = tostrprintf("%02x", mbus_primary_address);
ids.push_back(id);
idsc = id;
return true;
mbus_ci = *pos;
addExplanationAndIncrementPos(pos, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02x dll-ci (%s)", mbus_ci, mbusCiField(mbus_ci));
if (mbus_ci == 0x72)
{
return parse_TPL_72(pos);
}
return false;
}
bool Telegram::parseDLL(vector<uchar>::iterator &pos)
@ -1918,8 +1951,7 @@ bool Telegram::parseMBUS(vector<uchar> &input_frame, MeterKeys *mk, bool warn)
vector<uchar>::iterator pos = frame.begin();
// Parsed accumulates parsed bytes.
parsed.clear();
// Fixes quirks from non-compliant meters to make telegram compatible with the standard
preProcess();
// ┌──────────────────────────────────────────────┐
// │ │
// │ Parse DLL Data Link Layer for Wireless MBUS. │
@ -2087,7 +2119,7 @@ string Telegram::autoDetectPossibleDrivers()
return possibles;
}
string mbusCField(uchar c_field)
const char *mbusCField(uchar c_field)
{
string s;
switch (c_field)
@ -2097,6 +2129,24 @@ string mbusCField(uchar c_field)
return "?";
}
const char *mbusCiField(uchar c_field)
{
string s;
switch (c_field)
{
case 0x78: return "no header";
case 0x7a: return "short header";
case 0x72: return "long header";
case 0x79: return "no header compact frame";
case 0x7b: return "short header compact frame";
case 0x73: return "long header compact frame";
case 0x69: return "no header format frame";
case 0x6a: return "short header format frame";
case 0x6b: return "long header format frame";
}
return "?";
}
string cType(int c_field)
{
string s;
@ -2131,6 +2181,22 @@ string cType(int c_field)
return s;
}
bool isValidWMBusCField(int c_field)
{
// These are the currently seen valid C fields for wmbus telegrams.
// 0x46 is only from an ei6500 meter.... all else is ox44
// However in the future we might see relayed telegrams which will perhaps have
// some other c field.
return
c_field == 0x44 ||
c_field == 0x46;
}
bool isValidMBusCField(int c_field)
{
return false;
}
string ccType(int cc_field)
{
string s = "";
@ -4223,7 +4289,10 @@ bool trimCRCsFrameFormatAInternal(std::vector<uchar> &payload, bool fail_is_ok)
if (calc_crc != check_crc && !FUZZING)
{
debug("(wmbus) ff a dll crc first (calculated %04x) did not match (expected %04x) for bytes 0-%zu!\n", calc_crc, check_crc, 10);
if (!fail_is_ok)
{
debug("(wmbus) ff a dll crc first (calculated %04x) did not match (expected %04x) for bytes 0-%zu!\n", calc_crc, check_crc, 10);
}
return false;
}
out.insert(out.end(), payload.begin(), payload.begin()+10);
@ -4276,10 +4345,7 @@ bool trimCRCsFrameFormatAInternal(std::vector<uchar> &payload, bool fail_is_ok)
}
}
if (fail_is_ok)
{
debugPayload("(wmbus) trimming frame A", payload);
}
debugPayload("(wmbus) trimming frame A", payload);
out[0] = out.size()-1;
size_t new_len = out[0]+1;
@ -4361,10 +4427,7 @@ bool trimCRCsFrameFormatBInternal(std::vector<uchar> &payload, bool fail_is_ok)
}
}
if (fail_is_ok)
{
debugPayload("(wmbus) trimming frame B", payload);
}
debugPayload("(wmbus) trimming frame B", payload);
out[0] = out.size()-1;
size_t new_len = out[0]+1;
@ -4397,7 +4460,8 @@ bool trimCRCsFrameFormatB(std::vector<uchar> &payload)
FrameStatus checkWMBusFrame(vector<uchar> &data,
size_t *frame_length,
int *payload_len_out,
int *payload_offset)
int *payload_offset,
bool only_test)
{
// Nice clean: 2A442D2C998734761B168D2021D0871921|58387802FF2071000413F81800004413F8180000615B
// Ugly: 00615B2A442D2C998734761B168D2021D0871921|58387802FF2071000413F81800004413F8180000615B
@ -4414,17 +4478,17 @@ FrameStatus checkWMBusFrame(vector<uchar> &data,
int type = data[1];
int offset = 1;
if (type != 0x44)
if (!isValidWMBusCField(type))
{
// Ouch, we are out of sync with the wmbus frames that we expect!
// Since we currently do not handle any other type of frame, we can
// look for the byte 0x44 in the buffer. If we find a 0x44 byte and
// the length byte before it maps to the end of the buffer,
// then we have found a valid telegram.
// look for a valid c field (ie 0x44 0x46 etc) in the buffer.
// If we find such a byte and the length byte before maps to the end
// of the buffer, then we have found a valid telegram.
bool found = false;
for (size_t i = 0; i < data.size()-2; ++i)
{
if (data[i+1] == 0x44)
if (isValidWMBusCField(data[i+1]))
{
payload_len = data[i];
size_t remaining = data.size()-i;
@ -4440,8 +4504,15 @@ FrameStatus checkWMBusFrame(vector<uchar> &data,
if (!found)
{
// No sensible telegram in the buffer. Flush it!
verbose("(wmbus) no sensible telegram found, clearing buffer.\n");
data.clear();
if (!only_test)
{
verbose("(wmbus) no sensible telegram found, clearing buffer.\n");
data.clear();
}
else
{
debug("(wmbus) not a proper wmbus frame.\n");
}
return ErrorInFrame;
}
}
@ -4450,18 +4521,25 @@ FrameStatus checkWMBusFrame(vector<uchar> &data,
*frame_length = payload_len+offset;
if (data.size() < *frame_length)
{
debug("(wmbus) not enough bytes, partial frame %d %d\n", data.size(), *frame_length);
if (!only_test)
{
debug("(wmbus) not enough bytes, partial frame %d %d\n", data.size(), *frame_length);
}
return PartialFrame;
}
debug("(wmbus) received full frame.\n");
if (!only_test)
{
debug("(wmbus) received full frame.\n");
}
return FullFrame;
}
FrameStatus checkMBusFrame(vector<uchar> &data,
size_t *frame_length,
int *payload_len_out,
int *payload_offset)
int *payload_offset,
bool only_test)
{
// Example:
// E5
@ -4471,48 +4549,78 @@ FrameStatus checkMBusFrame(vector<uchar> &data,
// 5E checksum
// 16 stop
debugPayload("(wmbus) checkMBUSFrame\n", data);
debugPayload("(mbus) checkMBUSFrame\n", data);
if (data.size() > 0 && data[0] == 0xe5)
{
// Single character confirmation frame.
if (only_test)
{
// For testing purposes we require the frame to be a single char as well.
// This happens when we pass a frame on the command line or in a simulation file.
// Otherwise a normal wmbus telegram with length e5 will triggere this mbus command.
// (When reading from a serial line, we might have data coming after the e5,
// but then we know that we are talking mbus.)
if (data.size() != 1)
{
return ErrorInFrame;
}
}
*payload_len_out = 0;
*payload_offset = 0;
*frame_length = 1;
debug("(wmbus) received E5 single byte frame.\n");
if (!only_test)
{
debug("(mbus) received E5 single byte frame.\n");
}
return FullFrame;
}
if (data.size() < 6)
{
// 4 byte start, 1 checksum, 1 stop
debug("(wmbus) less than 6 bytes, partial frame\n");
if (!only_test)
{
debug("(mbus) less than 6 bytes, partial frame\n");
}
return PartialFrame;
}
if (data[0] != 0x68 && data[3] != 0x68)
{
verbose("(wmbus) no 0x68 byte found, clearing buffer.\n");
data.clear();
if (!only_test)
{
verbose("(mbus) no 0x68 byte found, clearing buffer.\n");
data.clear();
}
return ErrorInFrame;
}
if (data[1] != data[2])
{
verbose("(wmbus) lengths not matching, clearing buffer.\n");
data.clear();
if (!only_test)
{
verbose("(mbus) lengths not matching, clearing buffer.\n");
data.clear();
}
return ErrorInFrame;
}
int payload_len = data[1];
*frame_length = payload_len+4+1+1; // start(4)+cs(1)+stop(1)
if (data.size() < *frame_length)
{
debug("(wmbus) not enough bytes, partial frame %d %d\n", data.size(), *frame_length);
if (!only_test)
{
debug("(mbus) not enough bytes, partial frame %d %d\n", data.size(), *frame_length);
}
return PartialFrame;
}
uchar stop = data[*frame_length-1];
if (stop != 0x16)
{
verbose("(wmbus) stop byte (0x%02x) at pos %d is not 0x16, clearing buffer.\n", stop, *frame_length-1);
data.clear();
if (!only_test)
{
verbose("(mbus) stop byte (0x%02x) at pos %d is not 0x16, clearing buffer.\n", stop, *frame_length-1);
data.clear();
}
return ErrorInFrame;
}
uchar csc = 0;
@ -4520,14 +4628,20 @@ FrameStatus checkMBusFrame(vector<uchar> &data,
uchar cs = data[*frame_length-2];
if (cs != csc)
{
verbose("(wmbus) expected checksum 0x%02x but got 0x%02x, clearing buffer.\n", csc, cs);
data.clear();
if (!only_test)
{
verbose("(mbus) expected checksum 0x%02x but got 0x%02x, clearing buffer.\n", csc, cs);
data.clear();
}
return ErrorInFrame;
}
*payload_len_out = *frame_length-6;
*payload_offset = 4;
debug("(wmbus) received full frame.\n");
if (!only_test)
{
debug("(mbus) received full frame.\n");
}
return FullFrame;
}

Wyświetl plik

@ -402,6 +402,7 @@ struct Telegram
int dll_mfct {};
uchar mbus_primary_address; // Single byte address 0-250 for mbus devices.
uchar mbus_ci; // MBus control information field.
vector<uchar> dll_a; // A field 6 bytes
// The 6 a field bytes are composed of 4 id bytes, version and type.
@ -701,6 +702,8 @@ bool isCiFieldOfType(int ci_field, CI_TYPE type);
int ciFieldLength(int ci_field);
string ciType(int ci_field);
string cType(int c_field);
bool isValidWMBusCField(int c_field);
bool isValidMBusCField(int c_field);
string ccType(int cc_field);
string difType(int dif);
double vifScale(int vif);
@ -726,12 +729,14 @@ enum FrameStatus { PartialFrame, FullFrame, ErrorInFrame, TextAndNotFrame };
FrameStatus checkWMBusFrame(vector<uchar> &data,
size_t *frame_length,
int *payload_len_out,
int *payload_offset);
int *payload_offset,
bool only_test);
FrameStatus checkMBusFrame(vector<uchar> &data,
size_t *frame_length,
int *payload_len_out,
int *payload_offset);
int *payload_offset,
bool only_test);
AccessCheck reDetectDevice(Detected *detected, shared_ptr<SerialCommunicationManager> handler);
@ -760,6 +765,7 @@ bool warned_for_telegram_before(Telegram *t, vector<uchar> &dll_a);
////////////////// MBUS
string mbusCField(uchar c_field);
const char *mbusCField(uchar c_field);
const char *mbusCiField(uchar ci_field);
#endif

Wyświetl plik

@ -423,13 +423,13 @@ FrameStatus WMBusAmber::checkAMB8465Frame(vector<uchar> &data,
size_t offset = 0;
// The data[0] must be at least 10 bytes. C MM AAAA V T Ci
// And C must be 0x44.
while ((payload_len = data[offset]) < 10 || data[offset+1] != 0x44)
// And C must be a valid wmbus c field.
while ((payload_len = data[offset]) < 10 || !isValidWMBusCField(data[offset+1]))
{
offset++;
if (offset + 2 >= data.size()) {
// No sensible telegram in the buffer. Flush it!
// But not the last char, because the next char could be a 0x44
// But not the last char, because the next char could be a valid c field.
verbose("(amb8465) no sensible telegram found, clearing buffer.\n");
uchar last = data[data.size()-1];
data.clear();

Wyświetl plik

@ -196,7 +196,7 @@ void WMBusRawTTY::processSerialData()
for (;;)
{
FrameStatus status = checkWMBusFrame(data_buffer_, &frame_length, &payload_len, &payload_offset);
FrameStatus status = checkWMBusFrame(data_buffer_, &frame_length, &payload_len, &payload_offset, false);
if (status == PartialFrame)
{

Wyświetl plik

@ -306,7 +306,7 @@ void WMBusRC1180::processSerialData()
for (;;)
{
FrameStatus status = checkWMBusFrame(read_buffer_, &frame_length, &payload_len, &payload_offset);
FrameStatus status = checkWMBusFrame(read_buffer_, &frame_length, &payload_len, &payload_offset, false);
if (status == PartialFrame)
{

Wyświetl plik

@ -174,15 +174,42 @@ void WMBusSimulator::simulate()
{
error("Not a valid string of hex bytes! \"%s\"\n", l.c_str());
}
AboutTelegram about("", 0, FrameType::WMBUS);
// Since this is a simulation, try to remove any frame format A or B
// data link layer crcs. These might remain if we have received the telegram
// to be simulated, from a CUL device or some other devices that does not remove the crcs.
// Normally the dongle (im871a/amb8465/rc1180/rtlwmbus/rtl443) removes the dll-crcs.
// Removing dll-crcs are also done explicitly in the wmbus_cul.cc driver.
removeAnyDLLCRCs(payload);
handleTelegram(about, payload);
size_t frame_length;
int payload_len, payload_offset;
bool is_mbus = FullFrame == checkMBusFrame(payload, &frame_length, &payload_len, &payload_offset, true);
bool is_wmbus = FullFrame == checkWMBusFrame(payload, &frame_length, &payload_len, &payload_offset, true);
debug("(simulator) is_mbus=%s is_wmbus=%s\n",
is_mbus?"true":"false",
is_wmbus?"true":"false");
if (is_mbus && is_wmbus)
{
warning("(mbus) telegram matches both mbus and wmbus! Assuming it is wmbus only.\n");
is_mbus = false;
}
if (is_mbus)
{
debug("(simulator) is mbus telegram.\n");
AboutTelegram about("", 0, FrameType::MBUS);
handleTelegram(about, payload);
}
if (is_wmbus)
{
debug("(simulator) is wmbus telegram.\n");
AboutTelegram about("", 0, FrameType::WMBUS);
// Since this is a simulation, try to remove any frame format A or B
// data link layer crcs. These might remain if we have received the telegram
// to be simulated, from a CUL device or some other devices that does not remove the crcs.
// Normally the dongle (im871a/amb8465/rc1180/rtlwmbus/rtl443) removes the dll-crcs.
// Removing dll-crcs are also done explicitly in the wmbus_cul.cc driver.
removeAnyDLLCRCs(payload);
handleTelegram(about, payload);
}
}
manager_->stop();
}