176 行
6.8 KiB
Python
176 行
6.8 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
"""
|
|
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
|
|
|
|
logger_name = 'STRINGTOOLS'
|
|
logger = logging.getLogger(logger_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:: ../examples/stp.stp.py
|
|
|
|
Will result to the following output:
|
|
|
|
.. literalinclude:: ../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:: ../examples/stp.build_frame.py
|
|
|
|
Will result to the following output:
|
|
|
|
.. literalinclude:: ../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
|