pyserial-3.4/0000775000175000017500000000000013134735350012554 5ustar lchlch00000000000000pyserial-3.4/setup.py0000664000175000017500000000566513064052733014301 0ustar lchlch00000000000000# setup.py for pySerial # # Direct install (all systems): # "python setup.py install" # # For Python 3.x use the corresponding Python executable, # e.g. "python3 setup.py ..." # # (C) 2001-2017 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause import io import os import re try: from setuptools import setup except ImportError: from distutils.core import setup def read(*names, **kwargs): """Python 2 and Python 3 compatible text file reading. Required for single-sourcing the version string. """ with io.open( os.path.join(os.path.dirname(__file__), *names), encoding=kwargs.get("encoding", "utf8") ) as fp: return fp.read() def find_version(*file_paths): """ Search the file for a version string. file_path contain string path components. Reads the supplied Python module as text without importing it. """ version_file = read(*file_paths) version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.") version = find_version('serial', '__init__.py') setup( name="pyserial", description="Python Serial Port Extension", version=version, author="Chris Liechti", author_email="cliechti@gmx.net", url="https://github.com/pyserial/pyserial", packages=['serial', 'serial.tools', 'serial.urlhandler', 'serial.threaded'], license="BSD", long_description="""\ Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython Stable: - Documentation: http://pythonhosted.org/pyserial/ - Download Page: https://pypi.python.org/pypi/pyserial Latest: - Documentation: http://pyserial.readthedocs.io/en/latest/ - Project Homepage: https://github.com/pyserial/pyserial """, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Intended Audience :: End Users/Desktop', 'License :: OSI Approved :: BSD License', 'Natural Language :: English', 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Operating System :: MacOS :: MacOS X', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Topic :: Communications', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Terminals :: Serial', ], platforms='any', scripts=['serial/tools/miniterm.py'], ) pyserial-3.4/CHANGES.rst0000664000175000017500000006027413134734666014400 0ustar lchlch00000000000000======================== pySerial Release Notes ======================== Version 1.0 13 Feb 2002 --------------------------- - First public release. - Split from the pybsl application (see http://mspgcc.sourceforge.net) New Features: - Added Jython support Version 1.1 14 Feb 2002 --------------------------- Bugfixes: - Win32, when not specifying a timeout - Typos in the Docs New Features: - added ``serialutil`` which provides a base class for the ``Serial`` objects. - ``readline``, ``readlines``, ``writelines`` and ``flush`` are now supported see README.txt for deatils. Version 1.11 14 Feb 2002 --------------------------- Same as 1.1 but added missing files. Version 1.12 18 Feb 2002 --------------------------- Removed unneded constants to fix RH7.x problems. Version 1.13 09 Apr 2002 --------------------------- Added alternate way for enabling rtscts (CNEW_RTSCTS is tried too) If port opening fails, a ``SerialException`` is raised on all platforms Version 1.14 29 May 2002 --------------------------- Added examples to archive Added non-blocking mode for ``timeout=0`` (tnx Mat Martineau) Bugfixes: - win32 does now return the remaining characters on timeout Version 1.15 04 Jun 2002 --------------------------- Bugfixes (win32): - removed debug messages - compatibility to win9x improved Version 1.16 02 Jul 2002 --------------------------- Added implementation of RI and corrected RTS/CTS on Win32 Version 1.17 03 Jul 2002 --------------------------- Silly mix of two versions in win32 code corrected Version 1.18 06 Dec 2002 --------------------------- Bugfixes (general): - remove the mapping of flush to the destructive flushOutput as this is not the expected behaviour. - readline: EOL character for lines can be chosen idea by John Florian. Bugfixes (posix): - cygwin port numbering fixed - test each and every constant for it's existence in termios module, use default if not existent (fix for Bug item #640214) - wrong exception on nonexistent ports with /dev file. bug report by Louis Cordier Bugfixes (win32): - RTS/CTS handling as suggested in Bug #635072 - bugfix of timeouts brought up by Markus Hoffrogge Version 1.19 19 Mar 2003 --------------------------- Bugfixes (posix): - removed ``dgux`` entry which actually had a wrong comment and is probably not in use anywhere. Bugfixes (win32): - added ``int()`` conversion, [Bug 702120] - remove code to set control lines in close method of win32 version. [Bug 669625] Version 1.20 28 Aug 2003 --------------------------- - Added ``serial.device()`` for all platforms Bugfixes (win32): - don't recreate overlapped structures and events on each read/write. - don't set unneeded event masks. - don't use DOS device names for ports > 9. - remove send timeout (it's not used in the linux impl. anyway). Version 1.21 30 Sep 2003 --------------------------- Bugfixes (win32): - name for COM10 was not built correctly, found by Norm Davis. Bugfixes (examples): - small change in ``miniterm.py`` that should mage it run on cygwin, [Bug 809904] submitted by Rolf Campbell. Version 2.0b1 1 Oct 2003 --------------------------- Transition to the Python 2.0 series: - New implementation only supports Python 2.2+, backwards compatibility should be maintained almost everywhere. The OS handles (like the ``hComPort`` or ``fd`` attribute) were prefixed with an underscore. The different names stay, as anyone that uses one of these has to write platform specific code anyway. - Common base class ``serialutil.SerialBase`` for all implementations. - ``PARITY_NONE``, ``PARITY_EVEN``, ``PARITY_ODD`` constants changed and all these constants moved to ``serialutil.py`` (still available as ``serial.PARITY_NONE`` etc. and they should be used that way) - Added ``serial.PARITY_NAMES`` (implemented in ``serialutil.PARITY_NAMES``). This dictionary can be used to convert parity constants to meaningful strings. - Each Serial class and instance has a list of supported values: ``BAUDRATES``, ``BYTESIZES``, ``PARITIES``, ``STOPBITS``Ggg (i.e. ``serial.Serial.BAUDRATES or s = serial.Serial; s.BAUDRATES``) these values can be used to fill in value sin GUI dialogs etc. - Creating a ``Serial()`` object without port spec returns an unconfigured, closed port. Useful if a GUI dialog should take a port and configure it. - New methods for ``serial.Serial`` instances: ``open()``, ``isOpen()`` - A port can be opened and closed as many times as desired. - Instances of ``serial.Serial`` have ``baudrate``, ``bytesize``, ``timeout`` etc. attributes implemented as properties, all can be set while the port is opened. It will then be reconfigured. - Improved ``__doc__``'s. - New ``test_advanced.py`` for the property setting/getting testing. - Small bugfix on posix with get* methods (return value should be true a boolean). - added a ``__repr__`` that returns a meaningful string will all the serial setting, easy for debugging. - The serialposix module does not throw an exception on unsupported platforms, the message is still printed. The idea that it may still work even if the platform itself s not known, it simply tries to do the posix stuff anyway (It's likely that opening ports by number fails, but by name it should work). Version 2.0b2 4 Oct 2003 --------------------------- - Added serial port configuration dialog for wxPython to the examples. - Added terminal application for wxPython with wxGlade design file to the examples. - Jython support is currently broken as Jython does not have a Python 2.2 compatible release out yet Version 2.0 6 Nov 2003 --------------------------- - Fixes ``setup.py`` for older distutils Version 2.1 28 Jul 2004 --------------------------- Bugfixes: - Fix XON/XOFF values [Bug 975250] Bugfixes (posix): - ``fd == 0`` fix from Vsevolod Lobko - netbsd fixes from Erik Lindgren - Dynamically lookup baudrates and some cleanups Bugfixes (examples): - CRLF handling of ``miniterm.py`` should be more consistent on Win32 and others. Added LF only command line option - Multithreading fixes to ``wxTerminal.py`` (helps with wxGTK) - Small change for wxPython 2.5 in ``wxSerialConfigDialog.py`` [Bug 994856] New Features: - Implement write timeouts (``writeTimeout`` parameter) Version 2.2 31 Jul 2005 --------------------------- Bugfixes: - [Bug 1014227]: property broken - [Bug 1105687]: ``serial_tcp_example.py``: ``--localport`` option - [Bug 1106313]: device (port) strings cannot be unicode Bugfixes (posix): - [Patch 1043436] Fix for [Bug 1043420] (OSError: EAGAIN) - [Patch 1102700] ``fileno()`` added - ensure disabled PARMRK Bugfixes (win32): - [Patch 983106]: keep RTS/CTS state on port setting changes New Features: - ``dsrdtr`` setting to enable/disable DSR/DTR flow control independently from the ``rtscts`` setting. (Currently Win32 only, ignored on other platforms) Version 2.3 19 Jun 2008 --------------------------- New Features: - iterator interface. ``for line in Serial(...): ...`` is now possible Suggested by Bernhard Bender - ``sendBreak()`` accepts a ``duration`` argument. Default duration increased. - win32 handles \\.\COMx format automatically for com ports of higher number (COM10 is internally translated to \\.\COM10 etc.) - miniterm.py has a new feature to send a file (upload) and configurable special characters for exit and upload. Refactored internals to class based structure (upload and class refactoring by Colin D Bennett) Bugfixes: - [Bug 1451535] TCP/serial redirect example "--help" - update VERSION variable - update wxSerialConfigDialog.py and wxTerminal.py compatibility with wxPython 2.8 (Peleg) - Check for string in write function. Using unicode causes errors, this helps catching errors early (Tom Lynn) Bugfixes (posix): - [Bug 1554183] setRTS/setDTR reference to non existing local "on" - [Bug 1513653] file descriptor not closed when exception is thrown - FreeBSD now uses cuadX instead of cuaaX (Patrick Phalen) Bugfixes (win32): - [Bug 1520357] Handle leak - [Bug 1679013] Ignore exception raised by SetCommTimeout() in close(). - [Bug 1938118] process hang forever under XP Version 2.4 6 Jul 2008 --------------------------- New Features: - [Patch 1616790] pyserial: Add inter-character timeout feature - [Patch 1924805] add a setBreak function - Add mark/space parity - Add .NET/Mono backend (IronPython) Bugfixes (posix): - [Bug 1783159] Arbitrary baud rates (Linux/Posix) Bugfixes (win32): - [Patch 1561423] Add mark/space parity, Win32 - [Bug 2000771] serial port CANNOT be specified by number on windows - examples/scanwin32.py does no longer return \\.\ names - fix \\.\ handling for some cases Bugfixes (jython): - The Jython backend tries javax.comm and gnu.io (Seo Sanghyeon) Version 2.5-rc1 2009-07-30 --------------------------- New Features: - Python 3.x support (through 2to3) - compatible with Python io library (Python 2.6+) - Support for Win32 is now written on the top of ctypes (bundled with Python 2.5+) instead of pywin32 (patch by Giovanni Bajo). - 1.5 stop bits (STOPBITS_ONE_POINT_FIVE, implemented on all platforms) - miniterm application extended (CTRL+T -> menu) - miniterm.py is now installed as "script" - add scanlinux.py example - add port_publisher example - experimental RFC-2217 server support (examples/rfc2217_server.py) - add ``getSettingsDict`` and ``applySettingsDict`` serial object methods - use a ``poll`` based implementation on Posix, instead of a ``select`` based, provides better error handling [removed again in later releases]. Bugfixes: - Improve and fix tcp_serial_redirector example. - [Bug 2603052] 5-bit mode (needs 1.5 stop bits in some cases) Bugfixes (posix): - [Bug 2810169] Propagate exceptions raised in serialposix _reconfigure - [Bug 2562610] setting non standard baud rates on Darwin (Emmanuel Blot) Bugfixes (win32): - [Bug 2469098] parity PARITY_MARK, PARITY_SPACE isn't supported on win32 - [SF 2446218] outWaiting implemented - [Bug 2392892] scanwin32.py better exception handling - [Bug 2505422] scanwin32.py Vista 64bit compatibility Version 2.5-rc2 2010-01-02 --------------------------- New Features: - Documentation update, now written with Sphinx/ReST - Updated miniterm.py example - experimental RFC-2217 client support (serial.rfc2217.Serial, see docs) - add ``loop://`` device for testing. - add ``serial.serial_for_url`` factory function (support for native ports and ``rfc2217``, ``socket`` and ``loop`` URLs) - add new example: ``rfc2217_server.py`` - tests live in their own directory now (no longer in examples) Bugfixes: - [Bug 2915810] Fix for suboption parsing in rfc2217 - Packaging bug (missed some files) Bugfixes (posix): - improve write timeout behavior - [Bug 2836297] move Linux specific constants to not break other platforms - ``poll`` based implementation for ``read`` is in a separate class ``PosixPollSerial``, as it is not supported well on all platforms (the default ``Serial`` class uses select). - changed error handling in ``read`` so that disconnected devices are detected. Bugfixes (win32): - [Bug 2886763] hComPort doesn't get initialized for Serial(port=None) Version 2.5 2010-07-22 --------------------------- New Features: - [Bug 2976262] dsrdtr should default to False ``dsrdtr`` parameter default value changed from ``None`` (follow ``rtscts`` setting) to ``False``. This means ``rtscts=True`` enables hardware flow control on RTS/CTS but no longer also on DTR/DSR. This change mostly affects Win32 as on other platforms, that setting was ignored anyway. - Improved xreadlines, it is now a generator function that yields lines as they are received (previously it called readlines which would only return all lines read after a read-timeout). However xreadlines is deprecated and not available when the io module is used. Use ``for line in Serial(...):`` instead. Bugfixes: - [Bug 2925854] test.py produces exception with python 3.1 - [Bug 3029812] 2.5rc2 readline(s) doesn't work Bugfixes (posix): - [BUG 3006606] Nonblocking error - Unix platform Bugfixes (win32): - [Bug 2998169] Memory corruption at faster transmission speeds. (bug introduced in 2.5-rc1) Version 2.6 2011-11-02 --------------------------- New Features: - Moved some of the examples to serial.tools so that they can be used with ``python -m`` - serial port enumeration now included as ``serial.tools.list_ports`` - URL handlers for ``serial_for_url`` are now imported dynamically. This allows to add protocols w/o editing files. The list ``serial.protocol_handler_packages`` can be used to add or remove user packages with protocol handlers (see docs for details). - new URL type: hwgrep:// uses list_ports module to search for ports by their description - several internal changes to improve Python 3.x compatibility (setup.py, use of absolute imports and more) Bugfixes: - [Bug 3093882] calling open() on an already open port now raises an exception - [Bug 3245627] connection-lost let rfc2217 hangs in closed loop - [Patch 3147043] readlines() to support multi-character eol Bugfixes (posix): - [Patch 3316943] Avoid unneeded termios.tcsetattr calls in serialposix.py - [Patch 2912349] Serial Scan as a Module with Mac Support Bugfixes (win32): - [Bug 3057499] writeTimeoutError when write Timeout is 0 - [Bug 3414327] Character out of range in list_ports_windows - [Patch 3036175] Windows 98 Support fix - [Patch 3054352] RTS automatic toggle, for RS485 functionality. - Fix type definitions for 64 bit Windows compatibility Version 2.7 2013-10-17 --------------------------- - Win32: setRTS and setDTR can be called before the port is opened and it will set the initial state on port open. - Posix: add platform specific method: outWaiting (already present for Win32) - Posix: rename flowControl to setXON to match name on Win32, add flowControlOut function - rfc2217: zero polls value (baudrate, data size, stop bits, parity) (Erik Lundh) - Posix: [Patch pyserial:28] Accept any speed on Linux [update] - Posix: [Patch pyserial:29] PosixSerial.read() should "ignore" errno.EINTR - OSX: [Patch pyserial:27] Scan by VendorID/Product ID for USB Serial devices - Ensure working with bytes in write() calls Bugfixes: - [Bug 3540332] SerialException not returned - [Bug pyserial:145] Error in socket_connection.py - [Bug pyserial:135] reading from socket with timeout=None causes TypeError - [Bug pyserial:130] setup.py should not append py3k to package name - [Bug pyserial:117] no error on lost conn w/socket:// Bugfixes (posix): - [Patch 3462364] Fix: NameError: global name 'base' is not defined - list_ports and device() for BSD updated (Anders Langworthy) - [Bug 3518380] python3.2 -m serial.tools.list_ports error - [Bug pyserial:137] Patch to add non-standard baudrates to Cygwin - [Bug pyserial:141] open: Pass errno from IOError to SerialException - [Bug pyserial:125] Undefined 'base' on list_ports_posix.py, function usb_lsusb - [Bug pyserial:151] Serial.write() without a timeout uses 100% CPU on POSIX - [Patch pyserial:30] [PATCH 1/1] serial.Serial() should not raise IOError. Bugfixes (win32): - [Bug 3444941] ctypes.WinError() unicode error - [Bug 3550043] on Windows in tools global name 'GetLastError' is not defined - [Bug pyserial:146] flush() does nothing in windows (despite docs) - [Bug pyserial:144] com0com ports ignored due to missing "friendly name" - [Bug pyserial:152] Cannot configure port, some setting was wrong. Can leave port handle open but port not accessible Version 3.0a0 2015-09-22 -------------------------- - Starting from this release, only Python 2.7 and 3.2 (or newer) are supported. The source code is compatible to the 2.x and 3.x series without any changes. The support for earlier Python versions than 2.7 is removed, please refer to the pyserial-legacy (V2.x) series if older Python versions are a requirement). - Development moved to github, update links in docs. - API changes: properties for ``rts``, ``dtr``, ``cts``, ``dsr``, ``cd``, ``ri``, ``in_waiting`` (instead of get/set functions) - remove file ``FileLike`` class, add ``read_until`` and ``iread_until`` to ``SerialBase`` - RS485 support changed (``rts_toggle`` removed, added ``serial.rs485`` module and ``rs485_mode`` property) - ``socket://`` and ``rfc2217://`` handlers use the IPv6 compatible ``socket.create_connection`` - New URL handler: ``spy:://``. - URL handlers now require the proper format (``?`` and ``&``) for arguments instead of ``/`` (e.g. ``rfc2217://localhost:7000?ign_set_control&timeout=5.5``) - Remove obsolete examples. - Finish update to BSD license. - Use setuptools if available, fall back to distutils if unavailable. - miniterm: changed command line options - miniterm: support encodings on serial port - miniterm: new transformations, by default escape/convert all control characters - list_ports: improved, added USB location (Linux, Win32) - refactored code - [FTR pyserial:37] Support fileno() function in the socket protocol - Posix: [Patch pyserial:31] Mark/space parity on Linux - Linux: [Patch pyserial:32] Module list_ports for linux should include the product information as description. - Java: fix 2 bugs (stop bits if/else and non-integer timeouts) (Torsten Roemer) - Update wxSerialConfigDialog.py to use serial.tools.list_ports. - [Patch pyserial:34] Improvements to port_publisher.py example - [Feature pyserial:39] Support BlueTooth serial port discovery on Linux Bugfixes: - [Bug pyserial:157] Implement inWaiting in protocol_socket - [Bug pyserial:166] RFC2217 connections always fail - [Bug pyserial:172] applySettingsDict() throws an error if the settings dictionary is not complete - [Bug pyserial:185] SocketSerial.read() never returns data when timeout==0 Bugfixes (posix): - [Bug pyserial:156] PosixSerial.open raises OSError rather than SerialException when port open fails - [Bug pyserial:163] serial.tools.list_ports.grep() fails if it encounters None type - fix setXON - [Patch pyserial:36 / 38] Make USB information work in python 3.4 and 2.7 - clear OCRNL/ONLCR flags (CR/LF translation settings) - [Feature pyserial:38] RS485 Support - [Bug pyserial:170] list_ports_posix not working properly for Cygwin - [Bug pyserial:187] improve support for FreeBSD (list_ports_posix) Bugfixes (win32): - [Bug pyserial:169] missing "import time" in serialwin32.py Bugfixes (cli): - [Bug pyserial:159] write() in serialcli.py not working with IronPython 2.7.4 Version 3.0b1 2015-10-19 -------------------------- - list_ports: add ``vid``, ``pid``, ``serial_number``, ``product``, ``manufacturer`` and ``location`` attribute for USB devices. - list_ports: update OSX implementation. - list_ports: Raspberry Pi: internal port is found. - serial_for_url: fix import (multiple packages in list) - threaded: added new module implementing a reader thread - tweak examples/wx* - posix: add experimental implementation ``VTIMESerial`` - new URL handler ``alt://`` to select alternative implementations Version 3.0 2015-12-28 ------------------------ - minor fixes to setup.py (file list), inter_byte_timeout (not stored when passed to __init__), rfc2217 (behavior of close when open failed), list_ports (__str__), loop://, renamed ReaderThread - hwgrep:// added options to pick n'th port, skip busy ports - miniterm: --ask option added Bugfixes (posix): - [#26/#30] always call tcsettattr on open - [#42] fix disregard read timeout if there is more data - [#45] check for write timeout, even if EAGAIN was raised Bugfixes (win32): - [#27] fix race condition in ``read()``, fix minimal timeout issue - race condition in nonblocking case - [#49] change exception type in case SetCommState fails - [#50] fixed issue with 0 timeout on windows 10 Version 3.0.1 2016-01-11 -------------------------- - special case for FDTIBUS in list_ports on win32 (#61) Bugfixes: - ``Serial`` keyword arguments, more on backward compatibility, fix #55 - list_ports: return name if product is None, fix for #54 - port_publisher: restore some sorting of ports Version 3.1.0 2016-05-27 -------------------------- Improvements: - improve error handling in ``alt://`` handler - ``socket://`` internally used select, improves timeout behavior - initial state of RTS/DTR: ignore error when setting on open posix (support connecting to pty's) - code style updates - posix: remove "number_to_device" which is not called anymore - add cancel_read and cancel_write to win32 and posix implementations Bugfixes: - [#68] aio: catch errors and close connection - [#87] hexlify: update codec for Python 2 - [#100] setPort not implemented - [#101] bug in serial.threaded.Packetizer with easy fix - [#104] rfc2217 and socket: set timeout in create_connection - [#107] miniterm.py fails to exit on failed serial port Bugfixes (posix): - [#59] fixes for RTS/DTR handling on open - [#77] list_ports_osx: add missing import - [#85] serialposix.py _set_rs485_mode() tries to read non-existing rs485_settings.delay_rts_before_send - [#96] patch: native RS485 is never enabled Bugfixes (win32): - fix bad super call and duplicate old-style __init__ call - [#80] list_ports: Compatibility issue between Windows/Linux Version 3.1.1 2016-06-12 -------------------------- Improvements: - deprecate ``nonblocking()`` method on posix, the port is already in this mode. - style: use .format() in various places instead of "%" formatting Bugfixes: - [#122] fix bug in FramedPacket - [#127] The Serial class in the .NET/Mono (IronPython) backend does not implement the _reconfigure_port method - [#123, #128] Avoid Python 3 syntax in aio module Bugfixes (posix): - [#126] PATCH: Check delay_before_tx/rx for None in serialposix.py - posix: retry if interrupted in Serial.read Bugfixes (win32): - win32: handle errors of GetOverlappedResult in read(), fixes #121 Version 3.2.0 2016-10-14 -------------------------- See 3.2.1, this one missed a merge request related to removing aio. Version 3.2.1 2016-10-14 -------------------------- Improvements: - remove ``serial.aio`` in favor of separate package, ``pyserial-asyncio`` - add client mode to example ``tcp_serial_redirect.py`` - use of monotonic clock for timeouts, when available (Python 3.3 and up) - [#169] arbitrary baud rate support for BSD family - improve tests, improve ``loop://`` Bugfixes: - [#137] Exception while cancel in miniterm (python3) - [#143] Class Serial in protocol_loop.py references variable before assigning to it - [#149] Python 3 fix for threaded.FramedPacket Bugfixes (posix): - [#133] _update_dtr_state throws Inappropriate ioctl for virtual serial port created by socat on OS X - [#157] Broken handling of CMSPAR in serialposix.py Bugfixes (win32): - [#144] Use Unicode API for list_ports - [#145] list_ports_windows: support devices with only VID - [#162] Write in non-blocking mode returns incorrect value on windows Version 3.3 2017-03-08 ------------------------ Improvements: - [#206] Exclusive access on POSIX. ``exclusive`` flag added. - [#172] list_ports_windows: list_ports with 'manufacturer' info property - [#174] miniterm: change cancel impl. for console - [#182] serialutil: add overall timeout for read_until - socket: use non-blocking socket and new Timeout class - socket: implement a functional a reset_input_buffer - rfc2217: improve read timeout implementation - win32: include error message from system in ClearCommError exception - and a few minor changes, docs Bugfixes: - [#183] rfc2217: Fix broken calls to to_bytes on Python3. - [#188] rfc2217: fix auto-open use case when port is given as parameter Bugfixes (posix): - [#178] in read, count length of converted data - [#189] fix return value of write Bugfixes (win32): - [#194] spurious write fails with ERROR_SUCCESS Version 3.4 2017-07-22 ------------------------ Improvements: - miniterm: suspend function (temporarily release port, :kbd:`Ctrl-T s`) - [#240] context manager automatically opens port on ``__enter__`` - [#141] list_ports: add interface number to location string - [#225] protocol_socket: Retry if ``BlockingIOError`` occurs in ``reset_input_buffer``. Bugfixes: - [#153] list_ports: option to include symlinked devices - [#237] list_ports: workaround for special characters in port names Bugfixes (posix): - allow calling cancel functions w/o error if port is closed - [#220] protocol_socket: sync error handling with posix version - [#227] posix: ignore more blocking errors and EINTR, timeout only applies to blocking I/O - [#228] fix: port_publisher typo pyserial-3.4/test/0000775000175000017500000000000013134735350013533 5ustar lchlch00000000000000pyserial-3.4/test/test_cancel.py0000664000175000017500000000666412775054046016413 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2016 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """ Test cancel functionality. """ import sys import unittest import threading import time import serial # on which port should the tests be performed: PORT = 'loop://' @unittest.skipIf(not hasattr(serial.Serial, 'cancel_read'), "cancel_read not supported on platform") class TestCancelRead(unittest.TestCase): """Test cancel_read functionality""" def setUp(self): # create a closed serial port self.s = serial.serial_for_url(PORT) self.assertTrue(hasattr(self.s, 'cancel_read'), "serial instance has no cancel_read") self.s.timeout = 10 self.cancel_called = 0 def tearDown(self): self.s.reset_output_buffer() self.s.close() def _cancel(self, num_times): for i in range(num_times): #~ print "cancel" self.cancel_called += 1 self.s.cancel_read() def test_cancel_once(self): """Cancel read""" threading.Timer(1, self._cancel, ((1,))).start() t1 = time.time() self.s.read(1000) t2 = time.time() self.assertEqual(self.cancel_called, 1) self.assertTrue(0.5 < (t2 - t1) < 2.5, 'Function did not return in time: {}'.format(t2 - t1)) #~ self.assertTrue(not self.s.isOpen()) #~ self.assertRaises(serial.SerialException, self.s.open) #~ def test_cancel_before_read(self): #~ self.s.cancel_read() #~ self.s.read() DATA = b'#' * 1024 @unittest.skipIf(not hasattr(serial.Serial, 'cancel_write'), "cancel_read not supported on platform") class TestCancelWrite(unittest.TestCase): """Test cancel_write functionality""" def setUp(self): # create a closed serial port self.s = serial.serial_for_url(PORT, baudrate=300) # extra slow ~30B/s => 1kb ~ 34s self.assertTrue(hasattr(self.s, 'cancel_write'), "serial instance has no cancel_write") self.s.write_timeout = 10 self.cancel_called = 0 def tearDown(self): self.s.reset_output_buffer() # not all USB-Serial adapters will actually flush the output (maybe # keeping the buffer in the MCU in the adapter) therefore, speed up by # changing the baudrate self.s.baudrate = 115200 self.s.flush() self.s.close() def _cancel(self, num_times): for i in range(num_times): self.cancel_called += 1 self.s.cancel_write() def test_cancel_once(self): """Cancel write""" threading.Timer(1, self._cancel, ((1,))).start() t1 = time.time() self.s.write(DATA) t2 = time.time() self.assertEqual(self.cancel_called, 1) self.assertTrue(0.5 < (t2 - t1) < 2.5, 'Function did not return in time: {}'.format(t2 - t1)) #~ self.assertTrue(not self.s.isOpen()) #~ self.assertRaises(serial.SerialException, self.s.open) #~ def test_cancel_before_write(self): #~ self.s.cancel_write() #~ self.s.write(DATA) #~ self.s.reset_output_buffer() if __name__ == '__main__': sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_context.py0000775000175000017500000000232613131763232016633 0ustar lchlch00000000000000#! /usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2017 Guillaume Galeazzi # # SPDX-License-Identifier: BSD-3-Clause """\ Some tests for the serial module. Part of pySerial (http://pyserial.sf.net) (C)2001-2011 cliechti@gmx.net Intended to be run on different platforms, to ensure portability of the code. Cover some of the aspects of context managment """ import unittest import serial # on which port should the tests be performed: PORT = 'loop://' class Test_Context(unittest.TestCase): """Test context""" def setUp(self): # create a closed serial port self.s = serial.serial_for_url(PORT) def tearDown(self): self.s.close() def test_with_idempotent(self): with self.s as stream: stream.write(b'1234') # do other stuff like calling an exe which use COM4 with self.s as stream: stream.write(b'5678') if __name__ == '__main__': import sys sys.stdout.write(__doc__) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_util.py0000664000175000017500000000217412775055557016143 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2016 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Tests for utility functions of serualutil. """ import os import unittest import serial class Test_util(unittest.TestCase): """Test serial utility functions""" def test_to_bytes(self): self.assertEqual(serial.to_bytes([1, 2, 3]), b'\x01\x02\x03') self.assertEqual(serial.to_bytes(b'\x01\x02\x03'), b'\x01\x02\x03') self.assertEqual(serial.to_bytes(bytearray([1,2,3])), b'\x01\x02\x03') # unicode is not supported test. use decode() instead of u'' syntax to be # compatible to Python 3.x < 3.4 self.assertRaises(TypeError, serial.to_bytes, b'hello'.decode('utf-8')) def test_iterbytes(self): self.assertEqual(list(serial.iterbytes(b'\x01\x02\x03')), [b'\x01', b'\x02', b'\x03']) if __name__ == '__main__': import sys sys.stdout.write(__doc__) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test.py0000664000175000017500000001740612732326347015101 0ustar lchlch00000000000000#! /usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Some tests for the serial module. Part of pyserial (http://pyserial.sf.net) (C)2001-2015 cliechti@gmx.net Intended to be run on different platforms, to ensure portability of the code. For all these tests a simple hardware is required. Loopback HW adapter: Shortcut these pin pairs: TX <-> RX RTS <-> CTS DTR <-> DSR On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8) """ import unittest import threading import time import sys import serial # on which port should the tests be performed: PORT = 'loop://' # indirection via bytearray b/c bytes(range(256)) does something else in Pyhton 2.7 bytes_0to255 = bytes(bytearray(range(256))) def segments(data, size=16): for a in range(0, len(data), size): yield data[a:a + size] class Test4_Nonblocking(unittest.TestCase): """Test with timeouts""" timeout = 0 def setUp(self): self.s = serial.serial_for_url(PORT, timeout=self.timeout) def tearDown(self): self.s.close() def test0_Messy(self): """NonBlocking (timeout=0)""" # this is only here to write out the message in verbose mode # because Test3 and Test4 print the same messages def test1_ReadEmpty(self): """timeout: After port open, the input buffer must be empty""" self.assertEqual(self.s.read(1), b'', "expected empty buffer") def test2_Loopback(self): """timeout: each sent character should return (binary test). this is also a test for the binary capability of a port.""" for block in segments(bytes_0to255): length = len(block) self.s.write(block) # there might be a small delay until the character is ready (especially on win32) time.sleep(0.05) self.assertEqual(self.s.in_waiting, length, "expected exactly {} character for inWainting()".format(length)) self.assertEqual(self.s.read(length), block) #, "expected a %r which was written before" % block) self.assertEqual(self.s.read(1), b'', "expected empty buffer after all sent chars are read") def test2_LoopbackTimeout(self): """timeout: test the timeout/immediate return. partial results should be returned.""" self.s.write(b"HELLO") time.sleep(0.1) # there might be a small delay until the character is ready (especially on win32 and rfc2217) # read more characters as are available to run in the timeout self.assertEqual(self.s.read(10), b'HELLO', "expected the 'HELLO' which was written before") self.assertEqual(self.s.read(1), b'', "expected empty buffer after all sent chars are read") class Test3_Timeout(Test4_Nonblocking): """Same tests as the NonBlocking ones but this time with timeout""" timeout = 1 def test0_Messy(self): """Blocking (timeout=1)""" # this is only here to write out the message in verbose mode # because Test3 and Test4 print the same messages class SendEvent(threading.Thread): def __init__(self, serial, delay=3): threading.Thread.__init__(self) self.serial = serial self.delay = delay self.x = threading.Event() self.stopped = 0 self.start() def run(self): time.sleep(self.delay) self.x.set() if not self.stopped: self.serial.write(b"E") self.serial.flush() def isSet(self): return self.x.isSet() def stop(self): self.stopped = 1 self.x.wait() class Test1_Forever(unittest.TestCase): """Tests a port with no timeout. These tests require that a character is sent after some time to stop the test, this is done through the SendEvent class and the Loopback HW.""" def setUp(self): self.s = serial.serial_for_url(PORT, timeout=None) self.event = SendEvent(self.s) def tearDown(self): self.event.stop() self.s.close() def test2_ReadEmpty(self): """no timeout: after port open, the input buffer must be empty (read). a character is sent after some time to terminate the test (SendEvent).""" c = self.s.read(1) if not (self.event.isSet() and c == b'E'): self.fail("expected marker (evt={!r}, c={!r})".format(self.event.isSet(), c)) class Test2_Forever(unittest.TestCase): """Tests a port with no timeout""" def setUp(self): self.s = serial.serial_for_url(PORT, timeout=None) def tearDown(self): self.s.close() def test1_inWaitingEmpty(self): """no timeout: after port open, the input buffer must be empty (in_waiting)""" self.assertEqual(self.s.in_waiting, 0, "expected empty buffer") def test2_Loopback(self): """no timeout: each sent character should return (binary test). this is also a test for the binary capability of a port.""" for block in segments(bytes_0to255): length = len(block) self.s.write(block) # there might be a small delay until the character is ready (especially on win32 and rfc2217) time.sleep(0.05) self.assertEqual(self.s.in_waiting, length) #, "expected exactly %d character for inWainting()" % length) self.assertEqual(self.s.read(length), block) #, "expected %r which was written before" % block) self.assertEqual(self.s.in_waiting, 0, "expected empty buffer after all sent chars are read") class Test0_DataWires(unittest.TestCase): """Test modem control lines""" def setUp(self): self.s = serial.serial_for_url(PORT) def tearDown(self): self.s.close() def test1_RTS(self): """Test RTS/CTS""" self.s.rts = False time.sleep(1.1) self.assertTrue(not self.s.cts, "CTS -> 0") self.s.rts = True time.sleep(1.1) self.assertTrue(self.s.cts, "CTS -> 1") def test2_DTR(self): """Test DTR/DSR""" self.s.dtr = False time.sleep(1.1) self.assertTrue(not self.s.dsr, "DSR -> 0") self.s.dtr = True time.sleep(1.1) self.assertTrue(self.s.dsr, "DSR -> 1") def test3_RI(self): """Test RI""" self.assertTrue(not self.s.ri, "RI -> 0") class Test_MoreTimeouts(unittest.TestCase): """Test with timeouts""" def setUp(self): # create an closed serial port self.s = serial.serial_for_url(PORT, do_not_open=True) def tearDown(self): self.s.reset_output_buffer() self.s.flush() #~ self.s.write(serial.XON) self.s.close() # reopen... some faulty USB-serial adapter make next test fail otherwise... self.s.timeout = 1 self.s.xonxoff = False self.s.open() self.s.read(3000) self.s.close() def test_WriteTimeout(self): """Test write() timeout.""" # use xonxoff setting and the loop-back adapter to switch traffic on hold self.s.port = PORT self.s.write_timeout = 1.0 self.s.xonxoff = True self.s.open() self.s.write(serial.XOFF) time.sleep(0.5) # some systems need a little delay so that they can react on XOFF t1 = time.time() self.assertRaises(serial.SerialTimeoutException, self.s.write, b"timeout please" * 200) t2 = time.time() self.assertTrue(0.9 <= (t2 - t1) < 2.1, "Timeout not in the given interval ({})".format(t2 - t1)) if __name__ == '__main__': sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_exclusive.py0000664000175000017500000000377113057611250017157 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2017 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Tests for exclusive access feature. """ import os import unittest import sys import serial # on which port should the tests be performed: PORT = 'loop://' class Test_exclusive(unittest.TestCase): """Test serial port locking""" def setUp(self): with serial.serial_for_url(PORT, do_not_open=True) as x: if not isinstance(x, serial.Serial): raise unittest.SkipTest("exclusive test only compatible with real serial port") def test_exclusive_none(self): """test for exclusive=None""" with serial.Serial(PORT, exclusive=None): pass # OK @unittest.skipUnless(os.name == 'posix', "exclusive=False not supported on platform") def test_exclusive_false(self): """test for exclusive=False""" with serial.Serial(PORT, exclusive=False): pass # OK @unittest.skipUnless(os.name in ('posix', 'nt'), "exclusive=True setting not supported on platform") def test_exclusive_true(self): """test for exclusive=True""" with serial.Serial(PORT, exclusive=True): with self.assertRaises(serial.SerialException): serial.Serial(PORT, exclusive=True) # fails to open twice @unittest.skipUnless(os.name == 'nt', "platform is not restricted to exclusive=True (and None)") def test_exclusive_only_true(self): """test if exclusive=False is not supported""" with self.assertRaises(ValueError): serial.Serial(PORT, exclusive=False) # expected to fail: False not supported if __name__ == '__main__': sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_timeout_class.py0000664000175000017500000000373112763114272020024 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2016 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """ Test Timeout helper class. """ import sys import unittest import time from serial import serialutil class TestTimeoutClass(unittest.TestCase): """Test the Timeout class""" def test_simple_timeout(self): """Test simple timeout""" t = serialutil.Timeout(2) self.assertFalse(t.expired()) self.assertTrue(t.time_left() > 0) time.sleep(2.1) self.assertTrue(t.expired()) self.assertEqual(t.time_left(), 0) def test_non_blocking(self): """Test nonblocking case (0)""" t = serialutil.Timeout(0) self.assertTrue(t.is_non_blocking) self.assertFalse(t.is_infinite) self.assertTrue(t.expired()) def test_blocking(self): """Test no timeout (None)""" t = serialutil.Timeout(None) self.assertFalse(t.is_non_blocking) self.assertTrue(t.is_infinite) #~ self.assertFalse(t.expired()) def test_changing_clock(self): """Test recovery from chaning clock""" class T(serialutil.Timeout): def TIME(self): return test_time test_time = 1000 t = T(10) self.assertEqual(t.target_time, 1010) self.assertFalse(t.expired()) self.assertTrue(t.time_left() > 0) test_time = 100 # clock jumps way back self.assertTrue(t.time_left() > 0) self.assertTrue(t.time_left() <= 10) self.assertEqual(t.target_time, 110) test_time = 10000 # jump way forward self.assertEqual(t.time_left(), 0) # if will expire immediately if __name__ == '__main__': sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_rs485.py0000664000175000017500000000413212774544101016032 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Test RS485 related functionality. """ import unittest import serial import serial.rs485 # on which port should the tests be performed: PORT = 'loop://' class Test_RS485_settings(unittest.TestCase): """Test RS485 related functionality""" def setUp(self): # create a closed serial port self.s = serial.serial_for_url(PORT, do_not_open=True) def tearDown(self): self.s.close() def test_enable_RS485(self): # XXX open() port - but will result in fail for most HW... #~ self.s.open() self.assertEqual(self.s._rs485_mode, None, 'RS485 is disabled by default') self.assertEqual(self.s.rs485_mode, None, 'RS485 is disabled by default') self.s.rs485_mode = serial.rs485.RS485Settings() self.assertTrue(self.s._rs485_mode is not None, 'RS485 is enabled') self.assertTrue(self.s.rs485_mode is not None, 'RS485 is enabled') self.s.rs485_mode = None self.assertEqual(self.s._rs485_mode, None, 'RS485 is disabled again') self.assertEqual(self.s.rs485_mode, None, 'RS485 is disabled again') class Test_RS485_class(unittest.TestCase): """Test RS485 class""" def setUp(self): if not isinstance(serial.serial_for_url(PORT), serial.Serial): raise unittest.SkipTest("RS485 test only compatible with real serial port") self.s = serial.rs485.RS485(PORT, timeout=1) def tearDown(self): self.s.close() def test_RS485_class(self): self.s.rs485_mode = serial.rs485.RS485Settings() self.s.write(b'hello') self.assertEqual(self.s.read(5), b'hello') if __name__ == '__main__': import sys sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_settings_dict.py0000664000175000017500000000516612775055275020032 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2002-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Test the ability to get and set the settings with a dictionary. Part of pySerial (http://pyserial.sf.net) (C) 2002-2015 cliechti@gmx.net """ import unittest import serial # on which port should the tests be performed: PORT = 'loop://' SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff', 'dsrdtr', 'rtscts', 'timeout', 'write_timeout', 'inter_byte_timeout') class Test_SettingsDict(unittest.TestCase): """Test with settings dictionary""" def test_getsettings(self): """the settings dict reflects the current settings""" ser = serial.serial_for_url(PORT, do_not_open=True) d = ser.get_settings() for setting in SETTINGS: self.assertEqual(getattr(ser, setting), d[setting]) def test_partial_settings(self): """partial settings dictionaries are also accepted""" ser = serial.serial_for_url(PORT, do_not_open=True) d = ser.get_settings() del d['baudrate'] del d['bytesize'] ser.apply_settings(d) for setting in d: self.assertEqual(getattr(ser, setting), d[setting]) def test_unknown_settings(self): """unknown settings are ignored""" ser = serial.serial_for_url(PORT, do_not_open=True) d = ser.get_settings() d['foobar'] = 'ignore me' ser.apply_settings(d) def test_init_sets_the_correct_attrs(self): """__init__ sets the fields that get_settings reads""" for setting, value in ( ('baudrate', 57600), ('timeout', 7), ('write_timeout', 12), ('inter_byte_timeout', 15), ('stopbits', serial.STOPBITS_TWO), ('bytesize', serial.SEVENBITS), ('parity', serial.PARITY_ODD), ('xonxoff', True), ('rtscts', True), ('dsrdtr', True)): kwargs = {'do_not_open': True, setting: value} ser = serial.serial_for_url(PORT, **kwargs) d = ser.get_settings() self.assertEqual(getattr(ser, setting), value) self.assertEqual(d[setting], value) if __name__ == '__main__': import sys sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/run_all_tests.py0000664000175000017500000000323112775056121016764 0ustar lchlch00000000000000#! /usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ UnitTest runner. This one searches for all files named test_*.py and collects all test cases from these files. Finally it runs all tests and prints a summary. """ import unittest import sys import os # inject local copy to avoid testing the installed version instead of the one in the repo sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import serial # noqa print("Patching sys.path to test local version. Testing Version: {}".format(serial.VERSION)) PORT = 'loop://' if len(sys.argv) > 1: PORT = sys.argv[1] # find files and the tests in them mainsuite = unittest.TestSuite() for modulename in [ os.path.splitext(x)[0] for x in os.listdir(os.path.dirname(__file__) or '.') if x != __file__ and x.startswith("test") and x.endswith(".py") ]: try: module = __import__(modulename) except ImportError: print("skipping {}".format(modulename)) else: module.PORT = PORT testsuite = unittest.findTestCases(module) print("found {} tests in {!r}".format(testsuite.countTestCases(), modulename)) mainsuite.addTest(testsuite) verbosity = 1 if '-v' in sys.argv[1:]: verbosity = 2 print('-' * 78) # run the collected tests testRunner = unittest.TextTestRunner(verbosity=verbosity) #~ testRunner = unittest.ConsoleTestRunner(verbosity=verbosity) result = testRunner.run(mainsuite) # set exit code accordingly to test results sys.exit(not result.wasSuccessful()) pyserial-3.4/test/test_high_load.py0000664000175000017500000000435412775055231017073 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """Some tests for the serial module. Part of pyserial (http://pyserial.sf.net) (C)2002-2003 cliechti@gmx.net Intended to be run on different platforms, to ensure portability of the code. For all these tests a simple hardware is required. Loopback HW adapter: Shortcut these pin pairs: TX <-> RX RTS <-> CTS DTR <-> DSR On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8) """ import unittest import sys import serial # on which port should the tests be performed: PORT = 'loop://' BAUDRATE = 115200 #~ BAUDRATE=9600 if sys.version_info >= (3, 0): bytes_0to255 = bytes(range(256)) else: bytes_0to255 = ''.join([chr(x) for x in range(256)]) class TestHighLoad(unittest.TestCase): """Test sending and receiving large amount of data""" N = 16 #~ N = 1 def setUp(self): self.s = serial.serial_for_url(PORT, BAUDRATE, timeout=10) def tearDown(self): self.s.close() def test0_WriteReadLoopback(self): """Send big strings, write/read order.""" for i in range(self.N): q = bytes_0to255 self.s.write(q) self.assertEqual(self.s.read(len(q)), q) # expected same which was written before self.assertEqual(self.s.inWaiting(), 0) # expected empty buffer after all sent chars are read def test1_WriteWriteReadLoopback(self): """Send big strings, multiple write one read.""" q = bytes_0to255 for i in range(self.N): self.s.write(q) read = self.s.read(len(q) * self.N) self.assertEqual(read, q * self.N, "expected what was written before. got {} bytes, expected {}".format(len(read), self.N * len(q))) self.assertEqual(self.s.inWaiting(), 0) # "expected empty buffer after all sent chars are read") if __name__ == '__main__': import sys sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_advanced.py0000664000175000017500000001362512775055464016733 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Some tests for the serial module. Part of pyserial (http://pyserial.sf.net) (C)2002 cliechti@gmx.net Intended to be run on different platforms, to ensure portability of the code. These tests open a serial port and change all the settings on the fly. If the port is really correctly configured cannot be determined - that would require external hardware or a null modem cable and an other serial port library... Thus it mainly tests that all features are correctly implemented and that the interface does what it should. """ import unittest import serial # on which port should the tests be performed: PORT = 'loop://' class Test_ChangeAttributes(unittest.TestCase): """Test with timeouts""" def setUp(self): # create a closed serial port self.s = serial.serial_for_url(PORT, do_not_open=True) def tearDown(self): self.s.close() def test_PortSetting(self): self.s.port = PORT self.assertEqual(self.s.portstr.lower(), PORT.lower()) # test internals self.assertEqual(self.s._port, PORT) # test on the fly change self.s.open() self.assertTrue(self.s.isOpen()) def test_DoubleOpen(self): self.s.open() # calling open for a second time is an error self.assertRaises(serial.SerialException, self.s.open) def test_BaudrateSetting(self): self.s.open() for baudrate in (300, 9600, 19200, 115200): self.s.baudrate = baudrate # test get method self.assertEqual(self.s.baudrate, baudrate) # test internals self.assertEqual(self.s._baudrate, baudrate) # test illegal values for illegal_value in (-300, -1, 'a', None): self.assertRaises(ValueError, setattr, self.s, 'baudrate', illegal_value) # skip this test as pyserial now tries to set even non standard baud rates. # therefore the test can not choose a value that fails on any system. def disabled_test_BaudrateSetting2(self): # test illegal values, depending on machine/port some of these may be valid... self.s.open() for illegal_value in (500000, 576000, 921600, 92160): self.assertRaises(ValueError, setattr, self.s, 'baudrate', illegal_value) def test_BytesizeSetting(self): for bytesize in (5, 6, 7, 8): self.s.bytesize = bytesize # test get method self.assertEqual(self.s.bytesize, bytesize) # test internals self.assertEqual(self.s._bytesize, bytesize) # test illegal values for illegal_value in (0, 1, 3, 4, 9, 10, 'a', None): self.assertRaises(ValueError, setattr, self.s, 'bytesize', illegal_value) def test_ParitySetting(self): for parity in (serial.PARITY_NONE, serial.PARITY_EVEN, serial.PARITY_ODD): self.s.parity = parity # test get method self.assertEqual(self.s.parity, parity) # test internals self.assertEqual(self.s._parity, parity) # test illegal values for illegal_value in (0, 57, 'a', None): self.assertRaises(ValueError, setattr, self.s, 'parity', illegal_value) def test_StopbitsSetting(self): for stopbits in (1, 2): self.s.stopbits = stopbits # test get method self.assertEqual(self.s.stopbits, stopbits) # test internals self.assertEqual(self.s._stopbits, stopbits) # test illegal values for illegal_value in (0, 3, 2.5, 57, 'a', None): self.assertRaises(ValueError, setattr, self.s, 'stopbits', illegal_value) def test_TimeoutSetting(self): for timeout in (None, 0, 1, 3.14159, 10, 1000, 3600): self.s.timeout = timeout # test get method self.assertEqual(self.s.timeout, timeout) # test internals self.assertEqual(self.s._timeout, timeout) # test illegal values for illegal_value in (-1, 'a'): self.assertRaises(ValueError, setattr, self.s, 'timeout', illegal_value) def test_XonXoffSetting(self): for xonxoff in (True, False): self.s.xonxoff = xonxoff # test get method self.assertEqual(self.s.xonxoff, xonxoff) # test internals self.assertEqual(self.s._xonxoff, xonxoff) # no illegal values here, normal rules for the boolean value of an # object are used thus all objects have a truth value. def test_RtsCtsSetting(self): for rtscts in (True, False): self.s.rtscts = rtscts # test get method self.assertEqual(self.s.rtscts, rtscts) # test internals self.assertEqual(self.s._rtscts, rtscts) # no illegal values here, normal rules for the boolean value of an # object are used thus all objects have a truth value. # this test does not work anymore since serial_for_url that is used # now, already sets a port def disabled_test_UnconfiguredPort(self): # an unconfigured port cannot be opened self.assertRaises(serial.SerialException, self.s.open) def test_PortOpenClose(self): for i in range(3): # open the port and check flag self.assertTrue(not self.s.isOpen()) self.s.open() self.assertTrue(self.s.isOpen()) self.s.close() self.assertTrue(not self.s.isOpen()) if __name__ == '__main__': import sys sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_iolib.py0000664000175000017500000000335712775055131016254 0ustar lchlch00000000000000#! /usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Some tests for the serial module. Part of pyserial (http://pyserial.sf.net) (C)2001-2009 cliechti@gmx.net Intended to be run on different platforms, to ensure portability of the code. This modules contains test for the interaction between Serial and the io library. This only works on Python 2.6+ that introduced the io library. For all these tests a simple hardware is required. Loopback HW adapter: Shortcut these pin pairs: TX <-> RX RTS <-> CTS DTR <-> DSR On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8) """ import io import sys import unittest import serial # on which port should the tests be performed: PORT = 'loop://' class Test_SerialAndIO(unittest.TestCase): def setUp(self): self.s = serial.serial_for_url(PORT, timeout=1) #~ self.io = io.TextIOWrapper(self.s) self.io = io.TextIOWrapper(io.BufferedRWPair(self.s, self.s)) def tearDown(self): self.s.close() def test_hello_raw(self): self.io.write(b"hello\n".decode('utf-8')) self.io.flush() # it is buffering. required to get the data out hello = self.io.readline() self.assertEqual(hello, b"hello\n".decode('utf-8')) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if __name__ == '__main__': import sys sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_url.py0000664000175000017500000000302512654020044015737 0ustar lchlch00000000000000#! /usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Some tests for the serial module. Part of pySerial (http://pyserial.sf.net) (C)2001-2011 cliechti@gmx.net Intended to be run on different platforms, to ensure portability of the code. Cover some of the aspects of serial_for_url and the extension mechanism. """ import unittest import serial class Test_URL(unittest.TestCase): """Test serial_for_url""" def test_loop(self): """loop interface""" serial.serial_for_url('loop://', do_not_open=True) def test_bad_url(self): """invalid protocol specified""" self.assertRaises(ValueError, serial.serial_for_url, "imnotknown://") def test_custom_url(self): """custom protocol handlers""" # it's unknown self.assertRaises(ValueError, serial.serial_for_url, "test://") # add search path serial.protocol_handler_packages.append('handlers') # now it should work serial.serial_for_url("test://") # remove our handler again serial.protocol_handler_packages.remove('handlers') # so it should not work anymore self.assertRaises(ValueError, serial.serial_for_url, "test://") if __name__ == '__main__': import sys sys.stdout.write(__doc__) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/handlers/0000775000175000017500000000000013134735350015333 5ustar lchlch00000000000000pyserial-3.4/test/handlers/protocol_test.py0000664000175000017500000001567312732326347020626 0ustar lchlch00000000000000#! python # # Python Serial Port Extension for Win32, Linux, BSD, Jython # see __init__.py # # This module implements a URL dummy handler for serial_for_url. # # (C) 2011 Chris Liechti # this is distributed under a free software license, see license.txt # # URL format: test:// from serial.serialutil import * import time import socket import logging # map log level names to constants. used in fromURL() LOGGER_LEVELS = { 'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR, } class DummySerial(SerialBase): """Serial port implementation for plain sockets.""" def open(self): """Open port with current settings. This may throw a SerialException if the port cannot be opened.""" self.logger = None if self._port is None: raise SerialException("Port must be configured before it can be used.") # not that there anything to configure... self._reconfigurePort() # all things set up get, now a clean start self._isOpen = True def _reconfigurePort(self): """Set communication parameters on opened port. for the test:// protocol all settings are ignored!""" if self.logger: self.logger.info('ignored port configuration change') def close(self): """Close port""" if self._isOpen: self._isOpen = False def makeDeviceName(self, port): raise SerialException("there is no sensible way to turn numbers into URLs") def fromURL(self, url): """extract host and port from an URL string""" if url.lower().startswith("test://"): url = url[7:] try: # is there a "path" (our options)? if '/' in url: # cut away options url, options = url.split('/', 1) # process options now, directly altering self for option in options.split('/'): if '=' in option: option, value = option.split('=', 1) else: value = None if option == 'logging': logging.basicConfig() # XXX is that good to call it here? self.logger = logging.getLogger('pySerial.test') self.logger.setLevel(LOGGER_LEVELS[value]) self.logger.debug('enabled logging') else: raise ValueError('unknown option: {!r}'.format(option)) except ValueError as e: raise SerialException('expected a string in the form "[test://][option[/option...]]": {}'.format(e)) return (host, port) # - - - - - - - - - - - - - - - - - - - - - - - - def inWaiting(self): """Return the number of characters currently in the input buffer.""" if not self._isOpen: raise portNotOpenError if self.logger: # set this one to debug as the function could be called often... self.logger.debug('WARNING: inWaiting returns dummy value') return 0 # hmmm, see comment in read() def read(self, size=1): """Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.""" if not self._isOpen: raise portNotOpenError data = '123' # dummy data return bytes(data) def write(self, data): """Output the given string over the serial port. Can block if the connection is blocked. May raise SerialException if the connection is closed.""" if not self._isOpen: raise portNotOpenError # nothing done return len(data) def flushInput(self): """Clear input buffer, discarding all that is in the buffer.""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('ignored flushInput') def flushOutput(self): """Clear output buffer, aborting the current output and discarding all that is in the buffer.""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('ignored flushOutput') def sendBreak(self, duration=0.25): """Send break condition. Timed, returns to idle state after given duration.""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('ignored sendBreak({!r})'.format(duration)) def setBreak(self, level=True): """Set break: Controls TXD. When active, to transmitting is possible.""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('ignored setBreak({!r})'.format(level)) def setRTS(self, level=True): """Set terminal status line: Request To Send""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('ignored setRTS({!r})'.format(level)) def setDTR(self, level=True): """Set terminal status line: Data Terminal Ready""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('ignored setDTR({!r})'.format(level)) def getCTS(self): """Read terminal status line: Clear To Send""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('returning dummy for getCTS()') return True def getDSR(self): """Read terminal status line: Data Set Ready""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('returning dummy for getDSR()') return True def getRI(self): """Read terminal status line: Ring Indicator""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('returning dummy for getRI()') return False def getCD(self): """Read terminal status line: Carrier Detect""" if not self._isOpen: raise portNotOpenError if self.logger: self.logger.info('returning dummy for getCD()') return True # - - - platform specific - - - # None so far # assemble Serial class with the platform specific implementation and the base # for file-like behavior. for Python 2.6 and newer, that provide the new I/O # library, derive from io.RawIOBase try: import io except ImportError: # classic version with our own file-like emulation class Serial(DummySerial, FileLike): pass else: # io library present class Serial(DummySerial, io.RawIOBase): pass # simple client test if __name__ == '__main__': import sys s = Serial('test://logging=debug') sys.stdout.write('{}\n'.format(s)) sys.stdout.write("write...\n") s.write("hello\n") s.flush() sys.stdout.write("read: {}\n".format(s.read(5))) s.close() pyserial-3.4/test/handlers/__init__.py0000664000175000017500000000000012557511411017430 0ustar lchlch00000000000000pyserial-3.4/test/test_threaded.py0000664000175000017500000000453312765616315016741 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2016 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Test serial.threaded related functionality. """ import os import unittest import serial import serial.threaded import time # on which port should the tests be performed: PORT = 'loop://' class Test_threaded(unittest.TestCase): """Test serial.threaded related functionality""" def test_line_reader(self): """simple test of line reader class""" class TestLines(serial.threaded.LineReader): def __init__(self): super(TestLines, self).__init__() self.received_lines = [] def handle_line(self, data): self.received_lines.append(data) ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) with serial.threaded.ReaderThread(ser, TestLines) as protocol: protocol.write_line('hello') protocol.write_line('world') time.sleep(1) self.assertEqual(protocol.received_lines, ['hello', 'world']) def test_framed_packet(self): """simple test of line reader class""" class TestFramedPacket(serial.threaded.FramedPacket): def __init__(self): super(TestFramedPacket, self).__init__() self.received_packets = [] def handle_packet(self, packet): self.received_packets.append(packet) def send_packet(self, packet): self.transport.write(self.START) self.transport.write(packet) self.transport.write(self.STOP) ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) with serial.threaded.ReaderThread(ser, TestFramedPacket) as protocol: protocol.send_packet(b'1') protocol.send_packet(b'2') protocol.send_packet(b'3') time.sleep(1) self.assertEqual(protocol.received_packets, [b'1', b'2', b'3']) if __name__ == '__main__': import sys sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_readline.py0000664000175000017500000000653512775055260016745 0ustar lchlch00000000000000#! /usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2010-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Some tests for the serial module. Part of pyserial (http://pyserial.sf.net) (C)2010 cliechti@gmx.net Intended to be run on different platforms, to ensure portability of the code. For all these tests a simple hardware is required. Loopback HW adapter: Shortcut these pin pairs: TX <-> RX RTS <-> CTS DTR <-> DSR On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8) """ import unittest import sys import serial #~ print serial.VERSION # on which port should the tests be performed: PORT = 'loop://' if sys.version_info >= (3, 0): def data(string): return bytes(string, 'latin1') else: def data(string): return string class Test_Readline(unittest.TestCase): """Test readline function""" def setUp(self): self.s = serial.serial_for_url(PORT, timeout=1) def tearDown(self): self.s.close() def test_readline(self): """Test readline method""" self.s.write(serial.to_bytes([0x31, 0x0a, 0x32, 0x0a, 0x33, 0x0a])) self.assertEqual(self.s.readline(), serial.to_bytes([0x31, 0x0a])) self.assertEqual(self.s.readline(), serial.to_bytes([0x32, 0x0a])) self.assertEqual(self.s.readline(), serial.to_bytes([0x33, 0x0a])) # this time we will get a timeout self.assertEqual(self.s.readline(), serial.to_bytes([])) def test_readlines(self): """Test readlines method""" self.s.write(serial.to_bytes([0x31, 0x0a, 0x32, 0x0a, 0x33, 0x0a])) self.assertEqual( self.s.readlines(), [serial.to_bytes([0x31, 0x0a]), serial.to_bytes([0x32, 0x0a]), serial.to_bytes([0x33, 0x0a])] ) def test_xreadlines(self): """Test xreadlines method (skipped for io based systems)""" if hasattr(self.s, 'xreadlines'): self.s.write(serial.to_bytes([0x31, 0x0a, 0x32, 0x0a, 0x33, 0x0a])) self.assertEqual( list(self.s.xreadlines()), [serial.to_bytes([0x31, 0x0a]), serial.to_bytes([0x32, 0x0a]), serial.to_bytes([0x33, 0x0a])] ) def test_for_in(self): """Test for line in s""" self.s.write(serial.to_bytes([0x31, 0x0a, 0x32, 0x0a, 0x33, 0x0a])) lines = [] for line in self.s: lines.append(line) self.assertEqual( lines, [serial.to_bytes([0x31, 0x0a]), serial.to_bytes([0x32, 0x0a]), serial.to_bytes([0x33, 0x0a])] ) def test_alternate_eol(self): """Test readline with alternative eol settings (skipped for io based systems)""" if hasattr(self.s, 'xreadlines'): # test if it is our FileLike base class self.s.write(serial.to_bytes("no\rno\nyes\r\n")) self.assertEqual( self.s.readline(eol=serial.to_bytes("\r\n")), serial.to_bytes("no\rno\nyes\r\n")) if __name__ == '__main__': import sys sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_pty.py0000664000175000017500000000506713102403125015753 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2016 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """ Test PTY related functionality. """ import os import sys try: import pty except ImportError: pty = None import unittest import serial DATA = b'Hello\n' @unittest.skipIf(pty is None, "pty module not supported on platform") class Test_Pty_Serial_Open(unittest.TestCase): """Test PTY serial open""" def setUp(self): # Open PTY self.master, self.slave = pty.openpty() def test_pty_serial_open_slave(self): with serial.Serial(os.ttyname(self.slave), timeout=1) as slave: pass # OK def test_pty_serial_write(self): with serial.Serial(os.ttyname(self.slave), timeout=1) as slave: with os.fdopen(self.master, "wb") as fd: fd.write(DATA) fd.flush() out = slave.read(len(DATA)) self.assertEqual(DATA, out) def test_pty_serial_read(self): with serial.Serial(os.ttyname(self.slave), timeout=1) as slave: with os.fdopen(self.master, "rb") as fd: slave.write(DATA) slave.flush() out = fd.read(len(DATA)) self.assertEqual(DATA, out) #~ def test_pty_serial_master_read(self): #~ with serial.Serial(os.ttyname(self.master), timeout=1) as master: #~ with os.fdopen(self.slave, "wb") as fd: #~ fd.write(DATA) #~ fd.flush() #~ out = master.read(len(DATA)) #~ self.assertEqual(DATA, out) #~ def test_pty_serial_master_write(self): #~ with serial.Serial(os.ttyname(self.master), timeout=1) as master: #~ with os.fdopen(self.slave, "rb") as fd: #~ master.write(DATA) #~ master.flush() #~ out = fd.read(len(DATA)) #~ self.assertEqual(DATA, out) #~ def test_pty_bidirectional(self): #~ with serial.Serial(os.ttyname(self.master), timeout=1) as master: #~ with serial.Serial(os.ttyname(self.slave), timeout=1) as slave: #~ with os.fdopen(self.master, "rb") as fd: #~ master.write(DATA) #~ master.flush() #~ out = slave.read(len(DATA)) #~ self.assertEqual(DATA, out) if __name__ == '__main__': sys.stdout.write(__doc__) # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_asyncio.py0000664000175000017500000000472612732326347016627 0ustar lchlch00000000000000#!/usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2016 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Test asyncio related functionality. """ import os import unittest import serial # on which port should the tests be performed: PORT = '/dev/ttyUSB0' try: import asyncio import serial.aio except (ImportError, SyntaxError): # not compatible with python 2.x pass else: @unittest.skipIf(os.name != 'posix', "asyncio not supported on platform") class Test_asyncio(unittest.TestCase): """Test asyncio related functionality""" def setUp(self): self.loop = asyncio.get_event_loop() # create a closed serial port def tearDown(self): self.loop.close() def test_asyncio(self): TEXT = b'hello world\n' received = [] actions = [] class Output(asyncio.Protocol): def connection_made(self, transport): self.transport = transport actions.append('open') transport.serial.rts = False transport.write(TEXT) def data_received(self, data): #~ print('data received', repr(data)) received.append(data) if b'\n' in data: self.transport.close() def connection_lost(self, exc): actions.append('close') asyncio.get_event_loop().stop() def pause_writing(self): actions.append('pause') print(self.transport.get_write_buffer_size()) def resume_writing(self): actions.append('resume') print(self.transport.get_write_buffer_size()) coro = serial.aio.create_serial_connection(self.loop, Output, PORT, baudrate=115200) self.loop.run_until_complete(coro) self.loop.run_forever() self.assertEqual(b''.join(received), TEXT) self.assertEqual(actions, ['open', 'close']) if __name__ == '__main__': import sys sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: {!r}\n".format(PORT)) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/test/test_rfc2217.py0000664000175000017500000000251612654017314016235 0ustar lchlch00000000000000#! /usr/bin/env python # # This file is part of pySerial - Cross platform serial port support for Python # (C) 2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Test RFC 2217 related functionality. """ import unittest import serial import serial.rfc2217 class Test_RFC2217(unittest.TestCase): """Test RFC 2217 related functionality""" def test_failed_connection(self): # connection to closed port s = serial.serial_for_url('rfc2217://127.99.99.99:2217', do_not_open=True) self.assertRaises(serial.SerialException, s.open) self.assertFalse(s.is_open) s.close() # no errors expected # invalid address s = serial.serial_for_url('rfc2217://127goingtofail', do_not_open=True) self.assertRaises(serial.SerialException, s.open) self.assertFalse(s.is_open) s.close() # no errors expected # close w/o open is also OK s = serial.serial_for_url('rfc2217://irrelevant', do_not_open=True) self.assertFalse(s.is_open) s.close() # no errors expected if __name__ == '__main__': import sys sys.stdout.write(__doc__) sys.stdout.write("Testing connection on localhost\n") sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.4/PKG-INFO0000664000175000017500000000317613134735350013660 0ustar lchlch00000000000000Metadata-Version: 1.1 Name: pyserial Version: 3.4 Summary: Python Serial Port Extension Home-page: https://github.com/pyserial/pyserial Author: Chris Liechti Author-email: cliechti@gmx.net License: BSD Description: Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython Stable: - Documentation: http://pythonhosted.org/pyserial/ - Download Page: https://pypi.python.org/pypi/pyserial Latest: - Documentation: http://pyserial.readthedocs.io/en/latest/ - Project Homepage: https://github.com/pyserial/pyserial Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: BSD License Classifier: Natural Language :: English Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: MacOS :: MacOS X Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Communications Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Terminals :: Serial pyserial-3.4/serial/0000775000175000017500000000000013134735350014033 5ustar lchlch00000000000000pyserial-3.4/serial/serialutil.py0000664000175000017500000005227413131763232016571 0ustar lchlch00000000000000#! python # # Base class and support functions used by various backends. # # This file is part of pySerial. https://github.com/pyserial/pyserial # (C) 2001-2016 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause import io import time # ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)`` # isn't returning the contents (very unfortunate). Therefore we need special # cases and test for it. Ensure that there is a ``memoryview`` object for older # Python versions. This is easier than making every test dependent on its # existence. try: memoryview except (NameError, AttributeError): # implementation does not matter as we do not really use it. # it just must not inherit from something else we might care for. class memoryview(object): # pylint: disable=redefined-builtin,invalid-name pass try: unicode except (NameError, AttributeError): unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name try: basestring except (NameError, AttributeError): basestring = (str,) # for Python 3, pylint: disable=redefined-builtin,invalid-name # "for byte in data" fails for python3 as it returns ints instead of bytes def iterbytes(b): """Iterate over bytes, returning bytes instead of ints (python3)""" if isinstance(b, memoryview): b = b.tobytes() i = 0 while True: a = b[i:i + 1] i += 1 if a: yield a else: break # all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11' # so a simple ``bytes(sequence)`` doesn't work for all versions def to_bytes(seq): """convert a sequence to a bytes type""" if isinstance(seq, bytes): return seq elif isinstance(seq, bytearray): return bytes(seq) elif isinstance(seq, memoryview): return seq.tobytes() elif isinstance(seq, unicode): raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq)) else: # handle list of integers and bytes (one or more items) for Python 2 and 3 return bytes(bytearray(seq)) # create control bytes XON = to_bytes([17]) XOFF = to_bytes([19]) CR = to_bytes([13]) LF = to_bytes([10]) PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S' STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2) FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8) PARITY_NAMES = { PARITY_NONE: 'None', PARITY_EVEN: 'Even', PARITY_ODD: 'Odd', PARITY_MARK: 'Mark', PARITY_SPACE: 'Space', } class SerialException(IOError): """Base class for serial port related exceptions.""" class SerialTimeoutException(SerialException): """Write timeouts give an exception""" writeTimeoutError = SerialTimeoutException('Write timeout') portNotOpenError = SerialException('Attempting to use a port that is not open') class Timeout(object): """\ Abstraction for timeout operations. Using time.monotonic() if available or time.time() in all other cases. The class can also be initialized with 0 or None, in order to support non-blocking and fully blocking I/O operations. The attributes is_non_blocking and is_infinite are set accordingly. """ if hasattr(time, 'monotonic'): # Timeout implementation with time.monotonic(). This function is only # supported by Python 3.3 and above. It returns a time in seconds # (float) just as time.time(), but is not affected by system clock # adjustments. TIME = time.monotonic else: # Timeout implementation with time.time(). This is compatible with all # Python versions but has issues if the clock is adjusted while the # timeout is running. TIME = time.time def __init__(self, duration): """Initialize a timeout with given duration""" self.is_infinite = (duration is None) self.is_non_blocking = (duration == 0) self.duration = duration if duration is not None: self.target_time = self.TIME() + duration else: self.target_time = None def expired(self): """Return a boolean, telling if the timeout has expired""" return self.target_time is not None and self.time_left() <= 0 def time_left(self): """Return how many seconds are left until the timeout expires""" if self.is_non_blocking: return 0 elif self.is_infinite: return None else: delta = self.target_time - self.TIME() if delta > self.duration: # clock jumped, recalculate self.target_time = self.TIME() + self.duration return self.duration else: return max(0, delta) def restart(self, duration): """\ Restart a timeout, only supported if a timeout was already set up before. """ self.duration = duration self.target_time = self.TIME() + duration class SerialBase(io.RawIOBase): """\ Serial port base class. Provides __init__ function and properties to get/set port settings. """ # default values, may be overridden in subclasses that do not support all values BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000) BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS) PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE) STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO) def __init__(self, port=None, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_NONE, stopbits=STOPBITS_ONE, timeout=None, xonxoff=False, rtscts=False, write_timeout=None, dsrdtr=False, inter_byte_timeout=None, exclusive=None, **kwargs): """\ Initialize comm port object. If a "port" is given, then the port will be opened immediately. Otherwise a Serial port object in closed state is returned. """ self.is_open = False self.portstr = None self.name = None # correct values are assigned below through properties self._port = None self._baudrate = None self._bytesize = None self._parity = None self._stopbits = None self._timeout = None self._write_timeout = None self._xonxoff = None self._rtscts = None self._dsrdtr = None self._inter_byte_timeout = None self._rs485_mode = None # disabled by default self._rts_state = True self._dtr_state = True self._break_state = False self._exclusive = None # assign values using get/set methods using the properties feature self.port = port self.baudrate = baudrate self.bytesize = bytesize self.parity = parity self.stopbits = stopbits self.timeout = timeout self.write_timeout = write_timeout self.xonxoff = xonxoff self.rtscts = rtscts self.dsrdtr = dsrdtr self.inter_byte_timeout = inter_byte_timeout self.exclusive = exclusive # watch for backward compatible kwargs if 'writeTimeout' in kwargs: self.write_timeout = kwargs.pop('writeTimeout') if 'interCharTimeout' in kwargs: self.inter_byte_timeout = kwargs.pop('interCharTimeout') if kwargs: raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs)) if port is not None: self.open() # - - - - - - - - - - - - - - - - - - - - - - - - # to be implemented by subclasses: # def open(self): # def close(self): # - - - - - - - - - - - - - - - - - - - - - - - - @property def port(self): """\ Get the current port setting. The value that was passed on init or using setPort() is passed back. """ return self._port @port.setter def port(self, port): """\ Change the port. """ if port is not None and not isinstance(port, basestring): raise ValueError('"port" must be None or a string, not {}'.format(type(port))) was_open = self.is_open if was_open: self.close() self.portstr = port self._port = port self.name = self.portstr if was_open: self.open() @property def baudrate(self): """Get the current baud rate setting.""" return self._baudrate @baudrate.setter def baudrate(self, baudrate): """\ Change baud rate. It raises a ValueError if the port is open and the baud rate is not possible. If the port is closed, then the value is accepted and the exception is raised when the port is opened. """ try: b = int(baudrate) except TypeError: raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) else: if b < 0: raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) self._baudrate = b if self.is_open: self._reconfigure_port() @property def bytesize(self): """Get the current byte size setting.""" return self._bytesize @bytesize.setter def bytesize(self, bytesize): """Change byte size.""" if bytesize not in self.BYTESIZES: raise ValueError("Not a valid byte size: {!r}".format(bytesize)) self._bytesize = bytesize if self.is_open: self._reconfigure_port() @property def exclusive(self): """Get the current exclusive access setting.""" return self._exclusive @exclusive.setter def exclusive(self, exclusive): """Change the exclusive access setting.""" self._exclusive = exclusive if self.is_open: self._reconfigure_port() @property def parity(self): """Get the current parity setting.""" return self._parity @parity.setter def parity(self, parity): """Change parity setting.""" if parity not in self.PARITIES: raise ValueError("Not a valid parity: {!r}".format(parity)) self._parity = parity if self.is_open: self._reconfigure_port() @property def stopbits(self): """Get the current stop bits setting.""" return self._stopbits @stopbits.setter def stopbits(self, stopbits): """Change stop bits size.""" if stopbits not in self.STOPBITS: raise ValueError("Not a valid stop bit size: {!r}".format(stopbits)) self._stopbits = stopbits if self.is_open: self._reconfigure_port() @property def timeout(self): """Get the current timeout setting.""" return self._timeout @timeout.setter def timeout(self, timeout): """Change timeout setting.""" if timeout is not None: try: timeout + 1 # test if it's a number, will throw a TypeError if not... except TypeError: raise ValueError("Not a valid timeout: {!r}".format(timeout)) if timeout < 0: raise ValueError("Not a valid timeout: {!r}".format(timeout)) self._timeout = timeout if self.is_open: self._reconfigure_port() @property def write_timeout(self): """Get the current timeout setting.""" return self._write_timeout @write_timeout.setter def write_timeout(self, timeout): """Change timeout setting.""" if timeout is not None: if timeout < 0: raise ValueError("Not a valid timeout: {!r}".format(timeout)) try: timeout + 1 # test if it's a number, will throw a TypeError if not... except TypeError: raise ValueError("Not a valid timeout: {!r}".format(timeout)) self._write_timeout = timeout if self.is_open: self._reconfigure_port() @property def inter_byte_timeout(self): """Get the current inter-character timeout setting.""" return self._inter_byte_timeout @inter_byte_timeout.setter def inter_byte_timeout(self, ic_timeout): """Change inter-byte timeout setting.""" if ic_timeout is not None: if ic_timeout < 0: raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) try: ic_timeout + 1 # test if it's a number, will throw a TypeError if not... except TypeError: raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) self._inter_byte_timeout = ic_timeout if self.is_open: self._reconfigure_port() @property def xonxoff(self): """Get the current XON/XOFF setting.""" return self._xonxoff @xonxoff.setter def xonxoff(self, xonxoff): """Change XON/XOFF setting.""" self._xonxoff = xonxoff if self.is_open: self._reconfigure_port() @property def rtscts(self): """Get the current RTS/CTS flow control setting.""" return self._rtscts @rtscts.setter def rtscts(self, rtscts): """Change RTS/CTS flow control setting.""" self._rtscts = rtscts if self.is_open: self._reconfigure_port() @property def dsrdtr(self): """Get the current DSR/DTR flow control setting.""" return self._dsrdtr @dsrdtr.setter def dsrdtr(self, dsrdtr=None): """Change DsrDtr flow control setting.""" if dsrdtr is None: # if not set, keep backwards compatibility and follow rtscts setting self._dsrdtr = self._rtscts else: # if defined independently, follow its value self._dsrdtr = dsrdtr if self.is_open: self._reconfigure_port() @property def rts(self): return self._rts_state @rts.setter def rts(self, value): self._rts_state = value if self.is_open: self._update_rts_state() @property def dtr(self): return self._dtr_state @dtr.setter def dtr(self, value): self._dtr_state = value if self.is_open: self._update_dtr_state() @property def break_condition(self): return self._break_state @break_condition.setter def break_condition(self, value): self._break_state = value if self.is_open: self._update_break_state() # - - - - - - - - - - - - - - - - - - - - - - - - # functions useful for RS-485 adapters @property def rs485_mode(self): """\ Enable RS485 mode and apply new settings, set to None to disable. See serial.rs485.RS485Settings for more info about the value. """ return self._rs485_mode @rs485_mode.setter def rs485_mode(self, rs485_settings): self._rs485_mode = rs485_settings if self.is_open: self._reconfigure_port() # - - - - - - - - - - - - - - - - - - - - - - - - _SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff', 'dsrdtr', 'rtscts', 'timeout', 'write_timeout', 'inter_byte_timeout') def get_settings(self): """\ Get current port settings as a dictionary. For use with apply_settings(). """ return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS]) def apply_settings(self, d): """\ Apply stored settings from a dictionary returned from get_settings(). It's allowed to delete keys from the dictionary. These values will simply left unchanged. """ for key in self._SAVED_SETTINGS: if key in d and d[key] != getattr(self, '_' + key): # check against internal "_" value setattr(self, key, d[key]) # set non "_" value to use properties write function # - - - - - - - - - - - - - - - - - - - - - - - - def __repr__(self): """String representation of the current port settings and its state.""" return '{name}(port={p.portstr!r}, ' \ 'baudrate={p.baudrate!r}, bytesize={p.bytesize!r}, parity={p.parity!r}, ' \ 'stopbits={p.stopbits!r}, timeout={p.timeout!r}, xonxoff={p.xonxoff!r}, ' \ 'rtscts={p.rtscts!r}, dsrdtr={p.dsrdtr!r})'.format( name=self.__class__.__name__, id=id(self), p=self) # - - - - - - - - - - - - - - - - - - - - - - - - # compatibility with io library # pylint: disable=invalid-name,missing-docstring def readable(self): return True def writable(self): return True def seekable(self): return False def readinto(self, b): data = self.read(len(b)) n = len(data) try: b[:n] = data except TypeError as err: import array if not isinstance(b, array.array): raise err b[:n] = array.array('b', data) return n # - - - - - - - - - - - - - - - - - - - - - - - - # context manager def __enter__(self): if not self.is_open: self.open() return self def __exit__(self, *args, **kwargs): self.close() # - - - - - - - - - - - - - - - - - - - - - - - - def send_break(self, duration=0.25): """\ Send break condition. Timed, returns to idle state after given duration. """ if not self.is_open: raise portNotOpenError self.break_condition = True time.sleep(duration) self.break_condition = False # - - - - - - - - - - - - - - - - - - - - - - - - # backwards compatibility / deprecated functions def flushInput(self): self.reset_input_buffer() def flushOutput(self): self.reset_output_buffer() def inWaiting(self): return self.in_waiting def sendBreak(self, duration=0.25): self.send_break(duration) def setRTS(self, value=1): self.rts = value def setDTR(self, value=1): self.dtr = value def getCTS(self): return self.cts def getDSR(self): return self.dsr def getRI(self): return self.ri def getCD(self): return self.cd def setPort(self, port): self.port = port @property def writeTimeout(self): return self.write_timeout @writeTimeout.setter def writeTimeout(self, timeout): self.write_timeout = timeout @property def interCharTimeout(self): return self.inter_byte_timeout @interCharTimeout.setter def interCharTimeout(self, interCharTimeout): self.inter_byte_timeout = interCharTimeout def getSettingsDict(self): return self.get_settings() def applySettingsDict(self, d): self.apply_settings(d) def isOpen(self): return self.is_open # - - - - - - - - - - - - - - - - - - - - - - - - # additional functionality def read_all(self): """\ Read all bytes currently available in the buffer of the OS. """ return self.read(self.in_waiting) def read_until(self, terminator=LF, size=None): """\ Read until a termination sequence is found ('\n' by default), the size is exceeded or until timeout occurs. """ lenterm = len(terminator) line = bytearray() timeout = Timeout(self._timeout) while True: c = self.read(1) if c: line += c if line[-lenterm:] == terminator: break if size is not None and len(line) >= size: break else: break if timeout.expired(): break return bytes(line) def iread_until(self, *args, **kwargs): """\ Read lines, implemented as generator. It will raise StopIteration on timeout (empty read). """ while True: line = self.read_until(*args, **kwargs) if not line: break yield line # - - - - - - - - - - - - - - - - - - - - - - - - - if __name__ == '__main__': import sys s = SerialBase() sys.stdout.write('port name: {}\n'.format(s.name)) sys.stdout.write('baud rates: {}\n'.format(s.BAUDRATES)) sys.stdout.write('byte sizes: {}\n'.format(s.BYTESIZES)) sys.stdout.write('parities: {}\n'.format(s.PARITIES)) sys.stdout.write('stop bits: {}\n'.format(s.STOPBITS)) sys.stdout.write('{}\n'.format(s)) pyserial-3.4/serial/rfc2217.py0000664000175000017500000016413613102403125015471 0ustar lchlch00000000000000#! python # # This module implements a RFC2217 compatible client. RF2217 descibes a # protocol to access serial ports over TCP/IP and allows setting the baud rate, # modem control lines etc. # # This file is part of pySerial. https://github.com/pyserial/pyserial # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause # TODO: # - setting control line -> answer is not checked (had problems with one of the # severs). consider implementing a compatibility mode flag to make check # conditional # - write timeout not implemented at all # ########################################################################### # observations and issues with servers # =========================================================================== # sredird V2.2.1 # - http://www.ibiblio.org/pub/Linux/system/serial/ sredird-2.2.2.tar.gz # - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding # [105 1] instead of the actual value. # - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger # numbers than 2**32? # - To get the signature [COM_PORT_OPTION 0] has to be sent. # - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done # =========================================================================== # telnetcpcd (untested) # - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz # - To get the signature [COM_PORT_OPTION] w/o data has to be sent. # =========================================================================== # ser2net # - does not negotiate BINARY or COM_PORT_OPTION for his side but at least # acknowledges that the client activates these options # - The configuration may be that the server prints a banner. As this client # implementation does a flushInput on connect, this banner is hidden from # the user application. # - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one # second. # - To get the signature [COM_PORT_OPTION 0] has to be sent. # - run a server: run ser2net daemon, in /etc/ser2net.conf: # 2000:telnet:0:/dev/ttyS0:9600 remctl banner # ########################################################################### # How to identify ports? pySerial might want to support other protocols in the # future, so lets use an URL scheme. # for RFC2217 compliant servers we will use this: # rfc2217://:[?option[&option...]] # # options: # - "logging" set log level print diagnostic messages (e.g. "logging=debug") # - "ign_set_control": do not look at the answers to SET_CONTROL # - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read. # Without this option it expects that the server sends notifications # automatically on change (which most servers do and is according to the # RFC). # the order of the options is not relevant import logging import socket import struct import threading import time try: import urlparse except ImportError: import urllib.parse as urlparse try: import Queue except ImportError: import queue as Queue import serial from serial.serialutil import SerialBase, SerialException, to_bytes, \ iterbytes, portNotOpenError, Timeout # port string is expected to be something like this: # rfc2217://host:port # host may be an IP or including domain, whatever. # port is 0...65535 # map log level names to constants. used in from_url() LOGGER_LEVELS = { 'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR, } # telnet protocol characters SE = b'\xf0' # Subnegotiation End NOP = b'\xf1' # No Operation DM = b'\xf2' # Data Mark BRK = b'\xf3' # Break IP = b'\xf4' # Interrupt process AO = b'\xf5' # Abort output AYT = b'\xf6' # Are You There EC = b'\xf7' # Erase Character EL = b'\xf8' # Erase Line GA = b'\xf9' # Go Ahead SB = b'\xfa' # Subnegotiation Begin WILL = b'\xfb' WONT = b'\xfc' DO = b'\xfd' DONT = b'\xfe' IAC = b'\xff' # Interpret As Command IAC_DOUBLED = b'\xff\xff' # selected telnet options BINARY = b'\x00' # 8-bit data path ECHO = b'\x01' # echo SGA = b'\x03' # suppress go ahead # RFC2217 COM_PORT_OPTION = b'\x2c' # Client to Access Server SET_BAUDRATE = b'\x01' SET_DATASIZE = b'\x02' SET_PARITY = b'\x03' SET_STOPSIZE = b'\x04' SET_CONTROL = b'\x05' NOTIFY_LINESTATE = b'\x06' NOTIFY_MODEMSTATE = b'\x07' FLOWCONTROL_SUSPEND = b'\x08' FLOWCONTROL_RESUME = b'\x09' SET_LINESTATE_MASK = b'\x0a' SET_MODEMSTATE_MASK = b'\x0b' PURGE_DATA = b'\x0c' SERVER_SET_BAUDRATE = b'\x65' SERVER_SET_DATASIZE = b'\x66' SERVER_SET_PARITY = b'\x67' SERVER_SET_STOPSIZE = b'\x68' SERVER_SET_CONTROL = b'\x69' SERVER_NOTIFY_LINESTATE = b'\x6a' SERVER_NOTIFY_MODEMSTATE = b'\x6b' SERVER_FLOWCONTROL_SUSPEND = b'\x6c' SERVER_FLOWCONTROL_RESUME = b'\x6d' SERVER_SET_LINESTATE_MASK = b'\x6e' SERVER_SET_MODEMSTATE_MASK = b'\x6f' SERVER_PURGE_DATA = b'\x70' RFC2217_ANSWER_MAP = { SET_BAUDRATE: SERVER_SET_BAUDRATE, SET_DATASIZE: SERVER_SET_DATASIZE, SET_PARITY: SERVER_SET_PARITY, SET_STOPSIZE: SERVER_SET_STOPSIZE, SET_CONTROL: SERVER_SET_CONTROL, NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE, NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE, FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND, FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME, SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK, SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK, PURGE_DATA: SERVER_PURGE_DATA, } SET_CONTROL_REQ_FLOW_SETTING = b'\x00' # Request Com Port Flow Control Setting (outbound/both) SET_CONTROL_USE_NO_FLOW_CONTROL = b'\x01' # Use No Flow Control (outbound/both) SET_CONTROL_USE_SW_FLOW_CONTROL = b'\x02' # Use XON/XOFF Flow Control (outbound/both) SET_CONTROL_USE_HW_FLOW_CONTROL = b'\x03' # Use HARDWARE Flow Control (outbound/both) SET_CONTROL_REQ_BREAK_STATE = b'\x04' # Request BREAK State SET_CONTROL_BREAK_ON = b'\x05' # Set BREAK State ON SET_CONTROL_BREAK_OFF = b'\x06' # Set BREAK State OFF SET_CONTROL_REQ_DTR = b'\x07' # Request DTR Signal State SET_CONTROL_DTR_ON = b'\x08' # Set DTR Signal State ON SET_CONTROL_DTR_OFF = b'\x09' # Set DTR Signal State OFF SET_CONTROL_REQ_RTS = b'\x0a' # Request RTS Signal State SET_CONTROL_RTS_ON = b'\x0b' # Set RTS Signal State ON SET_CONTROL_RTS_OFF = b'\x0c' # Set RTS Signal State OFF SET_CONTROL_REQ_FLOW_SETTING_IN = b'\x0d' # Request Com Port Flow Control Setting (inbound) SET_CONTROL_USE_NO_FLOW_CONTROL_IN = b'\x0e' # Use No Flow Control (inbound) SET_CONTROL_USE_SW_FLOW_CONTOL_IN = b'\x0f' # Use XON/XOFF Flow Control (inbound) SET_CONTROL_USE_HW_FLOW_CONTOL_IN = b'\x10' # Use HARDWARE Flow Control (inbound) SET_CONTROL_USE_DCD_FLOW_CONTROL = b'\x11' # Use DCD Flow Control (outbound/both) SET_CONTROL_USE_DTR_FLOW_CONTROL = b'\x12' # Use DTR Flow Control (inbound) SET_CONTROL_USE_DSR_FLOW_CONTROL = b'\x13' # Use DSR Flow Control (outbound/both) LINESTATE_MASK_TIMEOUT = 128 # Time-out Error LINESTATE_MASK_SHIFTREG_EMPTY = 64 # Transfer Shift Register Empty LINESTATE_MASK_TRANSREG_EMPTY = 32 # Transfer Holding Register Empty LINESTATE_MASK_BREAK_DETECT = 16 # Break-detect Error LINESTATE_MASK_FRAMING_ERROR = 8 # Framing Error LINESTATE_MASK_PARTIY_ERROR = 4 # Parity Error LINESTATE_MASK_OVERRUN_ERROR = 2 # Overrun Error LINESTATE_MASK_DATA_READY = 1 # Data Ready MODEMSTATE_MASK_CD = 128 # Receive Line Signal Detect (also known as Carrier Detect) MODEMSTATE_MASK_RI = 64 # Ring Indicator MODEMSTATE_MASK_DSR = 32 # Data-Set-Ready Signal State MODEMSTATE_MASK_CTS = 16 # Clear-To-Send Signal State MODEMSTATE_MASK_CD_CHANGE = 8 # Delta Receive Line Signal Detect MODEMSTATE_MASK_RI_CHANGE = 4 # Trailing-edge Ring Detector MODEMSTATE_MASK_DSR_CHANGE = 2 # Delta Data-Set-Ready MODEMSTATE_MASK_CTS_CHANGE = 1 # Delta Clear-To-Send PURGE_RECEIVE_BUFFER = b'\x01' # Purge access server receive data buffer PURGE_TRANSMIT_BUFFER = b'\x02' # Purge access server transmit data buffer PURGE_BOTH_BUFFERS = b'\x03' # Purge both the access server receive data # buffer and the access server transmit data buffer RFC2217_PARITY_MAP = { serial.PARITY_NONE: 1, serial.PARITY_ODD: 2, serial.PARITY_EVEN: 3, serial.PARITY_MARK: 4, serial.PARITY_SPACE: 5, } RFC2217_REVERSE_PARITY_MAP = dict((v, k) for k, v in RFC2217_PARITY_MAP.items()) RFC2217_STOPBIT_MAP = { serial.STOPBITS_ONE: 1, serial.STOPBITS_ONE_POINT_FIVE: 3, serial.STOPBITS_TWO: 2, } RFC2217_REVERSE_STOPBIT_MAP = dict((v, k) for k, v in RFC2217_STOPBIT_MAP.items()) # Telnet filter states M_NORMAL = 0 M_IAC_SEEN = 1 M_NEGOTIATE = 2 # TelnetOption and TelnetSubnegotiation states REQUESTED = 'REQUESTED' ACTIVE = 'ACTIVE' INACTIVE = 'INACTIVE' REALLY_INACTIVE = 'REALLY_INACTIVE' class TelnetOption(object): """Manage a single telnet option, keeps track of DO/DONT WILL/WONT.""" def __init__(self, connection, name, option, send_yes, send_no, ack_yes, ack_no, initial_state, activation_callback=None): """\ Initialize option. :param connection: connection used to transmit answers :param name: a readable name for debug outputs :param send_yes: what to send when option is to be enabled. :param send_no: what to send when option is to be disabled. :param ack_yes: what to expect when remote agrees on option. :param ack_no: what to expect when remote disagrees on option. :param initial_state: options initialized with REQUESTED are tried to be enabled on startup. use INACTIVE for all others. """ self.connection = connection self.name = name self.option = option self.send_yes = send_yes self.send_no = send_no self.ack_yes = ack_yes self.ack_no = ack_no self.state = initial_state self.active = False self.activation_callback = activation_callback def __repr__(self): """String for debug outputs""" return "{o.name}:{o.active}({o.state})".format(o=self) def process_incoming(self, command): """\ A DO/DONT/WILL/WONT was received for this option, update state and answer when needed. """ if command == self.ack_yes: if self.state is REQUESTED: self.state = ACTIVE self.active = True if self.activation_callback is not None: self.activation_callback() elif self.state is ACTIVE: pass elif self.state is INACTIVE: self.state = ACTIVE self.connection.telnet_send_option(self.send_yes, self.option) self.active = True if self.activation_callback is not None: self.activation_callback() elif self.state is REALLY_INACTIVE: self.connection.telnet_send_option(self.send_no, self.option) else: raise ValueError('option in illegal state {!r}'.format(self)) elif command == self.ack_no: if self.state is REQUESTED: self.state = INACTIVE self.active = False elif self.state is ACTIVE: self.state = INACTIVE self.connection.telnet_send_option(self.send_no, self.option) self.active = False elif self.state is INACTIVE: pass elif self.state is REALLY_INACTIVE: pass else: raise ValueError('option in illegal state {!r}'.format(self)) class TelnetSubnegotiation(object): """\ A object to handle subnegotiation of options. In this case actually sub-sub options for RFC 2217. It is used to track com port options. """ def __init__(self, connection, name, option, ack_option=None): if ack_option is None: ack_option = option self.connection = connection self.name = name self.option = option self.value = None self.ack_option = ack_option self.state = INACTIVE def __repr__(self): """String for debug outputs.""" return "{sn.name}:{sn.state}".format(sn=self) def set(self, value): """\ Request a change of the value. a request is sent to the server. if the client needs to know if the change is performed he has to check the state of this object. """ self.value = value self.state = REQUESTED self.connection.rfc2217_send_subnegotiation(self.option, self.value) if self.connection.logger: self.connection.logger.debug("SB Requesting {} -> {!r}".format(self.name, self.value)) def is_ready(self): """\ Check if answer from server has been received. when server rejects the change, raise a ValueError. """ if self.state == REALLY_INACTIVE: raise ValueError("remote rejected value for option {!r}".format(self.name)) return self.state == ACTIVE # add property to have a similar interface as TelnetOption active = property(is_ready) def wait(self, timeout=3): """\ Wait until the subnegotiation has been acknowledged or timeout. It can also throw a value error when the answer from the server does not match the value sent. """ timeout_timer = Timeout(timeout) while not timeout_timer.expired(): time.sleep(0.05) # prevent 100% CPU load if self.is_ready(): break else: raise SerialException("timeout while waiting for option {!r}".format(self.name)) def check_answer(self, suboption): """\ Check an incoming subnegotiation block. The parameter already has cut off the header like sub option number and com port option value. """ if self.value == suboption[:len(self.value)]: self.state = ACTIVE else: # error propagation done in is_ready self.state = REALLY_INACTIVE if self.connection.logger: self.connection.logger.debug("SB Answer {} -> {!r} -> {}".format(self.name, suboption, self.state)) class Serial(SerialBase): """Serial port implementation for RFC 2217 remote serial ports.""" BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200) def __init__(self, *args, **kwargs): self._thread = None self._socket = None self._linestate = 0 self._modemstate = None self._modemstate_timeout = Timeout(-1) self._remote_suspend_flow = False self._write_lock = None self.logger = None self._ignore_set_control_answer = False self._poll_modem_state = False self._network_timeout = 3 self._telnet_options = None self._rfc2217_port_settings = None self._rfc2217_options = None self._read_buffer = None super(Serial, self).__init__(*args, **kwargs) # must be last call in case of auto-open def open(self): """\ Open port with current settings. This may throw a SerialException if the port cannot be opened. """ self.logger = None self._ignore_set_control_answer = False self._poll_modem_state = False self._network_timeout = 3 if self._port is None: raise SerialException("Port must be configured before it can be used.") if self.is_open: raise SerialException("Port is already open.") try: self._socket = socket.create_connection(self.from_url(self.portstr), timeout=5) # XXX good value? self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) except Exception as msg: self._socket = None raise SerialException("Could not open port {}: {}".format(self.portstr, msg)) # use a thread save queue as buffer. it also simplifies implementing # the read timeout self._read_buffer = Queue.Queue() # to ensure that user writes does not interfere with internal # telnet/rfc2217 options establish a lock self._write_lock = threading.Lock() # name the following separately so that, below, a check can be easily done mandadory_options = [ TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED), ] # all supported telnet options self._telnet_options = [ TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED), TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED), TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE), TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED), ] + mandadory_options # RFC 2217 specific states # COM port settings self._rfc2217_port_settings = { 'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE), 'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE), 'parity': TelnetSubnegotiation(self, 'parity', SET_PARITY, SERVER_SET_PARITY), 'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE), } # There are more subnegotiation objects, combine all in one dictionary # for easy access self._rfc2217_options = { 'purge': TelnetSubnegotiation(self, 'purge', PURGE_DATA, SERVER_PURGE_DATA), 'control': TelnetSubnegotiation(self, 'control', SET_CONTROL, SERVER_SET_CONTROL), } self._rfc2217_options.update(self._rfc2217_port_settings) # cache for line and modem states that the server sends to us self._linestate = 0 self._modemstate = None self._modemstate_timeout = Timeout(-1) # RFC 2217 flow control between server and client self._remote_suspend_flow = False self.is_open = True self._thread = threading.Thread(target=self._telnet_read_loop) self._thread.setDaemon(True) self._thread.setName('pySerial RFC 2217 reader thread for {}'.format(self._port)) self._thread.start() try: # must clean-up if open fails # negotiate Telnet/RFC 2217 -> send initial requests for option in self._telnet_options: if option.state is REQUESTED: self.telnet_send_option(option.send_yes, option.option) # now wait until important options are negotiated timeout = Timeout(self._network_timeout) while not timeout.expired(): time.sleep(0.05) # prevent 100% CPU load if sum(o.active for o in mandadory_options) == sum(o.state != INACTIVE for o in mandadory_options): break else: raise SerialException( "Remote does not seem to support RFC2217 or BINARY mode {!r}".format(mandadory_options)) if self.logger: self.logger.info("Negotiated options: {}".format(self._telnet_options)) # fine, go on, set RFC 2271 specific things self._reconfigure_port() # all things set up get, now a clean start if not self._dsrdtr: self._update_dtr_state() if not self._rtscts: self._update_rts_state() self.reset_input_buffer() self.reset_output_buffer() except: self.close() raise def _reconfigure_port(self): """Set communication parameters on opened port.""" if self._socket is None: raise SerialException("Can only operate on open ports") # if self._timeout != 0 and self._interCharTimeout is not None: # XXX if self._write_timeout is not None: raise NotImplementedError('write_timeout is currently not supported') # XXX # Setup the connection # to get good performance, all parameter changes are sent first... if not 0 < self._baudrate < 2 ** 32: raise ValueError("invalid baudrate: {!r}".format(self._baudrate)) self._rfc2217_port_settings['baudrate'].set(struct.pack(b'!I', self._baudrate)) self._rfc2217_port_settings['datasize'].set(struct.pack(b'!B', self._bytesize)) self._rfc2217_port_settings['parity'].set(struct.pack(b'!B', RFC2217_PARITY_MAP[self._parity])) self._rfc2217_port_settings['stopsize'].set(struct.pack(b'!B', RFC2217_STOPBIT_MAP[self._stopbits])) # and now wait until parameters are active items = self._rfc2217_port_settings.values() if self.logger: self.logger.debug("Negotiating settings: {}".format(items)) timeout = Timeout(self._network_timeout) while not timeout.expired(): time.sleep(0.05) # prevent 100% CPU load if sum(o.active for o in items) == len(items): break else: raise SerialException("Remote does not accept parameter change (RFC2217): {!r}".format(items)) if self.logger: self.logger.info("Negotiated settings: {}".format(items)) if self._rtscts and self._xonxoff: raise ValueError('xonxoff and rtscts together are not supported') elif self._rtscts: self.rfc2217_set_control(SET_CONTROL_USE_HW_FLOW_CONTROL) elif self._xonxoff: self.rfc2217_set_control(SET_CONTROL_USE_SW_FLOW_CONTROL) else: self.rfc2217_set_control(SET_CONTROL_USE_NO_FLOW_CONTROL) def close(self): """Close port""" self.is_open = False if self._socket: try: self._socket.shutdown(socket.SHUT_RDWR) self._socket.close() except: # ignore errors. pass if self._thread: self._thread.join(7) # XXX more than socket timeout self._thread = None # in case of quick reconnects, give the server some time time.sleep(0.3) self._socket = None def from_url(self, url): """\ extract host and port from an URL string, other settings are extracted an stored in instance """ parts = urlparse.urlsplit(url) if parts.scheme != "rfc2217": raise SerialException( 'expected a string in the form ' '"rfc2217://:[?option[&option...]]": ' 'not starting with rfc2217:// ({!r})'.format(parts.scheme)) try: # process options now, directly altering self for option, values in urlparse.parse_qs(parts.query, True).items(): if option == 'logging': logging.basicConfig() # XXX is that good to call it here? self.logger = logging.getLogger('pySerial.rfc2217') self.logger.setLevel(LOGGER_LEVELS[values[0]]) self.logger.debug('enabled logging') elif option == 'ign_set_control': self._ignore_set_control_answer = True elif option == 'poll_modem': self._poll_modem_state = True elif option == 'timeout': self._network_timeout = float(values[0]) else: raise ValueError('unknown option: {!r}'.format(option)) if not 0 <= parts.port < 65536: raise ValueError("port not in range 0...65535") except ValueError as e: raise SerialException( 'expected a string in the form ' '"rfc2217://:[?option[&option...]]": {}'.format(e)) return (parts.hostname, parts.port) # - - - - - - - - - - - - - - - - - - - - - - - - @property def in_waiting(self): """Return the number of bytes currently in the input buffer.""" if not self.is_open: raise portNotOpenError return self._read_buffer.qsize() def read(self, size=1): """\ Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read. """ if not self.is_open: raise portNotOpenError data = bytearray() try: timeout = Timeout(self._timeout) while len(data) < size: if self._thread is None: raise SerialException('connection failed (reader thread died)') data += self._read_buffer.get(True, timeout.time_left()) if timeout.expired(): break except Queue.Empty: # -> timeout pass return bytes(data) def write(self, data): """\ Output the given byte string over the serial port. Can block if the connection is blocked. May raise SerialException if the connection is closed. """ if not self.is_open: raise portNotOpenError # XXX use protocol_socket's write with self._write_lock: try: self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED)) except socket.error as e: raise SerialException("connection failed (socket error): {}".format(e)) return len(data) def reset_input_buffer(self): """Clear input buffer, discarding all that is in the buffer.""" if not self.is_open: raise portNotOpenError self.rfc2217_send_purge(PURGE_RECEIVE_BUFFER) # empty read buffer while self._read_buffer.qsize(): self._read_buffer.get(False) def reset_output_buffer(self): """\ Clear output buffer, aborting the current output and discarding all that is in the buffer. """ if not self.is_open: raise portNotOpenError self.rfc2217_send_purge(PURGE_TRANSMIT_BUFFER) def _update_break_state(self): """\ Set break: Controls TXD. When active, to transmitting is possible. """ if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('set BREAK to {}'.format('active' if self._break_state else 'inactive')) if self._break_state: self.rfc2217_set_control(SET_CONTROL_BREAK_ON) else: self.rfc2217_set_control(SET_CONTROL_BREAK_OFF) def _update_rts_state(self): """Set terminal status line: Request To Send.""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('set RTS to {}'.format('active' if self._rts_state else 'inactive')) if self._rts_state: self.rfc2217_set_control(SET_CONTROL_RTS_ON) else: self.rfc2217_set_control(SET_CONTROL_RTS_OFF) def _update_dtr_state(self): """Set terminal status line: Data Terminal Ready.""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('set DTR to {}'.format('active' if self._dtr_state else 'inactive')) if self._dtr_state: self.rfc2217_set_control(SET_CONTROL_DTR_ON) else: self.rfc2217_set_control(SET_CONTROL_DTR_OFF) @property def cts(self): """Read terminal status line: Clear To Send.""" if not self.is_open: raise portNotOpenError return bool(self.get_modem_state() & MODEMSTATE_MASK_CTS) @property def dsr(self): """Read terminal status line: Data Set Ready.""" if not self.is_open: raise portNotOpenError return bool(self.get_modem_state() & MODEMSTATE_MASK_DSR) @property def ri(self): """Read terminal status line: Ring Indicator.""" if not self.is_open: raise portNotOpenError return bool(self.get_modem_state() & MODEMSTATE_MASK_RI) @property def cd(self): """Read terminal status line: Carrier Detect.""" if not self.is_open: raise portNotOpenError return bool(self.get_modem_state() & MODEMSTATE_MASK_CD) # - - - platform specific - - - # None so far # - - - RFC2217 specific - - - def _telnet_read_loop(self): """Read loop for the socket.""" mode = M_NORMAL suboption = None try: while self.is_open: try: data = self._socket.recv(1024) except socket.timeout: # just need to get out of recv form time to time to check if # still alive continue except socket.error as e: # connection fails -> terminate loop if self.logger: self.logger.debug("socket error in reader thread: {}".format(e)) break if not data: break # lost connection for byte in iterbytes(data): if mode == M_NORMAL: # interpret as command or as data if byte == IAC: mode = M_IAC_SEEN else: # store data in read buffer or sub option buffer # depending on state if suboption is not None: suboption += byte else: self._read_buffer.put(byte) elif mode == M_IAC_SEEN: if byte == IAC: # interpret as command doubled -> insert character # itself if suboption is not None: suboption += IAC else: self._read_buffer.put(IAC) mode = M_NORMAL elif byte == SB: # sub option start suboption = bytearray() mode = M_NORMAL elif byte == SE: # sub option end -> process it now self._telnet_process_subnegotiation(bytes(suboption)) suboption = None mode = M_NORMAL elif byte in (DO, DONT, WILL, WONT): # negotiation telnet_command = byte mode = M_NEGOTIATE else: # other telnet commands self._telnet_process_command(byte) mode = M_NORMAL elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following self._telnet_negotiate_option(telnet_command, byte) mode = M_NORMAL finally: self._thread = None if self.logger: self.logger.debug("read thread terminated") # - incoming telnet commands and options def _telnet_process_command(self, command): """Process commands other than DO, DONT, WILL, WONT.""" # Currently none. RFC2217 only uses negotiation and subnegotiation. if self.logger: self.logger.warning("ignoring Telnet command: {!r}".format(command)) def _telnet_negotiate_option(self, command, option): """Process incoming DO, DONT, WILL, WONT.""" # check our registered telnet options and forward command to them # they know themselves if they have to answer or not known = False for item in self._telnet_options: # can have more than one match! as some options are duplicated for # 'us' and 'them' if item.option == option: item.process_incoming(command) known = True if not known: # handle unknown options # only answer to positive requests and deny them if command == WILL or command == DO: self.telnet_send_option((DONT if command == WILL else WONT), option) if self.logger: self.logger.warning("rejected Telnet option: {!r}".format(option)) def _telnet_process_subnegotiation(self, suboption): """Process subnegotiation, the data between IAC SB and IAC SE.""" if suboption[0:1] == COM_PORT_OPTION: if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3: self._linestate = ord(suboption[2:3]) # ensure it is a number if self.logger: self.logger.info("NOTIFY_LINESTATE: {}".format(self._linestate)) elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3: self._modemstate = ord(suboption[2:3]) # ensure it is a number if self.logger: self.logger.info("NOTIFY_MODEMSTATE: {}".format(self._modemstate)) # update time when we think that a poll would make sense self._modemstate_timeout.restart(0.3) elif suboption[1:2] == FLOWCONTROL_SUSPEND: self._remote_suspend_flow = True elif suboption[1:2] == FLOWCONTROL_RESUME: self._remote_suspend_flow = False else: for item in self._rfc2217_options.values(): if item.ack_option == suboption[1:2]: #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:]) item.check_answer(bytes(suboption[2:])) break else: if self.logger: self.logger.warning("ignoring COM_PORT_OPTION: {!r}".format(suboption)) else: if self.logger: self.logger.warning("ignoring subnegotiation: {!r}".format(suboption)) # - outgoing telnet commands and options def _internal_raw_write(self, data): """internal socket write with no data escaping. used to send telnet stuff.""" with self._write_lock: self._socket.sendall(data) def telnet_send_option(self, action, option): """Send DO, DONT, WILL, WONT.""" self._internal_raw_write(IAC + action + option) def rfc2217_send_subnegotiation(self, option, value=b''): """Subnegotiation of RFC2217 parameters.""" value = value.replace(IAC, IAC_DOUBLED) self._internal_raw_write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) def rfc2217_send_purge(self, value): """\ Send purge request to the remote. (PURGE_RECEIVE_BUFFER / PURGE_TRANSMIT_BUFFER / PURGE_BOTH_BUFFERS) """ item = self._rfc2217_options['purge'] item.set(value) # transmit desired purge type item.wait(self._network_timeout) # wait for acknowledge from the server def rfc2217_set_control(self, value): """transmit change of control line to remote""" item = self._rfc2217_options['control'] item.set(value) # transmit desired control type if self._ignore_set_control_answer: # answers are ignored when option is set. compatibility mode for # servers that answer, but not the expected one... (or no answer # at all) i.e. sredird time.sleep(0.1) # this helps getting the unit tests passed else: item.wait(self._network_timeout) # wait for acknowledge from the server def rfc2217_flow_server_ready(self): """\ check if server is ready to receive data. block for some time when not. """ #~ if self._remote_suspend_flow: #~ wait--- def get_modem_state(self): """\ get last modem state (cached value. If value is "old", request a new one. This cache helps that we don't issue to many requests when e.g. all status lines, one after the other is queried by the user (CTS, DSR etc.) """ # active modem state polling enabled? is the value fresh enough? if self._poll_modem_state and self._modemstate_timeout.expired(): if self.logger: self.logger.debug('polling modem state') # when it is older, request an update self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE) timeout = Timeout(self._network_timeout) while not timeout.expired(): time.sleep(0.05) # prevent 100% CPU load # when expiration time is updated, it means that there is a new # value if not self._modemstate_timeout.expired(): break else: if self.logger: self.logger.warning('poll for modem state failed') # even when there is a timeout, do not generate an error just # return the last known value. this way we can support buggy # servers that do not respond to polls, but send automatic # updates. if self._modemstate is not None: if self.logger: self.logger.debug('using cached modem state') return self._modemstate else: # never received a notification from the server raise SerialException("remote sends no NOTIFY_MODEMSTATE") ############################################################################# # The following is code that helps implementing an RFC 2217 server. class PortManager(object): """\ This class manages the state of Telnet and RFC 2217. It needs a serial instance and a connection to work with. Connection is expected to implement a (thread safe) write function, that writes the string to the network. """ def __init__(self, serial_port, connection, logger=None): self.serial = serial_port self.connection = connection self.logger = logger self._client_is_rfc2217 = False # filter state machine self.mode = M_NORMAL self.suboption = None self.telnet_command = None # states for modem/line control events self.modemstate_mask = 255 self.last_modemstate = None self.linstate_mask = 0 # all supported telnet options self._telnet_options = [ TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED), TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE), TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED), TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok), TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok), ] # negotiate Telnet/RFC2217 -> send initial requests if self.logger: self.logger.debug("requesting initial Telnet/RFC 2217 options") for option in self._telnet_options: if option.state is REQUESTED: self.telnet_send_option(option.send_yes, option.option) # issue 1st modem state notification def _client_ok(self): """\ callback of telnet option. It gets called when option is activated. This one here is used to detect when the client agrees on RFC 2217. A flag is set so that other functions like check_modem_lines know if the client is OK. """ # The callback is used for we and they so if one party agrees, we're # already happy. it seems not all servers do the negotiation correctly # and i guess there are incorrect clients too.. so be happy if client # answers one or the other positively. self._client_is_rfc2217 = True if self.logger: self.logger.info("client accepts RFC 2217") # this is to ensure that the client gets a notification, even if there # was no change self.check_modem_lines(force_notification=True) # - outgoing telnet commands and options def telnet_send_option(self, action, option): """Send DO, DONT, WILL, WONT.""" self.connection.write(IAC + action + option) def rfc2217_send_subnegotiation(self, option, value=b''): """Subnegotiation of RFC 2217 parameters.""" value = value.replace(IAC, IAC_DOUBLED) self.connection.write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) # - check modem lines, needs to be called periodically from user to # establish polling def check_modem_lines(self, force_notification=False): """\ read control lines from serial port and compare the last value sent to remote. send updates on changes. """ modemstate = ( (self.serial.cts and MODEMSTATE_MASK_CTS) | (self.serial.dsr and MODEMSTATE_MASK_DSR) | (self.serial.ri and MODEMSTATE_MASK_RI) | (self.serial.cd and MODEMSTATE_MASK_CD)) # check what has changed deltas = modemstate ^ (self.last_modemstate or 0) # when last is None -> 0 if deltas & MODEMSTATE_MASK_CTS: modemstate |= MODEMSTATE_MASK_CTS_CHANGE if deltas & MODEMSTATE_MASK_DSR: modemstate |= MODEMSTATE_MASK_DSR_CHANGE if deltas & MODEMSTATE_MASK_RI: modemstate |= MODEMSTATE_MASK_RI_CHANGE if deltas & MODEMSTATE_MASK_CD: modemstate |= MODEMSTATE_MASK_CD_CHANGE # if new state is different and the mask allows this change, send # notification. suppress notifications when client is not rfc2217 if modemstate != self.last_modemstate or force_notification: if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification: self.rfc2217_send_subnegotiation( SERVER_NOTIFY_MODEMSTATE, to_bytes([modemstate & self.modemstate_mask])) if self.logger: self.logger.info("NOTIFY_MODEMSTATE: {}".format(modemstate)) # save last state, but forget about deltas. # otherwise it would also notify about changing deltas which is # probably not very useful self.last_modemstate = modemstate & 0xf0 # - outgoing data escaping def escape(self, data): """\ This generator function is for the user. All outgoing data has to be properly escaped, so that no IAC character in the data stream messes up the Telnet state machine in the server. socket.sendall(escape(data)) """ for byte in iterbytes(data): if byte == IAC: yield IAC yield IAC else: yield byte # - incoming data filter def filter(self, data): """\ Handle a bunch of incoming bytes. This is a generator. It will yield all characters not of interest for Telnet/RFC 2217. The idea is that the reader thread pushes data from the socket through this filter: for byte in filter(socket.recv(1024)): # do things like CR/LF conversion/whatever # and write data to the serial port serial.write(byte) (socket error handling code left as exercise for the reader) """ for byte in iterbytes(data): if self.mode == M_NORMAL: # interpret as command or as data if byte == IAC: self.mode = M_IAC_SEEN else: # store data in sub option buffer or pass it to our # consumer depending on state if self.suboption is not None: self.suboption += byte else: yield byte elif self.mode == M_IAC_SEEN: if byte == IAC: # interpret as command doubled -> insert character # itself if self.suboption is not None: self.suboption += byte else: yield byte self.mode = M_NORMAL elif byte == SB: # sub option start self.suboption = bytearray() self.mode = M_NORMAL elif byte == SE: # sub option end -> process it now self._telnet_process_subnegotiation(bytes(self.suboption)) self.suboption = None self.mode = M_NORMAL elif byte in (DO, DONT, WILL, WONT): # negotiation self.telnet_command = byte self.mode = M_NEGOTIATE else: # other telnet commands self._telnet_process_command(byte) self.mode = M_NORMAL elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following self._telnet_negotiate_option(self.telnet_command, byte) self.mode = M_NORMAL # - incoming telnet commands and options def _telnet_process_command(self, command): """Process commands other than DO, DONT, WILL, WONT.""" # Currently none. RFC2217 only uses negotiation and subnegotiation. if self.logger: self.logger.warning("ignoring Telnet command: {!r}".format(command)) def _telnet_negotiate_option(self, command, option): """Process incoming DO, DONT, WILL, WONT.""" # check our registered telnet options and forward command to them # they know themselves if they have to answer or not known = False for item in self._telnet_options: # can have more than one match! as some options are duplicated for # 'us' and 'them' if item.option == option: item.process_incoming(command) known = True if not known: # handle unknown options # only answer to positive requests and deny them if command == WILL or command == DO: self.telnet_send_option((DONT if command == WILL else WONT), option) if self.logger: self.logger.warning("rejected Telnet option: {!r}".format(option)) def _telnet_process_subnegotiation(self, suboption): """Process subnegotiation, the data between IAC SB and IAC SE.""" if suboption[0:1] == COM_PORT_OPTION: if self.logger: self.logger.debug('received COM_PORT_OPTION: {!r}'.format(suboption)) if suboption[1:2] == SET_BAUDRATE: backup = self.serial.baudrate try: (baudrate,) = struct.unpack(b"!I", suboption[2:6]) if baudrate != 0: self.serial.baudrate = baudrate except ValueError as e: if self.logger: self.logger.error("failed to set baud rate: {}".format(e)) self.serial.baudrate = backup else: if self.logger: self.logger.info("{} baud rate: {}".format('set' if baudrate else 'get', self.serial.baudrate)) self.rfc2217_send_subnegotiation(SERVER_SET_BAUDRATE, struct.pack(b"!I", self.serial.baudrate)) elif suboption[1:2] == SET_DATASIZE: backup = self.serial.bytesize try: (datasize,) = struct.unpack(b"!B", suboption[2:3]) if datasize != 0: self.serial.bytesize = datasize except ValueError as e: if self.logger: self.logger.error("failed to set data size: {}".format(e)) self.serial.bytesize = backup else: if self.logger: self.logger.info("{} data size: {}".format('set' if datasize else 'get', self.serial.bytesize)) self.rfc2217_send_subnegotiation(SERVER_SET_DATASIZE, struct.pack(b"!B", self.serial.bytesize)) elif suboption[1:2] == SET_PARITY: backup = self.serial.parity try: parity = struct.unpack(b"!B", suboption[2:3])[0] if parity != 0: self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity] except ValueError as e: if self.logger: self.logger.error("failed to set parity: {}".format(e)) self.serial.parity = backup else: if self.logger: self.logger.info("{} parity: {}".format('set' if parity else 'get', self.serial.parity)) self.rfc2217_send_subnegotiation( SERVER_SET_PARITY, struct.pack(b"!B", RFC2217_PARITY_MAP[self.serial.parity])) elif suboption[1:2] == SET_STOPSIZE: backup = self.serial.stopbits try: stopbits = struct.unpack(b"!B", suboption[2:3])[0] if stopbits != 0: self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits] except ValueError as e: if self.logger: self.logger.error("failed to set stop bits: {}".format(e)) self.serial.stopbits = backup else: if self.logger: self.logger.info("{} stop bits: {}".format('set' if stopbits else 'get', self.serial.stopbits)) self.rfc2217_send_subnegotiation( SERVER_SET_STOPSIZE, struct.pack(b"!B", RFC2217_STOPBIT_MAP[self.serial.stopbits])) elif suboption[1:2] == SET_CONTROL: if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING: if self.serial.xonxoff: self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) elif self.serial.rtscts: self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) else: self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL: self.serial.xonxoff = False self.serial.rtscts = False if self.logger: self.logger.info("changed flow control to None") self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL: self.serial.xonxoff = True if self.logger: self.logger.info("changed flow control to XON/XOFF") self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL: self.serial.rtscts = True if self.logger: self.logger.info("changed flow control to RTS/CTS") self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE: if self.logger: self.logger.warning("requested break state - not implemented") pass # XXX needs cached value elif suboption[2:3] == SET_CONTROL_BREAK_ON: self.serial.break_condition = True if self.logger: self.logger.info("changed BREAK to active") self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON) elif suboption[2:3] == SET_CONTROL_BREAK_OFF: self.serial.break_condition = False if self.logger: self.logger.info("changed BREAK to inactive") self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF) elif suboption[2:3] == SET_CONTROL_REQ_DTR: if self.logger: self.logger.warning("requested DTR state - not implemented") pass # XXX needs cached value elif suboption[2:3] == SET_CONTROL_DTR_ON: self.serial.dtr = True if self.logger: self.logger.info("changed DTR to active") self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON) elif suboption[2:3] == SET_CONTROL_DTR_OFF: self.serial.dtr = False if self.logger: self.logger.info("changed DTR to inactive") self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF) elif suboption[2:3] == SET_CONTROL_REQ_RTS: if self.logger: self.logger.warning("requested RTS state - not implemented") pass # XXX needs cached value #~ self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) elif suboption[2:3] == SET_CONTROL_RTS_ON: self.serial.rts = True if self.logger: self.logger.info("changed RTS to active") self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) elif suboption[2:3] == SET_CONTROL_RTS_OFF: self.serial.rts = False if self.logger: self.logger.info("changed RTS to inactive") self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF) #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN: #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN: #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN: #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN: #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL: #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL: #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL: elif suboption[1:2] == NOTIFY_LINESTATE: # client polls for current state self.rfc2217_send_subnegotiation( SERVER_NOTIFY_LINESTATE, to_bytes([0])) # sorry, nothing like that implemented elif suboption[1:2] == NOTIFY_MODEMSTATE: if self.logger: self.logger.info("request for modem state") # client polls for current state self.check_modem_lines(force_notification=True) elif suboption[1:2] == FLOWCONTROL_SUSPEND: if self.logger: self.logger.info("suspend") self._remote_suspend_flow = True elif suboption[1:2] == FLOWCONTROL_RESUME: if self.logger: self.logger.info("resume") self._remote_suspend_flow = False elif suboption[1:2] == SET_LINESTATE_MASK: self.linstate_mask = ord(suboption[2:3]) # ensure it is a number if self.logger: self.logger.info("line state mask: 0x{:02x}".format(self.linstate_mask)) elif suboption[1:2] == SET_MODEMSTATE_MASK: self.modemstate_mask = ord(suboption[2:3]) # ensure it is a number if self.logger: self.logger.info("modem state mask: 0x{:02x}".format(self.modemstate_mask)) elif suboption[1:2] == PURGE_DATA: if suboption[2:3] == PURGE_RECEIVE_BUFFER: self.serial.reset_input_buffer() if self.logger: self.logger.info("purge in") self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER) elif suboption[2:3] == PURGE_TRANSMIT_BUFFER: self.serial.reset_output_buffer() if self.logger: self.logger.info("purge out") self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER) elif suboption[2:3] == PURGE_BOTH_BUFFERS: self.serial.reset_input_buffer() self.serial.reset_output_buffer() if self.logger: self.logger.info("purge both") self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS) else: if self.logger: self.logger.error("undefined PURGE_DATA: {!r}".format(list(suboption[2:]))) else: if self.logger: self.logger.error("undefined COM_PORT_OPTION: {!r}".format(list(suboption[1:]))) else: if self.logger: self.logger.warning("unknown subnegotiation: {!r}".format(suboption)) # simple client test if __name__ == '__main__': import sys s = Serial('rfc2217://localhost:7000', 115200) sys.stdout.write('{}\n'.format(s)) sys.stdout.write("write...\n") s.write(b"hello\n") s.flush() sys.stdout.write("read: {}\n".format(s.read(5))) s.close() pyserial-3.4/serial/serialposix.py0000664000175000017500000007564613132220072016755 0ustar lchlch00000000000000#!/usr/bin/env python # # backend for serial IO for POSIX compatible systems, like Linux, OSX # # This file is part of pySerial. https://github.com/pyserial/pyserial # (C) 2001-2016 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause # # parts based on code from Grant B. Edwards : # ftp://ftp.visi.com/users/grante/python/PosixSerial.py # # references: http://www.easysw.com/~mike/serial/serial.html # Collection of port names (was previously used by number_to_device which was # removed. # - Linux /dev/ttyS%d (confirmed) # - cygwin/win32 /dev/com%d (confirmed) # - openbsd (OpenBSD) /dev/cua%02d # - bsd*, freebsd* /dev/cuad%d # - darwin (OS X) /dev/cuad%d # - netbsd /dev/dty%02d (NetBSD 1.6 testing by Erk) # - irix (IRIX) /dev/ttyf%d (partially tested) names depending on flow control # - hp (HP-UX) /dev/tty%dp0 (not tested) # - sunos (Solaris/SunOS) /dev/tty%c (letters, 'a'..'z') (confirmed) # - aix (AIX) /dev/tty%d # pylint: disable=abstract-method import errno import fcntl import os import select import struct import sys import termios import serial from serial.serialutil import SerialBase, SerialException, to_bytes, \ portNotOpenError, writeTimeoutError, Timeout class PlatformSpecificBase(object): BAUDRATE_CONSTANTS = {} def _set_special_baudrate(self, baudrate): raise NotImplementedError('non-standard baudrates are not supported on this platform') def _set_rs485_mode(self, rs485_settings): raise NotImplementedError('RS485 not supported on this platform') # some systems support an extra flag to enable the two in POSIX unsupported # paritiy settings for MARK and SPACE CMSPAR = 0 # default, for unsupported platforms, override below # try to detect the OS so that a device can be selected... # this code block should supply a device() and set_special_baudrate() function # for the platform plat = sys.platform.lower() if plat[:5] == 'linux': # Linux (confirmed) # noqa import array # extra termios flags CMSPAR = 0o10000000000 # Use "stick" (mark/space) parity # baudrate ioctls TCGETS2 = 0x802C542A TCSETS2 = 0x402C542B BOTHER = 0o010000 # RS485 ioctls TIOCGRS485 = 0x542E TIOCSRS485 = 0x542F SER_RS485_ENABLED = 0b00000001 SER_RS485_RTS_ON_SEND = 0b00000010 SER_RS485_RTS_AFTER_SEND = 0b00000100 SER_RS485_RX_DURING_TX = 0b00010000 class PlatformSpecific(PlatformSpecificBase): BAUDRATE_CONSTANTS = { 0: 0o000000, # hang up 50: 0o000001, 75: 0o000002, 110: 0o000003, 134: 0o000004, 150: 0o000005, 200: 0o000006, 300: 0o000007, 600: 0o000010, 1200: 0o000011, 1800: 0o000012, 2400: 0o000013, 4800: 0o000014, 9600: 0o000015, 19200: 0o000016, 38400: 0o000017, 57600: 0o010001, 115200: 0o010002, 230400: 0o010003, 460800: 0o010004, 500000: 0o010005, 576000: 0o010006, 921600: 0o010007, 1000000: 0o010010, 1152000: 0o010011, 1500000: 0o010012, 2000000: 0o010013, 2500000: 0o010014, 3000000: 0o010015, 3500000: 0o010016, 4000000: 0o010017 } def _set_special_baudrate(self, baudrate): # right size is 44 on x86_64, allow for some growth buf = array.array('i', [0] * 64) try: # get serial_struct fcntl.ioctl(self.fd, TCGETS2, buf) # set custom speed buf[2] &= ~termios.CBAUD buf[2] |= BOTHER buf[9] = buf[10] = baudrate # set serial_struct fcntl.ioctl(self.fd, TCSETS2, buf) except IOError as e: raise ValueError('Failed to set custom baud rate ({}): {}'.format(baudrate, e)) def _set_rs485_mode(self, rs485_settings): buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding try: fcntl.ioctl(self.fd, TIOCGRS485, buf) buf[0] |= SER_RS485_ENABLED if rs485_settings is not None: if rs485_settings.loopback: buf[0] |= SER_RS485_RX_DURING_TX else: buf[0] &= ~SER_RS485_RX_DURING_TX if rs485_settings.rts_level_for_tx: buf[0] |= SER_RS485_RTS_ON_SEND else: buf[0] &= ~SER_RS485_RTS_ON_SEND if rs485_settings.rts_level_for_rx: buf[0] |= SER_RS485_RTS_AFTER_SEND else: buf[0] &= ~SER_RS485_RTS_AFTER_SEND if rs485_settings.delay_before_tx is not None: buf[1] = int(rs485_settings.delay_before_tx * 1000) if rs485_settings.delay_before_rx is not None: buf[2] = int(rs485_settings.delay_before_rx * 1000) else: buf[0] = 0 # clear SER_RS485_ENABLED fcntl.ioctl(self.fd, TIOCSRS485, buf) except IOError as e: raise ValueError('Failed to set RS485 mode: {}'.format(e)) elif plat == 'cygwin': # cygwin/win32 (confirmed) class PlatformSpecific(PlatformSpecificBase): BAUDRATE_CONSTANTS = { 128000: 0x01003, 256000: 0x01005, 500000: 0x01007, 576000: 0x01008, 921600: 0x01009, 1000000: 0x0100a, 1152000: 0x0100b, 1500000: 0x0100c, 2000000: 0x0100d, 2500000: 0x0100e, 3000000: 0x0100f } elif plat[:6] == 'darwin': # OS X import array IOSSIOSPEED = 0x80045402 # _IOW('T', 2, speed_t) class PlatformSpecific(PlatformSpecificBase): osx_version = os.uname()[2].split('.') # Tiger or above can support arbitrary serial speeds if int(osx_version[0]) >= 8: def _set_special_baudrate(self, baudrate): # use IOKit-specific call to set up high speeds buf = array.array('i', [baudrate]) fcntl.ioctl(self.fd, IOSSIOSPEED, buf, 1) elif plat[:3] == 'bsd' or \ plat[:7] == 'freebsd' or \ plat[:6] == 'netbsd' or \ plat[:7] == 'openbsd': class ReturnBaudrate(object): def __getitem__(self, key): return key class PlatformSpecific(PlatformSpecificBase): # Only tested on FreeBSD: # The baud rate may be passed in as # a literal value. BAUDRATE_CONSTANTS = ReturnBaudrate() else: class PlatformSpecific(PlatformSpecificBase): pass # load some constants for later use. # try to use values from termios, use defaults from linux otherwise TIOCMGET = getattr(termios, 'TIOCMGET', 0x5415) TIOCMBIS = getattr(termios, 'TIOCMBIS', 0x5416) TIOCMBIC = getattr(termios, 'TIOCMBIC', 0x5417) TIOCMSET = getattr(termios, 'TIOCMSET', 0x5418) # TIOCM_LE = getattr(termios, 'TIOCM_LE', 0x001) TIOCM_DTR = getattr(termios, 'TIOCM_DTR', 0x002) TIOCM_RTS = getattr(termios, 'TIOCM_RTS', 0x004) # TIOCM_ST = getattr(termios, 'TIOCM_ST', 0x008) # TIOCM_SR = getattr(termios, 'TIOCM_SR', 0x010) TIOCM_CTS = getattr(termios, 'TIOCM_CTS', 0x020) TIOCM_CAR = getattr(termios, 'TIOCM_CAR', 0x040) TIOCM_RNG = getattr(termios, 'TIOCM_RNG', 0x080) TIOCM_DSR = getattr(termios, 'TIOCM_DSR', 0x100) TIOCM_CD = getattr(termios, 'TIOCM_CD', TIOCM_CAR) TIOCM_RI = getattr(termios, 'TIOCM_RI', TIOCM_RNG) # TIOCM_OUT1 = getattr(termios, 'TIOCM_OUT1', 0x2000) # TIOCM_OUT2 = getattr(termios, 'TIOCM_OUT2', 0x4000) if hasattr(termios, 'TIOCINQ'): TIOCINQ = termios.TIOCINQ else: TIOCINQ = getattr(termios, 'FIONREAD', 0x541B) TIOCOUTQ = getattr(termios, 'TIOCOUTQ', 0x5411) TIOCM_zero_str = struct.pack('I', 0) TIOCM_RTS_str = struct.pack('I', TIOCM_RTS) TIOCM_DTR_str = struct.pack('I', TIOCM_DTR) TIOCSBRK = getattr(termios, 'TIOCSBRK', 0x5427) TIOCCBRK = getattr(termios, 'TIOCCBRK', 0x5428) class Serial(SerialBase, PlatformSpecific): """\ Serial port class POSIX implementation. Serial port configuration is done with termios and fcntl. Runs on Linux and many other Un*x like systems. """ def open(self): """\ Open port with current settings. This may throw a SerialException if the port cannot be opened.""" if self._port is None: raise SerialException("Port must be configured before it can be used.") if self.is_open: raise SerialException("Port is already open.") self.fd = None # open try: self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK) except OSError as msg: self.fd = None raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg)) #~ fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # set blocking try: self._reconfigure_port(force_update=True) except: try: os.close(self.fd) except: # ignore any exception when closing the port # also to keep original exception that happened when setting up pass self.fd = None raise else: self.is_open = True try: if not self._dsrdtr: self._update_dtr_state() if not self._rtscts: self._update_rts_state() except IOError as e: if e.errno in (errno.EINVAL, errno.ENOTTY): # ignore Invalid argument and Inappropriate ioctl pass else: raise self.reset_input_buffer() self.pipe_abort_read_r, self.pipe_abort_read_w = os.pipe() self.pipe_abort_write_r, self.pipe_abort_write_w = os.pipe() fcntl.fcntl(self.pipe_abort_read_r, fcntl.F_SETFL, os.O_NONBLOCK) fcntl.fcntl(self.pipe_abort_write_r, fcntl.F_SETFL, os.O_NONBLOCK) def _reconfigure_port(self, force_update=False): """Set communication parameters on opened port.""" if self.fd is None: raise SerialException("Can only operate on a valid file descriptor") # if exclusive lock is requested, create it before we modify anything else if self._exclusive is not None: if self._exclusive: try: fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError as msg: raise SerialException(msg.errno, "Could not exclusively lock port {}: {}".format(self._port, msg)) else: fcntl.flock(self.fd, fcntl.LOCK_UN) custom_baud = None vmin = vtime = 0 # timeout is done via select if self._inter_byte_timeout is not None: vmin = 1 vtime = int(self._inter_byte_timeout * 10) try: orig_attr = termios.tcgetattr(self.fd) iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here raise SerialException("Could not configure port: {}".format(msg)) # set up raw mode / no echo / binary cflag |= (termios.CLOCAL | termios.CREAD) lflag &= ~(termios.ICANON | termios.ECHO | termios.ECHOE | termios.ECHOK | termios.ECHONL | termios.ISIG | termios.IEXTEN) # |termios.ECHOPRT for flag in ('ECHOCTL', 'ECHOKE'): # netbsd workaround for Erk if hasattr(termios, flag): lflag &= ~getattr(termios, flag) oflag &= ~(termios.OPOST | termios.ONLCR | termios.OCRNL) iflag &= ~(termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IGNBRK) if hasattr(termios, 'IUCLC'): iflag &= ~termios.IUCLC if hasattr(termios, 'PARMRK'): iflag &= ~termios.PARMRK # setup baud rate try: ispeed = ospeed = getattr(termios, 'B{}'.format(self._baudrate)) except AttributeError: try: ispeed = ospeed = self.BAUDRATE_CONSTANTS[self._baudrate] except KeyError: #~ raise ValueError('Invalid baud rate: %r' % self._baudrate) # may need custom baud rate, it isn't in our list. ispeed = ospeed = getattr(termios, 'B38400') try: custom_baud = int(self._baudrate) # store for later except ValueError: raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) else: if custom_baud < 0: raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) # setup char len cflag &= ~termios.CSIZE if self._bytesize == 8: cflag |= termios.CS8 elif self._bytesize == 7: cflag |= termios.CS7 elif self._bytesize == 6: cflag |= termios.CS6 elif self._bytesize == 5: cflag |= termios.CS5 else: raise ValueError('Invalid char len: {!r}'.format(self._bytesize)) # setup stop bits if self._stopbits == serial.STOPBITS_ONE: cflag &= ~(termios.CSTOPB) elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: cflag |= (termios.CSTOPB) # XXX same as TWO.. there is no POSIX support for 1.5 elif self._stopbits == serial.STOPBITS_TWO: cflag |= (termios.CSTOPB) else: raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits)) # setup parity iflag &= ~(termios.INPCK | termios.ISTRIP) if self._parity == serial.PARITY_NONE: cflag &= ~(termios.PARENB | termios.PARODD | CMSPAR) elif self._parity == serial.PARITY_EVEN: cflag &= ~(termios.PARODD | CMSPAR) cflag |= (termios.PARENB) elif self._parity == serial.PARITY_ODD: cflag &= ~CMSPAR cflag |= (termios.PARENB | termios.PARODD) elif self._parity == serial.PARITY_MARK and CMSPAR: cflag |= (termios.PARENB | CMSPAR | termios.PARODD) elif self._parity == serial.PARITY_SPACE and CMSPAR: cflag |= (termios.PARENB | CMSPAR) cflag &= ~(termios.PARODD) else: raise ValueError('Invalid parity: {!r}'.format(self._parity)) # setup flow control # xonxoff if hasattr(termios, 'IXANY'): if self._xonxoff: iflag |= (termios.IXON | termios.IXOFF) # |termios.IXANY) else: iflag &= ~(termios.IXON | termios.IXOFF | termios.IXANY) else: if self._xonxoff: iflag |= (termios.IXON | termios.IXOFF) else: iflag &= ~(termios.IXON | termios.IXOFF) # rtscts if hasattr(termios, 'CRTSCTS'): if self._rtscts: cflag |= (termios.CRTSCTS) else: cflag &= ~(termios.CRTSCTS) elif hasattr(termios, 'CNEW_RTSCTS'): # try it with alternate constant name if self._rtscts: cflag |= (termios.CNEW_RTSCTS) else: cflag &= ~(termios.CNEW_RTSCTS) # XXX should there be a warning if setting up rtscts (and xonxoff etc) fails?? # buffer # vmin "minimal number of characters to be read. 0 for non blocking" if vmin < 0 or vmin > 255: raise ValueError('Invalid vmin: {!r}'.format(vmin)) cc[termios.VMIN] = vmin # vtime if vtime < 0 or vtime > 255: raise ValueError('Invalid vtime: {!r}'.format(vtime)) cc[termios.VTIME] = vtime # activate settings if force_update or [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr: termios.tcsetattr( self.fd, termios.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) # apply custom baud rate, if any if custom_baud is not None: self._set_special_baudrate(custom_baud) if self._rs485_mode is not None: self._set_rs485_mode(self._rs485_mode) def close(self): """Close port""" if self.is_open: if self.fd is not None: os.close(self.fd) self.fd = None os.close(self.pipe_abort_read_w) os.close(self.pipe_abort_read_r) os.close(self.pipe_abort_write_w) os.close(self.pipe_abort_write_r) self.pipe_abort_read_r, self.pipe_abort_read_w = None, None self.pipe_abort_write_r, self.pipe_abort_write_w = None, None self.is_open = False # - - - - - - - - - - - - - - - - - - - - - - - - @property def in_waiting(self): """Return the number of bytes currently in the input buffer.""" #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str) return struct.unpack('I', s)[0] # select based implementation, proved to work on many systems def read(self, size=1): """\ Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read. """ if not self.is_open: raise portNotOpenError read = bytearray() timeout = Timeout(self._timeout) while len(read) < size: try: ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout.time_left()) if self.pipe_abort_read_r in ready: os.read(self.pipe_abort_read_r, 1000) break # If select was used with a timeout, and the timeout occurs, it # returns with empty lists -> thus abort read operation. # For timeout == 0 (non-blocking operation) also abort when # there is nothing to read. if not ready: break # timeout buf = os.read(self.fd, size - len(read)) # read should always return some data as select reported it was # ready to read when we get to this point. if not buf: # Disconnected devices, at least on Linux, show the # behavior that they are always ready to read immediately # but reading returns nothing. raise SerialException( 'device reports readiness to read but returned no data ' '(device disconnected or multiple access on port?)') read.extend(buf) except OSError as e: # this is for Python 3.x where select.error is a subclass of # OSError ignore BlockingIOErrors and EINTR. other errors are shown # https://www.python.org/dev/peps/pep-0475. if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('read failed: {}'.format(e)) except select.error as e: # this is for Python 2.x # ignore BlockingIOErrors and EINTR. all errors are shown # see also http://www.python.org/dev/peps/pep-3151/#select if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('read failed: {}'.format(e)) if timeout.expired(): break return bytes(read) def cancel_read(self): if self.is_open: os.write(self.pipe_abort_read_w, b"x") def cancel_write(self): if self.is_open: os.write(self.pipe_abort_write_w, b"x") def write(self, data): """Output the given byte string over the serial port.""" if not self.is_open: raise portNotOpenError d = to_bytes(data) tx_len = length = len(d) timeout = Timeout(self._write_timeout) while tx_len > 0: try: n = os.write(self.fd, d) if timeout.is_non_blocking: # Zero timeout indicates non-blocking - simply return the # number of bytes of data actually written return n elif not timeout.is_infinite: # when timeout is set, use select to wait for being ready # with the time left as timeout if timeout.expired(): raise writeTimeoutError abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], timeout.time_left()) if abort: os.read(self.pipe_abort_write_r, 1000) break if not ready: raise writeTimeoutError else: assert timeout.time_left() is None # wait for write operation abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], None) if abort: os.read(self.pipe_abort_write_r, 1) break if not ready: raise SerialException('write failed (select)') d = d[n:] tx_len -= n except SerialException: raise except OSError as e: # this is for Python 3.x where select.error is a subclass of # OSError ignore BlockingIOErrors and EINTR. other errors are shown # https://www.python.org/dev/peps/pep-0475. if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('write failed: {}'.format(e)) except select.error as e: # this is for Python 2.x # ignore BlockingIOErrors and EINTR. all errors are shown # see also http://www.python.org/dev/peps/pep-3151/#select if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('write failed: {}'.format(e)) if not timeout.is_non_blocking and timeout.expired(): raise writeTimeoutError return length - len(d) def flush(self): """\ Flush of file like objects. In this case, wait until all data is written. """ if not self.is_open: raise portNotOpenError termios.tcdrain(self.fd) def reset_input_buffer(self): """Clear input buffer, discarding all that is in the buffer.""" if not self.is_open: raise portNotOpenError termios.tcflush(self.fd, termios.TCIFLUSH) def reset_output_buffer(self): """\ Clear output buffer, aborting the current output and discarding all that is in the buffer. """ if not self.is_open: raise portNotOpenError termios.tcflush(self.fd, termios.TCOFLUSH) def send_break(self, duration=0.25): """\ Send break condition. Timed, returns to idle state after given duration. """ if not self.is_open: raise portNotOpenError termios.tcsendbreak(self.fd, int(duration / 0.25)) def _update_break_state(self): """\ Set break: Controls TXD. When active, no transmitting is possible. """ if self._break_state: fcntl.ioctl(self.fd, TIOCSBRK) else: fcntl.ioctl(self.fd, TIOCCBRK) def _update_rts_state(self): """Set terminal status line: Request To Send""" if self._rts_state: fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str) else: fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str) def _update_dtr_state(self): """Set terminal status line: Data Terminal Ready""" if self._dtr_state: fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str) else: fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str) @property def cts(self): """Read terminal status line: Clear To Send""" if not self.is_open: raise portNotOpenError s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) return struct.unpack('I', s)[0] & TIOCM_CTS != 0 @property def dsr(self): """Read terminal status line: Data Set Ready""" if not self.is_open: raise portNotOpenError s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) return struct.unpack('I', s)[0] & TIOCM_DSR != 0 @property def ri(self): """Read terminal status line: Ring Indicator""" if not self.is_open: raise portNotOpenError s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) return struct.unpack('I', s)[0] & TIOCM_RI != 0 @property def cd(self): """Read terminal status line: Carrier Detect""" if not self.is_open: raise portNotOpenError s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) return struct.unpack('I', s)[0] & TIOCM_CD != 0 # - - platform specific - - - - @property def out_waiting(self): """Return the number of bytes currently in the output buffer.""" #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str) return struct.unpack('I', s)[0] def fileno(self): """\ For easier use of the serial port instance with select. WARNING: this function is not portable to different platforms! """ if not self.is_open: raise portNotOpenError return self.fd def set_input_flow_control(self, enable=True): """\ Manually control flow - when software flow control is enabled. This will send XON (true) or XOFF (false) to the other device. WARNING: this function is not portable to different platforms! """ if not self.is_open: raise portNotOpenError if enable: termios.tcflow(self.fd, termios.TCION) else: termios.tcflow(self.fd, termios.TCIOFF) def set_output_flow_control(self, enable=True): """\ Manually control flow of outgoing data - when hardware or software flow control is enabled. WARNING: this function is not portable to different platforms! """ if not self.is_open: raise portNotOpenError if enable: termios.tcflow(self.fd, termios.TCOON) else: termios.tcflow(self.fd, termios.TCOOFF) def nonblocking(self): """DEPRECATED - has no use""" import warnings warnings.warn("nonblocking() has no effect, already nonblocking", DeprecationWarning) class PosixPollSerial(Serial): """\ Poll based read implementation. Not all systems support poll properly. However this one has better handling of errors, such as a device disconnecting while it's in use (e.g. USB-serial unplugged). """ def read(self, size=1): """\ Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read. """ if not self.is_open: raise portNotOpenError read = bytearray() poll = select.poll() poll.register(self.fd, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL) if size > 0: while len(read) < size: # print "\tread(): size",size, "have", len(read) #debug # wait until device becomes ready to read (or something fails) for fd, event in poll.poll(self._timeout * 1000): if event & (select.POLLERR | select.POLLHUP | select.POLLNVAL): raise SerialException('device reports error (poll)') # we don't care if it is select.POLLIN or timeout, that's # handled below buf = os.read(self.fd, size - len(read)) read.extend(buf) if ((self._timeout is not None and self._timeout >= 0) or (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0)) and not buf: break # early abort on timeout return bytes(read) class VTIMESerial(Serial): """\ Implement timeout using vtime of tty device instead of using select. This means that no inter character timeout can be specified and that the error handling is degraded. Overall timeout is disabled when inter-character timeout is used. """ def _reconfigure_port(self, force_update=True): """Set communication parameters on opened port.""" super(VTIMESerial, self)._reconfigure_port() fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # clear O_NONBLOCK if self._inter_byte_timeout is not None: vmin = 1 vtime = int(self._inter_byte_timeout * 10) elif self._timeout is None: vmin = 1 vtime = 0 else: vmin = 0 vtime = int(self._timeout * 10) try: orig_attr = termios.tcgetattr(self.fd) iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here raise serial.SerialException("Could not configure port: {}".format(msg)) if vtime < 0 or vtime > 255: raise ValueError('Invalid vtime: {!r}'.format(vtime)) cc[termios.VTIME] = vtime cc[termios.VMIN] = vmin termios.tcsetattr( self.fd, termios.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) def read(self, size=1): """\ Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read. """ if not self.is_open: raise portNotOpenError read = bytearray() while len(read) < size: buf = os.read(self.fd, size - len(read)) if not buf: break read.extend(buf) return bytes(read) # hack to make hasattr return false cancel_read = property() pyserial-3.4/serial/serialjava.py0000664000175000017500000002033612653235677016546 0ustar lchlch00000000000000#!jython # # Backend Jython with JavaComm # # This file is part of pySerial. https://github.com/pyserial/pyserial # (C) 2002-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause from serial.serialutil import * def my_import(name): mod = __import__(name) components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod def detect_java_comm(names): """try given list of modules and return that imports""" for name in names: try: mod = my_import(name) mod.SerialPort return mod except (ImportError, AttributeError): pass raise ImportError("No Java Communications API implementation found") # Java Communications API implementations # http://mho.republika.pl/java/comm/ comm = detect_java_comm([ 'javax.comm', # Sun/IBM 'gnu.io', # RXTX ]) def device(portnumber): """Turn a port number into a device name""" enum = comm.CommPortIdentifier.getPortIdentifiers() ports = [] while enum.hasMoreElements(): el = enum.nextElement() if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL: ports.append(el) return ports[portnumber].getName() class Serial(SerialBase): """\ Serial port class, implemented with Java Communications API and thus usable with jython and the appropriate java extension. """ def open(self): """\ Open port with current settings. This may throw a SerialException if the port cannot be opened. """ if self._port is None: raise SerialException("Port must be configured before it can be used.") if self.is_open: raise SerialException("Port is already open.") if type(self._port) == type(''): # strings are taken directly portId = comm.CommPortIdentifier.getPortIdentifier(self._port) else: portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port)) # numbers are transformed to a comport id obj try: self.sPort = portId.open("python serial module", 10) except Exception as msg: self.sPort = None raise SerialException("Could not open port: %s" % msg) self._reconfigurePort() self._instream = self.sPort.getInputStream() self._outstream = self.sPort.getOutputStream() self.is_open = True def _reconfigurePort(self): """Set communication parameters on opened port.""" if not self.sPort: raise SerialException("Can only operate on a valid port handle") self.sPort.enableReceiveTimeout(30) if self._bytesize == FIVEBITS: jdatabits = comm.SerialPort.DATABITS_5 elif self._bytesize == SIXBITS: jdatabits = comm.SerialPort.DATABITS_6 elif self._bytesize == SEVENBITS: jdatabits = comm.SerialPort.DATABITS_7 elif self._bytesize == EIGHTBITS: jdatabits = comm.SerialPort.DATABITS_8 else: raise ValueError("unsupported bytesize: %r" % self._bytesize) if self._stopbits == STOPBITS_ONE: jstopbits = comm.SerialPort.STOPBITS_1 elif self._stopbits == STOPBITS_ONE_POINT_FIVE: jstopbits = comm.SerialPort.STOPBITS_1_5 elif self._stopbits == STOPBITS_TWO: jstopbits = comm.SerialPort.STOPBITS_2 else: raise ValueError("unsupported number of stopbits: %r" % self._stopbits) if self._parity == PARITY_NONE: jparity = comm.SerialPort.PARITY_NONE elif self._parity == PARITY_EVEN: jparity = comm.SerialPort.PARITY_EVEN elif self._parity == PARITY_ODD: jparity = comm.SerialPort.PARITY_ODD elif self._parity == PARITY_MARK: jparity = comm.SerialPort.PARITY_MARK elif self._parity == PARITY_SPACE: jparity = comm.SerialPort.PARITY_SPACE else: raise ValueError("unsupported parity type: %r" % self._parity) jflowin = jflowout = 0 if self._rtscts: jflowin |= comm.SerialPort.FLOWCONTROL_RTSCTS_IN jflowout |= comm.SerialPort.FLOWCONTROL_RTSCTS_OUT if self._xonxoff: jflowin |= comm.SerialPort.FLOWCONTROL_XONXOFF_IN jflowout |= comm.SerialPort.FLOWCONTROL_XONXOFF_OUT self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity) self.sPort.setFlowControlMode(jflowin | jflowout) if self._timeout >= 0: self.sPort.enableReceiveTimeout(int(self._timeout*1000)) else: self.sPort.disableReceiveTimeout() def close(self): """Close port""" if self.is_open: if self.sPort: self._instream.close() self._outstream.close() self.sPort.close() self.sPort = None self.is_open = False # - - - - - - - - - - - - - - - - - - - - - - - - @property def in_waiting(self): """Return the number of characters currently in the input buffer.""" if not self.sPort: raise portNotOpenError return self._instream.available() def read(self, size=1): """\ Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read. """ if not self.sPort: raise portNotOpenError read = bytearray() if size > 0: while len(read) < size: x = self._instream.read() if x == -1: if self.timeout >= 0: break else: read.append(x) return bytes(read) def write(self, data): """Output the given string over the serial port.""" if not self.sPort: raise portNotOpenError if not isinstance(data, (bytes, bytearray)): raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) self._outstream.write(data) return len(data) def reset_input_buffer(self): """Clear input buffer, discarding all that is in the buffer.""" if not self.sPort: raise portNotOpenError self._instream.skip(self._instream.available()) def reset_output_buffer(self): """\ Clear output buffer, aborting the current output and discarding all that is in the buffer. """ if not self.sPort: raise portNotOpenError self._outstream.flush() def send_break(self, duration=0.25): """Send break condition. Timed, returns to idle state after given duration.""" if not self.sPort: raise portNotOpenError self.sPort.sendBreak(duration*1000.0) def _update_break_state(self): """Set break: Controls TXD. When active, to transmitting is possible.""" if self.fd is None: raise portNotOpenError raise SerialException("The _update_break_state function is not implemented in java.") def _update_rts_state(self): """Set terminal status line: Request To Send""" if not self.sPort: raise portNotOpenError self.sPort.setRTS(self._rts_state) def _update_dtr_state(self): """Set terminal status line: Data Terminal Ready""" if not self.sPort: raise portNotOpenError self.sPort.setDTR(self._dtr_state) @property def cts(self): """Read terminal status line: Clear To Send""" if not self.sPort: raise portNotOpenError self.sPort.isCTS() @property def dsr(self): """Read terminal status line: Data Set Ready""" if not self.sPort: raise portNotOpenError self.sPort.isDSR() @property def ri(self): """Read terminal status line: Ring Indicator""" if not self.sPort: raise portNotOpenError self.sPort.isRI() @property def cd(self): """Read terminal status line: Carrier Detect""" if not self.sPort: raise portNotOpenError self.sPort.isCD() pyserial-3.4/serial/win32.py0000664000175000017500000002514512767251002015354 0ustar lchlch00000000000000#! python # # Constants and types for use with Windows API, used by serialwin32.py # # This file is part of pySerial. https://github.com/pyserial/pyserial # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause # pylint: disable=invalid-name,too-few-public-methods,protected-access,too-many-instance-attributes from ctypes import c_ulong, c_void_p, c_int64, c_char, \ WinDLL, sizeof, Structure, Union, POINTER from ctypes.wintypes import HANDLE from ctypes.wintypes import BOOL from ctypes.wintypes import LPCWSTR from ctypes.wintypes import DWORD from ctypes.wintypes import WORD from ctypes.wintypes import BYTE _stdcall_libraries = {} _stdcall_libraries['kernel32'] = WinDLL('kernel32') INVALID_HANDLE_VALUE = HANDLE(-1).value # some details of the windows API differ between 32 and 64 bit systems.. def is_64bit(): """Returns true when running on a 64 bit system""" return sizeof(c_ulong) != sizeof(c_void_p) # ULONG_PTR is a an ordinary number, not a pointer and contrary to the name it # is either 32 or 64 bits, depending on the type of windows... # so test if this a 32 bit windows... if is_64bit(): ULONG_PTR = c_int64 else: ULONG_PTR = c_ulong class _SECURITY_ATTRIBUTES(Structure): pass LPSECURITY_ATTRIBUTES = POINTER(_SECURITY_ATTRIBUTES) try: CreateEventW = _stdcall_libraries['kernel32'].CreateEventW except AttributeError: # Fallback to non wide char version for old OS... from ctypes.wintypes import LPCSTR CreateEventA = _stdcall_libraries['kernel32'].CreateEventA CreateEventA.restype = HANDLE CreateEventA.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCSTR] CreateEvent = CreateEventA CreateFileA = _stdcall_libraries['kernel32'].CreateFileA CreateFileA.restype = HANDLE CreateFileA.argtypes = [LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE] CreateFile = CreateFileA else: CreateEventW.restype = HANDLE CreateEventW.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR] CreateEvent = CreateEventW # alias CreateFileW = _stdcall_libraries['kernel32'].CreateFileW CreateFileW.restype = HANDLE CreateFileW.argtypes = [LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE] CreateFile = CreateFileW # alias class _OVERLAPPED(Structure): pass OVERLAPPED = _OVERLAPPED class _COMSTAT(Structure): pass COMSTAT = _COMSTAT class _DCB(Structure): pass DCB = _DCB class _COMMTIMEOUTS(Structure): pass COMMTIMEOUTS = _COMMTIMEOUTS GetLastError = _stdcall_libraries['kernel32'].GetLastError GetLastError.restype = DWORD GetLastError.argtypes = [] LPOVERLAPPED = POINTER(_OVERLAPPED) LPDWORD = POINTER(DWORD) GetOverlappedResult = _stdcall_libraries['kernel32'].GetOverlappedResult GetOverlappedResult.restype = BOOL GetOverlappedResult.argtypes = [HANDLE, LPOVERLAPPED, LPDWORD, BOOL] ResetEvent = _stdcall_libraries['kernel32'].ResetEvent ResetEvent.restype = BOOL ResetEvent.argtypes = [HANDLE] LPCVOID = c_void_p WriteFile = _stdcall_libraries['kernel32'].WriteFile WriteFile.restype = BOOL WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED] LPVOID = c_void_p ReadFile = _stdcall_libraries['kernel32'].ReadFile ReadFile.restype = BOOL ReadFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED] CloseHandle = _stdcall_libraries['kernel32'].CloseHandle CloseHandle.restype = BOOL CloseHandle.argtypes = [HANDLE] ClearCommBreak = _stdcall_libraries['kernel32'].ClearCommBreak ClearCommBreak.restype = BOOL ClearCommBreak.argtypes = [HANDLE] LPCOMSTAT = POINTER(_COMSTAT) ClearCommError = _stdcall_libraries['kernel32'].ClearCommError ClearCommError.restype = BOOL ClearCommError.argtypes = [HANDLE, LPDWORD, LPCOMSTAT] SetupComm = _stdcall_libraries['kernel32'].SetupComm SetupComm.restype = BOOL SetupComm.argtypes = [HANDLE, DWORD, DWORD] EscapeCommFunction = _stdcall_libraries['kernel32'].EscapeCommFunction EscapeCommFunction.restype = BOOL EscapeCommFunction.argtypes = [HANDLE, DWORD] GetCommModemStatus = _stdcall_libraries['kernel32'].GetCommModemStatus GetCommModemStatus.restype = BOOL GetCommModemStatus.argtypes = [HANDLE, LPDWORD] LPDCB = POINTER(_DCB) GetCommState = _stdcall_libraries['kernel32'].GetCommState GetCommState.restype = BOOL GetCommState.argtypes = [HANDLE, LPDCB] LPCOMMTIMEOUTS = POINTER(_COMMTIMEOUTS) GetCommTimeouts = _stdcall_libraries['kernel32'].GetCommTimeouts GetCommTimeouts.restype = BOOL GetCommTimeouts.argtypes = [HANDLE, LPCOMMTIMEOUTS] PurgeComm = _stdcall_libraries['kernel32'].PurgeComm PurgeComm.restype = BOOL PurgeComm.argtypes = [HANDLE, DWORD] SetCommBreak = _stdcall_libraries['kernel32'].SetCommBreak SetCommBreak.restype = BOOL SetCommBreak.argtypes = [HANDLE] SetCommMask = _stdcall_libraries['kernel32'].SetCommMask SetCommMask.restype = BOOL SetCommMask.argtypes = [HANDLE, DWORD] SetCommState = _stdcall_libraries['kernel32'].SetCommState SetCommState.restype = BOOL SetCommState.argtypes = [HANDLE, LPDCB] SetCommTimeouts = _stdcall_libraries['kernel32'].SetCommTimeouts SetCommTimeouts.restype = BOOL SetCommTimeouts.argtypes = [HANDLE, LPCOMMTIMEOUTS] WaitForSingleObject = _stdcall_libraries['kernel32'].WaitForSingleObject WaitForSingleObject.restype = DWORD WaitForSingleObject.argtypes = [HANDLE, DWORD] CancelIoEx = _stdcall_libraries['kernel32'].CancelIoEx CancelIoEx.restype = BOOL CancelIoEx.argtypes = [HANDLE, LPOVERLAPPED] ONESTOPBIT = 0 # Variable c_int TWOSTOPBITS = 2 # Variable c_int ONE5STOPBITS = 1 NOPARITY = 0 # Variable c_int ODDPARITY = 1 # Variable c_int EVENPARITY = 2 # Variable c_int MARKPARITY = 3 SPACEPARITY = 4 RTS_CONTROL_HANDSHAKE = 2 # Variable c_int RTS_CONTROL_DISABLE = 0 # Variable c_int RTS_CONTROL_ENABLE = 1 # Variable c_int RTS_CONTROL_TOGGLE = 3 # Variable c_int SETRTS = 3 CLRRTS = 4 DTR_CONTROL_HANDSHAKE = 2 # Variable c_int DTR_CONTROL_DISABLE = 0 # Variable c_int DTR_CONTROL_ENABLE = 1 # Variable c_int SETDTR = 5 CLRDTR = 6 MS_DSR_ON = 32 # Variable c_ulong EV_RING = 256 # Variable c_int EV_PERR = 512 # Variable c_int EV_ERR = 128 # Variable c_int SETXOFF = 1 # Variable c_int EV_RXCHAR = 1 # Variable c_int GENERIC_WRITE = 1073741824 # Variable c_long PURGE_TXCLEAR = 4 # Variable c_int FILE_FLAG_OVERLAPPED = 1073741824 # Variable c_int EV_DSR = 16 # Variable c_int MAXDWORD = 4294967295 # Variable c_uint EV_RLSD = 32 # Variable c_int ERROR_SUCCESS = 0 ERROR_NOT_ENOUGH_MEMORY = 8 ERROR_OPERATION_ABORTED = 995 ERROR_IO_INCOMPLETE = 996 ERROR_IO_PENDING = 997 # Variable c_long ERROR_INVALID_USER_BUFFER = 1784 MS_CTS_ON = 16 # Variable c_ulong EV_EVENT1 = 2048 # Variable c_int EV_RX80FULL = 1024 # Variable c_int PURGE_RXABORT = 2 # Variable c_int FILE_ATTRIBUTE_NORMAL = 128 # Variable c_int PURGE_TXABORT = 1 # Variable c_int SETXON = 2 # Variable c_int OPEN_EXISTING = 3 # Variable c_int MS_RING_ON = 64 # Variable c_ulong EV_TXEMPTY = 4 # Variable c_int EV_RXFLAG = 2 # Variable c_int MS_RLSD_ON = 128 # Variable c_ulong GENERIC_READ = 2147483648 # Variable c_ulong EV_EVENT2 = 4096 # Variable c_int EV_CTS = 8 # Variable c_int EV_BREAK = 64 # Variable c_int PURGE_RXCLEAR = 8 # Variable c_int INFINITE = 0xFFFFFFFF class N11_OVERLAPPED4DOLLAR_48E(Union): pass class N11_OVERLAPPED4DOLLAR_484DOLLAR_49E(Structure): pass N11_OVERLAPPED4DOLLAR_484DOLLAR_49E._fields_ = [ ('Offset', DWORD), ('OffsetHigh', DWORD), ] PVOID = c_void_p N11_OVERLAPPED4DOLLAR_48E._anonymous_ = ['_0'] N11_OVERLAPPED4DOLLAR_48E._fields_ = [ ('_0', N11_OVERLAPPED4DOLLAR_484DOLLAR_49E), ('Pointer', PVOID), ] _OVERLAPPED._anonymous_ = ['_0'] _OVERLAPPED._fields_ = [ ('Internal', ULONG_PTR), ('InternalHigh', ULONG_PTR), ('_0', N11_OVERLAPPED4DOLLAR_48E), ('hEvent', HANDLE), ] _SECURITY_ATTRIBUTES._fields_ = [ ('nLength', DWORD), ('lpSecurityDescriptor', LPVOID), ('bInheritHandle', BOOL), ] _COMSTAT._fields_ = [ ('fCtsHold', DWORD, 1), ('fDsrHold', DWORD, 1), ('fRlsdHold', DWORD, 1), ('fXoffHold', DWORD, 1), ('fXoffSent', DWORD, 1), ('fEof', DWORD, 1), ('fTxim', DWORD, 1), ('fReserved', DWORD, 25), ('cbInQue', DWORD), ('cbOutQue', DWORD), ] _DCB._fields_ = [ ('DCBlength', DWORD), ('BaudRate', DWORD), ('fBinary', DWORD, 1), ('fParity', DWORD, 1), ('fOutxCtsFlow', DWORD, 1), ('fOutxDsrFlow', DWORD, 1), ('fDtrControl', DWORD, 2), ('fDsrSensitivity', DWORD, 1), ('fTXContinueOnXoff', DWORD, 1), ('fOutX', DWORD, 1), ('fInX', DWORD, 1), ('fErrorChar', DWORD, 1), ('fNull', DWORD, 1), ('fRtsControl', DWORD, 2), ('fAbortOnError', DWORD, 1), ('fDummy2', DWORD, 17), ('wReserved', WORD), ('XonLim', WORD), ('XoffLim', WORD), ('ByteSize', BYTE), ('Parity', BYTE), ('StopBits', BYTE), ('XonChar', c_char), ('XoffChar', c_char), ('ErrorChar', c_char), ('EofChar', c_char), ('EvtChar', c_char), ('wReserved1', WORD), ] _COMMTIMEOUTS._fields_ = [ ('ReadIntervalTimeout', DWORD), ('ReadTotalTimeoutMultiplier', DWORD), ('ReadTotalTimeoutConstant', DWORD), ('WriteTotalTimeoutMultiplier', DWORD), ('WriteTotalTimeoutConstant', DWORD), ] __all__ = ['GetLastError', 'MS_CTS_ON', 'FILE_ATTRIBUTE_NORMAL', 'DTR_CONTROL_ENABLE', '_COMSTAT', 'MS_RLSD_ON', 'GetOverlappedResult', 'SETXON', 'PURGE_TXABORT', 'PurgeComm', 'N11_OVERLAPPED4DOLLAR_48E', 'EV_RING', 'ONESTOPBIT', 'SETXOFF', 'PURGE_RXABORT', 'GetCommState', 'RTS_CONTROL_ENABLE', '_DCB', 'CreateEvent', '_COMMTIMEOUTS', '_SECURITY_ATTRIBUTES', 'EV_DSR', 'EV_PERR', 'EV_RXFLAG', 'OPEN_EXISTING', 'DCB', 'FILE_FLAG_OVERLAPPED', 'EV_CTS', 'SetupComm', 'LPOVERLAPPED', 'EV_TXEMPTY', 'ClearCommBreak', 'LPSECURITY_ATTRIBUTES', 'SetCommBreak', 'SetCommTimeouts', 'COMMTIMEOUTS', 'ODDPARITY', 'EV_RLSD', 'GetCommModemStatus', 'EV_EVENT2', 'PURGE_TXCLEAR', 'EV_BREAK', 'EVENPARITY', 'LPCVOID', 'COMSTAT', 'ReadFile', 'PVOID', '_OVERLAPPED', 'WriteFile', 'GetCommTimeouts', 'ResetEvent', 'EV_RXCHAR', 'LPCOMSTAT', 'ClearCommError', 'ERROR_IO_PENDING', 'EscapeCommFunction', 'GENERIC_READ', 'RTS_CONTROL_HANDSHAKE', 'OVERLAPPED', 'DTR_CONTROL_HANDSHAKE', 'PURGE_RXCLEAR', 'GENERIC_WRITE', 'LPDCB', 'CreateEventW', 'SetCommMask', 'EV_EVENT1', 'SetCommState', 'LPVOID', 'CreateFileW', 'LPDWORD', 'EV_RX80FULL', 'TWOSTOPBITS', 'LPCOMMTIMEOUTS', 'MAXDWORD', 'MS_DSR_ON', 'MS_RING_ON', 'N11_OVERLAPPED4DOLLAR_484DOLLAR_49E', 'EV_ERR', 'ULONG_PTR', 'CreateFile', 'NOPARITY', 'CloseHandle'] pyserial-3.4/serial/urlhandler/0000775000175000017500000000000013134735350016173 5ustar lchlch00000000000000pyserial-3.4/serial/urlhandler/protocol_loop.py0000664000175000017500000002360712775053567021464 0ustar lchlch00000000000000#! python # # This module implements a loop back connection receiving itself what it sent. # # The purpose of this module is.. well... You can run the unit tests with it. # and it was so easy to implement ;-) # # This file is part of pySerial. https://github.com/pyserial/pyserial # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause # # URL format: loop://[option[/option...]] # options: # - "debug" print diagnostic messages import logging import numbers import time try: import urlparse except ImportError: import urllib.parse as urlparse try: import queue except ImportError: import Queue as queue from serial.serialutil import SerialBase, SerialException, to_bytes, iterbytes, writeTimeoutError, portNotOpenError # map log level names to constants. used in from_url() LOGGER_LEVELS = { 'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR, } class Serial(SerialBase): """Serial port implementation that simulates a loop back connection in plain software.""" BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200) def __init__(self, *args, **kwargs): self.buffer_size = 4096 self.queue = None self.logger = None self._cancel_write = False super(Serial, self).__init__(*args, **kwargs) def open(self): """\ Open port with current settings. This may throw a SerialException if the port cannot be opened. """ if self.is_open: raise SerialException("Port is already open.") self.logger = None self.queue = queue.Queue(self.buffer_size) if self._port is None: raise SerialException("Port must be configured before it can be used.") # not that there is anything to open, but the function applies the # options found in the URL self.from_url(self.port) # not that there anything to configure... self._reconfigure_port() # all things set up get, now a clean start self.is_open = True if not self._dsrdtr: self._update_dtr_state() if not self._rtscts: self._update_rts_state() self.reset_input_buffer() self.reset_output_buffer() def close(self): if self.is_open: self.is_open = False try: self.queue.put_nowait(None) except queue.Full: pass super(Serial, self).close() def _reconfigure_port(self): """\ Set communication parameters on opened port. For the loop:// protocol all settings are ignored! """ # not that's it of any real use, but it helps in the unit tests if not isinstance(self._baudrate, numbers.Integral) or not 0 < self._baudrate < 2 ** 32: raise ValueError("invalid baudrate: {!r}".format(self._baudrate)) if self.logger: self.logger.info('_reconfigure_port()') def from_url(self, url): """extract host and port from an URL string""" parts = urlparse.urlsplit(url) if parts.scheme != "loop": raise SerialException( 'expected a string in the form ' '"loop://[?logging={debug|info|warning|error}]": not starting ' 'with loop:// ({!r})'.format(parts.scheme)) try: # process options now, directly altering self for option, values in urlparse.parse_qs(parts.query, True).items(): if option == 'logging': logging.basicConfig() # XXX is that good to call it here? self.logger = logging.getLogger('pySerial.loop') self.logger.setLevel(LOGGER_LEVELS[values[0]]) self.logger.debug('enabled logging') else: raise ValueError('unknown option: {!r}'.format(option)) except ValueError as e: raise SerialException( 'expected a string in the form ' '"loop://[?logging={debug|info|warning|error}]": {}'.format(e)) # - - - - - - - - - - - - - - - - - - - - - - - - @property def in_waiting(self): """Return the number of bytes currently in the input buffer.""" if not self.is_open: raise portNotOpenError if self.logger: # attention the logged value can differ from return value in # threaded environments... self.logger.debug('in_waiting -> {:d}'.format(self.queue.qsize())) return self.queue.qsize() def read(self, size=1): """\ Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read. """ if not self.is_open: raise portNotOpenError if self._timeout is not None and self._timeout != 0: timeout = time.time() + self._timeout else: timeout = None data = bytearray() while size > 0 and self.is_open: try: b = self.queue.get(timeout=self._timeout) # XXX inter char timeout except queue.Empty: if self._timeout == 0: break else: if b is not None: data += b size -= 1 else: break # check for timeout now, after data has been read. # useful for timeout = 0 (non blocking) read if timeout and time.time() > timeout: if self.logger: self.logger.info('read timeout') break return bytes(data) def cancel_read(self): self.queue.put_nowait(None) def cancel_write(self): self._cancel_write = True def write(self, data): """\ Output the given byte string over the serial port. Can block if the connection is blocked. May raise SerialException if the connection is closed. """ self._cancel_write = False if not self.is_open: raise portNotOpenError data = to_bytes(data) # calculate aprox time that would be used to send the data time_used_to_send = 10.0 * len(data) / self._baudrate # when a write timeout is configured check if we would be successful # (not sending anything, not even the part that would have time) if self._write_timeout is not None and time_used_to_send > self._write_timeout: # must wait so that unit test succeeds time_left = self._write_timeout while time_left > 0 and not self._cancel_write: time.sleep(min(time_left, 0.5)) time_left -= 0.5 if self._cancel_write: return 0 # XXX raise writeTimeoutError for byte in iterbytes(data): self.queue.put(byte, timeout=self._write_timeout) return len(data) def reset_input_buffer(self): """Clear input buffer, discarding all that is in the buffer.""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('reset_input_buffer()') try: while self.queue.qsize(): self.queue.get_nowait() except queue.Empty: pass def reset_output_buffer(self): """\ Clear output buffer, aborting the current output and discarding all that is in the buffer. """ if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('reset_output_buffer()') try: while self.queue.qsize(): self.queue.get_nowait() except queue.Empty: pass def _update_break_state(self): """\ Set break: Controls TXD. When active, to transmitting is possible. """ if self.logger: self.logger.info('_update_break_state({!r})'.format(self._break_state)) def _update_rts_state(self): """Set terminal status line: Request To Send""" if self.logger: self.logger.info('_update_rts_state({!r}) -> state of CTS'.format(self._rts_state)) def _update_dtr_state(self): """Set terminal status line: Data Terminal Ready""" if self.logger: self.logger.info('_update_dtr_state({!r}) -> state of DSR'.format(self._dtr_state)) @property def cts(self): """Read terminal status line: Clear To Send""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('CTS -> state of RTS ({!r})'.format(self._rts_state)) return self._rts_state @property def dsr(self): """Read terminal status line: Data Set Ready""" if self.logger: self.logger.info('DSR -> state of DTR ({!r})'.format(self._dtr_state)) return self._dtr_state @property def ri(self): """Read terminal status line: Ring Indicator""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('returning dummy for RI') return False @property def cd(self): """Read terminal status line: Carrier Detect""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('returning dummy for CD') return True # - - - platform specific - - - # None so far # simple client test if __name__ == '__main__': import sys s = Serial('loop://') sys.stdout.write('{}\n'.format(s)) sys.stdout.write("write...\n") s.write("hello\n") s.flush() sys.stdout.write("read: {!r}\n".format(s.read(5))) s.close() pyserial-3.4/serial/urlhandler/protocol_socket.py0000664000175000017500000003346213103436434021763 0ustar lchlch00000000000000#! python # # This module implements a simple socket based client. # It does not support changing any port parameters and will silently ignore any # requests to do so. # # The purpose of this module is that applications using pySerial can connect to # TCP/IP to serial port converters that do not support RFC 2217. # # This file is part of pySerial. https://github.com/pyserial/pyserial # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause # # URL format: socket://:[/option[/option...]] # options: # - "debug" print diagnostic messages import errno import logging import select import socket import time try: import urlparse except ImportError: import urllib.parse as urlparse from serial.serialutil import SerialBase, SerialException, to_bytes, \ portNotOpenError, writeTimeoutError, Timeout # map log level names to constants. used in from_url() LOGGER_LEVELS = { 'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR, } POLL_TIMEOUT = 5 class Serial(SerialBase): """Serial port implementation for plain sockets.""" BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200) def open(self): """\ Open port with current settings. This may throw a SerialException if the port cannot be opened. """ self.logger = None if self._port is None: raise SerialException("Port must be configured before it can be used.") if self.is_open: raise SerialException("Port is already open.") try: # timeout is used for write timeout support :/ and to get an initial connection timeout self._socket = socket.create_connection(self.from_url(self.portstr), timeout=POLL_TIMEOUT) except Exception as msg: self._socket = None raise SerialException("Could not open port {}: {}".format(self.portstr, msg)) # after connecting, switch to non-blocking, we're using select self._socket.setblocking(False) # not that there is anything to configure... self._reconfigure_port() # all things set up get, now a clean start self.is_open = True if not self._dsrdtr: self._update_dtr_state() if not self._rtscts: self._update_rts_state() self.reset_input_buffer() self.reset_output_buffer() def _reconfigure_port(self): """\ Set communication parameters on opened port. For the socket:// protocol all settings are ignored! """ if self._socket is None: raise SerialException("Can only operate on open ports") if self.logger: self.logger.info('ignored port configuration change') def close(self): """Close port""" if self.is_open: if self._socket: try: self._socket.shutdown(socket.SHUT_RDWR) self._socket.close() except: # ignore errors. pass self._socket = None self.is_open = False # in case of quick reconnects, give the server some time time.sleep(0.3) def from_url(self, url): """extract host and port from an URL string""" parts = urlparse.urlsplit(url) if parts.scheme != "socket": raise SerialException( 'expected a string in the form ' '"socket://:[?logging={debug|info|warning|error}]": ' 'not starting with socket:// ({!r})'.format(parts.scheme)) try: # process options now, directly altering self for option, values in urlparse.parse_qs(parts.query, True).items(): if option == 'logging': logging.basicConfig() # XXX is that good to call it here? self.logger = logging.getLogger('pySerial.socket') self.logger.setLevel(LOGGER_LEVELS[values[0]]) self.logger.debug('enabled logging') else: raise ValueError('unknown option: {!r}'.format(option)) if not 0 <= parts.port < 65536: raise ValueError("port not in range 0...65535") except ValueError as e: raise SerialException( 'expected a string in the form ' '"socket://:[?logging={debug|info|warning|error}]": {}'.format(e)) return (parts.hostname, parts.port) # - - - - - - - - - - - - - - - - - - - - - - - - @property def in_waiting(self): """Return the number of bytes currently in the input buffer.""" if not self.is_open: raise portNotOpenError # Poll the socket to see if it is ready for reading. # If ready, at least one byte will be to read. lr, lw, lx = select.select([self._socket], [], [], 0) return len(lr) # select based implementation, similar to posix, but only using socket API # to be portable, additionally handle socket timeout which is used to # emulate write timeouts def read(self, size=1): """\ Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read. """ if not self.is_open: raise portNotOpenError read = bytearray() timeout = Timeout(self._timeout) while len(read) < size: try: ready, _, _ = select.select([self._socket], [], [], timeout.time_left()) # If select was used with a timeout, and the timeout occurs, it # returns with empty lists -> thus abort read operation. # For timeout == 0 (non-blocking operation) also abort when # there is nothing to read. if not ready: break # timeout buf = self._socket.recv(size - len(read)) # read should always return some data as select reported it was # ready to read when we get to this point, unless it is EOF if not buf: raise SerialException('socket disconnected') read.extend(buf) except OSError as e: # this is for Python 3.x where select.error is a subclass of # OSError ignore BlockingIOErrors and EINTR. other errors are shown # https://www.python.org/dev/peps/pep-0475. if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('read failed: {}'.format(e)) except (select.error, socket.error) as e: # this is for Python 2.x # ignore BlockingIOErrors and EINTR. all errors are shown # see also http://www.python.org/dev/peps/pep-3151/#select if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('read failed: {}'.format(e)) if timeout.expired(): break return bytes(read) def write(self, data): """\ Output the given byte string over the serial port. Can block if the connection is blocked. May raise SerialException if the connection is closed. """ if not self.is_open: raise portNotOpenError d = to_bytes(data) tx_len = length = len(d) timeout = Timeout(self._write_timeout) while tx_len > 0: try: n = self._socket.send(d) if timeout.is_non_blocking: # Zero timeout indicates non-blocking - simply return the # number of bytes of data actually written return n elif not timeout.is_infinite: # when timeout is set, use select to wait for being ready # with the time left as timeout if timeout.expired(): raise writeTimeoutError _, ready, _ = select.select([], [self._socket], [], timeout.time_left()) if not ready: raise writeTimeoutError else: assert timeout.time_left() is None # wait for write operation _, ready, _ = select.select([], [self._socket], [], None) if not ready: raise SerialException('write failed (select)') d = d[n:] tx_len -= n except SerialException: raise except OSError as e: # this is for Python 3.x where select.error is a subclass of # OSError ignore BlockingIOErrors and EINTR. other errors are shown # https://www.python.org/dev/peps/pep-0475. if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('write failed: {}'.format(e)) except select.error as e: # this is for Python 2.x # ignore BlockingIOErrors and EINTR. all errors are shown # see also http://www.python.org/dev/peps/pep-3151/#select if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('write failed: {}'.format(e)) if not timeout.is_non_blocking and timeout.expired(): raise writeTimeoutError return length - len(d) def reset_input_buffer(self): """Clear input buffer, discarding all that is in the buffer.""" if not self.is_open: raise portNotOpenError # just use recv to remove input, while there is some ready = True while ready: ready, _, _ = select.select([self._socket], [], [], 0) try: self._socket.recv(4096) except OSError as e: # this is for Python 3.x where select.error is a subclass of # OSError ignore BlockingIOErrors and EINTR. other errors are shown # https://www.python.org/dev/peps/pep-0475. if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('read failed: {}'.format(e)) except (select.error, socket.error) as e: # this is for Python 2.x # ignore BlockingIOErrors and EINTR. all errors are shown # see also http://www.python.org/dev/peps/pep-3151/#select if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('read failed: {}'.format(e)) def reset_output_buffer(self): """\ Clear output buffer, aborting the current output and discarding all that is in the buffer. """ if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('ignored reset_output_buffer') def send_break(self, duration=0.25): """\ Send break condition. Timed, returns to idle state after given duration. """ if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('ignored send_break({!r})'.format(duration)) def _update_break_state(self): """Set break: Controls TXD. When active, to transmitting is possible.""" if self.logger: self.logger.info('ignored _update_break_state({!r})'.format(self._break_state)) def _update_rts_state(self): """Set terminal status line: Request To Send""" if self.logger: self.logger.info('ignored _update_rts_state({!r})'.format(self._rts_state)) def _update_dtr_state(self): """Set terminal status line: Data Terminal Ready""" if self.logger: self.logger.info('ignored _update_dtr_state({!r})'.format(self._dtr_state)) @property def cts(self): """Read terminal status line: Clear To Send""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('returning dummy for cts') return True @property def dsr(self): """Read terminal status line: Data Set Ready""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('returning dummy for dsr') return True @property def ri(self): """Read terminal status line: Ring Indicator""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('returning dummy for ri') return False @property def cd(self): """Read terminal status line: Carrier Detect""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('returning dummy for cd)') return True # - - - platform specific - - - # works on Linux and probably all the other POSIX systems def fileno(self): """Get the file handle of the underlying socket for use with select""" return self._socket.fileno() # # simple client test if __name__ == '__main__': import sys s = Serial('socket://localhost:7000') sys.stdout.write('{}\n'.format(s)) sys.stdout.write("write...\n") s.write(b"hello\n") s.flush() sys.stdout.write("read: {}\n".format(s.read(5))) s.close() pyserial-3.4/serial/urlhandler/protocol_hwgrep.py0000664000175000017500000000605712725714634022001 0ustar lchlch00000000000000#! python # # This module implements a special URL handler that uses the port listing to # find ports by searching the string descriptions. # # This file is part of pySerial. https://github.com/pyserial/pyserial # (C) 2011-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause # # URL format: hwgrep://&