issue-51-telegram WIP
This commit is contained in:
parent
3e0332963c
commit
2adcd2b207
@ -24,9 +24,9 @@ ELECTRICITY_ACTIVE_TARIFF = r'\d-\d:96\.14\.0.+?\r\n'
|
||||
EQUIPMENT_IDENTIFIER = r'\d-\d:96\.1\.1.+?\r\n'
|
||||
CURRENT_ELECTRICITY_USAGE = r'\d-\d:1\.7\.0.+?\r\n'
|
||||
CURRENT_ELECTRICITY_DELIVERY = r'\d-\d:2\.7\.0.+?\r\n'
|
||||
LONG_POWER_FAILURE_COUNT = r'96\.7\.9.+?\r\n'
|
||||
SHORT_POWER_FAILURE_COUNT = r'96\.7\.21.+?\r\n'
|
||||
POWER_EVENT_FAILURE_LOG = r'99\.97\.0.+?\r\n'
|
||||
LONG_POWER_FAILURE_COUNT = r'\d-\d:96\.7\.9.+?\r\n'
|
||||
SHORT_POWER_FAILURE_COUNT = r'\d-\d:96\.7\.21.+?\r\n'
|
||||
POWER_EVENT_FAILURE_LOG = r'\d-\d:99\.97\.0.+?\r\n'
|
||||
VOLTAGE_SAG_L1_COUNT = r'\d-\d:32\.32\.0.+?\r\n'
|
||||
VOLTAGE_SAG_L2_COUNT = r'\d-\d:52\.32\.0.+?\r\n'
|
||||
VOLTAGE_SAG_L3_COUNT = r'\d-\d:72\.32\.0.+?\r\n'
|
||||
|
@ -24,11 +24,18 @@ class Telegram(object):
|
||||
self._reverse_obis_name_mapping = dsmr_parser.obis_name_mapping.REVERSE_EN
|
||||
self._item_names = self._get_item_names()
|
||||
|
||||
def add(self, obis_reference, value):
|
||||
self._telegram_data[obis_reference].append(value)
|
||||
def add(self, obis_reference, dsmr_object):
|
||||
self._telegram_data[obis_reference].append(dsmr_object)
|
||||
|
||||
def get(self, obis_reference, channel):
|
||||
# TODO experiment with api to see what is nice
|
||||
def get(self, obis_reference, channel=None):
|
||||
if channel is None:
|
||||
return self._telegram_data[obis_reference]
|
||||
|
||||
try:
|
||||
return next(filter(lambda x: x.channel == channel, self._telegram_data[obis_reference]))
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
def __getattr__(self, name):
|
||||
""" will only get called for undefined attributes """
|
||||
@ -66,13 +73,10 @@ class DSMRObject(object):
|
||||
Represents all data from a single telegram line.
|
||||
"""
|
||||
|
||||
def __init__(self, values):
|
||||
def __init__(self, channel, values):
|
||||
self.channel = channel # TODO consider if only MBus should have channels
|
||||
self.values = values
|
||||
|
||||
@property
|
||||
def channel(self):
|
||||
return 0 # TODO
|
||||
|
||||
|
||||
class MBusObject(DSMRObject):
|
||||
|
||||
@ -203,8 +207,8 @@ class ProfileGenericObject(DSMRObject):
|
||||
containing the datetime (timestamp) and the value.
|
||||
"""
|
||||
|
||||
def __init__(self, values):
|
||||
super().__init__(values)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._buffer_list = None
|
||||
|
||||
@property
|
||||
@ -230,9 +234,16 @@ class ProfileGenericObject(DSMRObject):
|
||||
if self._buffer_list is None:
|
||||
self._buffer_list = []
|
||||
values_offset = 2
|
||||
|
||||
for i in range(self.buffer_length):
|
||||
offset = values_offset + i * 2
|
||||
self._buffer_list.append(MBusObject([self.values[offset], self.values[offset + 1]]))
|
||||
self._buffer_list.append(
|
||||
MBusObject(
|
||||
channel=self.channel,
|
||||
values=[self.values[offset], self.values[offset + 1]]
|
||||
)
|
||||
)
|
||||
|
||||
return self._buffer_list
|
||||
|
||||
def __str__(self):
|
||||
|
@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
|
||||
class TelegramParser(object):
|
||||
crc16_tab = []
|
||||
|
||||
def __init__(self, telegram_specification, apply_checksum_validation=True, gas_meter_channel=None):
|
||||
def __init__(self, telegram_specification, apply_checksum_validation=True):
|
||||
"""
|
||||
:param telegram_specification: determines how the telegram is parsed
|
||||
:param apply_checksum_validation: validate checksum if applicable for
|
||||
@ -87,12 +87,12 @@ class TelegramParser(object):
|
||||
# so only parse lines that match
|
||||
for match in matches:
|
||||
try:
|
||||
value = parser.parse(match)
|
||||
dsmr_object = parser.parse(match)
|
||||
except Exception:
|
||||
logger.error("ignore line with signature {}, because parsing failed.".format(signature),
|
||||
exc_info=True)
|
||||
else:
|
||||
telegram.add(obis_reference=signature, value=value)
|
||||
telegram.add(obis_reference=signature, dsmr_object=dsmr_object)
|
||||
|
||||
return telegram
|
||||
|
||||
@ -174,6 +174,20 @@ class DSMRObjectParser(object):
|
||||
return [self.value_formats[i].parse(value)
|
||||
for i, value in enumerate(values)]
|
||||
|
||||
def _parse_channel(self, line):
|
||||
"""
|
||||
Get the channel identifier of a line.
|
||||
|
||||
Line format:
|
||||
'0-2:24.2.1(200426223001S)(00246.138*m3)'
|
||||
^
|
||||
channel
|
||||
"""
|
||||
try:
|
||||
return int(line[2])
|
||||
except ValueError:
|
||||
raise ParseError("Invalid channel for line '%s' in '%s'", line, self)
|
||||
|
||||
def _parse(self, line):
|
||||
# Match value groups, but exclude the parentheses
|
||||
pattern = re.compile(r'((?<=\()[0-9a-zA-Z\.\*\-\:]{0,}(?=\)))')
|
||||
@ -207,7 +221,10 @@ class MBusParser(DSMRObjectParser):
|
||||
"""
|
||||
|
||||
def parse(self, line):
|
||||
return MBusObject(self._parse(line))
|
||||
return MBusObject(
|
||||
channel=self._parse_channel(line),
|
||||
values=self._parse(line)
|
||||
)
|
||||
|
||||
|
||||
class MaxDemandParser(DSMRObjectParser):
|
||||
@ -235,6 +252,8 @@ class MaxDemandParser(DSMRObjectParser):
|
||||
pattern = re.compile(r'((?<=\()[0-9a-zA-Z\.\*\-\:]{0,}(?=\)))')
|
||||
values = re.findall(pattern, line)
|
||||
|
||||
channel = self._parse_channel(line)
|
||||
|
||||
objects = []
|
||||
|
||||
count = int(values[0])
|
||||
@ -242,7 +261,10 @@ class MaxDemandParser(DSMRObjectParser):
|
||||
timestamp_month = ValueParser(timestamp).parse(values[i * 3 + 1])
|
||||
timestamp_occurred = ValueParser(timestamp).parse(values[i * 3 + 1])
|
||||
value = ValueParser(Decimal).parse(values[i * 3 + 2])
|
||||
objects.append(MBusObjectPeak([timestamp_month, timestamp_occurred, value]))
|
||||
objects.append(MBusObjectPeak(
|
||||
channel=channel,
|
||||
values=[timestamp_month, timestamp_occurred, value]
|
||||
))
|
||||
|
||||
return objects
|
||||
|
||||
@ -268,7 +290,10 @@ class CosemParser(DSMRObjectParser):
|
||||
"""
|
||||
|
||||
def parse(self, line):
|
||||
return CosemObject(self._parse(line))
|
||||
return CosemObject(
|
||||
channel=self._parse_channel(line),
|
||||
values=self._parse(line)
|
||||
)
|
||||
|
||||
|
||||
class ProfileGenericParser(DSMRObjectParser):
|
||||
@ -327,7 +352,10 @@ class ProfileGenericParser(DSMRObjectParser):
|
||||
return [self.value_formats[i].parse(value) for i, value in enumerate(values)]
|
||||
|
||||
def parse(self, line):
|
||||
return ProfileGenericObject(self._parse(line))
|
||||
return ProfileGenericObject(
|
||||
channel=self._parse_channel(line),
|
||||
values=self._parse(line)
|
||||
)
|
||||
|
||||
|
||||
class ValueParser(object):
|
||||
@ -335,7 +363,7 @@ class ValueParser(object):
|
||||
Parses a single value from DSMRObject's.
|
||||
|
||||
Example with coerce_type being int:
|
||||
(002*A) becomes {'value': 1, 'unit': 'A'}
|
||||
(002*A) becomes {'value': 2, 'unit': 'A'}
|
||||
|
||||
Example with coerce_type being str:
|
||||
(42) becomes {'value': '42', 'unit': None}
|
||||
|
Loading…
Reference in New Issue
Block a user