[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