From f1c0281f0827699b5990c5dd21908d2384407578 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Wed, 2 Apr 2008 23:02:59 +0200 Subject: * host/proto: - added proto interface. --- host/proto/proto.py | 165 +++++++++++++++++++++++++++++++++++++++++ host/proto/test/asserv.py | 36 +++++++++ host/proto/test/fio.py | 22 ++++++ host/proto/test/interactive.py | 29 ++++++++ 4 files changed, 252 insertions(+) create mode 100644 host/proto/proto.py create mode 100644 host/proto/test/asserv.py create mode 100644 host/proto/test/fio.py create mode 100644 host/proto/test/interactive.py 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 () -- cgit v1.2.3