commit
d61f2229c8
@ -63,6 +63,6 @@ ELECTRICITY_DELIVERED_TARIFF_ALL = (
|
|||||||
|
|
||||||
# Alternate codes for foreign countries.
|
# Alternate codes for foreign countries.
|
||||||
BELGIUM_HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.3.+?\r\n' # Different code, same format.
|
BELGIUM_HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.3.+?\r\n' # Different code, same format.
|
||||||
LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'\d-\d:42\.0\.0.+?\r\n' # Logical device name
|
LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'\d-\d:42\.0\.0.+?\r\n' # Logical device name
|
||||||
LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+)
|
LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+)
|
||||||
LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-)
|
LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-)
|
||||||
|
@ -225,6 +225,10 @@ class ProfileGenericParser(DSMRObjectParser):
|
|||||||
self.parsers_for_unidentified = parsers_for_unidentified
|
self.parsers_for_unidentified = parsers_for_unidentified
|
||||||
|
|
||||||
def _is_line_wellformed(self, line, values):
|
def _is_line_wellformed(self, line, values):
|
||||||
|
if values and (len(values) == 1) and (values[0] == ''):
|
||||||
|
# special case: single empty parentheses (indicated by empty string)
|
||||||
|
return True
|
||||||
|
|
||||||
if values and (len(values) >= 2) and (values[0].isdigit()):
|
if values and (len(values) >= 2) and (values[0].isdigit()):
|
||||||
buffer_length = int(values[0])
|
buffer_length = int(values[0])
|
||||||
return (buffer_length <= 10) and (len(values) == (buffer_length * 2 + 2))
|
return (buffer_length <= 10) and (len(values) == (buffer_length * 2 + 2))
|
||||||
@ -232,6 +236,9 @@ class ProfileGenericParser(DSMRObjectParser):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _parse_values(self, values):
|
def _parse_values(self, values):
|
||||||
|
if values and (len(values) == 1) and (values[0] is None):
|
||||||
|
# special case: single empty parentheses; make sure empty ProfileGenericObject is created
|
||||||
|
values = [0, None] # buffer_length=0, buffer_value_obis_ID=None
|
||||||
buffer_length = int(values[0])
|
buffer_length = int(values[0])
|
||||||
buffer_value_obis_ID = values[1]
|
buffer_value_obis_ID = values[1]
|
||||||
if (buffer_length > 0):
|
if (buffer_length > 0):
|
||||||
|
@ -127,4 +127,4 @@ TELEGRAM_V5 = (
|
|||||||
'0-2:24.1.0(003)\r\n'
|
'0-2:24.1.0(003)\r\n'
|
||||||
'0-2:96.1.0()\r\n'
|
'0-2:96.1.0()\r\n'
|
||||||
'!6EEE\r\n'
|
'!6EEE\r\n'
|
||||||
)
|
)
|
||||||
|
@ -241,7 +241,6 @@ class TelegramParserV5Test(unittest.TestCase):
|
|||||||
|
|
||||||
def test_checksum_missing(self):
|
def test_checksum_missing(self):
|
||||||
# Remove the checksum value causing a ParseError.
|
# Remove the checksum value causing a ParseError.
|
||||||
corrupted_telegram = TELEGRAM_V5.replace('!87B3\r\n', '')
|
corrupted_telegram = TELEGRAM_V5.replace('!6EEE\r\n', '')
|
||||||
|
|
||||||
with self.assertRaises(ParseError):
|
with self.assertRaises(ParseError):
|
||||||
TelegramParser.validate_checksum(corrupted_telegram)
|
TelegramParser.validate_checksum(corrupted_telegram)
|
||||||
|
89
test/test_parser_corner_cases.py
Normal file
89
test/test_parser_corner_cases.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from dsmr_parser import telegram_specifications
|
||||||
|
|
||||||
|
from dsmr_parser.objects import Telegram
|
||||||
|
from dsmr_parser.objects import ProfileGenericObject
|
||||||
|
from dsmr_parser.parsers import TelegramParser
|
||||||
|
from dsmr_parser.parsers import ProfileGenericParser
|
||||||
|
from dsmr_parser.profile_generic_specifications import BUFFER_TYPES
|
||||||
|
from dsmr_parser.profile_generic_specifications import PG_HEAD_PARSERS
|
||||||
|
from dsmr_parser.profile_generic_specifications import PG_UNIDENTIFIED_BUFFERTYPE_PARSERS
|
||||||
|
from test.example_telegrams import TELEGRAM_V5
|
||||||
|
|
||||||
|
|
||||||
|
class TestParserCornerCases(unittest.TestCase):
|
||||||
|
""" Test instantiation of Telegram object """
|
||||||
|
|
||||||
|
def test_power_event_log_empty_1(self):
|
||||||
|
# POWER_EVENT_FAILURE_LOG (1-0:99.97.0)
|
||||||
|
parser = TelegramParser(telegram_specifications.V5)
|
||||||
|
telegram = Telegram(TELEGRAM_V5, parser, telegram_specifications.V5)
|
||||||
|
|
||||||
|
object_type = ProfileGenericObject
|
||||||
|
testitem = telegram.POWER_EVENT_FAILURE_LOG
|
||||||
|
assert isinstance(testitem, object_type)
|
||||||
|
assert testitem.buffer_length == 0
|
||||||
|
assert testitem.buffer_type == '0-0:96.7.19'
|
||||||
|
buffer = testitem.buffer
|
||||||
|
assert isinstance(testitem.buffer, list)
|
||||||
|
assert len(buffer) == 0
|
||||||
|
|
||||||
|
def test_power_event_log_empty_2(self):
|
||||||
|
pef_parser = ProfileGenericParser(BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS)
|
||||||
|
object_type = ProfileGenericObject
|
||||||
|
|
||||||
|
# Power Event Log with 0 items and no object type
|
||||||
|
pefl_line = r'1-0:99.97.0(0)()\r\n'
|
||||||
|
testitem = pef_parser.parse(pefl_line)
|
||||||
|
|
||||||
|
assert isinstance(testitem, object_type)
|
||||||
|
assert testitem.buffer_length == 0
|
||||||
|
assert testitem.buffer_type is None
|
||||||
|
buffer = testitem.buffer
|
||||||
|
assert isinstance(testitem.buffer, list)
|
||||||
|
assert len(buffer) == 0
|
||||||
|
assert testitem.values == [{'value': 0, 'unit': None}, {'value': None, 'unit': None}]
|
||||||
|
json = testitem.to_json()
|
||||||
|
assert json == '{"buffer_length": 0, "buffer_type": null, "buffer": []}'
|
||||||
|
|
||||||
|
def test_power_event_log_null_values(self):
|
||||||
|
pef_parser = ProfileGenericParser(BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS)
|
||||||
|
object_type = ProfileGenericObject
|
||||||
|
|
||||||
|
# Power Event Log with 1 item and no object type and nno values for the item
|
||||||
|
pefl_line = r'1-0:99.97.0(1)()()()\r\n'
|
||||||
|
testitem = pef_parser.parse(pefl_line)
|
||||||
|
|
||||||
|
assert isinstance(testitem, object_type)
|
||||||
|
assert testitem.buffer_length == 1
|
||||||
|
assert testitem.buffer_type is None
|
||||||
|
buffer = testitem.buffer
|
||||||
|
assert isinstance(testitem.buffer, list)
|
||||||
|
assert len(buffer) == 1
|
||||||
|
assert testitem.values == [{'value': 1, 'unit': None}, {'value': None, 'unit': None},
|
||||||
|
{'value': None, 'unit': None}, {'value': None, 'unit': None}]
|
||||||
|
json = testitem.to_json()
|
||||||
|
assert json == \
|
||||||
|
'{"buffer_length": 1, "buffer_type": null, "buffer": [{"datetime": null, "value": null, "unit": null}]}'
|
||||||
|
|
||||||
|
def test_power_event_log_brackets_only(self):
|
||||||
|
# POWER_EVENT_FAILURE_LOG (1-0:99.97.0)
|
||||||
|
# Issue 57
|
||||||
|
# Test of an ill formatted empty POWER_EVENT_FAILURE_LOG, observed on some smartmeters
|
||||||
|
# The idea is that instead of failing, the parser converts it to an empty POWER_EVENT_FAILURE_LOG
|
||||||
|
pef_parser = ProfileGenericParser(BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS)
|
||||||
|
object_type = ProfileGenericObject
|
||||||
|
|
||||||
|
pefl_line = r'1-0:99.97.0()\r\n'
|
||||||
|
testitem = pef_parser.parse(pefl_line)
|
||||||
|
|
||||||
|
assert isinstance(testitem, object_type)
|
||||||
|
assert testitem.buffer_length == 0
|
||||||
|
assert testitem.buffer_type is None
|
||||||
|
buffer = testitem.buffer
|
||||||
|
assert isinstance(testitem.buffer, list)
|
||||||
|
assert len(buffer) == 0
|
||||||
|
assert testitem.values == [{'value': 0, 'unit': None}, {'value': None, 'unit': None}]
|
||||||
|
json = testitem.to_json()
|
||||||
|
assert json == '{"buffer_length": 0, "buffer_type": null, "buffer": []}'
|
Loading…
Reference in New Issue
Block a user