[Twisted-Python] Guidance needed on serial device interaction

Jason Heeris jason.heeris at gmail.com
Tue Feb 1 23:36:55 MST 2011


This is a bit long, sorry...

I have a PyGTK program that uses threads and pyserial's blocking
methods to interact with an RS232 connected device. I'd like to throw
out the threading awfulness and redo it in Twisted, if possible, but
I'm a little lost.

The real protocol is a bit convoluted, but basically:
  - You can issue single character commands to the device that give a fixed
    length response, such as sending 'C' and getting an eight-digit hex
    string back (the program flash CRC)
  - You can put the device into "programming mode" (command 'P'), where it
    takes an arbitrary length sequence of records, verifying each record and
    stopping when it sees the special "end record"
  - The device will send back '!' to indicate an error
  - The device will send back '>' to indicate that it's ready for more
    commands

I know I need to use the SerialPort transport, and since that takes a
protocol I've tried to sketch one out but can't seem to get very far.

I want to have methods that can be called from the UI, returning
deferreds to which UI responses can be added as callbacks. I also want
to be able to monitor progress of the programming, so I need a
callback for that separate from the deferred itself. I figure that
calling, say, device.program(program, progress_cb) should return a
Deferred and queue the command, and that somehow the dataReceived()
method should be the start of a chain of events that either:
  - updates state and waits for the next piece of data, or
  - calls back on the appropriate Deferred

I'd also like to structure things so that successive calls to the
DeviceProtocol object queue up, something like:

----
class DeviceProtocol(Protocol):

    def dataReceived(self, data):
        # ...?

    def checksum(self):
        res = defer.Deferred()
        # When ready, send 'C' over the serial line (ie.
        # self.transport.write('C'))
        # Somehow callback via a deferred when the checksum comes back
        return res

    def program(self, program_records, progress_cb):
        res = defer.Deferred()
        # When ready, send 'P' over the serial line
        # Write all the data in "program_records", checking each
        # response, calling back via a deferred when done.
        # ...
        return res

    def connectionLost(self):
        # maybe have something here, like calling the errback of all
        # pending deferreds

def go(reactor):
    # callbacks/errbacks not shown

    device = DeviceProtocol()
    transport = SerialPort(device, portname, reactor)

    program_result_1 = device.program(records, good_programming_progress)
    program_result_1.addCallbacks(programming_done, programming_err)

    checksum_result = device.checksum()
    checksum_result.addCallbacks(checksum, checksum_err)

    # This will fail at some point:
    program_result_2 = device.program(bad_records)
    cp_deferred.addCallbacks(programming_done, programming_err)

if __name__ == "__main__":
    reactor.callWhenRunning(go, reactor)
    reactor.run()
----

But I'm really lost as to how to start structuring things within the
protocol object itself. Should I even be doing this in a Protocol
subclass, or should I be putting some of this functionality into a
Factory of some sort (eg. a ClientFactory)? And if so, how do I
actually connect the factory to the serial port transport? Or am I on
the wrong track altogether?

More to the point, has a problem like this already been solved
somewhere? Is the solution obvious to Twisted gurus?

Any help would be appreciated.

Cheers,
Jason




More information about the Twisted-Python mailing list