blob: 36cdf1fdf8fd6b9cd2dfbc9ff47990cebef1ab98 [file] [log] [blame]
cliechtiab90e072009-08-06 01:44:34 +00001#! python
2#
cliechtiab90e072009-08-06 01:44:34 +00003# This module implements a simple socket based client.
4# It does not support changing any port parameters and will silently ignore any
5# requests to do so.
6#
7# The purpose of this module is that applications using pySerial can connect to
8# TCP/IP to serial port converters that do not support RFC 2217.
9#
Chris Liechti3e02f702015-12-16 23:06:04 +010010# This file is part of pySerial. https://github.com/pyserial/pyserial
Chris Liechtia4222112015-08-07 01:03:12 +020011# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
Chris Liechtifbdd8a02015-08-09 02:37:45 +020012#
13# SPDX-License-Identifier: BSD-3-Clause
cliechtiab90e072009-08-06 01:44:34 +000014#
15# URL format: socket://<host>:<port>[/option[/option...]]
16# options:
17# - "debug" print diagnostic messages
18
Kurt McKee057387c2018-02-07 22:10:38 -060019from __future__ import absolute_import
20
Chris Liechtib10daf42016-01-26 00:27:10 +010021import errno
cliechtic64ba692009-08-12 00:32:47 +000022import logging
Chris Liechtifbdd8a02015-08-09 02:37:45 +020023import select
24import socket
25import time
Chris Liechtic4bca9e2015-08-07 14:40:41 +020026try:
27 import urlparse
28except ImportError:
29 import urllib.parse as urlparse
cliechtic64ba692009-08-12 00:32:47 +000030
Chris Liechtieb163262016-12-18 22:40:22 +010031from serial.serialutil import SerialBase, SerialException, to_bytes, \
32 portNotOpenError, writeTimeoutError, Timeout
Chris Liechtifbdd8a02015-08-09 02:37:45 +020033
Chris Liechti3ad62fb2015-08-29 21:53:32 +020034# map log level names to constants. used in from_url()
cliechtic64ba692009-08-12 00:32:47 +000035LOGGER_LEVELS = {
Chris Liechti6594df62016-02-04 21:13:41 +010036 'debug': logging.DEBUG,
37 'info': logging.INFO,
38 'warning': logging.WARNING,
39 'error': logging.ERROR,
40}
cliechtic64ba692009-08-12 00:32:47 +000041
Chris Liechti4c3ec662016-04-21 22:52:31 +020042POLL_TIMEOUT = 5
cliechtiab90e072009-08-06 01:44:34 +000043
Chris Liechti033f17c2015-08-30 21:28:04 +020044
Chris Liechtief6b7b42015-08-06 22:19:26 +020045class Serial(SerialBase):
cliechtiab90e072009-08-06 01:44:34 +000046 """Serial port implementation for plain sockets."""
47
48 BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
49 9600, 19200, 38400, 57600, 115200)
50
51 def open(self):
cliechti7d448562014-08-03 21:57:45 +000052 """\
53 Open port with current settings. This may throw a SerialException
54 if the port cannot be opened.
55 """
cliechti6a300772009-08-12 02:28:56 +000056 self.logger = None
cliechtiab90e072009-08-06 01:44:34 +000057 if self._port is None:
58 raise SerialException("Port must be configured before it can be used.")
Chris Liechti3ad62fb2015-08-29 21:53:32 +020059 if self.is_open:
cliechti8f69e702011-03-19 00:22:32 +000060 raise SerialException("Port is already open.")
cliechtiab90e072009-08-06 01:44:34 +000061 try:
Chris Liechti4c3ec662016-04-21 22:52:31 +020062 # timeout is used for write timeout support :/ and to get an initial connection timeout
63 self._socket = socket.create_connection(self.from_url(self.portstr), timeout=POLL_TIMEOUT)
Chris Liechti68340d72015-08-03 14:15:48 +020064 except Exception as msg:
cliechtiab90e072009-08-06 01:44:34 +000065 self._socket = None
Chris Liechti4daa9d52016-03-22 00:32:01 +010066 raise SerialException("Could not open port {}: {}".format(self.portstr, msg))
Chris Liechtieb163262016-12-18 22:40:22 +010067 # after connecting, switch to non-blocking, we're using select
68 self._socket.setblocking(False)
cliechtiab90e072009-08-06 01:44:34 +000069
Chris Liechti4c3ec662016-04-21 22:52:31 +020070 # not that there is anything to configure...
Chris Liechti3ad62fb2015-08-29 21:53:32 +020071 self._reconfigure_port()
cliechtiab90e072009-08-06 01:44:34 +000072 # all things set up get, now a clean start
Chris Liechti3ad62fb2015-08-29 21:53:32 +020073 self.is_open = True
74 if not self._dsrdtr:
Chris Liechtief1fe252015-08-27 23:25:21 +020075 self._update_dtr_state()
cliechtiab90e072009-08-06 01:44:34 +000076 if not self._rtscts:
Chris Liechtief1fe252015-08-27 23:25:21 +020077 self._update_rts_state()
78 self.reset_input_buffer()
79 self.reset_output_buffer()
cliechtiab90e072009-08-06 01:44:34 +000080
Chris Liechti3ad62fb2015-08-29 21:53:32 +020081 def _reconfigure_port(self):
cliechti7d448562014-08-03 21:57:45 +000082 """\
83 Set communication parameters on opened port. For the socket://
84 protocol all settings are ignored!
85 """
cliechtiab90e072009-08-06 01:44:34 +000086 if self._socket is None:
87 raise SerialException("Can only operate on open ports")
cliechti6a300772009-08-12 02:28:56 +000088 if self.logger:
89 self.logger.info('ignored port configuration change')
cliechtiab90e072009-08-06 01:44:34 +000090
91 def close(self):
92 """Close port"""
Chris Liechti3ad62fb2015-08-29 21:53:32 +020093 if self.is_open:
cliechtiab90e072009-08-06 01:44:34 +000094 if self._socket:
95 try:
96 self._socket.shutdown(socket.SHUT_RDWR)
97 self._socket.close()
98 except:
99 # ignore errors.
100 pass
101 self._socket = None
Chris Liechti3ad62fb2015-08-29 21:53:32 +0200102 self.is_open = False
cliechtiab90e072009-08-06 01:44:34 +0000103 # in case of quick reconnects, give the server some time
104 time.sleep(0.3)
105
Chris Liechti3ad62fb2015-08-29 21:53:32 +0200106 def from_url(self, url):
cliechtiab90e072009-08-06 01:44:34 +0000107 """extract host and port from an URL string"""
Chris Liechtia4222112015-08-07 01:03:12 +0200108 parts = urlparse.urlsplit(url)
Chris Liechtid14b1ab2015-08-21 00:28:53 +0200109 if parts.scheme != "socket":
Chris Liechti4daa9d52016-03-22 00:32:01 +0100110 raise SerialException(
111 'expected a string in the form '
112 '"socket://<host>:<port>[?logging={debug|info|warning|error}]": '
113 'not starting with socket:// ({!r})'.format(parts.scheme))
cliechtiab90e072009-08-06 01:44:34 +0000114 try:
Chris Liechtia4222112015-08-07 01:03:12 +0200115 # process options now, directly altering self
Chris Liechtid14b1ab2015-08-21 00:28:53 +0200116 for option, values in urlparse.parse_qs(parts.query, True).items():
117 if option == 'logging':
Chris Liechtia4222112015-08-07 01:03:12 +0200118 logging.basicConfig() # XXX is that good to call it here?
119 self.logger = logging.getLogger('pySerial.socket')
Chris Liechtid14b1ab2015-08-21 00:28:53 +0200120 self.logger.setLevel(LOGGER_LEVELS[values[0]])
Chris Liechtia4222112015-08-07 01:03:12 +0200121 self.logger.debug('enabled logging')
122 else:
Chris Liechti4daa9d52016-03-22 00:32:01 +0100123 raise ValueError('unknown option: {!r}'.format(option))
124 if not 0 <= parts.port < 65536:
Chris Liechti033f17c2015-08-30 21:28:04 +0200125 raise ValueError("port not in range 0...65535")
Chris Liechti68340d72015-08-03 14:15:48 +0200126 except ValueError as e:
Chris Liechti4daa9d52016-03-22 00:32:01 +0100127 raise SerialException(
128 'expected a string in the form '
129 '"socket://<host>:<port>[?logging={debug|info|warning|error}]": {}'.format(e))
130
131 return (parts.hostname, parts.port)
cliechtiab90e072009-08-06 01:44:34 +0000132
133 # - - - - - - - - - - - - - - - - - - - - - - - -
134
Chris Liechtief1fe252015-08-27 23:25:21 +0200135 @property
136 def in_waiting(self):
Chris Liechti3ad62fb2015-08-29 21:53:32 +0200137 """Return the number of bytes currently in the input buffer."""
Chris Liechti033f17c2015-08-30 21:28:04 +0200138 if not self.is_open:
139 raise portNotOpenError
cliechti77e088a2014-08-04 10:29:24 +0000140 # Poll the socket to see if it is ready for reading.
141 # If ready, at least one byte will be to read.
142 lr, lw, lx = select.select([self._socket], [], [], 0)
143 return len(lr)
cliechtiab90e072009-08-06 01:44:34 +0000144
Chris Liechti6032cf52016-01-15 23:12:20 +0100145 # select based implementation, similar to posix, but only using socket API
146 # to be portable, additionally handle socket timeout which is used to
147 # emulate write timeouts
cliechtiab90e072009-08-06 01:44:34 +0000148 def read(self, size=1):
cliechti7d448562014-08-03 21:57:45 +0000149 """\
150 Read size bytes from the serial port. If a timeout is set it may
cliechtiab90e072009-08-06 01:44:34 +0000151 return less characters as requested. With no timeout it will block
cliechti7d448562014-08-03 21:57:45 +0000152 until the requested number of bytes is read.
153 """
Chris Liechti033f17c2015-08-30 21:28:04 +0200154 if not self.is_open:
155 raise portNotOpenError
Chris Liechti6032cf52016-01-15 23:12:20 +0100156 read = bytearray()
Chris Liechtieb163262016-12-18 22:40:22 +0100157 timeout = Timeout(self._timeout)
Chris Liechti6032cf52016-01-15 23:12:20 +0100158 while len(read) < size:
cliechtiab90e072009-08-06 01:44:34 +0000159 try:
Chris Liechtieb163262016-12-18 22:40:22 +0100160 ready, _, _ = select.select([self._socket], [], [], timeout.time_left())
Chris Liechti6032cf52016-01-15 23:12:20 +0100161 # If select was used with a timeout, and the timeout occurs, it
162 # returns with empty lists -> thus abort read operation.
163 # For timeout == 0 (non-blocking operation) also abort when
164 # there is nothing to read.
165 if not ready:
166 break # timeout
167 buf = self._socket.recv(size - len(read))
168 # read should always return some data as select reported it was
169 # ready to read when we get to this point, unless it is EOF
170 if not buf:
171 raise SerialException('socket disconnected')
172 read.extend(buf)
Chris Liechti6032cf52016-01-15 23:12:20 +0100173 except OSError as e:
174 # this is for Python 3.x where select.error is a subclass of
Chris Liechtifc1bf5a2017-05-06 23:39:05 +0200175 # OSError ignore BlockingIOErrors and EINTR. other errors are shown
176 # https://www.python.org/dev/peps/pep-0475.
177 if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
Chris Liechti4daa9d52016-03-22 00:32:01 +0100178 raise SerialException('read failed: {}'.format(e))
Chris Liechtieb163262016-12-18 22:40:22 +0100179 except (select.error, socket.error) as e:
Chris Liechti6032cf52016-01-15 23:12:20 +0100180 # this is for Python 2.x
Chris Liechtifc1bf5a2017-05-06 23:39:05 +0200181 # ignore BlockingIOErrors and EINTR. all errors are shown
Chris Liechti6032cf52016-01-15 23:12:20 +0100182 # see also http://www.python.org/dev/peps/pep-3151/#select
Chris Liechtifc1bf5a2017-05-06 23:39:05 +0200183 if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
Chris Liechti4daa9d52016-03-22 00:32:01 +0100184 raise SerialException('read failed: {}'.format(e))
Chris Liechtieb163262016-12-18 22:40:22 +0100185 if timeout.expired():
186 break
Chris Liechti6032cf52016-01-15 23:12:20 +0100187 return bytes(read)
cliechtiab90e072009-08-06 01:44:34 +0000188
189 def write(self, data):
cliechti7d448562014-08-03 21:57:45 +0000190 """\
Chris Liechti3ad62fb2015-08-29 21:53:32 +0200191 Output the given byte string over the serial port. Can block if the
cliechtiab90e072009-08-06 01:44:34 +0000192 connection is blocked. May raise SerialException if the connection is
cliechti7d448562014-08-03 21:57:45 +0000193 closed.
194 """
Chris Liechti033f17c2015-08-30 21:28:04 +0200195 if not self.is_open:
196 raise portNotOpenError
Chris Liechtieb163262016-12-18 22:40:22 +0100197
198 d = to_bytes(data)
199 tx_len = length = len(d)
200 timeout = Timeout(self._write_timeout)
201 while tx_len > 0:
202 try:
203 n = self._socket.send(d)
204 if timeout.is_non_blocking:
205 # Zero timeout indicates non-blocking - simply return the
206 # number of bytes of data actually written
207 return n
208 elif not timeout.is_infinite:
209 # when timeout is set, use select to wait for being ready
210 # with the time left as timeout
211 if timeout.expired():
212 raise writeTimeoutError
213 _, ready, _ = select.select([], [self._socket], [], timeout.time_left())
214 if not ready:
215 raise writeTimeoutError
216 else:
217 assert timeout.time_left() is None
218 # wait for write operation
219 _, ready, _ = select.select([], [self._socket], [], None)
220 if not ready:
221 raise SerialException('write failed (select)')
222 d = d[n:]
223 tx_len -= n
224 except SerialException:
225 raise
Chris Liechtifc1bf5a2017-05-06 23:39:05 +0200226 except OSError as e:
227 # this is for Python 3.x where select.error is a subclass of
228 # OSError ignore BlockingIOErrors and EINTR. other errors are shown
229 # https://www.python.org/dev/peps/pep-0475.
230 if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
231 raise SerialException('write failed: {}'.format(e))
232 except select.error as e:
233 # this is for Python 2.x
234 # ignore BlockingIOErrors and EINTR. all errors are shown
235 # see also http://www.python.org/dev/peps/pep-3151/#select
236 if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
237 raise SerialException('write failed: {}'.format(e))
238 if not timeout.is_non_blocking and timeout.expired():
239 raise writeTimeoutError
Chris Liechtieb163262016-12-18 22:40:22 +0100240 return length - len(d)
cliechtiab90e072009-08-06 01:44:34 +0000241
Chris Liechtief1fe252015-08-27 23:25:21 +0200242 def reset_input_buffer(self):
cliechtiab90e072009-08-06 01:44:34 +0000243 """Clear input buffer, discarding all that is in the buffer."""
Chris Liechti033f17c2015-08-30 21:28:04 +0200244 if not self.is_open:
245 raise portNotOpenError
Chris Liechti64d59922016-12-19 03:12:05 +0100246
247 # just use recv to remove input, while there is some
248 ready = True
249 while ready:
250 ready, _, _ = select.select([self._socket], [], [], 0)
251 try:
252 self._socket.recv(4096)
253 except OSError as e:
254 # this is for Python 3.x where select.error is a subclass of
Chris Liechtifc1bf5a2017-05-06 23:39:05 +0200255 # OSError ignore BlockingIOErrors and EINTR. other errors are shown
256 # https://www.python.org/dev/peps/pep-0475.
257 if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
258 raise SerialException('read failed: {}'.format(e))
Chris Liechti64d59922016-12-19 03:12:05 +0100259 except (select.error, socket.error) as e:
260 # this is for Python 2.x
Chris Liechtifc1bf5a2017-05-06 23:39:05 +0200261 # ignore BlockingIOErrors and EINTR. all errors are shown
Chris Liechti64d59922016-12-19 03:12:05 +0100262 # see also http://www.python.org/dev/peps/pep-3151/#select
Chris Liechtifc1bf5a2017-05-06 23:39:05 +0200263 if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
264 raise SerialException('read failed: {}'.format(e))
cliechtiab90e072009-08-06 01:44:34 +0000265
Chris Liechtief1fe252015-08-27 23:25:21 +0200266 def reset_output_buffer(self):
cliechti7d448562014-08-03 21:57:45 +0000267 """\
268 Clear output buffer, aborting the current output and
269 discarding all that is in the buffer.
270 """
Chris Liechti033f17c2015-08-30 21:28:04 +0200271 if not self.is_open:
272 raise portNotOpenError
cliechti6a300772009-08-12 02:28:56 +0000273 if self.logger:
Chris Liechtief1fe252015-08-27 23:25:21 +0200274 self.logger.info('ignored reset_output_buffer')
cliechtiab90e072009-08-06 01:44:34 +0000275
Chris Liechtief1fe252015-08-27 23:25:21 +0200276 def send_break(self, duration=0.25):
cliechti7d448562014-08-03 21:57:45 +0000277 """\
278 Send break condition. Timed, returns to idle state after given
279 duration.
280 """
Chris Liechti033f17c2015-08-30 21:28:04 +0200281 if not self.is_open:
282 raise portNotOpenError
cliechti6a300772009-08-12 02:28:56 +0000283 if self.logger:
Chris Liechti4daa9d52016-03-22 00:32:01 +0100284 self.logger.info('ignored send_break({!r})'.format(duration))
cliechtiab90e072009-08-06 01:44:34 +0000285
Chris Liechtief1fe252015-08-27 23:25:21 +0200286 def _update_break_state(self):
cliechtiab90e072009-08-06 01:44:34 +0000287 """Set break: Controls TXD. When active, to transmitting is
288 possible."""
cliechti6a300772009-08-12 02:28:56 +0000289 if self.logger:
Chris Liechti4daa9d52016-03-22 00:32:01 +0100290 self.logger.info('ignored _update_break_state({!r})'.format(self._break_state))
cliechtiab90e072009-08-06 01:44:34 +0000291
Chris Liechtief1fe252015-08-27 23:25:21 +0200292 def _update_rts_state(self):
cliechtiab90e072009-08-06 01:44:34 +0000293 """Set terminal status line: Request To Send"""
cliechti6a300772009-08-12 02:28:56 +0000294 if self.logger:
Chris Liechti4daa9d52016-03-22 00:32:01 +0100295 self.logger.info('ignored _update_rts_state({!r})'.format(self._rts_state))
cliechtiab90e072009-08-06 01:44:34 +0000296
Chris Liechtief1fe252015-08-27 23:25:21 +0200297 def _update_dtr_state(self):
cliechtiab90e072009-08-06 01:44:34 +0000298 """Set terminal status line: Data Terminal Ready"""
cliechti6a300772009-08-12 02:28:56 +0000299 if self.logger:
Chris Liechti4daa9d52016-03-22 00:32:01 +0100300 self.logger.info('ignored _update_dtr_state({!r})'.format(self._dtr_state))
cliechtiab90e072009-08-06 01:44:34 +0000301
Chris Liechtief1fe252015-08-27 23:25:21 +0200302 @property
303 def cts(self):
cliechtiab90e072009-08-06 01:44:34 +0000304 """Read terminal status line: Clear To Send"""
Chris Liechti033f17c2015-08-30 21:28:04 +0200305 if not self.is_open:
306 raise portNotOpenError
cliechti6a300772009-08-12 02:28:56 +0000307 if self.logger:
Chris Liechtief1fe252015-08-27 23:25:21 +0200308 self.logger.info('returning dummy for cts')
cliechtiab90e072009-08-06 01:44:34 +0000309 return True
310
Chris Liechtief1fe252015-08-27 23:25:21 +0200311 @property
312 def dsr(self):
cliechtiab90e072009-08-06 01:44:34 +0000313 """Read terminal status line: Data Set Ready"""
Chris Liechti033f17c2015-08-30 21:28:04 +0200314 if not self.is_open:
315 raise portNotOpenError
cliechti6a300772009-08-12 02:28:56 +0000316 if self.logger:
Chris Liechtief1fe252015-08-27 23:25:21 +0200317 self.logger.info('returning dummy for dsr')
cliechtiab90e072009-08-06 01:44:34 +0000318 return True
319
Chris Liechtief1fe252015-08-27 23:25:21 +0200320 @property
321 def ri(self):
cliechtiab90e072009-08-06 01:44:34 +0000322 """Read terminal status line: Ring Indicator"""
Chris Liechti033f17c2015-08-30 21:28:04 +0200323 if not self.is_open:
324 raise portNotOpenError
cliechti6a300772009-08-12 02:28:56 +0000325 if self.logger:
Chris Liechtief1fe252015-08-27 23:25:21 +0200326 self.logger.info('returning dummy for ri')
cliechtiab90e072009-08-06 01:44:34 +0000327 return False
328
Chris Liechtief1fe252015-08-27 23:25:21 +0200329 @property
330 def cd(self):
cliechtiab90e072009-08-06 01:44:34 +0000331 """Read terminal status line: Carrier Detect"""
Chris Liechti033f17c2015-08-30 21:28:04 +0200332 if not self.is_open:
333 raise portNotOpenError
cliechti6a300772009-08-12 02:28:56 +0000334 if self.logger:
Chris Liechtief1fe252015-08-27 23:25:21 +0200335 self.logger.info('returning dummy for cd)')
cliechtiab90e072009-08-06 01:44:34 +0000336 return True
337
338 # - - - platform specific - - -
cliechtib869edb2014-07-31 22:13:19 +0000339
340 # works on Linux and probably all the other POSIX systems
341 def fileno(self):
342 """Get the file handle of the underlying socket for use with select"""
343 return self._socket.fileno()
cliechtiab90e072009-08-06 01:44:34 +0000344
345
Chris Liechtief6b7b42015-08-06 22:19:26 +0200346#
cliechtiab90e072009-08-06 01:44:34 +0000347# simple client test
348if __name__ == '__main__':
349 import sys
350 s = Serial('socket://localhost:7000')
Chris Liechti4daa9d52016-03-22 00:32:01 +0100351 sys.stdout.write('{}\n'.format(s))
cliechtiab90e072009-08-06 01:44:34 +0000352
353 sys.stdout.write("write...\n")
Chris Liechtifbdd8a02015-08-09 02:37:45 +0200354 s.write(b"hello\n")
cliechtiab90e072009-08-06 01:44:34 +0000355 s.flush()
Chris Liechti4daa9d52016-03-22 00:32:01 +0100356 sys.stdout.write("read: {}\n".format(s.read(5)))
cliechtiab90e072009-08-06 01:44:34 +0000357
358 s.close()