Python Library Stringtools
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. """
  5. stringtools.stp (Serial transfer protocol)
  6. ==========================================
  7. **Author:**
  8. * Dirk Alders <sudo-dirk@mount-mockery.de>
  9. **Description:**
  10. This module is a submodule of :mod:`stringtools` and creates an serial frame to transmit and receive messages via an serial interface.
  11. **Submodules:**
  12. * :class:`stringtools.stp.stp`
  13. * :func:`stringtools.stp.build_frame`
  14. """
  15. import stringtools
  16. import logging
  17. import sys
  18. try:
  19. from config import APP_NAME as ROOT_LOGGER_NAME
  20. except ImportError:
  21. ROOT_LOGGER_NAME = 'root'
  22. logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
  23. DATA_SYNC = b'\x3a'
  24. """The data sync byte"""
  25. DATA_CLEAR_BUFFER = b'\x3c'
  26. """The clear buffer byte ('\\\\x3a\\\\x3c' -> start of message)"""
  27. DATA_VALID_MSG = b'\x3e'
  28. """The valid message byte ('\\\\x3a\\\\x3e' -> end of message)"""
  29. DATA_STORE_SYNC_VALUE = b'\x3d'
  30. """The store sync value byte ('\\\\x3a\\\\x3d' -> '\\\\x3a' inside a message)"""
  31. STP_STATE_IDLE = 0x00
  32. """Idle state definition (default)"""
  33. STP_STATE_ESCAPE_1 = 0x01
  34. """Escape 1 state definition ('\\\\x3a\\\\x3c' found)"""
  35. STP_STATE_ESCAPE_2 = 0x02
  36. """Escape 2 state definition ('\\\\x3a' found inside a message)"""
  37. STP_STATE_STORE_DATA = 0x03
  38. """Store data state definition (start of message found; data will be stored)"""
  39. class stp(object):
  40. """This class extracts messages from an "stp-stream".
  41. **Example:**
  42. .. literalinclude:: stringtools/_examples_/stp.stp.py
  43. Will result to the following output:
  44. .. literalinclude:: stringtools/_examples_/stp.stp.log
  45. """
  46. LOG_PREFIX = 'STP:'
  47. def __init__(self):
  48. self.state = STP_STATE_IDLE
  49. self.__buffer__ = b''
  50. self.__clear_buffer__()
  51. def __clear_buffer__(self):
  52. if len(self.__buffer__) > 0:
  53. logger.warning('%s Chunking "%s" from buffer', self.LOG_PREFIX, stringtools.hexlify(self.__buffer__))
  54. self.__buffer__ = b''
  55. def process(self, data):
  56. """
  57. This processes a byte out of a "stp-stream".
  58. :param bytes data: A byte stream
  59. :returns: The extracted message or None, if no message is identified yet
  60. :rtype: str
  61. """
  62. if type(data) is list:
  63. raise TypeError
  64. if sys.version_info <= (3, 0):
  65. if type(data) is unicode:
  66. raise TypeError
  67. #
  68. rv = []
  69. #
  70. while len(data) > 0:
  71. if sys.version_info >= (3, 0):
  72. b = bytes([data[0]])
  73. else:
  74. b = data[0]
  75. data = data[1:]
  76. #
  77. if self.state == STP_STATE_IDLE:
  78. if b == DATA_SYNC:
  79. self.state = STP_STATE_ESCAPE_1
  80. logger.debug('%s data sync (%02x) received => changing state STP_STATE_IDLE -> STP_STATE_ESCAPE_1', self.LOG_PREFIX, ord(b))
  81. else:
  82. logger.warning('%s no data sync (%02x) received => ignoring byte', self.LOG_PREFIX, ord(b))
  83. elif self.state == STP_STATE_ESCAPE_1:
  84. if b == DATA_CLEAR_BUFFER:
  85. 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))
  86. self.state = STP_STATE_STORE_DATA
  87. self.__clear_buffer__()
  88. elif b != DATA_SYNC:
  89. self.state = STP_STATE_IDLE
  90. 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))
  91. else:
  92. logger.warning('%s 2nd data sync (%02x) received => keep state', self.LOG_PREFIX, ord(b))
  93. elif self.state == STP_STATE_STORE_DATA:
  94. if b == DATA_SYNC:
  95. self.state = STP_STATE_ESCAPE_2
  96. logger.debug('%s data sync (%02x) received => changing state STP_STATE_STORE_DATA -> STP_STATE_ESCAPE_2', self.LOG_PREFIX, ord(b))
  97. else:
  98. self.__buffer__ += b
  99. elif self.state == STP_STATE_ESCAPE_2:
  100. if b == DATA_CLEAR_BUFFER:
  101. 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))
  102. self.state = STP_STATE_STORE_DATA
  103. self.__clear_buffer__()
  104. elif b == DATA_VALID_MSG:
  105. self.state = STP_STATE_IDLE
  106. 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))
  107. rv.append(self.__buffer__)
  108. self.__buffer__ = b''
  109. elif b == DATA_STORE_SYNC_VALUE:
  110. self.state = STP_STATE_STORE_DATA
  111. 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))
  112. self.__buffer__ += DATA_SYNC
  113. elif b == DATA_SYNC:
  114. self.state = STP_STATE_ESCAPE_1
  115. logger.warning('%s second data sync (%02x) received => changing state STP_STATE_ESCAPE_2 -> STP_STATE_ESCAPE_1', self.LOG_PREFIX, ord(b))
  116. self.__clear_buffer__()
  117. else:
  118. self.state = STP_STATE_IDLE
  119. logger.warning('%s data (%02x) received => changing state STP_STATE_ESCAPE_2 -> STP_STATE_IDLE', self.LOG_PREFIX, ord(b))
  120. self.__clear_buffer__()
  121. else:
  122. 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))
  123. self.state = STP_STATE_IDLE
  124. self.__clear_buffer__()
  125. data = b + data
  126. for msg in rv:
  127. logger.info('%s message identified - %s', self.LOG_PREFIX, stringtools.hexlify(msg))
  128. return rv
  129. def build_frame(data):
  130. """This Method builds an "stp-frame" to be transfered via a stream.
  131. :param str data: A String (Bytes) to be framed
  132. :returns: The "stp-framed" message to be sent
  133. :rtype: str
  134. **Example:**
  135. .. literalinclude:: stringtools/_examples_/stp.build_frame.py
  136. Will result to the following output:
  137. .. literalinclude:: stringtools/_examples_/stp.build_frame.log
  138. """
  139. rv = DATA_SYNC + DATA_CLEAR_BUFFER
  140. for byte in data:
  141. if sys.version_info >= (3, 0):
  142. byte = bytes([byte])
  143. if byte == DATA_SYNC:
  144. rv += DATA_SYNC + DATA_STORE_SYNC_VALUE
  145. else:
  146. rv += byte
  147. rv += DATA_SYNC + DATA_VALID_MSG
  148. return rv