3 # Python Serial Port Extension for Win32, Linux, BSD, Jython
6 # This module implements a RFC2217 compatible client. RF2217 descibes a
7 # protocol to access serial ports over TCP/IP and allows setting the baud rate,
8 # modem control lines etc.
10 # (C) 2001-2013 Chris Liechti <cliechti@gmx.net>
11 # this is distributed under a free software license, see license.txt
14 # - setting control line -> answer is not checked (had problems with one of the
15 # severs). consider implementing a compatibility mode flag to make check
17 # - write timeout not implemented at all
19 ##############################################################################
20 # observations and issues with servers
21 #=============================================================================
23 # - http://www.ibiblio.org/pub/Linux/system/serial/ sredird-2.2.2.tar.gz
24 # - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding
25 # [105 1] instead of the actual value.
26 # - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger
28 # - To get the signature [COM_PORT_OPTION 0] has to be sent.
29 # - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done
30 #=============================================================================
31 # telnetcpcd (untested)
32 # - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz
33 # - To get the signature [COM_PORT_OPTION] w/o data has to be sent.
34 #=============================================================================
36 # - does not negotiate BINARY or COM_PORT_OPTION for his side but at least
37 # acknowledges that the client activates these options
38 # - The configuration may be that the server prints a banner. As this client
39 # implementation does a flushInput on connect, this banner is hidden from
40 # the user application.
41 # - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one
43 # - To get the signature [COM_PORT_OPTION 0] has to be sent.
44 # - run a server: run ser2net daemon, in /etc/ser2net.conf:
45 # 2000:telnet:0:/dev/ttyS0:9600 remctl banner
46 ##############################################################################
48 # How to identify ports? pySerial might want to support other protocols in the
49 # future, so lets use an URL scheme.
50 # for RFC2217 compliant servers we will use this:
51 # rfc2217://<host>:<port>[/option[/option...]]
54 # - "debug" print diagnostic messages
55 # - "ign_set_control": do not look at the answers to SET_CONTROL
56 # - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read.
57 # Without this option it expects that the server sends notifications
58 # automatically on change (which most servers do and is according to the
60 # the order of the options is not relevant
62 from serial
.serialutil
import *
70 # port string is expected to be something like this:
72 # host may be an IP or including domain, whatever.
75 # map log level names to constants. used in fromURL()
77 'debug': logging
.DEBUG
,
79 'warning': logging
.WARNING
,
80 'error': logging
.ERROR
,
84 # telnet protocol characters
85 IAC
= to_bytes([255]) # Interpret As Command
86 DONT
= to_bytes([254])
88 WONT
= to_bytes([252])
89 WILL
= to_bytes([251])
90 IAC_DOUBLED
= to_bytes([IAC
, IAC
])
92 SE
= to_bytes([240]) # Subnegotiation End
93 NOP
= to_bytes([241]) # No Operation
94 DM
= to_bytes([242]) # Data Mark
95 BRK
= to_bytes([243]) # Break
96 IP
= to_bytes([244]) # Interrupt process
97 AO
= to_bytes([245]) # Abort output
98 AYT
= to_bytes([246]) # Are You There
99 EC
= to_bytes([247]) # Erase Character
100 EL
= to_bytes([248]) # Erase Line
101 GA
= to_bytes([249]) # Go Ahead
102 SB
= to_bytes([250]) # Subnegotiation Begin
104 # selected telnet options
105 BINARY
= to_bytes([0]) # 8-bit data path
106 ECHO
= to_bytes([1]) # echo
107 SGA
= to_bytes([3]) # suppress go ahead
110 COM_PORT_OPTION
= to_bytes([44])
112 # Client to Access Server
113 SET_BAUDRATE
= to_bytes([1])
114 SET_DATASIZE
= to_bytes([2])
115 SET_PARITY
= to_bytes([3])
116 SET_STOPSIZE
= to_bytes([4])
117 SET_CONTROL
= to_bytes([5])
118 NOTIFY_LINESTATE
= to_bytes([6])
119 NOTIFY_MODEMSTATE
= to_bytes([7])
120 FLOWCONTROL_SUSPEND
= to_bytes([8])
121 FLOWCONTROL_RESUME
= to_bytes([9])
122 SET_LINESTATE_MASK
= to_bytes([10])
123 SET_MODEMSTATE_MASK
= to_bytes([11])
124 PURGE_DATA
= to_bytes([12])
126 SERVER_SET_BAUDRATE
= to_bytes([101])
127 SERVER_SET_DATASIZE
= to_bytes([102])
128 SERVER_SET_PARITY
= to_bytes([103])
129 SERVER_SET_STOPSIZE
= to_bytes([104])
130 SERVER_SET_CONTROL
= to_bytes([105])
131 SERVER_NOTIFY_LINESTATE
= to_bytes([106])
132 SERVER_NOTIFY_MODEMSTATE
= to_bytes([107])
133 SERVER_FLOWCONTROL_SUSPEND
= to_bytes([108])
134 SERVER_FLOWCONTROL_RESUME
= to_bytes([109])
135 SERVER_SET_LINESTATE_MASK
= to_bytes([110])
136 SERVER_SET_MODEMSTATE_MASK
= to_bytes([111])
137 SERVER_PURGE_DATA
= to_bytes([112])
139 RFC2217_ANSWER_MAP
= {
140 SET_BAUDRATE
: SERVER_SET_BAUDRATE
,
141 SET_DATASIZE
: SERVER_SET_DATASIZE
,
142 SET_PARITY
: SERVER_SET_PARITY
,
143 SET_STOPSIZE
: SERVER_SET_STOPSIZE
,
144 SET_CONTROL
: SERVER_SET_CONTROL
,
145 NOTIFY_LINESTATE
: SERVER_NOTIFY_LINESTATE
,
146 NOTIFY_MODEMSTATE
: SERVER_NOTIFY_MODEMSTATE
,
147 FLOWCONTROL_SUSPEND
: SERVER_FLOWCONTROL_SUSPEND
,
148 FLOWCONTROL_RESUME
: SERVER_FLOWCONTROL_RESUME
,
149 SET_LINESTATE_MASK
: SERVER_SET_LINESTATE_MASK
,
150 SET_MODEMSTATE_MASK
: SERVER_SET_MODEMSTATE_MASK
,
151 PURGE_DATA
: SERVER_PURGE_DATA
,
154 SET_CONTROL_REQ_FLOW_SETTING
= to_bytes([0]) # Request Com Port Flow Control Setting (outbound/both)
155 SET_CONTROL_USE_NO_FLOW_CONTROL
= to_bytes([1]) # Use No Flow Control (outbound/both)
156 SET_CONTROL_USE_SW_FLOW_CONTROL
= to_bytes([2]) # Use XON/XOFF Flow Control (outbound/both)
157 SET_CONTROL_USE_HW_FLOW_CONTROL
= to_bytes([3]) # Use HARDWARE Flow Control (outbound/both)
158 SET_CONTROL_REQ_BREAK_STATE
= to_bytes([4]) # Request BREAK State
159 SET_CONTROL_BREAK_ON
= to_bytes([5]) # Set BREAK State ON
160 SET_CONTROL_BREAK_OFF
= to_bytes([6]) # Set BREAK State OFF
161 SET_CONTROL_REQ_DTR
= to_bytes([7]) # Request DTR Signal State
162 SET_CONTROL_DTR_ON
= to_bytes([8]) # Set DTR Signal State ON
163 SET_CONTROL_DTR_OFF
= to_bytes([9]) # Set DTR Signal State OFF
164 SET_CONTROL_REQ_RTS
= to_bytes([10]) # Request RTS Signal State
165 SET_CONTROL_RTS_ON
= to_bytes([11]) # Set RTS Signal State ON
166 SET_CONTROL_RTS_OFF
= to_bytes([12]) # Set RTS Signal State OFF
167 SET_CONTROL_REQ_FLOW_SETTING_IN
= to_bytes([13]) # Request Com Port Flow Control Setting (inbound)
168 SET_CONTROL_USE_NO_FLOW_CONTROL_IN
= to_bytes([14]) # Use No Flow Control (inbound)
169 SET_CONTROL_USE_SW_FLOW_CONTOL_IN
= to_bytes([15]) # Use XON/XOFF Flow Control (inbound)
170 SET_CONTROL_USE_HW_FLOW_CONTOL_IN
= to_bytes([16]) # Use HARDWARE Flow Control (inbound)
171 SET_CONTROL_USE_DCD_FLOW_CONTROL
= to_bytes([17]) # Use DCD Flow Control (outbound/both)
172 SET_CONTROL_USE_DTR_FLOW_CONTROL
= to_bytes([18]) # Use DTR Flow Control (inbound)
173 SET_CONTROL_USE_DSR_FLOW_CONTROL
= to_bytes([19]) # Use DSR Flow Control (outbound/both)
175 LINESTATE_MASK_TIMEOUT
= 128 # Time-out Error
176 LINESTATE_MASK_SHIFTREG_EMPTY
= 64 # Transfer Shift Register Empty
177 LINESTATE_MASK_TRANSREG_EMPTY
= 32 # Transfer Holding Register Empty
178 LINESTATE_MASK_BREAK_DETECT
= 16 # Break-detect Error
179 LINESTATE_MASK_FRAMING_ERROR
= 8 # Framing Error
180 LINESTATE_MASK_PARTIY_ERROR
= 4 # Parity Error
181 LINESTATE_MASK_OVERRUN_ERROR
= 2 # Overrun Error
182 LINESTATE_MASK_DATA_READY
= 1 # Data Ready
184 MODEMSTATE_MASK_CD
= 128 # Receive Line Signal Detect (also known as Carrier Detect)
185 MODEMSTATE_MASK_RI
= 64 # Ring Indicator
186 MODEMSTATE_MASK_DSR
= 32 # Data-Set-Ready Signal State
187 MODEMSTATE_MASK_CTS
= 16 # Clear-To-Send Signal State
188 MODEMSTATE_MASK_CD_CHANGE
= 8 # Delta Receive Line Signal Detect
189 MODEMSTATE_MASK_RI_CHANGE
= 4 # Trailing-edge Ring Detector
190 MODEMSTATE_MASK_DSR_CHANGE
= 2 # Delta Data-Set-Ready
191 MODEMSTATE_MASK_CTS_CHANGE
= 1 # Delta Clear-To-Send
193 PURGE_RECEIVE_BUFFER
= to_bytes([1]) # Purge access server receive data buffer
194 PURGE_TRANSMIT_BUFFER
= to_bytes([2]) # Purge access server transmit data buffer
195 PURGE_BOTH_BUFFERS
= to_bytes([3]) # Purge both the access server receive data buffer and the access server transmit data buffer
198 RFC2217_PARITY_MAP
= {
205 RFC2217_REVERSE_PARITY_MAP
= dict((v
,k
) for k
,v
in RFC2217_PARITY_MAP
.items())
207 RFC2217_STOPBIT_MAP
= {
209 STOPBITS_ONE_POINT_FIVE
: 3,
212 RFC2217_REVERSE_STOPBIT_MAP
= dict((v
,k
) for k
,v
in RFC2217_STOPBIT_MAP
.items())
214 # Telnet filter states
219 # TelnetOption and TelnetSubnegotiation states
220 REQUESTED
= 'REQUESTED'
222 INACTIVE
= 'INACTIVE'
223 REALLY_INACTIVE
= 'REALLY_INACTIVE'
225 class TelnetOption(object):
226 """Manage a single telnet option, keeps track of DO/DONT WILL/WONT."""
228 def __init__(self
, connection
, name
, option
, send_yes
, send_no
, ack_yes
, ack_no
, initial_state
, activation_callback
=None):
231 :param connection: connection used to transmit answers
232 :param name: a readable name for debug outputs
233 :param send_yes: what to send when option is to be enabled.
234 :param send_no: what to send when option is to be disabled.
235 :param ack_yes: what to expect when remote agrees on option.
236 :param ack_no: what to expect when remote disagrees on option.
237 :param initial_state: options initialized with REQUESTED are tried to
238 be enabled on startup. use INACTIVE for all others.
240 self
.connection
= connection
243 self
.send_yes
= send_yes
244 self
.send_no
= send_no
245 self
.ack_yes
= ack_yes
247 self
.state
= initial_state
249 self
.activation_callback
= activation_callback
252 """String for debug outputs"""
253 return "%s:%s(%s)" % (self
.name
, self
.active
, self
.state
)
255 def process_incoming(self
, command
):
256 """A DO/DONT/WILL/WONT was received for this option, update state and
257 answer when needed."""
258 if command
== self
.ack_yes
:
259 if self
.state
is REQUESTED
:
262 if self
.activation_callback
is not None:
263 self
.activation_callback()
264 elif self
.state
is ACTIVE
:
266 elif self
.state
is INACTIVE
:
268 self
.connection
.telnetSendOption(self
.send_yes
, self
.option
)
270 if self
.activation_callback
is not None:
271 self
.activation_callback()
272 elif self
.state
is REALLY_INACTIVE
:
273 self
.connection
.telnetSendOption(self
.send_no
, self
.option
)
275 raise ValueError('option in illegal state %r' % self
)
276 elif command
== self
.ack_no
:
277 if self
.state
is REQUESTED
:
278 self
.state
= INACTIVE
280 elif self
.state
is ACTIVE
:
281 self
.state
= INACTIVE
282 self
.connection
.telnetSendOption(self
.send_no
, self
.option
)
284 elif self
.state
is INACTIVE
:
286 elif self
.state
is REALLY_INACTIVE
:
289 raise ValueError('option in illegal state %r' % self
)
292 class TelnetSubnegotiation(object):
294 A object to handle subnegotiation of options. In this case actually
295 sub-sub options for RFC 2217. It is used to track com port options.
298 def __init__(self
, connection
, name
, option
, ack_option
=None):
299 if ack_option
is None: ack_option
= option
300 self
.connection
= connection
304 self
.ack_option
= ack_option
305 self
.state
= INACTIVE
308 """String for debug outputs."""
309 return "%s:%s" % (self
.name
, self
.state
)
311 def set(self
, value
):
313 request a change of the value. a request is sent to the server. if
314 the client needs to know if the change is performed he has to check the
315 state of this object.
318 self
.state
= REQUESTED
319 self
.connection
.rfc2217SendSubnegotiation(self
.option
, self
.value
)
320 if self
.connection
.logger
:
321 self
.connection
.logger
.debug("SB Requesting %s -> %r" % (self
.name
, self
.value
))
325 check if answer from server has been received. when server rejects
326 the change, raise a ValueError.
328 if self
.state
== REALLY_INACTIVE
:
329 raise ValueError("remote rejected value for option %r" % (self
.name
))
330 return self
.state
== ACTIVE
331 # add property to have a similar interface as TelnetOption
332 active
= property(isReady
)
334 def wait(self
, timeout
=3):
336 wait until the subnegotiation has been acknowledged or timeout. It
337 can also throw a value error when the answer from the server does not
338 match the value sent.
340 timeout_time
= time
.time() + timeout
341 while time
.time() < timeout_time
:
342 time
.sleep(0.05) # prevent 100% CPU load
346 raise SerialException("timeout while waiting for option %r" % (self
.name
))
348 def checkAnswer(self
, suboption
):
350 check an incoming subnegotiation block. the parameter already has
351 cut off the header like sub option number and com port option value.
353 if self
.value
== suboption
[:len(self
.value
)]:
356 # error propagation done in isReady
357 self
.state
= REALLY_INACTIVE
358 if self
.connection
.logger
:
359 self
.connection
.logger
.debug("SB Answer %s -> %r -> %s" % (self
.name
, suboption
, self
.state
))
362 class RFC2217Serial(SerialBase
):
363 """Serial port implementation for RFC 2217 remote serial ports."""
365 BAUDRATES
= (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
366 9600, 19200, 38400, 57600, 115200)
370 Open port with current settings. This may throw a SerialException
371 if the port cannot be opened.
374 self
._ignore
_set
_control
_answer
= False
375 self
._poll
_modem
_state
= False
376 self
._network
_timeout
= 3
377 if self
._port
is None:
378 raise SerialException("Port must be configured before it can be used.")
380 raise SerialException("Port is already open.")
382 self
._socket
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
383 self
._socket
.connect(self
.fromURL(self
.portstr
))
384 self
._socket
.setsockopt(socket
.IPPROTO_TCP
, socket
.TCP_NODELAY
, 1)
385 except Exception, msg
:
387 raise SerialException("Could not open port %s: %s" % (self
.portstr
, msg
))
389 self
._socket
.settimeout(5) # XXX good value?
391 # use a thread save queue as buffer. it also simplifies implementing
393 self
._read
_buffer
= Queue
.Queue()
394 # to ensure that user writes does not interfere with internal
395 # telnet/rfc2217 options establish a lock
396 self
._write
_lock
= threading
.Lock()
397 # name the following separately so that, below, a check can be easily done
398 mandadory_options
= [
399 TelnetOption(self
, 'we-BINARY', BINARY
, WILL
, WONT
, DO
, DONT
, INACTIVE
),
400 TelnetOption(self
, 'we-RFC2217', COM_PORT_OPTION
, WILL
, WONT
, DO
, DONT
, REQUESTED
),
402 # all supported telnet options
403 self
._telnet
_options
= [
404 TelnetOption(self
, 'ECHO', ECHO
, DO
, DONT
, WILL
, WONT
, REQUESTED
),
405 TelnetOption(self
, 'we-SGA', SGA
, WILL
, WONT
, DO
, DONT
, REQUESTED
),
406 TelnetOption(self
, 'they-SGA', SGA
, DO
, DONT
, WILL
, WONT
, REQUESTED
),
407 TelnetOption(self
, 'they-BINARY', BINARY
, DO
, DONT
, WILL
, WONT
, INACTIVE
),
408 TelnetOption(self
, 'they-RFC2217', COM_PORT_OPTION
, DO
, DONT
, WILL
, WONT
, REQUESTED
),
409 ] + mandadory_options
410 # RFC 2217 specific states
412 self
._rfc
2217_port
_settings
= {
413 'baudrate': TelnetSubnegotiation(self
, 'baudrate', SET_BAUDRATE
, SERVER_SET_BAUDRATE
),
414 'datasize': TelnetSubnegotiation(self
, 'datasize', SET_DATASIZE
, SERVER_SET_DATASIZE
),
415 'parity': TelnetSubnegotiation(self
, 'parity', SET_PARITY
, SERVER_SET_PARITY
),
416 'stopsize': TelnetSubnegotiation(self
, 'stopsize', SET_STOPSIZE
, SERVER_SET_STOPSIZE
),
418 # There are more subnegotiation objects, combine all in one dictionary
420 self
._rfc
2217_options
= {
421 'purge': TelnetSubnegotiation(self
, 'purge', PURGE_DATA
, SERVER_PURGE_DATA
),
422 'control': TelnetSubnegotiation(self
, 'control', SET_CONTROL
, SERVER_SET_CONTROL
),
424 self
._rfc
2217_options
.update(self
._rfc
2217_port
_settings
)
425 # cache for line and modem states that the server sends to us
427 self
._modemstate
= None
428 self
._modemstate
_expires
= 0
429 # RFC 2217 flow control between server and client
430 self
._remote
_suspend
_flow
= False
432 self
._thread
= threading
.Thread(target
=self
._telnetReadLoop
)
433 self
._thread
.setDaemon(True)
434 self
._thread
.setName('pySerial RFC 2217 reader thread for %s' % (self
._port
,))
437 # negotiate Telnet/RFC 2217 -> send initial requests
438 for option
in self
._telnet
_options
:
439 if option
.state
is REQUESTED
:
440 self
.telnetSendOption(option
.send_yes
, option
.option
)
441 # now wait until important options are negotiated
442 timeout_time
= time
.time() + self
._network
_timeout
443 while time
.time() < timeout_time
:
444 time
.sleep(0.05) # prevent 100% CPU load
445 if sum(o
.active
for o
in mandadory_options
) == len(mandadory_options
):
448 raise SerialException("Remote does not seem to support RFC2217 or BINARY mode %r" % mandadory_options
)
450 self
.logger
.info("Negotiated options: %s" % self
._telnet
_options
)
452 # fine, go on, set RFC 2271 specific things
453 self
._reconfigurePort
()
454 # all things set up get, now a clean start
462 def _reconfigurePort(self
):
463 """Set communication parameters on opened port."""
464 if self
._socket
is None:
465 raise SerialException("Can only operate on open ports")
467 # if self._timeout != 0 and self._interCharTimeout is not None:
470 if self
._writeTimeout
is not None:
471 raise NotImplementedError('writeTimeout is currently not supported')
474 # Setup the connection
475 # to get good performance, all parameter changes are sent first...
476 if not isinstance(self
._baudrate
, (int, long)) or not 0 < self
._baudrate
< 2**32:
477 raise ValueError("invalid baudrate: %r" % (self
._baudrate
))
478 self
._rfc
2217_port
_settings
['baudrate'].set(struct
.pack('!I', self
._baudrate
))
479 self
._rfc
2217_port
_settings
['datasize'].set(struct
.pack('!B', self
._bytesize
))
480 self
._rfc
2217_port
_settings
['parity'].set(struct
.pack('!B', RFC2217_PARITY_MAP
[self
._parity
]))
481 self
._rfc
2217_port
_settings
['stopsize'].set(struct
.pack('!B', RFC2217_STOPBIT_MAP
[self
._stopbits
]))
483 # and now wait until parameters are active
484 items
= self
._rfc
2217_port
_settings
.values()
486 self
.logger
.debug("Negotiating settings: %s" % (items
,))
487 timeout_time
= time
.time() + self
._network
_timeout
488 while time
.time() < timeout_time
:
489 time
.sleep(0.05) # prevent 100% CPU load
490 if sum(o
.active
for o
in items
) == len(items
):
493 raise SerialException("Remote does not accept parameter change (RFC2217): %r" % items
)
495 self
.logger
.info("Negotiated settings: %s" % (items
,))
497 if self
._rtscts
and self
._xonxoff
:
498 raise ValueError('xonxoff and rtscts together are not supported')
500 self
.rfc2217SetControl(SET_CONTROL_USE_HW_FLOW_CONTROL
)
502 self
.rfc2217SetControl(SET_CONTROL_USE_SW_FLOW_CONTROL
)
504 self
.rfc2217SetControl(SET_CONTROL_USE_NO_FLOW_CONTROL
)
511 self
._socket
.shutdown(socket
.SHUT_RDWR
)
520 # in case of quick reconnects, give the server some time
523 def makeDeviceName(self
, port
):
524 raise SerialException("there is no sensible way to turn numbers into URLs")
526 def fromURL(self
, url
):
527 """extract host and port from an URL string"""
528 if url
.lower().startswith("rfc2217://"): url
= url
[10:]
530 # is there a "path" (our options)?
533 url
, options
= url
.split('/', 1)
534 # process options now, directly altering self
535 for option
in options
.split('/'):
537 option
, value
= option
.split('=', 1)
540 if option
== 'logging':
541 logging
.basicConfig() # XXX is that good to call it here?
542 self
.logger
= logging
.getLogger('pySerial.rfc2217')
543 self
.logger
.setLevel(LOGGER_LEVELS
[value
])
544 self
.logger
.debug('enabled logging')
545 elif option
== 'ign_set_control':
546 self
._ignore
_set
_control
_answer
= True
547 elif option
== 'poll_modem':
548 self
._poll
_modem
_state
= True
549 elif option
== 'timeout':
550 self
._network
_timeout
= float(value
)
552 raise ValueError('unknown option: %r' % (option
,))
554 host
, port
= url
.split(':', 1) # may raise ValueError because of unpacking
555 port
= int(port
) # and this if it's not a number
556 if not 0 <= port
< 65536: raise ValueError("port not in range 0...65535")
557 except ValueError, e
:
558 raise SerialException('expected a string in the form "[rfc2217://]<host>:<port>[/option[/option...]]": %s' % e
)
561 # - - - - - - - - - - - - - - - - - - - - - - - -
564 """Return the number of characters currently in the input buffer."""
565 if not self
._isOpen
: raise portNotOpenError
566 return self
._read
_buffer
.qsize()
568 def read(self
, size
=1):
570 Read size bytes from the serial port. If a timeout is set it may
571 return less characters as requested. With no timeout it will block
572 until the requested number of bytes is read.
574 if not self
._isOpen
: raise portNotOpenError
577 while len(data
) < size
:
578 if self
._thread
is None:
579 raise SerialException('connection failed (reader thread died)')
580 data
.append(self
._read
_buffer
.get(True, self
._timeout
))
581 except Queue
.Empty
: # -> timeout
585 def write(self
, data
):
587 Output the given string over the serial port. Can block if the
588 connection is blocked. May raise SerialException if the connection is
591 if not self
._isOpen
: raise portNotOpenError
592 self
._write
_lock
.acquire()
595 self
._socket
.sendall(to_bytes(data
).replace(IAC
, IAC_DOUBLED
))
596 except socket
.error
, e
:
597 raise SerialException("connection failed (socket error): %s" % e
) # XXX what exception if socket connection fails
599 self
._write
_lock
.release()
602 def flushInput(self
):
603 """Clear input buffer, discarding all that is in the buffer."""
604 if not self
._isOpen
: raise portNotOpenError
605 self
.rfc2217SendPurge(PURGE_RECEIVE_BUFFER
)
607 while self
._read
_buffer
.qsize():
608 self
._read
_buffer
.get(False)
610 def flushOutput(self
):
612 Clear output buffer, aborting the current output and
613 discarding all that is in the buffer.
615 if not self
._isOpen
: raise portNotOpenError
616 self
.rfc2217SendPurge(PURGE_TRANSMIT_BUFFER
)
618 def sendBreak(self
, duration
=0.25):
619 """Send break condition. Timed, returns to idle state after given
621 if not self
._isOpen
: raise portNotOpenError
626 def setBreak(self
, level
=True):
628 Set break: Controls TXD. When active, to transmitting is
631 if not self
._isOpen
: raise portNotOpenError
633 self
.logger
.info('set BREAK to %s' % ('inactive', 'active')[bool(level
)])
635 self
.rfc2217SetControl(SET_CONTROL_BREAK_ON
)
637 self
.rfc2217SetControl(SET_CONTROL_BREAK_OFF
)
639 def setRTS(self
, level
=True):
640 """Set terminal status line: Request To Send."""
641 if not self
._isOpen
: raise portNotOpenError
643 self
.logger
.info('set RTS to %s' % ('inactive', 'active')[bool(level
)])
645 self
.rfc2217SetControl(SET_CONTROL_RTS_ON
)
647 self
.rfc2217SetControl(SET_CONTROL_RTS_OFF
)
649 def setDTR(self
, level
=True):
650 """Set terminal status line: Data Terminal Ready."""
651 if not self
._isOpen
: raise portNotOpenError
653 self
.logger
.info('set DTR to %s' % ('inactive', 'active')[bool(level
)])
655 self
.rfc2217SetControl(SET_CONTROL_DTR_ON
)
657 self
.rfc2217SetControl(SET_CONTROL_DTR_OFF
)
660 """Read terminal status line: Clear To Send."""
661 if not self
._isOpen
: raise portNotOpenError
662 return bool(self
.getModemState() & MODEMSTATE_MASK_CTS
)
665 """Read terminal status line: Data Set Ready."""
666 if not self
._isOpen
: raise portNotOpenError
667 return bool(self
.getModemState() & MODEMSTATE_MASK_DSR
)
670 """Read terminal status line: Ring Indicator."""
671 if not self
._isOpen
: raise portNotOpenError
672 return bool(self
.getModemState() & MODEMSTATE_MASK_RI
)
675 """Read terminal status line: Carrier Detect."""
676 if not self
._isOpen
: raise portNotOpenError
677 return bool(self
.getModemState() & MODEMSTATE_MASK_CD
)
679 # - - - platform specific - - -
682 # - - - RFC2217 specific - - -
684 def _telnetReadLoop(self
):
685 """read loop for the socket."""
689 while self
._socket
is not None:
691 data
= self
._socket
.recv(1024)
692 except socket
.timeout
:
693 # just need to get out of recv form time to time to check if
696 except socket
.error
, e
:
697 # connection fails -> terminate loop
699 self
.logger
.debug("socket error in reader thread: %s" % (e
,))
701 if not data
: break # lost connection
704 # interpret as command or as data
708 # store data in read buffer or sub option buffer
710 if suboption
is not None:
711 suboption
.append(byte
)
713 self
._read
_buffer
.put(byte
)
714 elif mode
== M_IAC_SEEN
:
716 # interpret as command doubled -> insert character
718 if suboption
is not None:
719 suboption
.append(IAC
)
721 self
._read
_buffer
.put(IAC
)
725 suboption
= bytearray()
728 # sub option end -> process it now
729 self
._telnetProcessSubnegotiation
(bytes(suboption
))
732 elif byte
in (DO
, DONT
, WILL
, WONT
):
734 telnet_command
= byte
737 # other telnet commands
738 self
._telnetProcessCommand
(byte
)
740 elif mode
== M_NEGOTIATE
: # DO, DONT, WILL, WONT was received, option now following
741 self
._telnetNegotiateOption
(telnet_command
, byte
)
746 self
.logger
.debug("read thread terminated")
748 # - incoming telnet commands and options
750 def _telnetProcessCommand(self
, command
):
751 """Process commands other than DO, DONT, WILL, WONT."""
752 # Currently none. RFC2217 only uses negotiation and subnegotiation.
754 self
.logger
.warning("ignoring Telnet command: %r" % (command
,))
756 def _telnetNegotiateOption(self
, command
, option
):
757 """Process incoming DO, DONT, WILL, WONT."""
758 # check our registered telnet options and forward command to them
759 # they know themselves if they have to answer or not
761 for item
in self
._telnet
_options
:
762 # can have more than one match! as some options are duplicated for
764 if item
.option
== option
:
765 item
.process_incoming(command
)
768 # handle unknown options
769 # only answer to positive requests and deny them
770 if command
== WILL
or command
== DO
:
771 self
.telnetSendOption((command
== WILL
and DONT
or WONT
), option
)
773 self
.logger
.warning("rejected Telnet option: %r" % (option
,))
776 def _telnetProcessSubnegotiation(self
, suboption
):
777 """Process subnegotiation, the data between IAC SB and IAC SE."""
778 if suboption
[0:1] == COM_PORT_OPTION
:
779 if suboption
[1:2] == SERVER_NOTIFY_LINESTATE
and len(suboption
) >= 3:
780 self
._linestate
= ord(suboption
[2:3]) # ensure it is a number
782 self
.logger
.info("NOTIFY_LINESTATE: %s" % self
._linestate
)
783 elif suboption
[1:2] == SERVER_NOTIFY_MODEMSTATE
and len(suboption
) >= 3:
784 self
._modemstate
= ord(suboption
[2:3]) # ensure it is a number
786 self
.logger
.info("NOTIFY_MODEMSTATE: %s" % self
._modemstate
)
787 # update time when we think that a poll would make sense
788 self
._modemstate
_expires
= time
.time() + 0.3
789 elif suboption
[1:2] == FLOWCONTROL_SUSPEND
:
790 self
._remote
_suspend
_flow
= True
791 elif suboption
[1:2] == FLOWCONTROL_RESUME
:
792 self
._remote
_suspend
_flow
= False
794 for item
in self
._rfc
2217_options
.values():
795 if item
.ack_option
== suboption
[1:2]:
796 #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:])
797 item
.checkAnswer(bytes(suboption
[2:]))
801 self
.logger
.warning("ignoring COM_PORT_OPTION: %r" % (suboption
,))
804 self
.logger
.warning("ignoring subnegotiation: %r" % (suboption
,))
806 # - outgoing telnet commands and options
808 def _internal_raw_write(self
, data
):
809 """internal socket write with no data escaping. used to send telnet stuff."""
810 self
._write
_lock
.acquire()
812 self
._socket
.sendall(data
)
814 self
._write
_lock
.release()
816 def telnetSendOption(self
, action
, option
):
817 """Send DO, DONT, WILL, WONT."""
818 self
._internal
_raw
_write
(to_bytes([IAC
, action
, option
]))
820 def rfc2217SendSubnegotiation(self
, option
, value
=''):
821 """Subnegotiation of RFC2217 parameters."""
822 value
= value
.replace(IAC
, IAC_DOUBLED
)
823 self
._internal
_raw
_write
(to_bytes([IAC
, SB
, COM_PORT_OPTION
, option
] + list(value
) + [IAC
, SE
]))
825 def rfc2217SendPurge(self
, value
):
826 item
= self
._rfc
2217_options
['purge']
827 item
.set(value
) # transmit desired purge type
828 item
.wait(self
._network
_timeout
) # wait for acknowledge from the server
830 def rfc2217SetControl(self
, value
):
831 item
= self
._rfc
2217_options
['control']
832 item
.set(value
) # transmit desired control type
833 if self
._ignore
_set
_control
_answer
:
834 # answers are ignored when option is set. compatibility mode for
835 # servers that answer, but not the expected one... (or no answer
836 # at all) i.e. sredird
837 time
.sleep(0.1) # this helps getting the unit tests passed
839 item
.wait(self
._network
_timeout
) # wait for acknowledge from the server
841 def rfc2217FlowServerReady(self
):
843 check if server is ready to receive data. block for some time when
846 #~ if self._remote_suspend_flow:
849 def getModemState(self
):
851 get last modem state (cached value. if value is "old", request a new
852 one. this cache helps that we don't issue to many requests when e.g. all
853 status lines, one after the other is queried by te user (getCTS, getDSR
856 # active modem state polling enabled? is the value fresh enough?
857 if self
._poll
_modem
_state
and self
._modemstate
_expires
< time
.time():
859 self
.logger
.debug('polling modem state')
860 # when it is older, request an update
861 self
.rfc2217SendSubnegotiation(NOTIFY_MODEMSTATE
)
862 timeout_time
= time
.time() + self
._network
_timeout
863 while time
.time() < timeout_time
:
864 time
.sleep(0.05) # prevent 100% CPU load
865 # when expiration time is updated, it means that there is a new
867 if self
._modemstate
_expires
> time
.time():
869 self
.logger
.warning('poll for modem state failed')
871 # even when there is a timeout, do not generate an error just
872 # return the last known value. this way we can support buggy
873 # servers that do not respond to polls, but send automatic
875 if self
._modemstate
is not None:
877 self
.logger
.debug('using cached modem state')
878 return self
._modemstate
880 # never received a notification from the server
881 raise SerialException("remote sends no NOTIFY_MODEMSTATE")
884 # assemble Serial class with the platform specific implementation and the base
885 # for file-like behavior. for Python 2.6 and newer, that provide the new I/O
886 # library, derive from io.RawIOBase
890 # classic version with our own file-like emulation
891 class Serial(RFC2217Serial
, FileLike
):
895 class Serial(RFC2217Serial
, io
.RawIOBase
):
899 #############################################################################
900 # The following is code that helps implementing an RFC 2217 server.
902 class PortManager(object):
904 This class manages the state of Telnet and RFC 2217. It needs a serial
905 instance and a connection to work with. Connection is expected to implement
906 a (thread safe) write function, that writes the string to the network.
909 def __init__(self
, serial_port
, connection
, logger
=None):
910 self
.serial
= serial_port
911 self
.connection
= connection
913 self
._client
_is
_rfc
2217 = False
915 # filter state machine
917 self
.suboption
= None
918 self
.telnet_command
= None
920 # states for modem/line control events
921 self
.modemstate_mask
= 255
922 self
.last_modemstate
= None
923 self
.linstate_mask
= 0
925 # all supported telnet options
926 self
._telnet
_options
= [
927 TelnetOption(self
, 'ECHO', ECHO
, WILL
, WONT
, DO
, DONT
, REQUESTED
),
928 TelnetOption(self
, 'we-SGA', SGA
, WILL
, WONT
, DO
, DONT
, REQUESTED
),
929 TelnetOption(self
, 'they-SGA', SGA
, DO
, DONT
, WILL
, WONT
, INACTIVE
),
930 TelnetOption(self
, 'we-BINARY', BINARY
, WILL
, WONT
, DO
, DONT
, INACTIVE
),
931 TelnetOption(self
, 'they-BINARY', BINARY
, DO
, DONT
, WILL
, WONT
, REQUESTED
),
932 TelnetOption(self
, 'we-RFC2217', COM_PORT_OPTION
, WILL
, WONT
, DO
, DONT
, REQUESTED
, self
._client
_ok
),
933 TelnetOption(self
, 'they-RFC2217', COM_PORT_OPTION
, DO
, DONT
, WILL
, WONT
, INACTIVE
, self
._client
_ok
),
936 # negotiate Telnet/RFC2217 -> send initial requests
938 self
.logger
.debug("requesting initial Telnet/RFC 2217 options")
939 for option
in self
._telnet
_options
:
940 if option
.state
is REQUESTED
:
941 self
.telnetSendOption(option
.send_yes
, option
.option
)
942 # issue 1st modem state notification
944 def _client_ok(self
):
946 callback of telnet option. it gets called when option is activated.
947 this one here is used to detect when the client agrees on RFC 2217. a
948 flag is set so that other functions like check_modem_lines know if the
951 # The callback is used for we and they so if one party agrees, we're
952 # already happy. it seems not all servers do the negotiation correctly
953 # and i guess there are incorrect clients too.. so be happy if client
954 # answers one or the other positively.
955 self
._client
_is
_rfc
2217 = True
957 self
.logger
.info("client accepts RFC 2217")
958 # this is to ensure that the client gets a notification, even if there
960 self
.check_modem_lines(force_notification
=True)
962 # - outgoing telnet commands and options
964 def telnetSendOption(self
, action
, option
):
965 """Send DO, DONT, WILL, WONT."""
966 self
.connection
.write(to_bytes([IAC
, action
, option
]))
968 def rfc2217SendSubnegotiation(self
, option
, value
=''):
969 """Subnegotiation of RFC 2217 parameters."""
970 value
= value
.replace(IAC
, IAC_DOUBLED
)
971 self
.connection
.write(to_bytes([IAC
, SB
, COM_PORT_OPTION
, option
] + list(value
) + [IAC
, SE
]))
973 # - check modem lines, needs to be called periodically from user to
976 def check_modem_lines(self
, force_notification
=False):
978 (self
.serial
.getCTS() and MODEMSTATE_MASK_CTS
) |
979 (self
.serial
.getDSR() and MODEMSTATE_MASK_DSR
) |
980 (self
.serial
.getRI() and MODEMSTATE_MASK_RI
) |
981 (self
.serial
.getCD() and MODEMSTATE_MASK_CD
)
983 # check what has changed
984 deltas
= modemstate ^
(self
.last_modemstate
or 0) # when last is None -> 0
985 if deltas
& MODEMSTATE_MASK_CTS
:
986 modemstate |
= MODEMSTATE_MASK_CTS_CHANGE
987 if deltas
& MODEMSTATE_MASK_DSR
:
988 modemstate |
= MODEMSTATE_MASK_DSR_CHANGE
989 if deltas
& MODEMSTATE_MASK_RI
:
990 modemstate |
= MODEMSTATE_MASK_RI_CHANGE
991 if deltas
& MODEMSTATE_MASK_CD
:
992 modemstate |
= MODEMSTATE_MASK_CD_CHANGE
993 # if new state is different and the mask allows this change, send
994 # notification. suppress notifications when client is not rfc2217
995 if modemstate
!= self
.last_modemstate
or force_notification
:
996 if (self
._client
_is
_rfc
2217 and (modemstate
& self
.modemstate_mask
)) or force_notification
:
997 self
.rfc2217SendSubnegotiation(
998 SERVER_NOTIFY_MODEMSTATE
,
999 to_bytes([modemstate
& self
.modemstate_mask
])
1002 self
.logger
.info("NOTIFY_MODEMSTATE: %s" % (modemstate
,))
1003 # save last state, but forget about deltas.
1004 # otherwise it would also notify about changing deltas which is
1005 # probably not very useful
1006 self
.last_modemstate
= modemstate
& 0xf0
1008 # - outgoing data escaping
1010 def escape(self
, data
):
1012 this generator function is for the user. all outgoing data has to be
1013 properly escaped, so that no IAC character in the data stream messes up
1014 the Telnet state machine in the server.
1016 socket.sendall(escape(data))
1025 # - incoming data filter
1027 def filter(self
, data
):
1029 handle a bunch of incoming bytes. this is a generator. it will yield
1030 all characters not of interest for Telnet/RFC 2217.
1032 The idea is that the reader thread pushes data from the socket through
1035 for byte in filter(socket.recv(1024)):
1036 # do things like CR/LF conversion/whatever
1037 # and write data to the serial port
1040 (socket error handling code left as exercise for the reader)
1043 if self
.mode
== M_NORMAL
:
1044 # interpret as command or as data
1046 self
.mode
= M_IAC_SEEN
1048 # store data in sub option buffer or pass it to our
1049 # consumer depending on state
1050 if self
.suboption
is not None:
1051 self
.suboption
.append(byte
)
1054 elif self
.mode
== M_IAC_SEEN
:
1056 # interpret as command doubled -> insert character
1058 if self
.suboption
is not None:
1059 self
.suboption
.append(byte
)
1062 self
.mode
= M_NORMAL
1065 self
.suboption
= bytearray()
1066 self
.mode
= M_NORMAL
1068 # sub option end -> process it now
1069 self
._telnetProcessSubnegotiation
(bytes(self
.suboption
))
1070 self
.suboption
= None
1071 self
.mode
= M_NORMAL
1072 elif byte
in (DO
, DONT
, WILL
, WONT
):
1074 self
.telnet_command
= byte
1075 self
.mode
= M_NEGOTIATE
1077 # other telnet commands
1078 self
._telnetProcessCommand
(byte
)
1079 self
.mode
= M_NORMAL
1080 elif self
.mode
== M_NEGOTIATE
: # DO, DONT, WILL, WONT was received, option now following
1081 self
._telnetNegotiateOption
(self
.telnet_command
, byte
)
1082 self
.mode
= M_NORMAL
1084 # - incoming telnet commands and options
1086 def _telnetProcessCommand(self
, command
):
1087 """Process commands other than DO, DONT, WILL, WONT."""
1088 # Currently none. RFC2217 only uses negotiation and subnegotiation.
1090 self
.logger
.warning("ignoring Telnet command: %r" % (command
,))
1092 def _telnetNegotiateOption(self
, command
, option
):
1093 """Process incoming DO, DONT, WILL, WONT."""
1094 # check our registered telnet options and forward command to them
1095 # they know themselves if they have to answer or not
1097 for item
in self
._telnet
_options
:
1098 # can have more than one match! as some options are duplicated for
1100 if item
.option
== option
:
1101 item
.process_incoming(command
)
1104 # handle unknown options
1105 # only answer to positive requests and deny them
1106 if command
== WILL
or command
== DO
:
1107 self
.telnetSendOption((command
== WILL
and DONT
or WONT
), option
)
1109 self
.logger
.warning("rejected Telnet option: %r" % (option
,))
1112 def _telnetProcessSubnegotiation(self
, suboption
):
1113 """Process subnegotiation, the data between IAC SB and IAC SE."""
1114 if suboption
[0:1] == COM_PORT_OPTION
:
1116 self
.logger
.debug('received COM_PORT_OPTION: %r' % (suboption
,))
1117 if suboption
[1:2] == SET_BAUDRATE
:
1118 backup
= self
.serial
.baudrate
1120 (baudrate
,) = struct
.unpack("!I", suboption
[2:6])
1122 self
.serial
.baudrate
= baudrate
1123 except ValueError, e
:
1125 self
.logger
.error("failed to set baud rate: %s" % (e
,))
1126 self
.serial
.baudrate
= backup
1129 self
.logger
.info("%s baud rate: %s" % (baudrate
and 'set' or 'get', self
.serial
.baudrate
))
1130 self
.rfc2217SendSubnegotiation(SERVER_SET_BAUDRATE
, struct
.pack("!I", self
.serial
.baudrate
))
1131 elif suboption
[1:2] == SET_DATASIZE
:
1132 backup
= self
.serial
.bytesize
1134 (datasize
,) = struct
.unpack("!B", suboption
[2:3])
1136 self
.serial
.bytesize
= datasize
1137 except ValueError, e
:
1139 self
.logger
.error("failed to set data size: %s" % (e
,))
1140 self
.serial
.bytesize
= backup
1143 self
.logger
.info("%s data size: %s" % (datasize
and 'set' or 'get', self
.serial
.bytesize
))
1144 self
.rfc2217SendSubnegotiation(SERVER_SET_DATASIZE
, struct
.pack("!B", self
.serial
.bytesize
))
1145 elif suboption
[1:2] == SET_PARITY
:
1146 backup
= self
.serial
.parity
1148 parity
= struct
.unpack("!B", suboption
[2:3])[0]
1150 self
.serial
.parity
= RFC2217_REVERSE_PARITY_MAP
[parity
]
1151 except ValueError, e
:
1153 self
.logger
.error("failed to set parity: %s" % (e
,))
1154 self
.serial
.parity
= backup
1157 self
.logger
.info("%s parity: %s" % (parity
and 'set' or 'get', self
.serial
.parity
))
1158 self
.rfc2217SendSubnegotiation(
1160 struct
.pack("!B", RFC2217_PARITY_MAP
[self
.serial
.parity
])
1162 elif suboption
[1:2] == SET_STOPSIZE
:
1163 backup
= self
.serial
.stopbits
1165 stopbits
= struct
.unpack("!B", suboption
[2:3])[0]
1167 self
.serial
.stopbits
= RFC2217_REVERSE_STOPBIT_MAP
[stopbits
]
1168 except ValueError, e
:
1170 self
.logger
.error("failed to set stop bits: %s" % (e
,))
1171 self
.serial
.stopbits
= backup
1174 self
.logger
.info("%s stop bits: %s" % (stopbits
and 'set' or 'get', self
.serial
.stopbits
))
1175 self
.rfc2217SendSubnegotiation(
1176 SERVER_SET_STOPSIZE
,
1177 struct
.pack("!B", RFC2217_STOPBIT_MAP
[self
.serial
.stopbits
])
1179 elif suboption
[1:2] == SET_CONTROL
:
1180 if suboption
[2:3] == SET_CONTROL_REQ_FLOW_SETTING
:
1181 if self
.serial
.xonxoff
:
1182 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_USE_SW_FLOW_CONTROL
)
1183 elif self
.serial
.rtscts
:
1184 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_USE_HW_FLOW_CONTROL
)
1186 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_USE_NO_FLOW_CONTROL
)
1187 elif suboption
[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL
:
1188 self
.serial
.xonxoff
= False
1189 self
.serial
.rtscts
= False
1191 self
.logger
.info("changed flow control to None")
1192 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_USE_NO_FLOW_CONTROL
)
1193 elif suboption
[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL
:
1194 self
.serial
.xonxoff
= True
1196 self
.logger
.info("changed flow control to XON/XOFF")
1197 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_USE_SW_FLOW_CONTROL
)
1198 elif suboption
[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL
:
1199 self
.serial
.rtscts
= True
1201 self
.logger
.info("changed flow control to RTS/CTS")
1202 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_USE_HW_FLOW_CONTROL
)
1203 elif suboption
[2:3] == SET_CONTROL_REQ_BREAK_STATE
:
1205 self
.logger
.warning("requested break state - not implemented")
1206 pass # XXX needs cached value
1207 elif suboption
[2:3] == SET_CONTROL_BREAK_ON
:
1208 self
.serial
.setBreak(True)
1210 self
.logger
.info("changed BREAK to active")
1211 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_BREAK_ON
)
1212 elif suboption
[2:3] == SET_CONTROL_BREAK_OFF
:
1213 self
.serial
.setBreak(False)
1215 self
.logger
.info("changed BREAK to inactive")
1216 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_BREAK_OFF
)
1217 elif suboption
[2:3] == SET_CONTROL_REQ_DTR
:
1219 self
.logger
.warning("requested DTR state - not implemented")
1220 pass # XXX needs cached value
1221 elif suboption
[2:3] == SET_CONTROL_DTR_ON
:
1222 self
.serial
.setDTR(True)
1224 self
.logger
.info("changed DTR to active")
1225 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_DTR_ON
)
1226 elif suboption
[2:3] == SET_CONTROL_DTR_OFF
:
1227 self
.serial
.setDTR(False)
1229 self
.logger
.info("changed DTR to inactive")
1230 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_DTR_OFF
)
1231 elif suboption
[2:3] == SET_CONTROL_REQ_RTS
:
1233 self
.logger
.warning("requested RTS state - not implemented")
1234 pass # XXX needs cached value
1235 #~ self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
1236 elif suboption
[2:3] == SET_CONTROL_RTS_ON
:
1237 self
.serial
.setRTS(True)
1239 self
.logger
.info("changed RTS to active")
1240 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_RTS_ON
)
1241 elif suboption
[2:3] == SET_CONTROL_RTS_OFF
:
1242 self
.serial
.setRTS(False)
1244 self
.logger
.info("changed RTS to inactive")
1245 self
.rfc2217SendSubnegotiation(SERVER_SET_CONTROL
, SET_CONTROL_RTS_OFF
)
1246 #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN:
1247 #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN:
1248 #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN:
1249 #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN:
1250 #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL:
1251 #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL:
1252 #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL:
1253 elif suboption
[1:2] == NOTIFY_LINESTATE
:
1254 # client polls for current state
1255 self
.rfc2217SendSubnegotiation(
1256 SERVER_NOTIFY_LINESTATE
,
1257 to_bytes([0]) # sorry, nothing like that implemented
1259 elif suboption
[1:2] == NOTIFY_MODEMSTATE
:
1261 self
.logger
.info("request for modem state")
1262 # client polls for current state
1263 self
.check_modem_lines(force_notification
=True)
1264 elif suboption
[1:2] == FLOWCONTROL_SUSPEND
:
1266 self
.logger
.info("suspend")
1267 self
._remote
_suspend
_flow
= True
1268 elif suboption
[1:2] == FLOWCONTROL_RESUME
:
1270 self
.logger
.info("resume")
1271 self
._remote
_suspend
_flow
= False
1272 elif suboption
[1:2] == SET_LINESTATE_MASK
:
1273 self
.linstate_mask
= ord(suboption
[2:3]) # ensure it is a number
1275 self
.logger
.info("line state mask: 0x%02x" % (self
.linstate_mask
,))
1276 elif suboption
[1:2] == SET_MODEMSTATE_MASK
:
1277 self
.modemstate_mask
= ord(suboption
[2:3]) # ensure it is a number
1279 self
.logger
.info("modem state mask: 0x%02x" % (self
.modemstate_mask
,))
1280 elif suboption
[1:2] == PURGE_DATA
:
1281 if suboption
[2:3] == PURGE_RECEIVE_BUFFER
:
1282 self
.serial
.flushInput()
1284 self
.logger
.info("purge in")
1285 self
.rfc2217SendSubnegotiation(SERVER_PURGE_DATA
, PURGE_RECEIVE_BUFFER
)
1286 elif suboption
[2:3] == PURGE_TRANSMIT_BUFFER
:
1287 self
.serial
.flushOutput()
1289 self
.logger
.info("purge out")
1290 self
.rfc2217SendSubnegotiation(SERVER_PURGE_DATA
, PURGE_TRANSMIT_BUFFER
)
1291 elif suboption
[2:3] == PURGE_BOTH_BUFFERS
:
1292 self
.serial
.flushInput()
1293 self
.serial
.flushOutput()
1295 self
.logger
.info("purge both")
1296 self
.rfc2217SendSubnegotiation(SERVER_PURGE_DATA
, PURGE_BOTH_BUFFERS
)
1299 self
.logger
.error("undefined PURGE_DATA: %r" % list(suboption
[2:]))
1302 self
.logger
.error("undefined COM_PORT_OPTION: %r" % list(suboption
[1:]))
1305 self
.logger
.warning("unknown subnegotiation: %r" % (suboption
,))
1308 # simple client test
1309 if __name__
== '__main__':
1311 s
= Serial('rfc2217://localhost:7000', 115200)
1312 sys
.stdout
.write('%s\n' % s
)
1314 #~ s.baudrate = 1898
1316 sys
.stdout
.write("write...\n")
1319 sys
.stdout
.write("read: %s\n" % s
.read(5))
1321 #~ s.baudrate = 19200