15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#! python
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Python Serial Port Extension for Win32, Linux, BSD, Jython
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# see __init__.py
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# This module implements a loop back connection receiving itself what it sent.
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# The purpose of this module is.. well... You can run the unit tests with it.
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# and it was so easy to implement ;-)
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# (C) 2001-2011 Chris Liechti <cliechti@gmx.net>
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# this is distributed under a free software license, see license.txt
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# URL format:    loop://[option[/option...]]
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# options:
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# - "debug" print diagnostic messages
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from serial.serialutil import *
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import threading
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import time
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import logging
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# map log level names to constants. used in fromURL()
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)LOGGER_LEVELS = {
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'debug': logging.DEBUG,
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'info': logging.INFO,
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'warning': logging.WARNING,
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'error': logging.ERROR,
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class LoopbackSerial(SerialBase):
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Serial port implementation that simulates a loop back connection in plain software."""
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 9600, 19200, 38400, 57600, 115200)
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def open(self):
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Open port with current settings. This may throw a SerialException
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)           if the port cannot be opened."""
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self._isOpen:
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            raise SerialException("Port is already open.")
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.logger = None
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.buffer_lock = threading.Lock()
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.loop_buffer = bytearray()
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.cts = False
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.dsr = False
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self._port is None:
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            raise SerialException("Port must be configured before it can be used.")
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # not that there is anything to open, but the function applies the
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # options found in the URL
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.fromURL(self.port)
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # not that there anything to configure...
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self._reconfigurePort()
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # all things set up get, now a clean start
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self._isOpen = True
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._rtscts:
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.setRTS(True)
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.setDTR(True)
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.flushInput()
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.flushOutput()
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def _reconfigurePort(self):
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Set communication parameters on opened port. for the loop://
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        protocol all settings are ignored!"""
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # not that's it of any real use, but it helps in the unit tests
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not isinstance(self._baudrate, (int, long)) or not 0 < self._baudrate < 2**32:
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            raise ValueError("invalid baudrate: %r" % (self._baudrate))
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('_reconfigurePort()')
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def close(self):
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Close port"""
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self._isOpen:
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self._isOpen = False
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # in case of quick reconnects, give the server some time
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            time.sleep(0.3)
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def makeDeviceName(self, port):
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        raise SerialException("there is no sensible way to turn numbers into URLs")
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def fromURL(self, url):
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """extract host and port from an URL string"""
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if url.lower().startswith("loop://"): url = url[7:]
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        try:
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # process options now, directly altering self
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            for option in url.split('/'):
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if '=' in option:
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    option, value = option.split('=', 1)
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                else:
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    value = None
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if not option:
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    pass
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                elif option == 'logging':
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    logging.basicConfig()   # XXX is that good to call it here?
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    self.logger = logging.getLogger('pySerial.loop')
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    self.logger.setLevel(LOGGER_LEVELS[value])
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    self.logger.debug('enabled logging')
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                else:
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    raise ValueError('unknown option: %r' % (option,))
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        except ValueError, e:
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            raise SerialException('expected a string in the form "[loop://][option[/option...]]": %s' % e)
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def inWaiting(self):
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Return the number of characters currently in the input buffer."""
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # attention the logged value can differ from return value in
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # threaded environments...
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.debug('inWaiting() -> %d' % (len(self.loop_buffer),))
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return len(self.loop_buffer)
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def read(self, size=1):
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Read size bytes from the serial port. If a timeout is set it may
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return less characters as requested. With no timeout it will block
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        until the requested number of bytes is read."""
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self._timeout is not None:
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            timeout = time.time() + self._timeout
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        else:
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            timeout = None
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        data = bytearray()
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        while size > 0:
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.buffer_lock.acquire()
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            try:
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                block = to_bytes(self.loop_buffer[:size])
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                del self.loop_buffer[:size]
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            finally:
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                self.buffer_lock.release()
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            data += block
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            size -= len(block)
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # check for timeout now, after data has been read.
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # useful for timeout = 0 (non blocking) read
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if timeout and time.time() > timeout:
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                break
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return bytes(data)
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def write(self, data):
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Output the given string over the serial port. Can block if the
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        connection is blocked. May raise SerialException if the connection is
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        closed."""
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # ensure we're working with bytes
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        data = to_bytes(data)
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # calculate aprox time that would be used to send the data
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        time_used_to_send = 10.0*len(data) / self._baudrate
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # when a write timeout is configured check if we would be successful
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # (not sending anything, not even the part that would have time)
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self._writeTimeout is not None and time_used_to_send > self._writeTimeout:
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            time.sleep(self._writeTimeout) # must wait so that unit test succeeds
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            raise writeTimeoutError
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.buffer_lock.acquire()
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        try:
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.loop_buffer += data
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        finally:
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.buffer_lock.release()
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return len(data)
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def flushInput(self):
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Clear input buffer, discarding all that is in the buffer."""
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('flushInput()')
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.buffer_lock.acquire()
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        try:
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            del self.loop_buffer[:]
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        finally:
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.buffer_lock.release()
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def flushOutput(self):
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Clear output buffer, aborting the current output and
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        discarding all that is in the buffer."""
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('flushOutput()')
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def sendBreak(self, duration=0.25):
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Send break condition. Timed, returns to idle state after given
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        duration."""
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def setBreak(self, level=True):
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Set break: Controls TXD. When active, to transmitting is
1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        possible."""
1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('setBreak(%r)' % (level,))
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def setRTS(self, level=True):
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Set terminal status line: Request To Send"""
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('setRTS(%r) -> state of CTS' % (level,))
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.cts = level
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def setDTR(self, level=True):
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Set terminal status line: Data Terminal Ready"""
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('setDTR(%r) -> state of DSR' % (level,))
2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.dsr = level
2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def getCTS(self):
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Read terminal status line: Clear To Send"""
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('getCTS() -> state of RTS (%r)' % (self.cts,))
2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return self.cts
2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def getDSR(self):
2155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Read terminal status line: Data Set Ready"""
2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('getDSR() -> state of DTR (%r)' % (self.dsr,))
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return self.dsr
2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def getRI(self):
2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Read terminal status line: Ring Indicator"""
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('returning dummy for getRI()')
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return False
2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def getCD(self):
2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Read terminal status line: Carrier Detect"""
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not self._isOpen: raise portNotOpenError
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.logger:
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.logger.info('returning dummy for getCD()')
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return True
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # - - - platform specific - - -
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # None so far
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# assemble Serial class with the platform specific implementation and the base
2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# library, derive from io.RawIOBase
2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)try:
2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    import io
2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)except ImportError:
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # classic version with our own file-like emulation
2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    class Serial(LoopbackSerial, FileLike):
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        pass
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)else:
2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # io library present
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    class Serial(LoopbackSerial, io.RawIOBase):
2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        pass
2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# simple client test
2555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)if __name__ == '__main__':
2565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    import sys
2575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    s = Serial('loop://')
2585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sys.stdout.write('%s\n' % s)
2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sys.stdout.write("write...\n")
2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    s.write("hello\n")
2625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    s.flush()
2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sys.stdout.write("read: %s\n" % s.read(5))
2645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    s.close()
266