1#! python 2# 3# Base class and support functions used by various backends. 4# 5# This file is part of pySerial. https://github.com/pyserial/pyserial 6# (C) 2001-2020 Chris Liechti <[email protected]> 7# 8# SPDX-License-Identifier: BSD-3-Clause 9 10from __future__ import absolute_import 11 12import io 13import time 14 15# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)`` 16# isn't returning the contents (very unfortunate). Therefore we need special 17# cases and test for it. Ensure that there is a ``memoryview`` object for older 18# Python versions. This is easier than making every test dependent on its 19# existence. 20try: 21 memoryview 22except (NameError, AttributeError): 23 # implementation does not matter as we do not really use it. 24 # it just must not inherit from something else we might care for. 25 class memoryview(object): # pylint: disable=redefined-builtin,invalid-name 26 pass 27 28try: 29 unicode 30except (NameError, AttributeError): 31 unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name 32 33try: 34 basestring 35except (NameError, AttributeError): 36 basestring = (str,) # for Python 3, pylint: disable=redefined-builtin,invalid-name 37 38 39# "for byte in data" fails for python3 as it returns ints instead of bytes 40def iterbytes(b): 41 """Iterate over bytes, returning bytes instead of ints (python3)""" 42 if isinstance(b, memoryview): 43 b = b.tobytes() 44 i = 0 45 while True: 46 a = b[i:i + 1] 47 i += 1 48 if a: 49 yield a 50 else: 51 break 52 53 54# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11' 55# so a simple ``bytes(sequence)`` doesn't work for all versions 56def to_bytes(seq): 57 """convert a sequence to a bytes type""" 58 if isinstance(seq, bytes): 59 return seq 60 elif isinstance(seq, bytearray): 61 return bytes(seq) 62 elif isinstance(seq, memoryview): 63 return seq.tobytes() 64 elif isinstance(seq, unicode): 65 raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq)) 66 else: 67 # handle list of integers and bytes (one or more items) for Python 2 and 3 68 return bytes(bytearray(seq)) 69 70 71# create control bytes 72XON = to_bytes([17]) 73XOFF = to_bytes([19]) 74 75CR = to_bytes([13]) 76LF = to_bytes([10]) 77 78 79PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S' 80STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2) 81FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8) 82 83PARITY_NAMES = { 84 PARITY_NONE: 'None', 85 PARITY_EVEN: 'Even', 86 PARITY_ODD: 'Odd', 87 PARITY_MARK: 'Mark', 88 PARITY_SPACE: 'Space', 89} 90 91 92class SerialException(IOError): 93 """Base class for serial port related exceptions.""" 94 95 96class SerialTimeoutException(SerialException): 97 """Write timeouts give an exception""" 98 99 100class PortNotOpenError(SerialException): 101 """Port is not open""" 102 def __init__(self): 103 super(PortNotOpenError, self).__init__('Attempting to use a port that is not open') 104 105 106class Timeout(object): 107 """\ 108 Abstraction for timeout operations. Using time.monotonic() if available 109 or time.time() in all other cases. 110 111 The class can also be initialized with 0 or None, in order to support 112 non-blocking and fully blocking I/O operations. The attributes 113 is_non_blocking and is_infinite are set accordingly. 114 """ 115 if hasattr(time, 'monotonic'): 116 # Timeout implementation with time.monotonic(). This function is only 117 # supported by Python 3.3 and above. It returns a time in seconds 118 # (float) just as time.time(), but is not affected by system clock 119 # adjustments. 120 TIME = time.monotonic 121 else: 122 # Timeout implementation with time.time(). This is compatible with all 123 # Python versions but has issues if the clock is adjusted while the 124 # timeout is running. 125 TIME = time.time 126 127 def __init__(self, duration): 128 """Initialize a timeout with given duration""" 129 self.is_infinite = (duration is None) 130 self.is_non_blocking = (duration == 0) 131 self.duration = duration 132 if duration is not None: 133 self.target_time = self.TIME() + duration 134 else: 135 self.target_time = None 136 137 def expired(self): 138 """Return a boolean, telling if the timeout has expired""" 139 return self.target_time is not None and self.time_left() <= 0 140 141 def time_left(self): 142 """Return how many seconds are left until the timeout expires""" 143 if self.is_non_blocking: 144 return 0 145 elif self.is_infinite: 146 return None 147 else: 148 delta = self.target_time - self.TIME() 149 if delta > self.duration: 150 # clock jumped, recalculate 151 self.target_time = self.TIME() + self.duration 152 return self.duration 153 else: 154 return max(0, delta) 155 156 def restart(self, duration): 157 """\ 158 Restart a timeout, only supported if a timeout was already set up 159 before. 160 """ 161 self.duration = duration 162 self.target_time = self.TIME() + duration 163 164 165class SerialBase(io.RawIOBase): 166 """\ 167 Serial port base class. Provides __init__ function and properties to 168 get/set port settings. 169 """ 170 171 # default values, may be overridden in subclasses that do not support all values 172 BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 173 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 174 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 175 3000000, 3500000, 4000000) 176 BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS) 177 PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE) 178 STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO) 179 180 def __init__(self, 181 port=None, 182 baudrate=9600, 183 bytesize=EIGHTBITS, 184 parity=PARITY_NONE, 185 stopbits=STOPBITS_ONE, 186 timeout=None, 187 xonxoff=False, 188 rtscts=False, 189 write_timeout=None, 190 dsrdtr=False, 191 inter_byte_timeout=None, 192 exclusive=None, 193 **kwargs): 194 """\ 195 Initialize comm port object. If a "port" is given, then the port will be 196 opened immediately. Otherwise a Serial port object in closed state 197 is returned. 198 """ 199 200 self.is_open = False 201 self.portstr = None 202 self.name = None 203 # correct values are assigned below through properties 204 self._port = None 205 self._baudrate = None 206 self._bytesize = None 207 self._parity = None 208 self._stopbits = None 209 self._timeout = None 210 self._write_timeout = None 211 self._xonxoff = None 212 self._rtscts = None 213 self._dsrdtr = None 214 self._inter_byte_timeout = None 215 self._rs485_mode = None # disabled by default 216 self._rts_state = True 217 self._dtr_state = True 218 self._break_state = False 219 self._exclusive = None 220 221 # assign values using get/set methods using the properties feature 222 self.port = port 223 self.baudrate = baudrate 224 self.bytesize = bytesize 225 self.parity = parity 226 self.stopbits = stopbits 227 self.timeout = timeout 228 self.write_timeout = write_timeout 229 self.xonxoff = xonxoff 230 self.rtscts = rtscts 231 self.dsrdtr = dsrdtr 232 self.inter_byte_timeout = inter_byte_timeout 233 self.exclusive = exclusive 234 235 # watch for backward compatible kwargs 236 if 'writeTimeout' in kwargs: 237 self.write_timeout = kwargs.pop('writeTimeout') 238 if 'interCharTimeout' in kwargs: 239 self.inter_byte_timeout = kwargs.pop('interCharTimeout') 240 if kwargs: 241 raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs)) 242 243 if port is not None: 244 self.open() 245 246 # - - - - - - - - - - - - - - - - - - - - - - - - 247 248 # to be implemented by subclasses: 249 # def open(self): 250 # def close(self): 251 252 # - - - - - - - - - - - - - - - - - - - - - - - - 253 254 @property 255 def port(self): 256 """\ 257 Get the current port setting. The value that was passed on init or using 258 setPort() is passed back. 259 """ 260 return self._port 261 262 @port.setter 263 def port(self, port): 264 """\ 265 Change the port. 266 """ 267 if port is not None and not isinstance(port, basestring): 268 raise ValueError('"port" must be None or a string, not {}'.format(type(port))) 269 was_open = self.is_open 270 if was_open: 271 self.close() 272 self.portstr = port 273 self._port = port 274 self.name = self.portstr 275 if was_open: 276 self.open() 277 278 @property 279 def baudrate(self): 280 """Get the current baud rate setting.""" 281 return self._baudrate 282 283 @baudrate.setter 284 def baudrate(self, baudrate): 285 """\ 286 Change baud rate. It raises a ValueError if the port is open and the 287 baud rate is not possible. If the port is closed, then the value is 288 accepted and the exception is raised when the port is opened. 289 """ 290 try: 291 b = int(baudrate) 292 except TypeError: 293 raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) 294 else: 295 if b < 0: 296 raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) 297 self._baudrate = b 298 if self.is_open: 299 self._reconfigure_port() 300 301 @property 302 def bytesize(self): 303 """Get the current byte size setting.""" 304 return self._bytesize 305 306 @bytesize.setter 307 def bytesize(self, bytesize): 308 """Change byte size.""" 309 if bytesize not in self.BYTESIZES: 310 raise ValueError("Not a valid byte size: {!r}".format(bytesize)) 311 self._bytesize = bytesize 312 if self.is_open: 313 self._reconfigure_port() 314 315 @property 316 def exclusive(self): 317 """Get the current exclusive access setting.""" 318 return self._exclusive 319 320 @exclusive.setter 321 def exclusive(self, exclusive): 322 """Change the exclusive access setting.""" 323 self._exclusive = exclusive 324 if self.is_open: 325 self._reconfigure_port() 326 327 @property 328 def parity(self): 329 """Get the current parity setting.""" 330 return self._parity 331 332 @parity.setter 333 def parity(self, parity): 334 """Change parity setting.""" 335 if parity not in self.PARITIES: 336 raise ValueError("Not a valid parity: {!r}".format(parity)) 337 self._parity = parity 338 if self.is_open: 339 self._reconfigure_port() 340 341 @property 342 def stopbits(self): 343 """Get the current stop bits setting.""" 344 return self._stopbits 345 346 @stopbits.setter 347 def stopbits(self, stopbits): 348 """Change stop bits size.""" 349 if stopbits not in self.STOPBITS: 350 raise ValueError("Not a valid stop bit size: {!r}".format(stopbits)) 351 self._stopbits = stopbits 352 if self.is_open: 353 self._reconfigure_port() 354 355 @property 356 def timeout(self): 357 """Get the current timeout setting.""" 358 return self._timeout 359 360 @timeout.setter 361 def timeout(self, timeout): 362 """Change timeout setting.""" 363 if timeout is not None: 364 try: 365 timeout + 1 # test if it's a number, will throw a TypeError if not... 366 except TypeError: 367 raise ValueError("Not a valid timeout: {!r}".format(timeout)) 368 if timeout < 0: 369 raise ValueError("Not a valid timeout: {!r}".format(timeout)) 370 self._timeout = timeout 371 if self.is_open: 372 self._reconfigure_port() 373 374 @property 375 def write_timeout(self): 376 """Get the current timeout setting.""" 377 return self._write_timeout 378 379 @write_timeout.setter 380 def write_timeout(self, timeout): 381 """Change timeout setting.""" 382 if timeout is not None: 383 if timeout < 0: 384 raise ValueError("Not a valid timeout: {!r}".format(timeout)) 385 try: 386 timeout + 1 # test if it's a number, will throw a TypeError if not... 387 except TypeError: 388 raise ValueError("Not a valid timeout: {!r}".format(timeout)) 389 390 self._write_timeout = timeout 391 if self.is_open: 392 self._reconfigure_port() 393 394 @property 395 def inter_byte_timeout(self): 396 """Get the current inter-character timeout setting.""" 397 return self._inter_byte_timeout 398 399 @inter_byte_timeout.setter 400 def inter_byte_timeout(self, ic_timeout): 401 """Change inter-byte timeout setting.""" 402 if ic_timeout is not None: 403 if ic_timeout < 0: 404 raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) 405 try: 406 ic_timeout + 1 # test if it's a number, will throw a TypeError if not... 407 except TypeError: 408 raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) 409 410 self._inter_byte_timeout = ic_timeout 411 if self.is_open: 412 self._reconfigure_port() 413 414 @property 415 def xonxoff(self): 416 """Get the current XON/XOFF setting.""" 417 return self._xonxoff 418 419 @xonxoff.setter 420 def xonxoff(self, xonxoff): 421 """Change XON/XOFF setting.""" 422 self._xonxoff = xonxoff 423 if self.is_open: 424 self._reconfigure_port() 425 426 @property 427 def rtscts(self): 428 """Get the current RTS/CTS flow control setting.""" 429 return self._rtscts 430 431 @rtscts.setter 432 def rtscts(self, rtscts): 433 """Change RTS/CTS flow control setting.""" 434 self._rtscts = rtscts 435 if self.is_open: 436 self._reconfigure_port() 437 438 @property 439 def dsrdtr(self): 440 """Get the current DSR/DTR flow control setting.""" 441 return self._dsrdtr 442 443 @dsrdtr.setter 444 def dsrdtr(self, dsrdtr=None): 445 """Change DsrDtr flow control setting.""" 446 if dsrdtr is None: 447 # if not set, keep backwards compatibility and follow rtscts setting 448 self._dsrdtr = self._rtscts 449 else: 450 # if defined independently, follow its value 451 self._dsrdtr = dsrdtr 452 if self.is_open: 453 self._reconfigure_port() 454 455 @property 456 def rts(self): 457 return self._rts_state 458 459 @rts.setter 460 def rts(self, value): 461 self._rts_state = value 462 if self.is_open: 463 self._update_rts_state() 464 465 @property 466 def dtr(self): 467 return self._dtr_state 468 469 @dtr.setter 470 def dtr(self, value): 471 self._dtr_state = value 472 if self.is_open: 473 self._update_dtr_state() 474 475 @property 476 def break_condition(self): 477 return self._break_state 478 479 @break_condition.setter 480 def break_condition(self, value): 481 self._break_state = value 482 if self.is_open: 483 self._update_break_state() 484 485 # - - - - - - - - - - - - - - - - - - - - - - - - 486 # functions useful for RS-485 adapters 487 488 @property 489 def rs485_mode(self): 490 """\ 491 Enable RS485 mode and apply new settings, set to None to disable. 492 See serial.rs485.RS485Settings for more info about the value. 493 """ 494 return self._rs485_mode 495 496 @rs485_mode.setter 497 def rs485_mode(self, rs485_settings): 498 self._rs485_mode = rs485_settings 499 if self.is_open: 500 self._reconfigure_port() 501 502 # - - - - - - - - - - - - - - - - - - - - - - - - 503 504 _SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff', 505 'dsrdtr', 'rtscts', 'timeout', 'write_timeout', 506 'inter_byte_timeout') 507 508 def get_settings(self): 509 """\ 510 Get current port settings as a dictionary. For use with 511 apply_settings(). 512 """ 513 return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS]) 514 515 def apply_settings(self, d): 516 """\ 517 Apply stored settings from a dictionary returned from 518 get_settings(). It's allowed to delete keys from the dictionary. These 519 values will simply left unchanged. 520 """ 521 for key in self._SAVED_SETTINGS: 522 if key in d and d[key] != getattr(self, '_' + key): # check against internal "_" value 523 setattr(self, key, d[key]) # set non "_" value to use properties write function 524 525 # - - - - - - - - - - - - - - - - - - - - - - - - 526 527 def __repr__(self): 528 """String representation of the current port settings and its state.""" 529 return '{name}<id=0x{id:x}, open={p.is_open}>(port={p.portstr!r}, ' \ 530 'baudrate={p.baudrate!r}, bytesize={p.bytesize!r}, parity={p.parity!r}, ' \ 531 'stopbits={p.stopbits!r}, timeout={p.timeout!r}, xonxoff={p.xonxoff!r}, ' \ 532 'rtscts={p.rtscts!r}, dsrdtr={p.dsrdtr!r})'.format( 533 name=self.__class__.__name__, id=id(self), p=self) 534 535 # - - - - - - - - - - - - - - - - - - - - - - - - 536 # compatibility with io library 537 # pylint: disable=invalid-name,missing-docstring 538 539 def readable(self): 540 return True 541 542 def writable(self): 543 return True 544 545 def seekable(self): 546 return False 547 548 def readinto(self, b): 549 data = self.read(len(b)) 550 n = len(data) 551 try: 552 b[:n] = data 553 except TypeError as err: 554 import array 555 if not isinstance(b, array.array): 556 raise err 557 b[:n] = array.array('b', data) 558 return n 559 560 # - - - - - - - - - - - - - - - - - - - - - - - - 561 # context manager 562 563 def __enter__(self): 564 if self._port is not None and not self.is_open: 565 self.open() 566 return self 567 568 def __exit__(self, *args, **kwargs): 569 self.close() 570 571 # - - - - - - - - - - - - - - - - - - - - - - - - 572 573 def send_break(self, duration=0.25): 574 """\ 575 Send break condition. Timed, returns to idle state after given 576 duration. 577 """ 578 if not self.is_open: 579 raise PortNotOpenError() 580 self.break_condition = True 581 time.sleep(duration) 582 self.break_condition = False 583 584 # - - - - - - - - - - - - - - - - - - - - - - - - 585 # backwards compatibility / deprecated functions 586 587 def flushInput(self): 588 self.reset_input_buffer() 589 590 def flushOutput(self): 591 self.reset_output_buffer() 592 593 def inWaiting(self): 594 return self.in_waiting 595 596 def sendBreak(self, duration=0.25): 597 self.send_break(duration) 598 599 def setRTS(self, value=1): 600 self.rts = value 601 602 def setDTR(self, value=1): 603 self.dtr = value 604 605 def getCTS(self): 606 return self.cts 607 608 def getDSR(self): 609 return self.dsr 610 611 def getRI(self): 612 return self.ri 613 614 def getCD(self): 615 return self.cd 616 617 def setPort(self, port): 618 self.port = port 619 620 @property 621 def writeTimeout(self): 622 return self.write_timeout 623 624 @writeTimeout.setter 625 def writeTimeout(self, timeout): 626 self.write_timeout = timeout 627 628 @property 629 def interCharTimeout(self): 630 return self.inter_byte_timeout 631 632 @interCharTimeout.setter 633 def interCharTimeout(self, interCharTimeout): 634 self.inter_byte_timeout = interCharTimeout 635 636 def getSettingsDict(self): 637 return self.get_settings() 638 639 def applySettingsDict(self, d): 640 self.apply_settings(d) 641 642 def isOpen(self): 643 return self.is_open 644 645 # - - - - - - - - - - - - - - - - - - - - - - - - 646 # additional functionality 647 648 def read_all(self): 649 """\ 650 Read all bytes currently available in the buffer of the OS. 651 """ 652 return self.read(self.in_waiting) 653 654 def read_until(self, expected=LF, size=None): 655 """\ 656 Read until an expected sequence is found (line feed by default), the size 657 is exceeded or until timeout occurs. 658 """ 659 lenterm = len(expected) 660 line = bytearray() 661 timeout = Timeout(self._timeout) 662 while True: 663 c = self.read(1) 664 if c: 665 line += c 666 if line[-lenterm:] == expected: 667 break 668 if size is not None and len(line) >= size: 669 break 670 else: 671 break 672 if timeout.expired(): 673 break 674 return bytes(line) 675 676 def iread_until(self, *args, **kwargs): 677 """\ 678 Read lines, implemented as generator. It will raise StopIteration on 679 timeout (empty read). 680 """ 681 while True: 682 line = self.read_until(*args, **kwargs) 683 if not line: 684 break 685 yield line 686 687 688# - - - - - - - - - - - - - - - - - - - - - - - - - 689if __name__ == '__main__': 690 import sys 691 s = SerialBase() 692 sys.stdout.write('port name: {}\n'.format(s.name)) 693 sys.stdout.write('baud rates: {}\n'.format(s.BAUDRATES)) 694 sys.stdout.write('byte sizes: {}\n'.format(s.BYTESIZES)) 695 sys.stdout.write('parities: {}\n'.format(s.PARITIES)) 696 sys.stdout.write('stop bits: {}\n'.format(s.STOPBITS)) 697 sys.stdout.write('{}\n'.format(s)) 698