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

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