dev progress

This commit is contained in:
Nigel Dokter 2017-01-07 22:29:02 +01:00
parent 60317a0dc5
commit 0e7819b535
3 changed files with 82 additions and 51 deletions

View File

@ -67,7 +67,7 @@ class DSMRProtocol(asyncio.Protocol):
# callback to call on complete telegram # callback to call on complete telegram
self.telegram_callback = telegram_callback self.telegram_callback = telegram_callback
# buffer to keep incomplete incoming data # buffer to keep incomplete incoming data
self.telegram_buffer = TelegramBuffer(self.handle_telegram) self.telegram_buffer = TelegramBuffer()
# keep a lock until the connection is closed # keep a lock until the connection is closed
self._closed = asyncio.Event() self._closed = asyncio.Event()
@ -80,7 +80,8 @@ class DSMRProtocol(asyncio.Protocol):
"""Add incoming data to buffer.""" """Add incoming data to buffer."""
data = data.decode('ascii') data = data.decode('ascii')
self.log.debug('received data: %s', data) self.log.debug('received data: %s', data)
self.telegram_buffer.append(data) self.telegram_buffer.put(data)
map(self.handle_telegram, self.telegram_buffer.get_all())
def connection_lost(self, exc): def connection_lost(self, exc):
"""Stop when connection is lost.""" """Stop when connection is lost."""
@ -94,8 +95,12 @@ class DSMRProtocol(asyncio.Protocol):
"""Send off parsed telegram to handling callback.""" """Send off parsed telegram to handling callback."""
self.log.debug('got telegram: %s', telegram) self.log.debug('got telegram: %s', telegram)
if self.telegram_callback: try:
self.telegram_callback(telegram) parsed_telegram = self.telegram_parser.parse(telegram)
except ParseError:
self.log.exception("failed to parse telegram")
else:
self.telegram_callback(parsed_telegram)
@asyncio.coroutine @asyncio.coroutine
def wait_closed(self): def wait_closed(self):

View File

@ -63,7 +63,7 @@ class SerialReader(object):
telegram_parser = TelegramParser telegram_parser = TelegramParser
self.telegram_parser = telegram_parser(telegram_specification) self.telegram_parser = telegram_parser(telegram_specification)
self.telegram_buffer = TelegramBuffer(self.handle_telegram) self.telegram_buffer = TelegramBuffer()
def read(self): def read(self):
""" """
@ -75,15 +75,16 @@ class SerialReader(object):
with serial.Serial(**self.serial_settings) as serial_handle: with serial.Serial(**self.serial_settings) as serial_handle:
while True: while True:
data = serial_handle.readline() data = serial_handle.readline()
self.telegram_buffer.append(data.decode('ascii')) self.telegram_buffer.put(data.decode('ascii'))
def handle_telegram(self, telegram): for telegram in self.telegram_buffer.get_all():
try: try:
yield self.telegram_parser.parse(telegram) yield self.telegram_parser.parse(telegram)
except ParseError as e: except ParseError as e:
logger.error('Failed to parse telegram: %s', e) logger.error('Failed to parse telegram: %s', e)
class AsyncSerialReader(SerialReader): class AsyncSerialReader(SerialReader):
"""Serial reader using asyncio pyserial.""" """Serial reader using asyncio pyserial."""
@ -108,16 +109,16 @@ class AsyncSerialReader(SerialReader):
# Read line if available or give control back to loop until new # Read line if available or give control back to loop until new
# data has arrived. # data has arrived.
data = yield from reader.readline() data = yield from reader.readline()
self.telegram_buffer.append(data.decode('ascii')) self.telegram_buffer.put(data.decode('ascii'))
# TODO for telegram in self.telegram_buffer.get_all():
# try: try:
# # Push new parsed telegram onto queue. # Push new parsed telegram onto queue.
# queue.put_nowait( queue.put_nowait(
# self.telegram_parser.parse(telegram) self.telegram_parser.parse(telegram)
# ) )
# except ParseError as e: except ParseError as e:
# logger.warning('Failed to parse telegram: %s', e) logger.warning('Failed to parse telegram: %s', e)
class TelegramBuffer(object): class TelegramBuffer(object):
@ -126,21 +127,25 @@ class TelegramBuffer(object):
when complete. when complete.
""" """
def __init__(self, callback): def __init__(self):
self._buffer = '' self._buffer = ''
self._callback = callback
def append(self, data): def get_all(self):
"""
Remove complete telegrams from buffer and yield them
:rtype generator:
"""
for telegram in self._find_telegrams():
self._remove(telegram)
yield telegram
def put(self, data):
""" """
Add telegram data to buffer. Add telegram data to buffer.
:param str data: chars, lines or full telegram strings of telegram data :param str data: chars, lines or full telegram strings of telegram data
""" """
self._buffer += data self._buffer += data
for telegram in self._find_telegrams():
self._callback(telegram)
self._remove(telegram)
def _remove(self, telegram): def _remove(self, telegram):
""" """
Remove telegram from buffer and incomplete data preceding it. This Remove telegram from buffer and incomplete data preceding it. This

