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