PageRenderTime 33ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/host/snippets for reference/pyRepRap/serial/urlhandler/protocol_loop.py

https://github.com/hradec/chemshapes
Python | 262 lines | 180 code | 25 blank | 57 comment | 48 complexity | a030900ac784c46ac82a60192eeeaadb MD5 | raw file
  1. #! python
  2. #
  3. # Python Serial Port Extension for Win32, Linux, BSD, Jython
  4. # see __init__.py
  5. #
  6. # This module implements a loop back connection receiving itself what it sent.
  7. #
  8. # The purpose of this module is.. well... You can run the unit tests with it.
  9. # and it was so easy to implement ;-)
  10. #
  11. # (C) 2001-2011 Chris Liechti <cliechti@gmx.net>
  12. # this is distributed under a free software license, see license.txt
  13. #
  14. # URL format: loop://[option[/option...]]
  15. # options:
  16. # - "debug" print diagnostic messages
  17. from serial.serialutil import *
  18. import threading
  19. import time
  20. import logging
  21. # map log level names to constants. used in fromURL()
  22. LOGGER_LEVELS = {
  23. 'debug': logging.DEBUG,
  24. 'info': logging.INFO,
  25. 'warning': logging.WARNING,
  26. 'error': logging.ERROR,
  27. }
  28. class LoopbackSerial(SerialBase):
  29. """Serial port implementation for plain sockets."""
  30. BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
  31. 9600, 19200, 38400, 57600, 115200)
  32. def open(self):
  33. """Open port with current settings. This may throw a SerialException
  34. if the port cannot be opened."""
  35. if self._isOpen:
  36. raise SerialException("Port is already open.")
  37. self.logger = None
  38. self.buffer_lock = threading.Lock()
  39. self.loop_buffer = bytearray()
  40. self.cts = False
  41. self.dsr = False
  42. if self._port is None:
  43. raise SerialException("Port must be configured before it can be used.")
  44. # not that there is anything to open, but the function applies the
  45. # options found in the URL
  46. self.fromURL(self.port)
  47. # not that there anything to configure...
  48. self._reconfigurePort()
  49. # all things set up get, now a clean start
  50. self._isOpen = True
  51. if not self._rtscts:
  52. self.setRTS(True)
  53. self.setDTR(True)
  54. self.flushInput()
  55. self.flushOutput()
  56. def _reconfigurePort(self):
  57. """Set communication parameters on opened port. for the loop://
  58. protocol all settings are ignored!"""
  59. # not that's it of any real use, but it helps in the unit tests
  60. if not isinstance(self._baudrate, (int, long)) or not 0 < self._baudrate < 2**32:
  61. raise ValueError("invalid baudrate: %r" % (self._baudrate))
  62. if self.logger:
  63. self.logger.info('_reconfigurePort()')
  64. def close(self):
  65. """Close port"""
  66. if self._isOpen:
  67. self._isOpen = False
  68. # in case of quick reconnects, give the server some time
  69. time.sleep(0.3)
  70. def makeDeviceName(self, port):
  71. raise SerialException("there is no sensible way to turn numbers into URLs")
  72. def fromURL(self, url):
  73. """extract host and port from an URL string"""
  74. if url.lower().startswith("loop://"): url = url[7:]
  75. try:
  76. # process options now, directly altering self
  77. for option in url.split('/'):
  78. if '=' in option:
  79. option, value = option.split('=', 1)
  80. else:
  81. value = None
  82. if not option:
  83. pass
  84. elif option == 'logging':
  85. logging.basicConfig() # XXX is that good to call it here?
  86. self.logger = logging.getLogger('pySerial.loop')
  87. self.logger.setLevel(LOGGER_LEVELS[value])
  88. self.logger.debug('enabled logging')
  89. else:
  90. raise ValueError('unknown option: %r' % (option,))
  91. except ValueError, e:
  92. raise SerialException('expected a string in the form "[loop://][option[/option...]]": %s' % e)
  93. # - - - - - - - - - - - - - - - - - - - - - - - -
  94. def inWaiting(self):
  95. """Return the number of characters currently in the input buffer."""
  96. if not self._isOpen: raise portNotOpenError
  97. if self.logger:
  98. # attention the logged value can differ from return value in
  99. # threaded environments...
  100. self.logger.debug('inWaiting() -> %d' % (len(self.loop_buffer),))
  101. return len(self.loop_buffer)
  102. def read(self, size=1):
  103. """Read size bytes from the serial port. If a timeout is set it may
  104. return less characters as requested. With no timeout it will block
  105. until the requested number of bytes is read."""
  106. if not self._isOpen: raise portNotOpenError
  107. if self._timeout is not None:
  108. timeout = time.time() + self._timeout
  109. else:
  110. timeout = None
  111. data = bytearray()
  112. while len(data) < size:
  113. self.buffer_lock.acquire()
  114. try:
  115. block = to_bytes(self.loop_buffer[:size])
  116. del self.loop_buffer[:size]
  117. finally:
  118. self.buffer_lock.release()
  119. data += block
  120. # check for timeout now, after data has been read.
  121. # useful for timeout = 0 (non blocking) read
  122. if timeout and time.time() > timeout:
  123. break
  124. return bytes(data)
  125. def write(self, data):
  126. """Output the given string over the serial port. Can block if the
  127. connection is blocked. May raise SerialException if the connection is
  128. closed."""
  129. if not self._isOpen: raise portNotOpenError
  130. # calculate aprox time that would be used to send the data
  131. time_used_to_send = 10.0*len(data) / self._baudrate
  132. # when a write timeout is configured check if we would be successful
  133. # (not sending anything, not even the part that would have time)
  134. if self._writeTimeout is not None and time_used_to_send > self._writeTimeout:
  135. time.sleep(self._writeTimeout) # must wait so that unit test succeeds
  136. raise writeTimeoutError
  137. self.buffer_lock.acquire()
  138. try:
  139. self.loop_buffer += bytes(data)
  140. finally:
  141. self.buffer_lock.release()
  142. return len(data)
  143. def flushInput(self):
  144. """Clear input buffer, discarding all that is in the buffer."""
  145. if not self._isOpen: raise portNotOpenError
  146. if self.logger:
  147. self.logger.info('flushInput()')
  148. self.buffer_lock.acquire()
  149. try:
  150. del self.loop_buffer[:]
  151. finally:
  152. self.buffer_lock.release()
  153. def flushOutput(self):
  154. """Clear output buffer, aborting the current output and
  155. discarding all that is in the buffer."""
  156. if not self._isOpen: raise portNotOpenError
  157. if self.logger:
  158. self.logger.info('flushOutput()')
  159. def sendBreak(self, duration=0.25):
  160. """Send break condition. Timed, returns to idle state after given
  161. duration."""
  162. if not self._isOpen: raise portNotOpenError
  163. def setBreak(self, level=True):
  164. """Set break: Controls TXD. When active, to transmitting is
  165. possible."""
  166. if not self._isOpen: raise portNotOpenError
  167. if self.logger:
  168. self.logger.info('setBreak(%r)' % (level,))
  169. def setRTS(self, level=True):
  170. """Set terminal status line: Request To Send"""
  171. if not self._isOpen: raise portNotOpenError
  172. if self.logger:
  173. self.logger.info('setRTS(%r) -> state of CTS' % (level,))
  174. self.cts = level
  175. def setDTR(self, level=True):
  176. """Set terminal status line: Data Terminal Ready"""
  177. if not self._isOpen: raise portNotOpenError
  178. if self.logger:
  179. self.logger.info('setDTR(%r) -> state of DSR' % (level,))
  180. self.dsr = level
  181. def getCTS(self):
  182. """Read terminal status line: Clear To Send"""
  183. if not self._isOpen: raise portNotOpenError
  184. if self.logger:
  185. self.logger.info('getCTS() -> state of RTS (%r)' % (self.cts,))
  186. return self.cts
  187. def getDSR(self):
  188. """Read terminal status line: Data Set Ready"""
  189. if not self._isOpen: raise portNotOpenError
  190. if self.logger:
  191. self.logger.info('getDSR() -> state of DTR (%r)' % (self.dsr,))
  192. return self.dsr
  193. def getRI(self):
  194. """Read terminal status line: Ring Indicator"""
  195. if not self._isOpen: raise portNotOpenError
  196. if self.logger:
  197. self.logger.info('returning dummy for getRI()')
  198. return False
  199. def getCD(self):
  200. """Read terminal status line: Carrier Detect"""
  201. if not self._isOpen: raise portNotOpenError
  202. if self.logger:
  203. self.logger.info('returning dummy for getCD()')
  204. return True
  205. # - - - platform specific - - -
  206. # None so far
  207. # assemble Serial class with the platform specific implementation and the base
  208. # for file-like behavior. for Python 2.6 and newer, that provide the new I/O
  209. # library, derive from io.RawIOBase
  210. try:
  211. import io
  212. except ImportError:
  213. # classic version with our own file-like emulation
  214. class Serial(LoopbackSerial, FileLike):
  215. pass
  216. else:
  217. # io library present
  218. class Serial(LoopbackSerial, io.RawIOBase):
  219. pass
  220. # simple client test
  221. if __name__ == '__main__':
  222. import sys
  223. s = Serial('socket://localhost:7000')
  224. sys.stdout.write('%s\n' % s)
  225. sys.stdout.write("write...\n")
  226. s.write("hello\n")
  227. s.flush()
  228. sys.stdout.write("read: %s\n" % s.read(5))
  229. s.close()