1# Copyright (c) Twisted Matrix Laboratories.
2# See LICENSE for details.
3
4"""
5Serial port support for Windows.
6
7Requires PySerial and pywin32.
8"""
9
10# system imports
11import serial
12from serial import PARITY_NONE, PARITY_EVEN, PARITY_ODD
13from serial import STOPBITS_ONE, STOPBITS_TWO
14from serial import FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS
15import win32file, win32event
16
17# twisted imports
18from twisted.internet import abstract
19
20# sibling imports
21from serialport import BaseSerialPort
22
23
24class SerialPort(BaseSerialPort, abstract.FileDescriptor):
25    """A serial device, acting as a transport, that uses a win32 event."""
26
27    connected = 1
28
29    def __init__(self, protocol, deviceNameOrPortNumber, reactor,
30        baudrate = 9600, bytesize = EIGHTBITS, parity = PARITY_NONE,
31        stopbits = STOPBITS_ONE, xonxoff = 0, rtscts = 0):
32        self._serial = self._serialFactory(
33            deviceNameOrPortNumber, baudrate=baudrate, bytesize=bytesize,
34            parity=parity, stopbits=stopbits, timeout=None,
35            xonxoff=xonxoff, rtscts=rtscts)
36        self.flushInput()
37        self.flushOutput()
38        self.reactor = reactor
39        self.protocol = protocol
40        self.outQueue = []
41        self.closed = 0
42        self.closedNotifies = 0
43        self.writeInProgress = 0
44
45        self.protocol = protocol
46        self._overlappedRead = win32file.OVERLAPPED()
47        self._overlappedRead.hEvent = win32event.CreateEvent(None, 1, 0, None)
48        self._overlappedWrite = win32file.OVERLAPPED()
49        self._overlappedWrite.hEvent = win32event.CreateEvent(None, 0, 0, None)
50
51        self.reactor.addEvent(self._overlappedRead.hEvent, self, 'serialReadEvent')
52        self.reactor.addEvent(self._overlappedWrite.hEvent, self, 'serialWriteEvent')
53
54        self.protocol.makeConnection(self)
55        self._finishPortSetup()
56
57
58    def _finishPortSetup(self):
59        """
60        Finish setting up the serial port.
61
62        This is a separate method to facilitate testing.
63        """
64        flags, comstat = win32file.ClearCommError(self._serial.hComPort)
65        rc, self.read_buf = win32file.ReadFile(self._serial.hComPort,
66                                               win32file.AllocateReadBuffer(1),
67                                               self._overlappedRead)
68
69
70    def serialReadEvent(self):
71        #get that character we set up
72        n = win32file.GetOverlappedResult(self._serial.hComPort, self._overlappedRead, 0)
73        if n:
74            first = str(self.read_buf[:n])
75            #now we should get everything that is already in the buffer
76            flags, comstat = win32file.ClearCommError(self._serial.hComPort)
77            if comstat.cbInQue:
78                win32event.ResetEvent(self._overlappedRead.hEvent)
79                rc, buf = win32file.ReadFile(self._serial.hComPort,
80                                             win32file.AllocateReadBuffer(comstat.cbInQue),
81                                             self._overlappedRead)
82                n = win32file.GetOverlappedResult(self._serial.hComPort, self._overlappedRead, 1)
83                #handle all the received data:
84                self.protocol.dataReceived(first + str(buf[:n]))
85            else:
86                #handle all the received data:
87                self.protocol.dataReceived(first)
88
89        #set up next one
90        win32event.ResetEvent(self._overlappedRead.hEvent)
91        rc, self.read_buf = win32file.ReadFile(self._serial.hComPort,
92                                               win32file.AllocateReadBuffer(1),
93                                               self._overlappedRead)
94
95
96    def write(self, data):
97        if data:
98            if self.writeInProgress:
99                self.outQueue.append(data)
100            else:
101                self.writeInProgress = 1
102                win32file.WriteFile(self._serial.hComPort, data, self._overlappedWrite)
103
104
105    def serialWriteEvent(self):
106        try:
107            dataToWrite = self.outQueue.pop(0)
108        except IndexError:
109            self.writeInProgress = 0
110            return
111        else:
112            win32file.WriteFile(self._serial.hComPort, dataToWrite, self._overlappedWrite)
113
114
115    def connectionLost(self, reason):
116        """
117        Called when the serial port disconnects.
118
119        Will call C{connectionLost} on the protocol that is handling the
120        serial data.
121        """
122        self.reactor.removeEvent(self._overlappedRead.hEvent)
123        self.reactor.removeEvent(self._overlappedWrite.hEvent)
124        abstract.FileDescriptor.connectionLost(self, reason)
125        self._serial.close()
126        self.protocol.connectionLost(reason)
127