summaryrefslogtreecommitdiff
path: root/host/proto
diff options
context:
space:
mode:
authorNicolas Schodet2008-04-02 23:02:59 +0200
committerNicolas Schodet2008-04-02 23:02:59 +0200
commitf1c0281f0827699b5990c5dd21908d2384407578 (patch)
tree92c70cc7539208d36ba13ea9ba05b323ee5e8df5 /host/proto
parent1197ceaf9f4a3730bf89542ad28ddd6aff634ade (diff)
* host/proto:
- added proto interface.
Diffstat (limited to 'host/proto')
-rw-r--r--host/proto/proto.py165
-rw-r--r--host/proto/test/asserv.py36
-rw-r--r--host/proto/test/fio.py22
-rw-r--r--host/proto/test/interactive.py29
4 files changed, 252 insertions, 0 deletions
diff --git a/host/proto/proto.py b/host/proto/proto.py
new file mode 100644
index 00000000..e6d3ab32
--- /dev/null
+++ b/host/proto/proto.py
@@ -0,0 +1,165 @@
+# proto - Proto interface. {{{
+#
+# Copyright (C) 2008 Nicolas Schodet
+#
+# APBTeam:
+# Web: http://apbteam.org/
+# Email: team AT apbteam DOT org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# }}}
+"""Proto interface module."""
+import binascii, struct
+
+START = 0
+BANG = 1
+CMD = 2
+ARG = 3
+
+class Proto:
+
+ def __init__ (self, file, date, timeout, log = None):
+ """Initialise and set file (serial port, pty, socket...), date
+ function and timeout value."""
+ self.file = file
+ self.date = date
+ self.last_send = None
+ self.timeout = timeout
+ self.send_queue = [ ]
+ self.state = START
+ self.log = log
+ self.handlers = { }
+
+ def send (self, *frame):
+ """Queue a frame to send."""
+ if not self.send_queue:
+ self.last_send = None
+ self.send_queue.append (Frame (*frame))
+
+ def read (self):
+ """Read from file and receive frames."""
+ for f in self.recv ():
+ if self.log:
+ self.log ('recv %s' % f)
+ if self.send_queue and f == self.send_queue[0]:
+ del self.send_queue[0]
+ if self.send_queue:
+ self.send_head ()
+ else:
+ self.dispatch (f)
+
+ def sync (self):
+ """Send frames, return True if all is sent."""
+ if self.send_queue and (self.last_send is None
+ or self.last_send + self.timeout < self.date ()):
+ self.send_head ()
+ return not self.send_queue
+
+ def register (self, command, fmt, handler):
+ """Register a handler for the specified command and format. The
+ handler will receive decoded arguments."""
+ key = (command, struct.calcsize ('!' + fmt))
+ assert key not in self.handlers
+ self.handlers[key] = (handler, fmt)
+
+ def fileno (self):
+ """Return file descriptor, for use with select."""
+ return self.file.fileno ()
+
+ def send_head (self):
+ """Send first frame from the send queue."""
+ if self.log:
+ self.log ('send %s' % self.send_queue[0])
+ self.file.write (self.send_queue[0].data ())
+ self.last_send = self.date ()
+
+ def recv (self):
+ """Receive a frame, used as a generator."""
+ for c in self.file.read (1):
+ if c == '!':
+ self.state = BANG
+ else:
+ if self.state == START:
+ pass
+ elif self.state == BANG:
+ if c.isalpha ():
+ self.recv_command = c
+ self.recv_args = ''
+ self.state = CMD
+ else:
+ self.recv_error ()
+ elif self.state == CMD:
+ if c == '\r':
+ f = Frame (self.recv_command)
+ f.args = binascii.unhexlify (self.recv_args)
+ yield f
+ elif (c >= '0' and c <= '9') or (c >= 'a' and c <= 'f'):
+ self.recv_args += c
+ self.state = ARG
+ else:
+ self.recv_error ()
+ else:
+ assert self.state == ARG
+ if (c >= '0' and c <= '9') or (c >= 'a' and c <= 'f'):
+ self.recv_args += c
+ self.state = CMD
+ else:
+ self.recv_error ()
+
+ def recv_error (self):
+ """Handle reception errors."""
+ self.state = START
+ if self.log:
+ self.log ('error')
+ # Resend now.
+ if self.send_queue:
+ self.send_head ()
+
+ def dispatch (self, frame):
+ """Pass a received frame to the correct handler."""
+ key = (frame.command, len (frame.args))
+ if key in self.handlers:
+ h = self.handlers[key]
+ h[0] (*(frame.decode (h[1])))
+
+class Frame:
+
+ def __init__ (self, command = None, fmt = '', *args):
+ """Initiliase a frame. If command is given, the frame is constructed
+ using a struct.pack like fmt string."""
+ if command:
+ assert len (command) == 1 and command.isalpha ()
+ self.command = command
+ self.args = struct.pack ('!' + fmt, *args)
+ else:
+ self.command = None
+ self.args = ''
+
+ def data (self):
+ """Get a frame representation ready to send."""
+ return '!' + self.command + binascii.hexlify (self.args) + '\r'
+
+ def decode (self, fmt):
+ """Decode using a struct.unpack like fmt string."""
+ return struct.unpack ('!' + fmt, self.args)
+
+ def __eq__ (self, other):
+ """Compare for equality."""
+ return self.command == other.command and self.args == other.args
+
+ def __str__ (self):
+ """Convert to string."""
+ return '!' + self.command + binascii.hexlify (self.args)
diff --git a/host/proto/test/asserv.py b/host/proto/test/asserv.py
new file mode 100644
index 00000000..e578d9dd
--- /dev/null
+++ b/host/proto/test/asserv.py
@@ -0,0 +1,36 @@
+import sys
+sys.path.append (sys.path[0] + '/..')
+
+import proto
+from fio import IO
+import time, select, os
+
+# Pass program name as argument.
+fout, fin = os.popen2 (sys.argv[1:], 't', 1)
+time.sleep (0.5)
+io = IO (fin, fout)
+
+def log (x):
+ print x
+
+p = proto.Proto (io, time.time, 0.5, log)
+
+done = 0
+
+def reset ():
+ print 'reset'
+
+def counter_stat (left, right, aux0):
+ print 'counter %u, %u, %u' % (left, right, aux0)
+ global done
+ done += 1
+
+p.register ('z', '', reset)
+p.register ('C', 'HHH', counter_stat)
+
+p.send ('C', 'B', 255)
+
+while not p.sync () or done != 3:
+ fds = select.select ((p,), (), (), 0.1)[0]
+ for i in fds:
+ i.read ()
diff --git a/host/proto/test/fio.py b/host/proto/test/fio.py
new file mode 100644
index 00000000..07646199
--- /dev/null
+++ b/host/proto/test/fio.py
@@ -0,0 +1,22 @@
+
+class IO:
+ def __init__ (self, fin = None, fout = None):
+ if fin is None:
+ import sys, tty
+ self.fin = sys.stdin
+ self.fout = sys.stdout
+ tty.setcbreak (sys.stdin.fileno ())
+ else:
+ self.fin = fin
+ self.fout = fout
+
+ def read (self, *args):
+ buf = self.fin.read (*args).replace ('\n', '\r')
+ return buf
+
+ def write (self, *args):
+ return self.fout.write (*[i.replace ('\r', '\n') for i in args])
+
+ def fileno (self):
+ return self.fin.fileno ()
+
diff --git a/host/proto/test/interactive.py b/host/proto/test/interactive.py
new file mode 100644
index 00000000..f68dd121
--- /dev/null
+++ b/host/proto/test/interactive.py
@@ -0,0 +1,29 @@
+import sys
+sys.path.append (sys.path[0] + '/..')
+
+import proto
+from fio import IO
+import time, select
+
+def log (x):
+ print x
+
+p = proto.Proto (IO (), time.time, 2, log)
+
+def a (i, j):
+ print 'a (%d, %d)' % (i, j)
+
+def b (i):
+ print 'b (%d)' % i
+
+p.register ('a', 'BH', a)
+p.register ('b', 'L', b)
+
+p.send ('a', 'BH', 1, 2)
+p.send ('b', 'L', 3)
+
+while True:
+ p.sync ()
+ fds = select.select ((p,), (), (), 0.1)[0]
+ for i in fds:
+ i.read ()