diff --git a/README.rst b/README.rst index 01b9af3..b97d440 100644 --- a/README.rst +++ b/README.rst @@ -46,6 +46,25 @@ process because the code is blocking (not asynchronous): To be documented. +**Socket client** + +Read a remote serial port (for example using ser2net) and work with the parsed telegrams. +It should be run in a separate process because the code is blocking (not asynchronous): + +.. code-block:: python + + from dsmr_parser import telegram_specifications + from dsmr_parser.clients import SocketReader + + socket_reader = SocketReader( + host='127.0.0.1', + port=2001, + telegram_specification=telegram_specifications.V4 + ) + + for telegram in socket_reader.read(): + print(telegram) # see 'Telegram object' docs below + Parsing module usage -------------------- diff --git a/dsmr_parser/clients/__init__.py b/dsmr_parser/clients/__init__.py index 7323ecd..9563399 100644 --- a/dsmr_parser/clients/__init__.py +++ b/dsmr_parser/clients/__init__.py @@ -1,5 +1,6 @@ from dsmr_parser.clients.settings import SERIAL_SETTINGS_V2_2, \ SERIAL_SETTINGS_V4, SERIAL_SETTINGS_V5 from dsmr_parser.clients.serial_ import SerialReader, AsyncSerialReader +from dsmr_parser.clients.socket_ import SocketReader from dsmr_parser.clients.protocol import create_dsmr_protocol, \ create_dsmr_reader, create_tcp_dsmr_reader diff --git a/dsmr_parser/clients/socket_.py b/dsmr_parser/clients/socket_.py new file mode 100644 index 0000000..7c13f02 --- /dev/null +++ b/dsmr_parser/clients/socket_.py @@ -0,0 +1,91 @@ +import logging +import socket + +from dsmr_parser.clients.telegram_buffer import TelegramBuffer +from dsmr_parser.exceptions import ParseError, InvalidChecksumError +from dsmr_parser.parsers import TelegramParser +from dsmr_parser.objects import Telegram + + +logger = logging.getLogger(__name__) + + +class SocketReader(object): + + BUFFER_SIZE = 256 + + def __init__(self, host, port, telegram_specification): + self.host = host + self.port = port + + self.telegram_parser = TelegramParser(telegram_specification) + self.telegram_buffer = TelegramBuffer() + self.telegram_specification = telegram_specification + + + def read(self): + """ + Read complete DSMR telegram's from remote interface and parse it + into CosemObject's and MbusObject's + + :rtype: generator + """ + buffer = b"" + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_handle: + + socket_handle.connect((self.host, self.port)) + + while True: + buffer += socket_handle.recv(self.BUFFER_SIZE) + + lines = buffer.splitlines(keepends=True) + + if len(lines) == 0: + continue + + for data in lines: + self.telegram_buffer.append(data.decode('ascii')) + + for telegram in self.telegram_buffer.get_all(): + try: + yield self.telegram_parser.parse(telegram) + except InvalidChecksumError as e: + logger.warning(str(e)) + except ParseError as e: + logger.error('Failed to parse telegram: %s', e) + + buffer = b"" + + def read_as_object(self): + """ + Read complete DSMR telegram's from remote and return a Telegram object. + + :rtype: generator + """ + buffer = b"" + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_handle: + + socket_handle.connect((self.host, self.port)) + + while True: + buffer += socket_handle.recv(self.BUFFER_SIZE) + + lines = buffer.splitlines(keepends=True) + + if len(lines) == 0: + continue + + for data in lines: + self.telegram_buffer.append(data.decode('ascii')) + + for telegram in self.telegram_buffer.get_all(): + try: + yield Telegram(telegram, self.telegram_parser, self.telegram_specification) + except InvalidChecksumError as e: + logger.warning(str(e)) + except ParseError as e: + logger.error('Failed to parse telegram: %s', e) + + buffer = b"" \ No newline at end of file