View File

@ -8,40 +8,51 @@ from test.example_telegrams import TELEGRAM_V2_2, TELEGRAM_V4_2
class TelegramBufferTest(TestCase): class TelegramBufferTest(TestCase):
def setUp(self): def setUp(self):
self.callback = mock.MagicMock() self.telegram_buffer = TelegramBuffer()
self.telegram_buffer = TelegramBuffer(self.callback)
def test_v22_telegram(self): def test_v22_telegram(self):
self.telegram_buffer.append(TELEGRAM_V2_2) self.telegram_buffer.put(TELEGRAM_V2_2)
self.callback.assert_called_once_with(TELEGRAM_V2_2) telegram = next(self.telegram_buffer.get_all())
self.assertEqual(telegram, TELEGRAM_V2_2)
self.assertEqual(self.telegram_buffer._buffer, '') self.assertEqual(self.telegram_buffer._buffer, '')
def test_v42_telegram(self): def test_v42_telegram(self):
self.telegram_buffer.append(TELEGRAM_V4_2) self.telegram_buffer.put(TELEGRAM_V4_2)
self.callback.assert_called_once_with(TELEGRAM_V4_2) telegram = next(self.telegram_buffer.get_all())
self.assertEqual(telegram, TELEGRAM_V4_2)
self.assertEqual(self.telegram_buffer._buffer, '') self.assertEqual(self.telegram_buffer._buffer, '')
def test_multiple_mixed_telegrams(self): def test_multiple_mixed_telegrams(self):
self.telegram_buffer.append( self.telegram_buffer.put(
''.join((TELEGRAM_V2_2, TELEGRAM_V4_2, TELEGRAM_V2_2)) ''.join((TELEGRAM_V2_2, TELEGRAM_V4_2, TELEGRAM_V2_2))
) )
self.callback.assert_has_calls([ telegrams = list(self.telegram_buffer.get_all())
call(TELEGRAM_V2_2),
call(TELEGRAM_V4_2), self.assertListEqual(
call(TELEGRAM_V2_2), telegrams,
]) [
TELEGRAM_V2_2,
TELEGRAM_V4_2,
TELEGRAM_V2_2
]
)
self.assertEqual(self.telegram_buffer._buffer, '') self.assertEqual(self.telegram_buffer._buffer, '')
def test_v42_telegram_preceded_with_unclosed_telegram(self): def test_v42_telegram_preceded_with_unclosed_telegram(self):
# There are unclosed telegrams at the start of the buffer. # There are unclosed telegrams at the start of the buffer.
incomplete_telegram = TELEGRAM_V4_2[:-1] incomplete_telegram = TELEGRAM_V4_2[:-1]
self.telegram_buffer.append(incomplete_telegram + TELEGRAM_V4_2) self.telegram_buffer.put(incomplete_telegram + TELEGRAM_V4_2)
self.callback.assert_called_once_with(TELEGRAM_V4_2) telegram = next(self.telegram_buffer.get_all())
self.assertEqual(telegram, TELEGRAM_V4_2)
self.assertEqual(self.telegram_buffer._buffer, '') self.assertEqual(self.telegram_buffer._buffer, '')
def test_v42_telegram_preceded_with_unopened_telegram(self): def test_v42_telegram_preceded_with_unopened_telegram(self):
@ -49,37 +60,47 @@ class TelegramBufferTest(TestCase):
# the buffer was being filled while the telegram was outputted halfway. # the buffer was being filled while the telegram was outputted halfway.
incomplete_telegram = TELEGRAM_V4_2[1:] incomplete_telegram = TELEGRAM_V4_2[1:]
self.telegram_buffer.append(incomplete_telegram + TELEGRAM_V4_2) self.telegram_buffer.put(incomplete_telegram + TELEGRAM_V4_2)
self.callback.assert_called_once_with(TELEGRAM_V4_2) telegram = next(self.telegram_buffer.get_all())
self.assertEqual(telegram, TELEGRAM_V4_2)
self.assertEqual(self.telegram_buffer._buffer, '') self.assertEqual(self.telegram_buffer._buffer, '')
def test_v42_telegram_trailed_by_unclosed_telegram(self): def test_v42_telegram_trailed_by_unclosed_telegram(self):
incomplete_telegram = TELEGRAM_V4_2[:-1] incomplete_telegram = TELEGRAM_V4_2[:-1]
self.telegram_buffer.append(TELEGRAM_V4_2 + incomplete_telegram) self.telegram_buffer.put(TELEGRAM_V4_2 + incomplete_telegram)
self.callback.assert_called_once_with(TELEGRAM_V4_2) telegram = next(self.telegram_buffer.get_all())
self.assertEqual(telegram, TELEGRAM_V4_2)
self.assertEqual(self.telegram_buffer._buffer, incomplete_telegram) self.assertEqual(self.telegram_buffer._buffer, incomplete_telegram)
def test_v42_telegram_trailed_by_unopened_telegram(self): def test_v42_telegram_trailed_by_unopened_telegram(self):
incomplete_telegram = TELEGRAM_V4_2[1:] incomplete_telegram = TELEGRAM_V4_2[1:]
self.telegram_buffer.append(TELEGRAM_V4_2 + incomplete_telegram) self.telegram_buffer.put(TELEGRAM_V4_2 + incomplete_telegram)
self.callback.assert_called_once_with(TELEGRAM_V4_2) telegram = next(self.telegram_buffer.get_all())
self.assertEqual(telegram, TELEGRAM_V4_2)
self.assertEqual(self.telegram_buffer._buffer, incomplete_telegram) self.assertEqual(self.telegram_buffer._buffer, incomplete_telegram)
def test_v42_telegram_adding_line_by_line(self): def test_v42_telegram_adding_line_by_line(self):
for line in TELEGRAM_V4_2.splitlines(keepends=True): for line in TELEGRAM_V4_2.splitlines(keepends=True):
self.telegram_buffer.append(line) self.telegram_buffer.put(line)
self.callback.assert_called_once_with(TELEGRAM_V4_2) telegram = next(self.telegram_buffer.get_all())
self.assertEqual(telegram, TELEGRAM_V4_2)
self.assertEqual(self.telegram_buffer._buffer, '') self.assertEqual(self.telegram_buffer._buffer, '')
def test_v42_telegram_adding_char_by_char(self): def test_v42_telegram_adding_char_by_char(self):
for char in TELEGRAM_V4_2: for char in TELEGRAM_V4_2:
self.telegram_buffer.append(char) self.telegram_buffer.put(char)
self.callback.assert_called_once_with(TELEGRAM_V4_2) telegram = next(self.telegram_buffer.get_all())
self.assertEqual(telegram, TELEGRAM_V4_2)
self.assertEqual(self.telegram_buffer._buffer, '') self.assertEqual(self.telegram_buffer._buffer, '')