]> git.llucax.com Git - z.facultad/66.09/etherled.git/commitdiff
Agrega implementación del protocolo y un server (emulador del dispositivo) de prueba.
authorLeandro Lucarella <llucax@gmail.com>
Tue, 1 Nov 2005 00:57:34 +0000 (00:57 +0000)
committerLeandro Lucarella <llucax@gmail.com>
Tue, 1 Nov 2005 00:57:34 +0000 (00:57 +0000)
cliente/etherled/__init__.py [new file with mode: 0644]
cliente/etherled/packet.py [new file with mode: 0644]
cliente/etherled/protocol.py [new file with mode: 0644]

diff --git a/cliente/etherled/__init__.py b/cliente/etherled/__init__.py
new file mode 100644 (file)
index 0000000..040f8d4
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vim: set expandtab tabstop=4 shiftwidth=4 :
+#----------------------------------------------------------------------------
+#                               Etherled
+#----------------------------------------------------------------------------
+# This file is part of etherled.
+#
+# etherled 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.
+#
+# etherled 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 etherled; if not, write to the Free Software Foundation, Inc., 59
+# Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#----------------------------------------------------------------------------
+# Creado:  sáb oct 29 21:56:59 ART 2005
+# Autores: Leandro Lucarella <llucare@fi.uba.ar>
+#----------------------------------------------------------------------------
+
+from packet import *
+from protocol import *
+
diff --git a/cliente/etherled/packet.py b/cliente/etherled/packet.py
new file mode 100644 (file)
index 0000000..2f38145
--- /dev/null
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vim: set expandtab tabstop=4 shiftwidth=4 :
+#----------------------------------------------------------------------------
+#                               Etherled
+#----------------------------------------------------------------------------
+# This file is part of etherled.
+#
+# etherled 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.
+#
+# etherled 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 etherled; if not, write to the Free Software Foundation, Inc., 59
+# Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#----------------------------------------------------------------------------
+# Creado:  sáb oct 29 00:45:52 ART 2005
+# Autores: Leandro Lucarella <llucare@fi.uba.ar>
+#----------------------------------------------------------------------------
+
+# Tipos de operación
+TYPE_GET = 0
+TYPE_SET = 1
+
+# Variables
+VAR_MATRIX = 0
+
+# Limites
+MAX_ID = 8
+MAX_VAR = 8
+
+class ParityError(ValueError):
+    pass
+
+class Packet(object):
+
+    def __init__(self, type, var=None, id=None, data=None):
+        if isinstance(type, str):
+            self.fromStr(type)
+        else:
+            self.type = type 
+            self.var = var
+            self.id = id
+            self.data = data
+
+    def fromStr(self, string):
+        header = ord(string[0])
+        self.type = header >> 7
+        self.var = (header & 0x70) >> 4
+        self.id = (header & 0x0E) >> 1
+        par = header & 0x01
+        if self.par != par:
+            raise ParityError
+        self.data = string[1:] or None
+
+    def __str__(self):
+        header = self._header_to_int()
+        data = ''
+        if self.data is not None:
+            data = self.data
+        return chr(header) + data
+
+    def __repr__(self):
+        return "Packet(type=%d, var=%d, id=%d, data=%s)" \
+            % (self.type, self.var, self.id, repr(self.data))
+
+    def __len__(self):
+        return len(str(self))
+
+    def __eq__(self, p):
+        return self.type == p.type and self.var == p.var and self.id == p.id
+
+    def _header_to_int(self):
+        res = (self.type << 7) + (self.var << 4) + (self.id << 1)
+        return res + self.par
+
+    def _getPar(self):
+        par = self.type
+        for i in xrange(3):
+            par += int((self.var & (1 << i)) != 0)
+        for i in xrange(3):
+            par += int((self.id & (1 << i)) != 0)
+        return par % 2
+
+    def _getType(self):
+        return self._type
+
+    def _setType(self, type):
+        if type != TYPE_GET and type != TYPE_SET:
+            raise ValueError, "type debe ser Packet.GET o Packet.SET"
+        self._type = type
+
+    def _getVar(self):
+        return self._var
+
+    def _setVar(self, var):
+        if var < 0 and var >= self.MAX_VAR:
+            raise ValueError, "var debe estar entre 0 y %d" % self.MAX_VAR-1
+        self._var = var
+
+    def _getId(self):
+        return self._id
+
+    def _setId(self, id):
+        if id < 0 and id >= self.MAX_ID:
+            raise ValueError, "id debe estar entre 0 y %d" % self.MAX_ID-1
+        self._id = id
+
+    type = property(_getType, _setType, doc="Tipo de operación")
+    var = property(_getVar, _setVar, doc="Variable con la cual operar")
+    id = property(_getId, _setId, doc="Identificador del paquete")
+    par = property(_getPar, doc="Paridad de la cabecera del paquete")
+
+class ClientPacket(Packet):
+
+    def __init__(self, type, var=None, id=None, data=None):
+        if isinstance(type, str):
+            self.fromStr(type)
+        else:
+            Packet.__init__(self, type, var, id, data)
+            if type == TYPE_GET and data is not None:
+                raise ValueError, "El paquete a enviar por el cliente no " \
+                    "puede contener datos si es de tipo GET"
+
+    def fromStr(self, string):
+        Packet.fromStr(self, string)
+        if self.type == TYPE_SET and self.data is not None:
+            raise ValueError, "El paquete recibido por el cliente no " \
+                "puede contener datos si es de tipo SET"
+
+class ServerPacket(Packet):
+
+    def __init__(self, type, var=None, id=None, data=None):
+        if isinstance(type, str):
+            self.fromStr(type)
+        else:
+            Packet.__init__(self, type, var, id, data)
+            if type == TYPE_SET and data is not None:
+                raise ValueError, "El paquete a enviar por el servidor no " \
+                    "puede contener datos si es de tipo SET"
+
+    def fromStr(self, string):
+        Packet.fromStr(self, string)
+        if self.type == TYPE_GET and self.data is not None:
+            raise ValueError, "El paquete recibido por el cliente no " \
+                "puede contener datos si es de tipo GET"
+
+# Prueba
+if __name__ == '__main__':
+    assert str(Packet(1, 7, 7)) == '\xFF'
+    assert str(Packet(0, 0, 0)) == '\x00'
+    assert str(Packet(1, 1, 1)) == '\x93'
+    assert str(Packet(TYPE_SET, VAR_MATRIX, 2)) == '\x84'
+    assert str(Packet(TYPE_GET, 4, 0)) == 'A'
+    assert str(Packet(TYPE_GET, 4, 0, 'hola')) == 'Ahola'
+    p = Packet(TYPE_GET, 4, 0, 'hola')
+    assert Packet(str(p)) == p
+    print "OK!"
+
diff --git a/cliente/etherled/protocol.py b/cliente/etherled/protocol.py
new file mode 100644 (file)
index 0000000..56bb34a
--- /dev/null
@@ -0,0 +1,244 @@
+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vim: set expandtab tabstop=4 shiftwidth=4 :
+#----------------------------------------------------------------------------
+#                               Etherled
+#----------------------------------------------------------------------------
+# This file is part of etherled.
+#
+# etherled 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.
+#
+# etherled 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 etherled; if not, write to the Free Software Foundation, Inc., 59
+# Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#----------------------------------------------------------------------------
+# Creado:  sáb oct 29 00:45:52 ART 2005
+# Autores: Leandro Lucarella <llucare@fi.uba.ar>
+#----------------------------------------------------------------------------
+
+import socket
+import packet
+
+__all__ = ('SendError', 'RecvError', 'Client', 'NetworkedDevice', 'DummyServer')
+
+# Tamaño del buffer
+_BUFSIZ = 65536
+
+class SendError(socket.error):
+    pass
+
+class RecvError(socket.error):
+    pass
+
+class Client(object):
+
+    def __init__(self, host='localhost', port=38437, timeout=3.0):
+        self._host = host
+        self._port = port
+        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        self._sock.connect(self.addr)
+        self._sock.settimeout(timeout)
+        self._ids = {}
+        self._drop = 0
+
+    def send(self, type, var, data=None):
+        pkt = packet.ClientPacket(type, var, self._getId(type, var), data)
+        try:
+            sent = self._sock.send(str(pkt))
+        except socket.timeout:
+            raise SendError, "Tiempo de espera agotado"
+        if sent != len(pkt):
+            raise SendError, "Sólo se enviaron %d bytes de %d" \
+                % (sent, len(pkt))
+        while True:
+            try:
+                msg = self._sock.recv(_BUFSIZ)
+            except socket.timeout:
+                raise RecvError, "Tiempo de espera agotado"
+            pkt_r = packet.ClientPacket(msg)
+            if pkt == pkt_r:
+                break # paquete ok
+            self._drop += 1
+        return pkt_r.data
+
+    def get(self, var):
+        return self.send(packet.TYPE_GET, var)
+
+    def set(self, var, data):
+        self.send(packet.TYPE_SET, var, data)
+
+    def _getId(self, type, var):
+        id = self._ids.get((type, var), 0)
+        self._ids[type, var] = (id + 1) % packet.MAX_ID
+        return id
+
+    def _getHost(self):
+        return self._host
+
+    def _getPort(self):
+        return self._port
+
+    def _getAddr(self):
+        return (self._host, self._port)
+
+    def _getDrop(self):
+        return self._drop
+
+    host = property(_getHost, doc='Host al cual enviar datos')
+    port = property(_getPort, doc='Puerto al cual enviar datos')
+    addr = property(_getAddr, doc='Tupla (host, port)')
+    drop = property(_getDrop, doc='Cantidad de paquetes descartados')
+
+class NetworkedDevice(Client):
+
+    LED_BYTES = 2
+
+    def _getMatrix(self):
+        stream = self.get(packet.VAR_MATRIX)
+        return self._stream2Matrix(stream)
+
+    def _setMatrix(self, matrix):
+        stream = self._matrix2Stream(matrix)
+        self.set(packet.VAR_MATRIX, stream)
+
+    def _stream2Matrix(self, stream):
+        cols = ord(stream[0]) # Obtiene tamaño
+        stream = stream[1:1+cols*self.LED_BYTES] # me quedo con el resto
+        matrix = {}
+        for col in xrange(cols-1, -1, -1):
+            for row_byte in xrange(self.LED_BYTES):
+                byte = ord(stream[(cols-col-1)*self.LED_BYTES+row_byte])
+                for i in xrange(8):
+                    shift = 8 - i - 1
+                    matrix[col, row_byte*8+i] = (byte >> shift) & 1
+        return matrix
+
+    def _matrix2Stream(self, matrix):
+        cols = len(matrix) / (self.LED_BYTES*8)
+        stream = chr(cols) # primero va el tamaño
+        for col in xrange(cols-1, -1, -1):
+            for i in xrange(self.LED_BYTES):
+                byte = 0
+                for row in xrange(8):
+                    shift = 8 - row - 1
+                    byte += matrix[col,row] << shift
+                stream += chr(byte)
+        return stream
+
+    matrix = property(_getMatrix, _setMatrix, doc='Matriz de leds')
+
+class DummyServer:
+
+    def __init__(self, host='localhost', port=38437, timeout=3.0):
+        self._host = host
+        self._port = port
+        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self._sock.bind(self.addr)
+        self._vars = [None for i in xrange(8)]
+
+    def run(self):
+        while True:
+            (msg, addr) = self._sock.recvfrom(_BUFSIZ)
+            pkt = packet.ServerPacket(msg)
+            if pkt.type == packet.TYPE_GET:
+                pkt.data = self._vars[pkt.var]
+            elif pkt.type == packet.TYPE_SET:
+                self._vars[pkt.var] = pkt.data
+                pkt = packet.ServerPacket(pkt.type, pkt.var, pkt.id)
+            sent = self._sock.sendto(str(pkt), addr)
+            if sent != len(pkt):
+                raise SendError, "Sólo se enviaron %d bytes de %d" \
+                    % (sent, len(packet))
+
+    def _getHost(self):
+        return self._host
+
+    def _getPort(self):
+        return self._port
+
+    def _getAddr(self):
+        return (self._host, self._port)
+
+    host = property(_getHost, doc='Host al cual enviar datos')
+    port = property(_getPort, doc='Puerto al cual enviar datos')
+    addr = property(_getAddr, doc='Tupla (host, port)')
+
+def _print_matrix(matrix):
+    for row in xrange(NetworkedDevice.LED_BYTES*8):
+        for col in xrange(len(matrix)/(NetworkedDevice.LED_BYTES*8)):
+            print matrix[row,col],
+        print
+    print
+
+def _print_stream(stream):
+    for c in stream:
+        print '0x%02X' % ord(c),
+    print
+    print
+
+# Prueba
+if __name__ == '__main__':
+    import os, sys, time
+    pid = os.fork()
+    if pid:
+        time.sleep(0.1)
+        # Creo dispositivo por red
+        dev = NetworkedDevice()
+        # Creo matriz
+        matrix = {}
+        for col in xrange(16):
+            for row in xrange(16):
+                matrix[row,col] = row % 2
+        # Mando matriz
+        print 'Matriz enviada:'
+        _print_matrix(matrix)
+        dev.matrix = matrix
+        print 'Matriz recibida:'
+        _print_matrix(dev.matrix)
+        # Verifico resultado
+        assert matrix == dev.matrix
+        ###########################
+        # Creo matriz
+        matrix = {}
+        for col in xrange(16):
+            for row in xrange(16):
+                matrix[row,col] = col % 2
+        # Mando matriz
+        print 'Matriz enviada:'
+        _print_matrix(matrix)
+        dev.matrix = matrix
+        print 'Matriz recibida:'
+        _print_matrix(dev.matrix)
+        # Verifico resultado
+        assert matrix == dev.matrix
+        ###########################
+        # Creo matriz
+        matrix = {}
+        for col in xrange(16):
+            for row in xrange(16):
+                matrix[row,col] = (col+row) % 2
+        # Mando matriz
+        print 'Matriz enviada:'
+        _print_matrix(matrix)
+        dev.matrix = matrix
+        print 'Matriz recibida:'
+        _print_matrix(dev.matrix)
+        # Verifico resultado
+        assert matrix == dev.matrix
+        # Matamos al servidor
+        os.kill(pid, 15)
+    else:
+        server = DummyServer()
+        server.run()
+        sys.exit(0)
+    print "OK!"
+