1#!jython
2#
3# Python Serial Port Extension for Win32, Linux, BSD, Jython
4# module for serial IO for Jython and JavaComm
5# see __init__.py
6#
7# (C) 2002-2008 Chris Liechti <cliechti@gmx.net>
8# this is distributed under a free software license, see license.txt
9
10from serial.serialutil import *
11
12def my_import(name):
13    mod = __import__(name)
14    components = name.split('.')
15    for comp in components[1:]:
16        mod = getattr(mod, comp)
17    return mod
18
19
20def detect_java_comm(names):
21    """try given list of modules and return that imports"""
22    for name in names:
23        try:
24            mod = my_import(name)
25            mod.SerialPort
26            return mod
27        except (ImportError, AttributeError):
28            pass
29    raise ImportError("No Java Communications API implementation found")
30
31
32# Java Communications API implementations
33# http://mho.republika.pl/java/comm/
34
35comm = detect_java_comm([
36    'javax.comm', # Sun/IBM
37    'gnu.io',     # RXTX
38])
39
40
41def device(portnumber):
42    """Turn a port number into a device name"""
43    enum = comm.CommPortIdentifier.getPortIdentifiers()
44    ports = []
45    while enum.hasMoreElements():
46        el = enum.nextElement()
47        if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL:
48            ports.append(el)
49    return ports[portnumber].getName()
50
51
52class JavaSerial(SerialBase):
53    """Serial port class, implemented with Java Communications API and
54       thus usable with jython and the appropriate java extension."""
55
56    def open(self):
57        """Open port with current settings. This may throw a SerialException
58           if the port cannot be opened."""
59        if self._port is None:
60            raise SerialException("Port must be configured before it can be used.")
61        if self._isOpen:
62            raise SerialException("Port is already open.")
63        if type(self._port) == type(''):      # strings are taken directly
64            portId = comm.CommPortIdentifier.getPortIdentifier(self._port)
65        else:
66            portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port))     # numbers are transformed to a comport id obj
67        try:
68            self.sPort = portId.open("python serial module", 10)
69        except Exception, msg:
70            self.sPort = None
71            raise SerialException("Could not open port: %s" % msg)
72        self._reconfigurePort()
73        self._instream = self.sPort.getInputStream()
74        self._outstream = self.sPort.getOutputStream()
75        self._isOpen = True
76
77    def _reconfigurePort(self):
78        """Set communication parameters on opened port."""
79        if not self.sPort:
80            raise SerialException("Can only operate on a valid port handle")
81
82        self.sPort.enableReceiveTimeout(30)
83        if self._bytesize == FIVEBITS:
84            jdatabits = comm.SerialPort.DATABITS_5
85        elif self._bytesize == SIXBITS:
86            jdatabits = comm.SerialPort.DATABITS_6
87        elif self._bytesize == SEVENBITS:
88            jdatabits = comm.SerialPort.DATABITS_7
89        elif self._bytesize == EIGHTBITS:
90            jdatabits = comm.SerialPort.DATABITS_8
91        else:
92            raise ValueError("unsupported bytesize: %r" % self._bytesize)
93
94        if self._stopbits == STOPBITS_ONE:
95            jstopbits = comm.SerialPort.STOPBITS_1
96        elif stopbits == STOPBITS_ONE_POINT_FIVE:
97            self._jstopbits = comm.SerialPort.STOPBITS_1_5
98        elif self._stopbits == STOPBITS_TWO:
99            jstopbits = comm.SerialPort.STOPBITS_2
100        else:
101            raise ValueError("unsupported number of stopbits: %r" % self._stopbits)
102
103        if self._parity == PARITY_NONE:
104            jparity = comm.SerialPort.PARITY_NONE
105        elif self._parity == PARITY_EVEN:
106            jparity = comm.SerialPort.PARITY_EVEN
107        elif self._parity == PARITY_ODD:
108            jparity = comm.SerialPort.PARITY_ODD
109        elif self._parity == PARITY_MARK:
110            jparity = comm.SerialPort.PARITY_MARK
111        elif self._parity == PARITY_SPACE:
112            jparity = comm.SerialPort.PARITY_SPACE
113        else:
114            raise ValueError("unsupported parity type: %r" % self._parity)
115
116        jflowin = jflowout = 0
117        if self._rtscts:
118            jflowin  |=  comm.SerialPort.FLOWCONTROL_RTSCTS_IN
119            jflowout |=  comm.SerialPort.FLOWCONTROL_RTSCTS_OUT
120        if self._xonxoff:
121            jflowin  |=  comm.SerialPort.FLOWCONTROL_XONXOFF_IN
122            jflowout |=  comm.SerialPort.FLOWCONTROL_XONXOFF_OUT
123
124        self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity)
125        self.sPort.setFlowControlMode(jflowin | jflowout)
126
127        if self._timeout >= 0:
128            self.sPort.enableReceiveTimeout(self._timeout*1000)
129        else:
130            self.sPort.disableReceiveTimeout()
131
132    def close(self):
133        """Close port"""
134        if self._isOpen:
135            if self.sPort:
136                self._instream.close()
137                self._outstream.close()
138                self.sPort.close()
139                self.sPort = None
140            self._isOpen = False
141
142    def makeDeviceName(self, port):
143        return device(port)
144
145    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
146
147    def inWaiting(self):
148        """Return the number of characters currently in the input buffer."""
149        if not self.sPort: raise portNotOpenError
150        return self._instream.available()
151
152    def read(self, size=1):
153        """Read size bytes from the serial port. If a timeout is set it may
154           return less characters as requested. With no timeout it will block
155           until the requested number of bytes is read."""
156        if not self.sPort: raise portNotOpenError
157        read = bytearray()
158        if size > 0:
159            while len(read) < size:
160                x = self._instream.read()
161                if x == -1:
162                    if self.timeout >= 0:
163                        break
164                else:
165                    read.append(x)
166        return bytes(read)
167
168    def write(self, data):
169        """Output the given string over the serial port."""
170        if not self.sPort: raise portNotOpenError
171        if not isinstance(data, (bytes, bytearray)):
172            raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
173        self._outstream.write(data)
174        return len(data)
175
176    def flushInput(self):
177        """Clear input buffer, discarding all that is in the buffer."""
178        if not self.sPort: raise portNotOpenError
179        self._instream.skip(self._instream.available())
180
181    def flushOutput(self):
182        """Clear output buffer, aborting the current output and
183        discarding all that is in the buffer."""
184        if not self.sPort: raise portNotOpenError
185        self._outstream.flush()
186
187    def sendBreak(self, duration=0.25):
188        """Send break condition. Timed, returns to idle state after given duration."""
189        if not self.sPort: raise portNotOpenError
190        self.sPort.sendBreak(duration*1000.0)
191
192    def setBreak(self, level=1):
193        """Set break: Controls TXD. When active, to transmitting is possible."""
194        if self.fd is None: raise portNotOpenError
195        raise SerialException("The setBreak function is not implemented in java.")
196
197    def setRTS(self, level=1):
198        """Set terminal status line: Request To Send"""
199        if not self.sPort: raise portNotOpenError
200        self.sPort.setRTS(level)
201
202    def setDTR(self, level=1):
203        """Set terminal status line: Data Terminal Ready"""
204        if not self.sPort: raise portNotOpenError
205        self.sPort.setDTR(level)
206
207    def getCTS(self):
208        """Read terminal status line: Clear To Send"""
209        if not self.sPort: raise portNotOpenError
210        self.sPort.isCTS()
211
212    def getDSR(self):
213        """Read terminal status line: Data Set Ready"""
214        if not self.sPort: raise portNotOpenError
215        self.sPort.isDSR()
216
217    def getRI(self):
218        """Read terminal status line: Ring Indicator"""
219        if not self.sPort: raise portNotOpenError
220        self.sPort.isRI()
221
222    def getCD(self):
223        """Read terminal status line: Carrier Detect"""
224        if not self.sPort: raise portNotOpenError
225        self.sPort.isCD()
226
227
228# assemble Serial class with the platform specific implementation and the base
229# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
230# library, derive from io.RawIOBase
231try:
232    import io
233except ImportError:
234    # classic version with our own file-like emulation
235    class Serial(JavaSerial, FileLike):
236        pass
237else:
238    # io library present
239    class Serial(JavaSerial, io.RawIOBase):
240        pass
241
242
243if __name__ == '__main__':
244    s = Serial(0,
245         baudrate=19200,        # baudrate
246         bytesize=EIGHTBITS,    # number of databits
247         parity=PARITY_EVEN,    # enable parity checking
248         stopbits=STOPBITS_ONE, # number of stopbits
249         timeout=3,             # set a timeout value, None for waiting forever
250         xonxoff=0,             # enable software flow control
251         rtscts=0,              # enable RTS/CTS flow control
252    )
253    s.setRTS(1)
254    s.setDTR(1)
255    s.flushInput()
256    s.flushOutput()
257    s.write('hello')
258    sys.stdio.write('%r\n' % s.read(5))
259    sys.stdio.write('%s\n' % s.inWaiting())
260    del s
261
262
263