123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- #
- """
- stringtools.stp (Serial transfer protocol)
- ==========================================
-
- **Author:**
-
- * Dirk Alders <sudo-dirk@mount-mockery.de>
-
- **Description:**
-
- This module is a submodule of :mod:`stringtools` and creates an serial frame to transmit and receive messages via an serial interface.
-
- **Submodules:**
-
- * :class:`stringtools.stp.stp`
- * :func:`stringtools.stp.build_frame`
- """
-
- import stringtools
-
- import logging
- import sys
-
- try:
- from config import APP_NAME as ROOT_LOGGER_NAME
- except ImportError:
- ROOT_LOGGER_NAME = 'root'
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
-
- DATA_SYNC = b'\x3a'
- """The data sync byte"""
- DATA_CLEAR_BUFFER = b'\x3c'
- """The clear buffer byte ('\\\\x3a\\\\x3c' -> start of message)"""
- DATA_VALID_MSG = b'\x3e'
- """The valid message byte ('\\\\x3a\\\\x3e' -> end of message)"""
- DATA_STORE_SYNC_VALUE = b'\x3d'
- """The store sync value byte ('\\\\x3a\\\\x3d' -> '\\\\x3a' inside a message)"""
-
- STP_STATE_IDLE = 0x00
- """Idle state definition (default)"""
- STP_STATE_ESCAPE_1 = 0x01
- """Escape 1 state definition ('\\\\x3a\\\\x3c' found)"""
- STP_STATE_ESCAPE_2 = 0x02
- """Escape 2 state definition ('\\\\x3a' found inside a message)"""
- STP_STATE_STORE_DATA = 0x03
- """Store data state definition (start of message found; data will be stored)"""
-
-
- class stp(object):
- """This class extracts messages from an "stp-stream".
-
- **Example:**
-
- .. literalinclude:: stringtools/_examples_/stp.stp.py
-
- Will result to the following output:
-
- .. literalinclude:: stringtools/_examples_/stp.stp.log
- """
- LOG_PREFIX = 'STP:'
-
- def __init__(self):
- self.state = STP_STATE_IDLE
- self.__buffer__ = b''
- self.__clear_buffer__()
-
- def __clear_buffer__(self):
- if len(self.__buffer__) > 0:
- logger.warning('%s Chunking "%s" from buffer', self.LOG_PREFIX, stringtools.hexlify(self.__buffer__))
- self.__buffer__ = b''
-
- def process(self, data):
- """
- This processes a byte out of a "stp-stream".
-
- :param bytes data: A byte stream
- :returns: The extracted message or None, if no message is identified yet
- :rtype: str
- """
- if type(data) is list:
- raise TypeError
- if sys.version_info <= (3, 0):
- if type(data) is unicode:
- raise TypeError
- #
- rv = []
- #
- while len(data) > 0:
- if sys.version_info >= (3, 0):
- b = bytes([data[0]])
- else:
- b = data[0]
- data = data[1:]
- #
- if self.state == STP_STATE_IDLE:
- if b == DATA_SYNC:
- self.state = STP_STATE_ESCAPE_1
- logger.debug('%s data sync (%02x) received => changing state STP_STATE_IDLE -> STP_STATE_ESCAPE_1', self.LOG_PREFIX, ord(b))
- else:
- logger.warning('%s no data sync (%02x) received => ignoring byte', self.LOG_PREFIX, ord(b))
- elif self.state == STP_STATE_ESCAPE_1:
- if b == DATA_CLEAR_BUFFER:
- logger.debug('%s start pattern (%02x %02x) received => changing state STP_STATE_ESCAPE_1 -> STP_STATE_STORE_DATA', self.LOG_PREFIX, ord(DATA_SYNC), ord(b))
- self.state = STP_STATE_STORE_DATA
- self.__clear_buffer__()
- elif b != DATA_SYNC:
- self.state = STP_STATE_IDLE
- logger.warning('%s no start pattern (%02x %02x) received => changing state STP_STATE_ESCAPE_1 -> STP_STATE_IDLE', self.LOG_PREFIX, ord(DATA_SYNC), ord(b))
- else:
- logger.warning('%s 2nd data sync (%02x) received => keep state', self.LOG_PREFIX, ord(b))
- elif self.state == STP_STATE_STORE_DATA:
- if b == DATA_SYNC:
- self.state = STP_STATE_ESCAPE_2
- logger.debug('%s data sync (%02x) received => changing state STP_STATE_STORE_DATA -> STP_STATE_ESCAPE_2', self.LOG_PREFIX, ord(b))
- else:
- self.__buffer__ += b
- elif self.state == STP_STATE_ESCAPE_2:
- if b == DATA_CLEAR_BUFFER:
- logger.warning('%s start pattern (%02x %02x) received => changing state STP_STATE_ESCAPE_2 -> STP_STATE_STORE_DATA', self.LOG_PREFIX, ord(DATA_SYNC), ord(b))
- self.state = STP_STATE_STORE_DATA
- self.__clear_buffer__()
- elif b == DATA_VALID_MSG:
- self.state = STP_STATE_IDLE
- logger.debug('%s end pattern (%02x %02x) received => storing message and changing state STP_STATE_ESCAPE_2 -> STP_STATE_IDLE', self.LOG_PREFIX, ord(DATA_SYNC), ord(b))
- rv.append(self.__buffer__)
- self.__buffer__ = b''
- elif b == DATA_STORE_SYNC_VALUE:
- self.state = STP_STATE_STORE_DATA
- logger.debug('%s store sync pattern (%02x %02x) received => changing state STP_STATE_ESCAPE_2 -> STP_STATE_STORE_DATA', self.LOG_PREFIX, ord(DATA_SYNC), ord(b))
- self.__buffer__ += DATA_SYNC
- elif b == DATA_SYNC:
- self.state = STP_STATE_ESCAPE_1
- logger.warning('%s second data sync (%02x) received => changing state STP_STATE_ESCAPE_2 -> STP_STATE_ESCAPE_1', self.LOG_PREFIX, ord(b))
- self.__clear_buffer__()
- else:
- self.state = STP_STATE_IDLE
- logger.warning('%s data (%02x) received => changing state STP_STATE_ESCAPE_2 -> STP_STATE_IDLE', self.LOG_PREFIX, ord(b))
- self.__clear_buffer__()
- else:
- logger.error('%s unknown state (%s) => adding value (%02x) back to data again and changing state -> STP_STATE_IDLE', self.LOG_PREFIX, repr(self.state), ord(b))
- self.state = STP_STATE_IDLE
- self.__clear_buffer__()
- data = b + data
- for msg in rv:
- logger.info('%s message identified - %s', self.LOG_PREFIX, stringtools.hexlify(msg))
- return rv
-
-
- def build_frame(data):
- """This Method builds an "stp-frame" to be transfered via a stream.
-
- :param str data: A String (Bytes) to be framed
- :returns: The "stp-framed" message to be sent
- :rtype: str
-
- **Example:**
-
- .. literalinclude:: stringtools/_examples_/stp.build_frame.py
-
- Will result to the following output:
-
- .. literalinclude:: stringtools/_examples_/stp.build_frame.log
- """
- rv = DATA_SYNC + DATA_CLEAR_BUFFER
-
- for byte in data:
- if sys.version_info >= (3, 0):
- byte = bytes([byte])
- if byte == DATA_SYNC:
- rv += DATA_SYNC + DATA_STORE_SYNC_VALUE
- else:
- rv += byte
-
- rv += DATA_SYNC + DATA_VALID_MSG
- return rv
|