| OLD | NEW |
| (Empty) |
| 1 #! python | |
| 2 # | |
| 3 # Python Serial Port Extension for Win32, Linux, BSD, Jython | |
| 4 # see __init__.py | |
| 5 # | |
| 6 # This module implements a simple socket based client. | |
| 7 # It does not support changing any port parameters and will silently ignore any | |
| 8 # requests to do so. | |
| 9 # | |
| 10 # The purpose of this module is that applications using pySerial can connect to | |
| 11 # TCP/IP to serial port converters that do not support RFC 2217. | |
| 12 # | |
| 13 # (C) 2001-2011 Chris Liechti <cliechti@gmx.net> | |
| 14 # this is distributed under a free software license, see license.txt | |
| 15 # | |
| 16 # URL format: socket://<host>:<port>[/option[/option...]] | |
| 17 # options: | |
| 18 # - "debug" print diagnostic messages | |
| 19 | |
| 20 from serial.serialutil import * | |
| 21 import time | |
| 22 import socket | |
| 23 import logging | |
| 24 | |
| 25 # map log level names to constants. used in fromURL() | |
| 26 LOGGER_LEVELS = { | |
| 27 'debug': logging.DEBUG, | |
| 28 'info': logging.INFO, | |
| 29 'warning': logging.WARNING, | |
| 30 'error': logging.ERROR, | |
| 31 } | |
| 32 | |
| 33 POLL_TIMEOUT = 2 | |
| 34 | |
| 35 class SocketSerial(SerialBase): | |
| 36 """Serial port implementation for plain sockets.""" | |
| 37 | |
| 38 BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, | |
| 39 9600, 19200, 38400, 57600, 115200) | |
| 40 | |
| 41 def open(self): | |
| 42 """Open port with current settings. This may throw a SerialException | |
| 43 if the port cannot be opened.""" | |
| 44 self.logger = None | |
| 45 if self._port is None: | |
| 46 raise SerialException("Port must be configured before it can be used
.") | |
| 47 if self._isOpen: | |
| 48 raise SerialException("Port is already open.") | |
| 49 try: | |
| 50 # XXX in future replace with create_connection (py >=2.6) | |
| 51 self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| 52 self._socket.connect(self.fromURL(self.portstr)) | |
| 53 except Exception, msg: | |
| 54 self._socket = None | |
| 55 raise SerialException("Could not open port %s: %s" % (self.portstr,
msg)) | |
| 56 | |
| 57 self._socket.settimeout(POLL_TIMEOUT) # used for write timeout support :
/ | |
| 58 | |
| 59 # not that there anything to configure... | |
| 60 self._reconfigurePort() | |
| 61 # all things set up get, now a clean start | |
| 62 self._isOpen = True | |
| 63 if not self._rtscts: | |
| 64 self.setRTS(True) | |
| 65 self.setDTR(True) | |
| 66 self.flushInput() | |
| 67 self.flushOutput() | |
| 68 | |
| 69 def _reconfigurePort(self): | |
| 70 """Set communication parameters on opened port. for the socket:// | |
| 71 protocol all settings are ignored!""" | |
| 72 if self._socket is None: | |
| 73 raise SerialException("Can only operate on open ports") | |
| 74 if self.logger: | |
| 75 self.logger.info('ignored port configuration change') | |
| 76 | |
| 77 def close(self): | |
| 78 """Close port""" | |
| 79 if self._isOpen: | |
| 80 if self._socket: | |
| 81 try: | |
| 82 self._socket.shutdown(socket.SHUT_RDWR) | |
| 83 self._socket.close() | |
| 84 except: | |
| 85 # ignore errors. | |
| 86 pass | |
| 87 self._socket = None | |
| 88 self._isOpen = False | |
| 89 # in case of quick reconnects, give the server some time | |
| 90 time.sleep(0.3) | |
| 91 | |
| 92 def makeDeviceName(self, port): | |
| 93 raise SerialException("there is no sensible way to turn numbers into URL
s") | |
| 94 | |
| 95 def fromURL(self, url): | |
| 96 """extract host and port from an URL string""" | |
| 97 if url.lower().startswith("socket://"): url = url[9:] | |
| 98 try: | |
| 99 # is there a "path" (our options)? | |
| 100 if '/' in url: | |
| 101 # cut away options | |
| 102 url, options = url.split('/', 1) | |
| 103 # process options now, directly altering self | |
| 104 for option in options.split('/'): | |
| 105 if '=' in option: | |
| 106 option, value = option.split('=', 1) | |
| 107 else: | |
| 108 value = None | |
| 109 if option == 'logging': | |
| 110 logging.basicConfig() # XXX is that good to call it he
re? | |
| 111 self.logger = logging.getLogger('pySerial.socket') | |
| 112 self.logger.setLevel(LOGGER_LEVELS[value]) | |
| 113 self.logger.debug('enabled logging') | |
| 114 else: | |
| 115 raise ValueError('unknown option: %r' % (option,)) | |
| 116 # get host and port | |
| 117 host, port = url.split(':', 1) # may raise ValueError because of unp
acking | |
| 118 port = int(port) # and this if it's not a number | |
| 119 if not 0 <= port < 65536: raise ValueError("port not in range 0...65
535") | |
| 120 except ValueError, e: | |
| 121 raise SerialException('expected a string in the form "[rfc2217://]<h
ost>:<port>[/option[/option...]]": %s' % e) | |
| 122 return (host, port) | |
| 123 | |
| 124 # - - - - - - - - - - - - - - - - - - - - - - - - | |
| 125 | |
| 126 def inWaiting(self): | |
| 127 """Return the number of characters currently in the input buffer.""" | |
| 128 if not self._isOpen: raise portNotOpenError | |
| 129 if self.logger: | |
| 130 # set this one to debug as the function could be called often... | |
| 131 self.logger.debug('WARNING: inWaiting returns dummy value') | |
| 132 return 0 # hmmm, see comment in read() | |
| 133 | |
| 134 def read(self, size=1): | |
| 135 """Read size bytes from the serial port. If a timeout is set it may | |
| 136 return less characters as requested. With no timeout it will block | |
| 137 until the requested number of bytes is read.""" | |
| 138 if not self._isOpen: raise portNotOpenError | |
| 139 data = bytearray() | |
| 140 if self._timeout is not None: | |
| 141 timeout = time.time() + self._timeout | |
| 142 else: | |
| 143 timeout = None | |
| 144 while len(data) < size and (timeout is None or time.time() < timeout): | |
| 145 try: | |
| 146 # an implementation with internal buffer would be better | |
| 147 # performing... | |
| 148 t = time.time() | |
| 149 block = self._socket.recv(size - len(data)) | |
| 150 duration = time.time() - t | |
| 151 if block: | |
| 152 data.extend(block) | |
| 153 else: | |
| 154 # no data -> EOF (connection probably closed) | |
| 155 break | |
| 156 except socket.timeout: | |
| 157 # just need to get out of recv from time to time to check if | |
| 158 # still alive | |
| 159 continue | |
| 160 except socket.error, e: | |
| 161 # connection fails -> terminate loop | |
| 162 raise SerialException('connection failed (%s)' % e) | |
| 163 return bytes(data) | |
| 164 | |
| 165 def write(self, data): | |
| 166 """Output the given string over the serial port. Can block if the | |
| 167 connection is blocked. May raise SerialException if the connection is | |
| 168 closed.""" | |
| 169 if not self._isOpen: raise portNotOpenError | |
| 170 try: | |
| 171 self._socket.sendall(to_bytes(data)) | |
| 172 except socket.error, e: | |
| 173 # XXX what exception if socket connection fails | |
| 174 raise SerialException("socket connection failed: %s" % e) | |
| 175 return len(data) | |
| 176 | |
| 177 def flushInput(self): | |
| 178 """Clear input buffer, discarding all that is in the buffer.""" | |
| 179 if not self._isOpen: raise portNotOpenError | |
| 180 if self.logger: | |
| 181 self.logger.info('ignored flushInput') | |
| 182 | |
| 183 def flushOutput(self): | |
| 184 """Clear output buffer, aborting the current output and | |
| 185 discarding all that is in the buffer.""" | |
| 186 if not self._isOpen: raise portNotOpenError | |
| 187 if self.logger: | |
| 188 self.logger.info('ignored flushOutput') | |
| 189 | |
| 190 def sendBreak(self, duration=0.25): | |
| 191 """Send break condition. Timed, returns to idle state after given | |
| 192 duration.""" | |
| 193 if not self._isOpen: raise portNotOpenError | |
| 194 if self.logger: | |
| 195 self.logger.info('ignored sendBreak(%r)' % (duration,)) | |
| 196 | |
| 197 def setBreak(self, level=True): | |
| 198 """Set break: Controls TXD. When active, to transmitting is | |
| 199 possible.""" | |
| 200 if not self._isOpen: raise portNotOpenError | |
| 201 if self.logger: | |
| 202 self.logger.info('ignored setBreak(%r)' % (level,)) | |
| 203 | |
| 204 def setRTS(self, level=True): | |
| 205 """Set terminal status line: Request To Send""" | |
| 206 if not self._isOpen: raise portNotOpenError | |
| 207 if self.logger: | |
| 208 self.logger.info('ignored setRTS(%r)' % (level,)) | |
| 209 | |
| 210 def setDTR(self, level=True): | |
| 211 """Set terminal status line: Data Terminal Ready""" | |
| 212 if not self._isOpen: raise portNotOpenError | |
| 213 if self.logger: | |
| 214 self.logger.info('ignored setDTR(%r)' % (level,)) | |
| 215 | |
| 216 def getCTS(self): | |
| 217 """Read terminal status line: Clear To Send""" | |
| 218 if not self._isOpen: raise portNotOpenError | |
| 219 if self.logger: | |
| 220 self.logger.info('returning dummy for getCTS()') | |
| 221 return True | |
| 222 | |
| 223 def getDSR(self): | |
| 224 """Read terminal status line: Data Set Ready""" | |
| 225 if not self._isOpen: raise portNotOpenError | |
| 226 if self.logger: | |
| 227 self.logger.info('returning dummy for getDSR()') | |
| 228 return True | |
| 229 | |
| 230 def getRI(self): | |
| 231 """Read terminal status line: Ring Indicator""" | |
| 232 if not self._isOpen: raise portNotOpenError | |
| 233 if self.logger: | |
| 234 self.logger.info('returning dummy for getRI()') | |
| 235 return False | |
| 236 | |
| 237 def getCD(self): | |
| 238 """Read terminal status line: Carrier Detect""" | |
| 239 if not self._isOpen: raise portNotOpenError | |
| 240 if self.logger: | |
| 241 self.logger.info('returning dummy for getCD()') | |
| 242 return True | |
| 243 | |
| 244 # - - - platform specific - - - | |
| 245 # None so far | |
| 246 | |
| 247 | |
| 248 # assemble Serial class with the platform specific implementation and the base | |
| 249 # for file-like behavior. for Python 2.6 and newer, that provide the new I/O | |
| 250 # library, derive from io.RawIOBase | |
| 251 try: | |
| 252 import io | |
| 253 except ImportError: | |
| 254 # classic version with our own file-like emulation | |
| 255 class Serial(SocketSerial, FileLike): | |
| 256 pass | |
| 257 else: | |
| 258 # io library present | |
| 259 class Serial(SocketSerial, io.RawIOBase): | |
| 260 pass | |
| 261 | |
| 262 | |
| 263 # simple client test | |
| 264 if __name__ == '__main__': | |
| 265 import sys | |
| 266 s = Serial('socket://localhost:7000') | |
| 267 sys.stdout.write('%s\n' % s) | |
| 268 | |
| 269 sys.stdout.write("write...\n") | |
| 270 s.write("hello\n") | |
| 271 s.flush() | |
| 272 sys.stdout.write("read: %s\n" % s.read(5)) | |
| 273 | |
| 274 s.close() | |
| OLD | NEW |