diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 14fc532..dbebfea 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,5 +1,9 @@ Change Log ---------- +**0.3** + +- Added asyncio reader for non-blocking reads. + **0.2** (2016-11-08) - User https://github.com/aequitas added support for DMSR version 2.2 diff --git a/README.rst b/README.rst index f63d373..23aff2f 100644 --- a/README.rst +++ b/README.rst @@ -11,7 +11,7 @@ also includes a serial client to directly read and parse smart meter data. Features -------- -DSMR Parser currently supports DSMR versions 2.2 and 4.x. It has been tested with Python 3.5 +DSMR Parser currently supports DSMR versions 2.2 and 4.x. It has been tested with Python 3.5 and 3.4. Examples diff --git a/dsmr_parser/serial.py b/dsmr_parser/serial.py index 3e7aa70..6f47b1c 100644 --- a/dsmr_parser/serial.py +++ b/dsmr_parser/serial.py @@ -1,5 +1,6 @@ import serial - +import asyncio +import serial_asyncio from dsmr_parser.parsers import TelegramParser, TelegramParserV2_2 SERIAL_SETTINGS_V2_2 = { @@ -33,9 +34,11 @@ def is_end_of_telegram(line): class SerialReader(object): + PORT_KEY = 'port' + def __init__(self, device, serial_settings, telegram_specification): self.serial_settings = serial_settings - self.serial_settings['port'] = device + self.serial_settings[self.PORT_KEY] = device if serial_settings is SERIAL_SETTINGS_V2_2: telegram_parser = TelegramParserV2_2 @@ -67,3 +70,44 @@ class SerialReader(object): if is_end_of_telegram(line): yield self.telegram_parser.parse(telegram) telegram = [] + + +class AsyncSerialReader(SerialReader): + """Serial reader using asyncio pyserial.""" + + PORT_KEY = 'url' + + @asyncio.coroutine + def read(self, queue): + """ + Read complete DSMR telegram's from the serial interface and parse it + into CosemObject's and MbusObject's. + + Instead of being a generator, values are pushed to provided queue for + asynchronous processing. + + :rtype Generator/Async + """ + # create Serial StreamReader + conn = serial_asyncio.open_serial_connection(**self.serial_settings) + reader, _ = yield from conn + + telegram = [] + + while True: + # read line if available or give control back to loop until + # new data has arrived + line = yield from reader.readline() + line = line.decode('ascii') + + # Telegrams need to be complete because the values belong to a + # particular reading and can also be related to eachother. + if not telegram and not is_start_of_telegram(line): + continue + + telegram.append(line) + + if is_end_of_telegram(line): + # push new parsed telegram onto queue + queue.put_nowait(self.telegram_parser.parse(telegram)) + telegram = [] diff --git a/setup.py b/setup.py index d02d7a3..3a919fa 100644 --- a/setup.py +++ b/setup.py @@ -6,10 +6,11 @@ setup( author='Nigel Dokter', author_email='nigeldokter@gmail.com', url='https://github.com/ndokter/dsmr_parser', - version='0.2', + version='0.3', packages=find_packages(), install_requires=[ 'pyserial>=3,<4', + 'pyserial-asyncio<1', 'pytz' ], entry_points={