summaryrefslogtreecommitdiff
path: root/host/proto
diff options
context:
space:
mode:
authorNicolas Schodet2008-12-17 23:40:36 +0100
committerNicolas Schodet2008-12-17 23:40:36 +0100
commit388dd88e6a66ce5eaad6d1392c742d43f328af30 (patch)
treefd051f71d1fabb4a061c63e1bb9336ae4f995f3a /host/proto
parent0b85557b4c5f80178c423cf9934435dbe8dc889f (diff)
* host/proto:
- added documentation. - fixed small discordance between documentation and behaviour.
Diffstat (limited to 'host/proto')
-rw-r--r--host/proto/doc/proto.txt128
-rw-r--r--host/proto/proto.py11
2 files changed, 137 insertions, 2 deletions
diff --git a/host/proto/doc/proto.txt b/host/proto/doc/proto.txt
new file mode 100644
index 00000000..afbeffbb
--- /dev/null
+++ b/host/proto/doc/proto.txt
@@ -0,0 +1,128 @@
+=======================================
+ proto - Host proto communication API.
+=======================================
+:Author: Nicolas Schodet
+
+Introduction
+============
+
+This module handles the communication protocol defined in the *proto* AVR
+module. It can be used to communicate through a read serial port with an
+embedded program running on an AVR, or using a pseudo-terminal or standard
+input/output to communicate with a program compiled for the host system.
+
+This module also handles the master part of the protocol:
+
+- Acknowledgement from the slave device.
+- Retransmission if no acknowledgement is received.
+- Retransmission if the slave signals an error.
+
+This module does not handle higher level protocols, such as duplicates
+handling or frame sequence number.
+
+To understand this documentation, you should be familiar with the protocol,
+which is explained in the AVR module documentation.
+
+Usage
+=====
+
+Initialisation
+--------------
+
+First, create a Proto instance. For example to connect with a real serial
+port::
+
+ proto = Proto (serial.Serial ('/dev/ttyS0'), time.time, 0.1)
+
+To connect to a program running on host, with a logging output::
+
+ def log (text):
+ print text
+ proto = Proto (proto.popen_io.PopenIO ('test_program.host'), time.time, 0.1,
+ log = log)
+
+Synchronisation
+---------------
+
+You need to synchronise regularly with the remote program. You have two
+options.
+
+The first one can be used if you have nothing else to execute apart from the
+protocol handling. In this case, just call the wait function. If a argument
+is given, this should be a callable object which will return True to stop
+waiting.
+
+The wait function will only return if all frames have been sent and the
+condition argument returns True.
+
+Example, wait forever::
+
+ proto.wait ()
+
+Or, wait until an internal counter has reached a value::
+
+ proto.wait (lambda: my_counter == 3)
+
+The second option should be used if you need concurrent access to several
+sources (GUI interface, several proto, other events...). In this case, the
+sync function should be called regularly (at least every timeout period), and
+the read function should be called if data is available on the proto file
+descriptor which can be retrieved as usual using the fileno function.
+
+Example::
+
+ while not proto.sync ():
+ fds = select.select ((proto, other_input), (), (), 0.1)[0]
+ for fd in fds:
+ fd.read ()
+
+Sending a frame
+---------------
+
+To send a frame, use the send function.
+
+First argument is the command character. Then comes the frame arguments format
+followed by the frame arguments. The format follows the struct module fmt
+string.
+
+Examples::
+
+ proto.send ('z') # Send !z
+ proto.send ('a', 'b', 1) # Send !a01
+ proto.send ('b', 'bhl', 1, 2, 84942856) # Send !b01000205102008
+
+The frame is not sent immediately, but once wait or sync is called and all the
+previously enqueued frames are acknowledged.
+
+Receiving a frame
+-----------------
+
+When a frame is received, a user callback will be called if it has been
+registered.
+
+To register a callback, use the register function. The first argument is the
+frame command to bind to this callback, the second one is the frame arguments
+format, and the last one is the callback.
+
+Here, the distinction between signed and unsigned arguments is important.
+
+If a frame is received with the registered command and a number of bytes
+matching the frame arguments format, the callback is called with arguments
+decoded.
+
+For example::
+
+ def my_callback1 (a, b):
+ print "callback1", a, b
+ def my_callback2 (a):
+ print "callback2", a
+ proto.register ('x', 'bB', my_callback1)
+ proto.register ('y', 'l', my_callback2)
+
+ # If !xffff is received, this will print:
+ # callback1 255 -1
+ # If !y05102008 is received, this will print:
+ # callback2 84942856
+
+Keep in mind that the protocol can not make the distinction between the format
+'bbbb' and 'l' as it uses the same number of bytes.
diff --git a/host/proto/proto.py b/host/proto/proto.py
index 36616d9c..9eb401e1 100644
--- a/host/proto/proto.py
+++ b/host/proto/proto.py
@@ -33,7 +33,14 @@ class Proto:
def __init__ (self, file, date, timeout, log = None):
"""Initialise and set file (serial port, pty, socket...), date
- function and timeout value."""
+ function and timeout value.
+
+ - file: open file connected to the slave device.
+ - date: when called, should return the current time.
+ - timeout: time after which retransmission is done.
+ - log: if defined, will be called with a log string.
+
+ """
self.file = file
self.date = date
self.last_send = None
@@ -70,7 +77,7 @@ class Proto:
def wait (self, cond = None):
"""Wait forever or until cond () is True."""
- while not (self.sync () and (cond is None or cond ())):
+ while not (self.sync () and (cond is not None or cond ())):
fds = select.select ((self,), (), (), self.timeout)[0]
for i in fds:
assert i is self