pyserial-3.0.1/0000775000175000017500000000000012645031302012677 5ustar lchlch00000000000000pyserial-3.0.1/setup.py0000664000175000017500000000377112612510612014421 0ustar lchlch00000000000000# setup.py for pySerial # # Windows installer: # "python setup.py bdist_wininst" # # Direct install (all systems): # "python setup.py install" # # For Python 3.x use the corresponding Python executable, # e.g. "python3 setup.py ..." # # (C) 2001-2015 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause try: from setuptools import setup except ImportError: from distutils.core import setup # importing version does not work with Python 3 as files have not yet been # converted. import serial version = serial.VERSION 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 = "Python", long_description = "Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython", 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', 'Topic :: Communications', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Terminals :: Serial', ], platforms = 'any', scripts = ['serial/tools/miniterm.py'], ) pyserial-3.0.1/CHANGES.rst0000664000175000017500000004742112645030470014516 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. - dont use DOS device names for ports > 9. - remove send timeout (its 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 - Dynamicaly 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. (Currenly 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 an 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 handers 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 - serveral 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 rised 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 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 pyserial-3.0.1/test/0000775000175000017500000000000012645031302013656 5ustar lchlch00000000000000pyserial-3.0.1/test/test.py0000664000175000017500000001725712632623450015232 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 %d character for inWainting()" % 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)" % (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.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(10) 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.writeTimeout = True 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 (%s)" % (t2-t1)) 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" % PORT) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.0.1/test/test_rs485.py0000664000175000017500000000365312632623565016201 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 = 0 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): 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" % PORT) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.0.1/test/test_settings_dict.py0000664000175000017500000000510512632623210020134 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 = 0 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) 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" % PORT) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.0.1/test/run_all_tests.py0000664000175000017500000000314212613770506017121 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 sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) import serial print("Patching sys.path to test local version. Testing Version: %s" % (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 %s" % (modulename,)) else: module.PORT = PORT testsuite = unittest.findTestCases(module) print("found %s tests in %r" % (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.0.1/test/test_high_load.py0000664000175000017500000000431712632622737017230 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 = 0 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 %d bytes, expected %d" % (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" % PORT) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.0.1/test/test_advanced.py0000664000175000017500000001554012632356300017045 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 = 0 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 # portstr has to be set if isinstance(PORT, str): self.assertEqual(self.s.portstr.lower(), PORT.lower()) else: self.assertEqual(self.s.portstr, serial.device(PORT)) # test internals self.assertEqual(self.s._port, PORT) # test on the fly change self.s.open() self.assertTrue(self.s.isOpen()) #~ try: #~ self.s.port = 0 #~ except serial.SerialException: # port not available on system #~ pass # can't test on this machine... #~ else: #~ self.failUnless(self.s.isOpen()) #~ self.failUnlessEqual(self.s.port, 0) #~ self.failUnlessEqual(self.s.portstr, serial.device(0)) #~ try: #~ self.s.port = 1 #~ except serial.SerialException: # port not available on system #~ pass # can't test on this machine... #~ else: #~ self.failUnless(self.s.isOpen()) #~ self.failUnlessEqual(self.s.port, 1) #~ self.failUnlessEqual(self.s.portstr, serial.device(1)) def test_DoubleOpen(self): self.s.port = PORT 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.port = PORT 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.port = PORT 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): self.s.port = PORT 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" % PORT) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.0.1/test/test_iolib.py0000664000175000017500000000471712632622776016417 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 unittest import sys if __name__ == '__main__' and sys.version_info < (2, 6): sys.stderr.write("""\ ============================================================================== WARNING: this test is intended for Python 2.6 and newer where the io library is available. This seems to be an older version of Python running. Continuing anyway... ============================================================================== """) import io import serial # trick to make that this test run under 2.6 and 3.x without modification. # problem is, io library on 2.6 does NOT accept type 'str' and 3.x doesn't # like u'nicode' strings with the prefix and it is not providing an unicode # function ('str' is now what 'unicode' used to be) if sys.version_info >= (3, 0): def unicode(x): return x # on which port should the tests be performed: PORT = 0 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" % PORT) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.0.1/test/test_url.py0000664000175000017500000000306412632623254016105 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 time import sys import serial class Test_URL(unittest.TestCase): """Test serial_for_url""" def test_loop(self): """loop interface""" s = 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 s = 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.0.1/test/handlers/0000775000175000017500000000000012645031302015456 5ustar lchlch00000000000000pyserial-3.0.1/test/handlers/protocol_test.py0000664000175000017500000001562012557511411020742 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' % (option,)) except ValueError as e: raise SerialException('expected a string in the form "[test://][option[/option...]]": %s' % 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)' % (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)' % (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)' % (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)' % (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('%s\n' % s) sys.stdout.write("write...\n") s.write("hello\n") s.flush() sys.stdout.write("read: %s\n" % s.read(5)) s.close() pyserial-3.0.1/test/handlers/__init__.py0000664000175000017500000000000012557511411017563 0ustar lchlch00000000000000pyserial-3.0.1/test/test_readline.py0000664000175000017500000000654212632623056017072 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 threading import time import sys import serial #~ print serial.VERSION # on which port should the tests be performed: PORT = 0 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" % PORT) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main() pyserial-3.0.1/test/test_rfc2217.py0000664000175000017500000000251212632623135016364 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.0.1/PKG-INFO0000664000175000017500000000240612645031302013776 0ustar lchlch00000000000000Metadata-Version: 1.1 Name: pyserial Version: 3.0.1 Summary: Python Serial Port Extension Home-page: https://github.com/pyserial/pyserial Author: Chris Liechti Author-email: cliechti@gmx.net License: Python Description: Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython 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: Topic :: Communications Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Terminals :: Serial pyserial-3.0.1/serial/0000775000175000017500000000000012645031302014156 5ustar lchlch00000000000000pyserial-3.0.1/serial/serialutil.py0000664000175000017500000004606612642046703016731 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-2015 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 realy use it. # it just must not inherit from something else we might care for. class memoryview(object): pass try: unicode except (NameError, AttributeError): unicode = str # for Python 3 # "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() x = 0 while True: a = b[x:x + 1] x += 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' % (seq,)) else: b = bytearray() for item in seq: # this one handles int and bytes in Python 2.7 # add conversion in case of Python 3.x if isinstance(item, bytes): item = ord(item) b.append(item) return bytes(b) # 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 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, # number of device, numbering starts at # zero. if everything fails, the user # can specify a device string, note # that this isn't portable anymore # port will be opened if one is specified baudrate=9600, # baud rate bytesize=EIGHTBITS, # number of data bits parity=PARITY_NONE, # enable parity checking stopbits=STOPBITS_ONE, # number of stop bits timeout=None, # set a timeout value, None to wait forever xonxoff=False, # enable software flow control rtscts=False, # enable RTS/CTS flow control write_timeout=None, # set a timeout for writes dsrdtr=False, # None: use rtscts setting, dsrdtr override if True or False inter_byte_timeout=None, # Inter-character timeout, None to disable **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 # 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 # 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 # 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' % (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. See also the attribute portstr which contains the name of the port as a string. """ return self._port @port.setter def port(self, port): """\ Change the port. The attribute portstr is set to a string that contains the name of the 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" % (baudrate,)) else: if b <= 0: raise ValueError("Not a valid baudrate: %r" % (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" % (bytesize,)) self._bytesize = bytesize 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" % (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" % (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" % (timeout,)) if timeout < 0: raise ValueError("Not a valid timeout: %r" % (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" % (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" % 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" % 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" % 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 "%s(port=%r, baudrate=%r, bytesize=%r, parity=%r, stopbits=%r, timeout=%r, xonxoff=%r, rtscts=%r, dsrdtr=%r)" % ( self.__class__.__name__, id(self), self.is_open, self.portstr, self.baudrate, self.bytesize, self.parity, self.stopbits, self.timeout, self.xonxoff, self.rtscts, self.dsrdtr, ) # - - - - - - - - - - - - - - - - - - - - - - - - # compatibility with io library 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): 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 @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() 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 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: %s\n' % s.name) sys.stdout.write('baud rates: %s\n' % s.BAUDRATES) sys.stdout.write('byte sizes: %s\n' % s.BYTESIZES) sys.stdout.write('parities: %s\n' % s.PARITIES) sys.stdout.write('stop bits: %s\n' % s.STOPBITS) sys.stdout.write('%s\n' % s) pyserial-3.0.1/serial/rfc2217.py0000664000175000017500000016146612634357023015645 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 # 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 "%s:%s(%s)" % (self.name, self.active, self.state) 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.telnetSendOption(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.telnetSendOption(self.send_no, self.option) else: raise ValueError('option in illegal state %r' % 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.telnetSendOption(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' % 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 "%s:%s" % (self.name, self.state) 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.rfc2217SendSubnegotiation(self.option, self.value) if self.connection.logger: self.connection.logger.debug("SB Requesting %s -> %r" % (self.name, self.value)) def isReady(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" % (self.name)) return self.state == ACTIVE # add property to have a similar interface as TelnetOption active = property(isReady) 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_time = time.time() + timeout while time.time() < timeout_time: time.sleep(0.05) # prevent 100% CPU load if self.isReady(): break else: raise SerialException("timeout while waiting for option %r" % (self.name)) def checkAnswer(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 isReady self.state = REALLY_INACTIVE if self.connection.logger: self.connection.logger.debug("SB Answer %s -> %r -> %s" % (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): super(Serial, self).__init__(*args, **kwargs) self._thread = None self._socket = None 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)) self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) except Exception as msg: self._socket = None raise SerialException("Could not open port %s: %s" % (self.portstr, msg)) self._socket.settimeout(5) # XXX good value? # 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_expires = 0 # RFC 2217 flow control between server and client self._remote_suspend_flow = False self.is_open = True self._thread = threading.Thread(target=self._telnetReadLoop) self._thread.setDaemon(True) self._thread.setName('pySerial RFC 2217 reader thread for %s' % (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.telnetSendOption(option.send_yes, option.option) # now wait until important options are negotiated timeout_time = time.time() + self._network_timeout while time.time() < timeout_time: 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" % mandadory_options) if self.logger: self.logger.info("Negotiated options: %s" % 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" % (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: %s" % (items,)) timeout_time = time.time() + self._network_timeout while time.time() < timeout_time: 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" % items) if self.logger: self.logger.info("Negotiated settings: %s" % (items,)) if self._rtscts and self._xonxoff: raise ValueError('xonxoff and rtscts together are not supported') elif self._rtscts: self.rfc2217SetControl(SET_CONTROL_USE_HW_FLOW_CONTROL) elif self._xonxoff: self.rfc2217SetControl(SET_CONTROL_USE_SW_FLOW_CONTROL) else: self.rfc2217SetControl(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""" parts = urlparse.urlsplit(url) if parts.scheme != "rfc2217": raise SerialException('expected a string in the form "rfc2217://:[?option[&option...]]": not starting with rfc2217:// (%r)' % (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' % (option,)) # get host and port host, port = parts.hostname, parts.port if not 0 <= 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...]]": %s' % e) return (host, 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: while len(data) < size: if self._thread is None: raise SerialException('connection failed (reader thread died)') data += self._read_buffer.get(True, self._timeout) 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 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): %s" % (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.rfc2217SendPurge(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.rfc2217SendPurge(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 %s' % ('active' if self._break_state else 'inactive')) if self._break_state: self.rfc2217SetControl(SET_CONTROL_BREAK_ON) else: self.rfc2217SetControl(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 %s' % ('active' if self._rts_state else 'inactive')) if self._rts_state: self.rfc2217SetControl(SET_CONTROL_RTS_ON) else: self.rfc2217SetControl(SET_CONTROL_RTS_OFF) def _update_dtr_state(self, level=True): """Set terminal status line: Data Terminal Ready.""" if not self.is_open: raise portNotOpenError if self.logger: self.logger.info('set DTR to %s' % ('active' if self._dtr_state else 'inactive')) if self._dtr_state: self.rfc2217SetControl(SET_CONTROL_DTR_ON) else: self.rfc2217SetControl(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.getModemState() & MODEMSTATE_MASK_CTS) @property def dsr(self): """Read terminal status line: Data Set Ready.""" if not self.is_open: raise portNotOpenError return bool(self.getModemState() & MODEMSTATE_MASK_DSR) @property def ri(self): """Read terminal status line: Ring Indicator.""" if not self.is_open: raise portNotOpenError return bool(self.getModemState() & MODEMSTATE_MASK_RI) @property def cd(self): """Read terminal status line: Carrier Detect.""" if not self.is_open: raise portNotOpenError return bool(self.getModemState() & MODEMSTATE_MASK_CD) # - - - platform specific - - - # None so far # - - - RFC2217 specific - - - def _telnetReadLoop(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: %s" % (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._telnetProcessSubnegotiation(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._telnetProcessCommand(byte) mode = M_NORMAL elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following self._telnetNegotiateOption(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 _telnetProcessCommand(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" % (command,)) def _telnetNegotiateOption(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.telnetSendOption((DONT if command == WILL else WONT), option) if self.logger: self.logger.warning("rejected Telnet option: %r" % (option,)) def _telnetProcessSubnegotiation(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: %s" % 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: %s" % self._modemstate) # update time when we think that a poll would make sense self._modemstate_expires = time.time() + 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.checkAnswer(bytes(suboption[2:])) break else: if self.logger: self.logger.warning("ignoring COM_PORT_OPTION: %r" % (suboption,)) else: if self.logger: self.logger.warning("ignoring subnegotiation: %r" % (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 telnetSendOption(self, action, option): """Send DO, DONT, WILL, WONT.""" self._internal_raw_write(to_bytes([IAC, action, option])) def rfc2217SendSubnegotiation(self, option, value=b''): """Subnegotiation of RFC2217 parameters.""" value = value.replace(IAC, IAC_DOUBLED) self._internal_raw_write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE])) def rfc2217SendPurge(self, value): item = self._rfc2217_options['purge'] item.set(value) # transmit desired purge type item.wait(self._network_timeout) # wait for acknowledge from the server def rfc2217SetControl(self, value): 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 rfc2217FlowServerReady(self): """\ check if server is ready to receive data. block for some time when not. """ #~ if self._remote_suspend_flow: #~ wait--- def getModemState(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 (getCTS, getDSR etc.) """ # active modem state polling enabled? is the value fresh enough? if self._poll_modem_state and self._modemstate_expires < time.time(): if self.logger: self.logger.debug('polling modem state') # when it is older, request an update self.rfc2217SendSubnegotiation(NOTIFY_MODEMSTATE) timeout_time = time.time() + self._network_timeout while time.time() < timeout_time: time.sleep(0.05) # prevent 100% CPU load # when expiration time is updated, it means that there is a new # value if self._modemstate_expires > time.time(): 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.telnetSendOption(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 telnetSendOption(self, action, option): """Send DO, DONT, WILL, WONT.""" self.connection.write(to_bytes([IAC, action, option])) def rfc2217SendSubnegotiation(self, option, value=b''): """Subnegotiation of RFC 2217 parameters.""" value = value.replace(IAC, IAC_DOUBLED) self.connection.write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE])) # - check modem lines, needs to be called periodically from user to # establish polling def check_modem_lines(self, force_notification=False): modemstate = ( (self.serial.getCTS() and MODEMSTATE_MASK_CTS) | (self.serial.getDSR() and MODEMSTATE_MASK_DSR) | (self.serial.getRI() and MODEMSTATE_MASK_RI) | (self.serial.getCD() 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.rfc2217SendSubnegotiation( SERVER_NOTIFY_MODEMSTATE, to_bytes([modemstate & self.modemstate_mask]) ) if self.logger: self.logger.info("NOTIFY_MODEMSTATE: %s" % (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._telnetProcessSubnegotiation(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._telnetProcessCommand(byte) self.mode = M_NORMAL elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following self._telnetNegotiateOption(self.telnet_command, byte) self.mode = M_NORMAL # - incoming telnet commands and options def _telnetProcessCommand(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" % (command,)) def _telnetNegotiateOption(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.telnetSendOption((DONT if command == WILL else WONT), option) if self.logger: self.logger.warning("rejected Telnet option: %r" % (option,)) def _telnetProcessSubnegotiation(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' % (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: %s" % (e,)) self.serial.baudrate = backup else: if self.logger: self.logger.info("%s baud rate: %s" % ('set' if baudrate else 'get', self.serial.baudrate)) self.rfc2217SendSubnegotiation(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: %s" % (e,)) self.serial.bytesize = backup else: if self.logger: self.logger.info("%s data size: %s" % ('set' if datasize else 'get', self.serial.bytesize)) self.rfc2217SendSubnegotiation(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: %s" % (e,)) self.serial.parity = backup else: if self.logger: self.logger.info("%s parity: %s" % ('set' if parity else 'get', self.serial.parity)) self.rfc2217SendSubnegotiation( 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: %s" % (e,)) self.serial.stopbits = backup else: if self.logger: self.logger.info("%s stop bits: %s" % ('set' if stopbits else 'get', self.serial.stopbits)) self.rfc2217SendSubnegotiation( 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.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) elif self.serial.rtscts: self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) else: self.rfc2217SendSubnegotiation(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.rfc2217SendSubnegotiation(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.rfc2217SendSubnegotiation(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.rfc2217SendSubnegotiation(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.setBreak(True) if self.logger: self.logger.info("changed BREAK to active") self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON) elif suboption[2:3] == SET_CONTROL_BREAK_OFF: self.serial.setBreak(False) if self.logger: self.logger.info("changed BREAK to inactive") self.rfc2217SendSubnegotiation(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.setDTR(True) if self.logger: self.logger.info("changed DTR to active") self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON) elif suboption[2:3] == SET_CONTROL_DTR_OFF: self.serial.setDTR(False) if self.logger: self.logger.info("changed DTR to inactive") self.rfc2217SendSubnegotiation(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.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) elif suboption[2:3] == SET_CONTROL_RTS_ON: self.serial.setRTS(True) if self.logger: self.logger.info("changed RTS to active") self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) elif suboption[2:3] == SET_CONTROL_RTS_OFF: self.serial.setRTS(False) if self.logger: self.logger.info("changed RTS to inactive") self.rfc2217SendSubnegotiation(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.rfc2217SendSubnegotiation( 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" % (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" % (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.rfc2217SendSubnegotiation(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.rfc2217SendSubnegotiation(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.rfc2217SendSubnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS) else: if self.logger: self.logger.error("undefined PURGE_DATA: %r" % list(suboption[2:])) else: if self.logger: self.logger.error("undefined COM_PORT_OPTION: %r" % list(suboption[1:])) else: if self.logger: self.logger.warning("unknown subnegotiation: %r" % (suboption,)) # simple client test if __name__ == '__main__': import sys s = Serial('rfc2217://localhost:7000', 115200) sys.stdout.write('%s\n' % s) sys.stdout.write("write...\n") s.write(b"hello\n") s.flush() sys.stdout.write("read: %s\n" % s.read(5)) s.close() pyserial-3.0.1/serial/serialposix.py0000664000175000017500000007273512634356552017127 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-2015 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 import errno import fcntl import os import select import struct import sys import termios import time import serial from serial.serialutil import SerialBase, SerialException, to_bytes, portNotOpenError, writeTimeoutError class PlatformSpecificBase(object): BAUDRATE_CONSTANTS = {} def number_to_device(self, port_number): sys.stderr.write("""\ don't know how to number ttys on this system. ! Use an explicit path (eg /dev/ttyS1) or send this information to ! the author of this module: sys.platform = %r os.name = %r serialposix.py version = %s also add the device name of the serial port and where the counting starts for the first serial port. e.g. 'first serial port: /dev/ttyS0' and with a bit luck you can get this module running... """ % (sys.platform, os.name, serial.VERSION)) raise NotImplementedError('no number-to-device mapping defined on this platform') 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') # 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) import array # 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 number_to_device(self, port_number): return '/dev/ttyS%d' % (port_number,) 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 (%s): %s' % (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) 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 buf[1] = int(rs485_settings.delay_rts_before_send * 1000) buf[2] = int(rs485_settings.delay_rts_after_send * 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: %s' % (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 } def number_to_device(self, port_number): return '/dev/com%d' % (port_number + 1,) elif plat[:7] == 'openbsd': # OpenBSD class PlatformSpecific(PlatformSpecificBase): def number_to_device(self, port_number): return '/dev/cua%02d' % (port_number,) elif plat[:3] == 'bsd' or plat[:7] == 'freebsd': class PlatformSpecific(PlatformSpecificBase): def number_to_device(self, port_number): return '/dev/cuad%d' % (port_number,) elif plat[:6] == 'darwin': # OS X import array IOSSIOSPEED = 0x80045402 # _IOW('T', 2, speed_t) class PlatformSpecific(PlatformSpecificBase): def number_to_device(self, port_number): return '/dev/cuad%d' % (port_number,) 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[:6] == 'netbsd': # NetBSD 1.6 testing by Erk class PlatformSpecific(PlatformSpecificBase): def number_to_device(self, port_number): return '/dev/dty%02d' % (port_number,) elif plat[:4] == 'irix': # IRIX (partially tested) class PlatformSpecific(PlatformSpecificBase): def number_to_device(self, port_number): return '/dev/ttyf%d' % (port_number + 1,) # XXX different device names depending on flow control elif plat[:2] == 'hp': # HP-UX (not tested) class PlatformSpecific(PlatformSpecificBase): def number_to_device(self, port_number): return '/dev/tty%dp0' % (port_number + 1,) elif plat[:5] == 'sunos': # Solaris/SunOS (confirmed) class PlatformSpecific(PlatformSpecificBase): def number_to_device(self, port_number): return '/dev/tty%c' % (ord('a') + port_number,) elif plat[:3] == 'aix': # AIX class PlatformSpecific(PlatformSpecificBase): def number_to_device(self, port_number): return '/dev/tty%d' % (port_number,) else: class PlatformSpecific(PlatformSpecificBase): pass # whats up with "aix", "beos", .... # they should work, just need to know the device names. # 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) CMSPAR = 0o10000000000 # Use "stick" (mark/space) parity 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 %s: %s" % (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 if not self._dsrdtr: self._update_dtr_state() if not self._rtscts: self._update_rts_state() self.reset_input_buffer() 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") 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: %s" % 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%s' % (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' % self._baudrate) else: if custom_baud < 0: raise ValueError('Invalid baud rate: %r' % 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' % 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' % self._stopbits) # setup parity iflag &= ~(termios.INPCK | termios.ISTRIP) if self._parity == serial.PARITY_NONE: cflag &= ~(termios.PARENB | termios.PARODD) elif self._parity == serial.PARITY_EVEN: cflag &= ~(termios.PARODD) cflag |= (termios.PARENB) elif self._parity == serial.PARITY_ODD: cflag |= (termios.PARENB | termios.PARODD) elif self._parity == serial.PARITY_MARK and plat[:5] == 'linux': cflag |= (termios.PARENB | CMSPAR | termios.PARODD) elif self._parity == serial.PARITY_SPACE and plat[:5] == 'linux': cflag |= (termios.PARENB | CMSPAR) cflag &= ~(termios.PARODD) else: raise ValueError('Invalid parity: %r' % 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 ' % vmin) cc[termios.VMIN] = vmin # vtime if vtime < 0 or vtime > 255: raise ValueError('Invalid vtime: %r' % 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 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 = self._timeout while len(read) < size: try: start_time = time.time() ready, _, _ = select.select([self.fd], [], [], timeout) # 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) if timeout is not None: timeout -= time.time() - start_time if timeout <= 0: break except OSError as e: # this is for Python 3.x where select.error is a subclass of # OSError ignore EAGAIN errors. all other errors are shown if e.errno != errno.EAGAIN: raise SerialException('read failed: %s' % (e,)) except select.error as e: # this is for Python 2.x # ignore EAGAIN errors. all other errors are shown # see also http://www.python.org/dev/peps/pep-3151/#select if e[0] != errno.EAGAIN: raise SerialException('read failed: %s' % (e,)) return bytes(read) 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 = len(d) if self._write_timeout is not None and self._write_timeout > 0: timeout = time.time() + self._write_timeout else: timeout = None while tx_len > 0: try: n = os.write(self.fd, d) if timeout: # when timeout is set, use select to wait for being ready # with the time left as timeout timeleft = timeout - time.time() if timeleft < 0: raise writeTimeoutError _, ready, _ = select.select([], [self.fd], [], timeleft) if not ready: raise writeTimeoutError else: # wait for write operation _, ready, _ = select.select([], [self.fd], [], None) if not ready: raise SerialException('write failed (select)') d = d[n:] tx_len -= n except SerialException: raise except OSError as v: if v.errno != errno.EAGAIN: raise SerialException('write failed: %s' % (v,)) # still calculate and check timeout if timeout and timeout - time.time() < 0: raise writeTimeoutError return len(data) 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 nonblocking(self): """internal - not portable!""" if not self.is_open: raise portNotOpenError fcntl.fcntl(self.fd, fcntl.F_SETFL, os.O_NONBLOCK) 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) 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 self.fd is None: 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) 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: %s" % msg) if vtime < 0 or vtime > 255: raise ValueError('Invalid vtime: %r' % 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) if __name__ == '__main__': s = Serial(0, baudrate=19200, # baud rate bytesize=serial.EIGHTBITS, # number of data bits parity=serial.PARITY_EVEN, # enable parity checking stopbits=serial.STOPBITS_ONE, # number of stop bits timeout=3, # set a timeout value, None for waiting forever xonxoff=0, # enable software flow control rtscts=0, # enable RTS/CTS flow control ) s.rts = True s.dtr = True s.reset_input_buffer() s.reset_output_buffer() s.write('hello') sys.stdout.write('%r\n' % s.read(5)) sys.stdout.write('%s\n' % s.inWaiting()) del s pyserial-3.0.1/serial/serialjava.py0000664000175000017500000002157212634356530016673 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() if __name__ == '__main__': s = Serial(0, baudrate=19200, # baudrate bytesize=EIGHTBITS, # number of databits parity=PARITY_EVEN, # enable parity checking stopbits=STOPBITS_ONE, # number of stopbits timeout=3, # set a timeout value, None for waiting forever xonxoff=0, # enable software flow control rtscts=0, # enable RTS/CTS flow control ) s.setRTS(1) s.setDTR(1) s.reset_input_buffer() s.reset_output_buffer() s.write('hello') sys.stdio.write('%r\n' % s.read(5)) sys.stdio.write('%s\n' % s.in_waiting()) del s pyserial-3.0.1/serial/win32.py0000664000175000017500000002431412634357003015505 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 from ctypes import * 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(): # assume 64 bits ULONG_PTR = c_int64 else: # 32 bits 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] 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_IO_PENDING = 997 # Variable c_long 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.0.1/serial/urlhandler/0000775000175000017500000000000012645031302016316 5ustar lchlch00000000000000pyserial-3.0.1/serial/urlhandler/protocol_loop.py0000664000175000017500000002241512634356354021605 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 * # 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): super(Serial, self).__init__(*args, **kwargs) self.buffer_size = 4096 self.queue = None self.logger = None 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" % (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)' % (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' % (option,)) except ValueError as e: raise SerialException('expected a string in the form "loop://[?logging={debug|info|warning|error}]": %s' % 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' % (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 data 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 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 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: time.sleep(self._write_timeout) # must wait so that unit test succeeds 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)' % (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' % (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' % (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)' % (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)' % (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('%s\n' % s) sys.stdout.write("write...\n") s.write("hello\n") s.flush() sys.stdout.write("read: %s\n" % s.read(5)) s.close() pyserial-3.0.1/serial/urlhandler/protocol_socket.py0000664000175000017500000002315212634357425022123 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 logging import select import socket import time try: import urlparse except ImportError: import urllib.parse as urlparse from serial.serialutil import SerialBase, SerialException, portNotOpenError, to_bytes # 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 = 2 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: self._socket = socket.create_connection(self.from_url(self.portstr)) except Exception as msg: self._socket = None raise SerialException("Could not open port %s: %s" % (self.portstr, msg)) self._socket.settimeout(POLL_TIMEOUT) # used for write timeout support :/ # 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 _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)' % (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' % (option,)) # get host and port host, port = parts.hostname, parts.port if not 0 <= 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}]": %s' % e) return (host, 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) 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() if self._timeout is not None: timeout = time.time() + self._timeout else: timeout = None while len(data) < size: try: # an implementation with internal buffer would be better # performing... block = self._socket.recv(size - len(data)) if block: data.extend(block) else: # no data -> EOF (connection probably closed) break except socket.timeout: # just need to get out of recv from time to time to check if # still alive and timeout did not expire pass except socket.error as e: # connection fails -> terminate loop raise SerialException('connection failed (%s)' % e) if timeout is not None and time.time() > timeout: break 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 try: self._socket.sendall(to_bytes(data)) except socket.error as e: # XXX what exception if socket connection fails raise SerialException("socket connection failed: %s" % 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 if self.logger: self.logger.info('ignored reset_input_buffer') 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)' % (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)' % (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)' % (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)' % (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('%s\n' % s) sys.stdout.write("write...\n") s.write(b"hello\n") s.flush() sys.stdout.write("read: %s\n" % s.read(5)) s.close() pyserial-3.0.1/serial/urlhandler/protocol_hwgrep.py0000664000175000017500000000573512636342255022133 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://&