Async serial communication/threads sharing data

Jean-Paul Calderone exarkun at divmod.com
Mon Mar 23 08:16:26 EDT 2009


On Mon, 23 Mar 2009 05:30:04 -0500, Nick Craig-Wood <nick at craig-wood.com> wrote:
>Jean-Paul Calderone <exarkun at divmod.com> wrote:
> [snip]
>>
>>  In the case of a TCP to serial forwarder, you don't actually have to
>>  implement either a producer or a consumer, since both the TCP connection
>>  and the serial connection are already both producers and consumers.  All
>>  you need to do is hook them up to each other so that when the send buffer
>>  of one fills up, the other one gets paused, and when the buffer is empty
>>  again, it gets resumed.
>
>I eventually came up with this which seems to work, but I'm not sure
>it is the best way of doing it as it had to mess about with the
>twisted internals to get the number of bytes in the serial port
>output buffer.

This is sort of on the right track.  Here's how to do it without
touching implementation details:

>
>class SerialPort(protocol.Protocol):
>    """Create a serial port connection and pass data from it to a known list of TCP ports."""
>
>    def __init__(self, port, reactor, baudrate, log_name=None):
>        self.tcp_ports = []
>        self.serial = serialport.SerialPort(self, reactor, port, baudrate, rtscts=0)

Here, register this object to receive buffer full and not-full events from
the serial port:

        self.serial.registerProducer(self, True)

And an attribute to keep track of our state:

        self.paused = False

>        self.log = None
>        if log_name is not None:
>            self.log = file('%s-0' % log_name, 'w')
>

I'm not exactly sure where `self.tcp_ports´ gets populated, so I'll make a method to do it:

    def addPort(self, port):
        if self.paused:
            port.transport.pauseProducing()
        self.tcp_ports.append(port)

Now a method to handle serial port buffer full events:

    def pauseProducing(self):
        self.paused = True
        for port in self.tcp_ports:
            port.transport.pauseProducing()

And a method to handle buffer no longer full events:

    def resumeProducing(self):
        self.paused = False
        for port in self.tcp_ports:
            port.transport.resumeProducing()

With these, you can get rid of `throttle´ entirely.      

>    def write(self, data):
>        """Write data to the serial port."""
>        self.serial.write(data)
>        if self.log:
>            self.log.write(data)
>        self.throttle()
>
>    def throttle(self):
>        """
>        Pause the inputs if there is too much data in the output buffer
>        """
>        bytes_in_buffer = len(self.serial.dataBuffer) +	self.serial._tempDataLen
>        too_full = bytes_in_buffer > 1024
>        for tcp_port in self.tcp_ports:
>            if too_full:
>                tcp_port.transport.pauseProducing()
>            else:
>                tcp_port.transport.resumeProducing()
>        if too_full:
>            reactor.callLater(0.1, self.throttle)
>

Jean-Paul



More information about the Python-list mailing list