]> git.llucax.com Git - z.facultad/66.09/etherled.git/commitdiff
Cliente con funcionalidad básica completa. El cliente tiene una interfaz de
authorLeandro Lucarella <llucax@gmail.com>
Wed, 14 Dec 2005 04:05:59 +0000 (04:05 +0000)
committerLeandro Lucarella <llucax@gmail.com>
Wed, 14 Dec 2005 04:05:59 +0000 (04:05 +0000)
línea de comandos que permite obtener y setear todos los valores básicos (delay,
pause, off y obtener la matriz). Además tiene una interfaz gráfica para dibujar
la matriz y enviarla (o recibirla).

cliente/cetherled.glade
cliente/cetherled.py
cliente/etherled/packet.py
cliente/etherled/protocol.py

index 3a720dfafd3b799630b8b62b9193691b5fd42ddb..664d46be71c5ac04e6310a6c8393a0ec7ef9596d 100644 (file)
       <property name="homogeneous">False</property>
       <property name="spacing">10</property>
 
-      <child>
-       <widget class="GtkHBox" id="hbox3">
-         <property name="visible">True</property>
-         <property name="homogeneous">False</property>
-         <property name="spacing">5</property>
-
-         <child>
-           <widget class="GtkLabel" id="label3">
-             <property name="visible">True</property>
-             <property name="label" translatable="yes">IP:</property>
-             <property name="use_underline">False</property>
-             <property name="use_markup">False</property>
-             <property name="justify">GTK_JUSTIFY_LEFT</property>
-             <property name="wrap">False</property>
-             <property name="selectable">False</property>
-             <property name="xalign">0.5</property>
-             <property name="yalign">0.5</property>
-             <property name="xpad">0</property>
-             <property name="ypad">0</property>
-             <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-             <property name="width_chars">-1</property>
-             <property name="single_line_mode">False</property>
-             <property name="angle">0</property>
-           </widget>
-           <packing>
-             <property name="padding">0</property>
-             <property name="expand">False</property>
-             <property name="fill">False</property>
-           </packing>
-         </child>
-
-         <child>
-           <widget class="GtkEntry" id="entry_ip">
-             <property name="visible">True</property>
-             <property name="can_focus">True</property>
-             <property name="editable">True</property>
-             <property name="visibility">True</property>
-             <property name="max_length">0</property>
-             <property name="text" translatable="yes"></property>
-             <property name="has_frame">True</property>
-             <property name="invisible_char">*</property>
-             <property name="activates_default">False</property>
-           </widget>
-           <packing>
-             <property name="padding">0</property>
-             <property name="expand">True</property>
-             <property name="fill">True</property>
-           </packing>
-         </child>
-       </widget>
-       <packing>
-         <property name="padding">0</property>
-         <property name="expand">True</property>
-         <property name="fill">True</property>
-       </packing>
-      </child>
-
       <child>
        <widget class="GtkTable" id="table_leds">
          <property name="visible">True</property>
index 966f25a56b079cad3ee347a18e2b327cf7b24ca1..0004cd8b353d3cda1ebc32c012e05f61b52671a8 100755 (executable)
@@ -28,6 +28,7 @@ import os
 import gtk
 from simplegladeapp import SimpleGladeApp
 from simplegladeapp import bindtextdomain
+from optparse import OptionParser
 #from dispatcher import Dispatcher
 from led import Led
 import etherled
@@ -39,19 +40,24 @@ locale_dir = ""
 
 bindtextdomain(app_name, locale_dir)
 
+ROWS = 16
+
 class MainWindow(SimpleGladeApp):
 
     def __init__(self, path="cetherled.glade", root="main_window",
             domain=app_name, **kwargs):
+        self.columns = kwargs.get('columns', 16)
+        self.host = kwargs.get('host', 'localhost')
+        self.port = kwargs.get('port', 9876)
         #notificar = Dispatcher(self.actualizar)
-        self.device = etherled.NetworkedDevice()
+        self.device = etherled.NetworkedDevice(self.host, self.port)
         path = os.path.join(glade_dir, path)
         SimpleGladeApp.__init__(self, path, root, domain, **kwargs)
 
     def new(self):
         self.tabla = {}
-        for i in xrange(16):
-            for j in xrange(16):
+        for i in xrange(ROWS):
+            for j in xrange(self.columns):
                 led = Led()
                 self.table_leds.attach(led, j, j+1, i, i+1)
                 led.show()
@@ -65,9 +71,10 @@ class MainWindow(SimpleGladeApp):
 
     def on_btn_recibir_clicked(self, widget, *args):
         matrix = self.device.matrix
-        for row in xrange(16):
-            for col in xrange(16):
+        for row in xrange(ROWS):
+            for col in xrange(self.columns):
                 self.tabla[row,col].prendido = matrix[row,col]
+                self.tabla[row,col].queue_draw()
         etherled.protocol._print_matrix(matrix)
 
     def on_main_window_delete_event(self, widget, event, *args):
@@ -75,18 +82,75 @@ class MainWindow(SimpleGladeApp):
 
     def leds2matrix(self):
         matrix = {}
-        for row in xrange(16):
-            for col in xrange(16):
+        for row in xrange(ROWS):
+            for col in xrange(self.columns):
                 matrix[row,col] = int(self.tabla[row,col].prendido)
         etherled.protocol._print_matrix(matrix)
         return matrix
 
+
+def parse_options():
+    parser = OptionParser(description="Cliente de etherled",
+        version="%prog " + app_version, prog='cetherled')
+    parser.add_option("-s", "--server", default='localhost', dest="host",
+        metavar="HOSTNAME", help="Nombre/IP del host del dispositivo "
+        "[default: localhost]")
+    parser.add_option("-p", "--port", default=9876, metavar="PORT",
+        type="int", help="Puerto UDP del dispositivo [default: 9876].")
+    parser.add_option("-c", "--columns", default=16, metavar="COLS",
+        type="int", help="Cantidad de columnas de la matriz [default: 16].")
+    parser.add_option("-g", "--gui", default=False, action="store_true",
+        help="Levanta la interfaz gráfica para dibujar la matriz")
+    (opts, args) = parser.parse_args()
+    return (parser, opts, args)
+
+
 def main():
-    gtk.threads_init()
-    main_window = MainWindow()
-    gtk.threads_enter()
-    main_window.run()
-    gtk.threads_leave()
+    (parser, opts, args) = parse_options()
+    if (opts.columns < 8) or (opts.columns > 32):
+        parser.error("El número de columnas debe estar entre 8 y 32.")
+    if opts.gui:
+        gtk.threads_init()
+        main_window = MainWindow(columns=opts.columns, host=opts.host,
+            port=opts.port)
+        gtk.threads_enter()
+        main_window.run()
+        gtk.threads_leave()
+    else:
+        if len(args) < 2:
+            parser.error("Debe especificarse un comando si no se usa la GUI.")
+        type = args[0]
+        var  = args[1]
+        dev  = etherled.NetworkedDevice(opts.host, opts.port)
+        if type == 'get':
+            if len(args) > 2:
+                parser.error("El comando get no puede llevar argumentos.")
+            if var == 'matrix':
+                etherled.protocol._print_matrix(dev.matrix)
+            elif var == 'pause':
+                print dev.paused
+            elif var == 'delay':
+                print dev.delay
+            else:
+                parser.error("Variable desconocida, debe ser una de: "
+                    "matrix, pause, delay.")
+        elif type == 'set':
+            if var == 'off':
+                dev.turn_off()
+            elif var == 'matrix':
+                parser.error("Use la GUI para enviar la matriz.")
+            elif var == 'pause':
+                dev.paused = True
+            elif var == 'continue':
+                dev.paused = False
+            elif var == 'delay':
+                if len(args) != 3:
+                    parser.error("Delay lleva 1 argumento.")
+                dev.delay = int(args[2])
+            else:
+                parser.error("Variable desconocida, debe ser una de: "
+                    "off, pause, continue, delay.")
+
 
 if __name__ == '__main__':
     main()
index 545f9a29bd90407ad665cb8c3430656870b45eab..77834586cf505296f18d4d5eba0230bbae433f9c 100644 (file)
 # Autores: Leandro Lucarella <llucare@fi.uba.ar>
 #----------------------------------------------------------------------------
 
+from sets import ImmutableSet as frozenset
+
 # Tipos de operación
 TYPE_GET = 0
 TYPE_SET = 1
 
 # Variables
-VAR_MATRIX = 0
+VAR_OFF     = 0
+VAR_MATRIX  = 1
+VAR_PAUSE   = 2
+VAR_DELAY   = 3
 
-# Limites
-MAX_ID = 8
-MAX_VAR = 8
+# Variables soportadas
+supported_vars = frozenset([VAR_OFF, VAR_MATRIX, VAR_PAUSE, VAR_DELAY])
 
-class ParityError(ValueError):
-    pass
+# Limites
+MAX_ID = 7
 
 class Packet(object):
 
@@ -51,12 +55,9 @@ class Packet(object):
 
     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.type = header >> 7         # bit 7
+        self.var = (header & 0x78) >> 3 # bits 6 5 4 3
+        self.id = (header & 0x07)       # bits 2 1 0
         self.data = string[1:] or None
 
     def __str__(self):
@@ -77,16 +78,7 @@ class Packet(object):
         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
+        return (self.type << 7) + (self.var << 3) + self.id
 
     def _getType(self):
         return self._type
@@ -100,22 +92,21 @@ class Packet(object):
         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
+        if var not in supported_vars:
+            raise ValueError, "var puede ser uno de %s" % tuple(supported_vars)
         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
+        if id < 0 or id > MAX_ID:
+            raise ValueError, "id debe estar entre 0 y %d" % MAX_ID
         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):
 
@@ -153,13 +144,29 @@ class ServerPacket(Packet):
 
 # 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')
+    # GET
+    assert str(Packet(TYPE_GET, VAR_OFF, 0)) == chr(0x00)
+    assert str(Packet(TYPE_GET, VAR_OFF, 7)) == chr(0x07)
+    assert str(Packet(TYPE_GET, VAR_MATRIX, 1)) == chr(0x09)
+    assert str(Packet(TYPE_GET, VAR_MATRIX, 6)) == chr(0x0E)
+    assert str(Packet(TYPE_GET, VAR_PAUSE, 2)) == chr(0x12)
+    assert str(Packet(TYPE_GET, VAR_PAUSE, 5)) == chr(0x15)
+    assert str(Packet(TYPE_GET, VAR_DELAY, 3)) == chr(0x1B)
+    assert str(Packet(TYPE_GET, VAR_DELAY, 4)) == chr(0x1C)
+    assert str(Packet(TYPE_GET, VAR_DELAY, 7, 'hola')) == chr(0x1F) + 'hola'
+    p = Packet(TYPE_GET, VAR_MATRIX, 0, 'hola')
+    assert Packet(str(p)) == p
+    # SET
+    assert str(Packet(TYPE_SET, VAR_OFF, 0)) == chr(0x80)
+    assert str(Packet(TYPE_SET, VAR_OFF, 7)) == chr(0x87)
+    assert str(Packet(TYPE_SET, VAR_MATRIX, 1)) == chr(0x89)
+    assert str(Packet(TYPE_SET, VAR_MATRIX, 6)) == chr(0x8E)
+    assert str(Packet(TYPE_SET, VAR_PAUSE, 2)) == chr(0x92)
+    assert str(Packet(TYPE_SET, VAR_PAUSE, 5)) == chr(0x95)
+    assert str(Packet(TYPE_SET, VAR_DELAY, 3)) == chr(0x9B)
+    assert str(Packet(TYPE_SET, VAR_DELAY, 4)) == chr(0x9C)
+    assert str(Packet(TYPE_SET, VAR_DELAY, 7, 'hola')) == chr(0x9F) + 'hola'
+    p = Packet(TYPE_SET, VAR_MATRIX, 0, 'hola')
     assert Packet(str(p)) == p
     print "OK!"
 
index a1236f482800fd94344f99f8965056783d95e484..c9873e30bda7f0485ea369cab2b8b315c2827427 100644 (file)
@@ -43,7 +43,7 @@ class RecvError(socket.error):
 
 class Client(object):
 
-    def __init__(self, host='localhost', port=38437, timeout=3.0):
+    def __init__(self, host='localhost', port=9876, timeout=3.0):
         self._host = host
         self._port = port
         self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -67,6 +67,7 @@ class Client(object):
             except socket.timeout:
                 raise RecvError, "Tiempo de espera agotado"
             pkt_r = packet.ClientPacket(msg)
+            # Verifica ACK
             if pkt == pkt_r:
                 break # paquete ok
             self._drop += 1
@@ -80,7 +81,7 @@ class Client(object):
 
     def _getId(self, type, var):
         id = self._ids.get((type, var), 0)
-        self._ids[type, var] = (id + 1) % packet.MAX_ID
+        self._ids[type, var] = (id + 1) % (packet.MAX_ID + 1)
         return id
 
     def _getHost(self):
@@ -102,6 +103,9 @@ class Client(object):
 
 class NetworkedDevice(Client):
 
+    def turn_off(self):
+        self.set(packet.VAR_OFF)
+
     def _getMatrix(self):
         stream = self.get(packet.VAR_MATRIX)
         return _stream2Matrix(stream)
@@ -110,11 +114,27 @@ class NetworkedDevice(Client):
         stream = _matrix2Stream(matrix)
         self.set(packet.VAR_MATRIX, stream)
 
+    def _getPaused(self):
+        return bool(ord(self.get(packet.VAR_PAUSE)))
+
+    def _setPaused(self, paused):
+        self.set(packet.VAR_PAUSE, chr(paused))
+
+    def _getDelay(self):
+        return ord(self.get(packet.VAR_DELAY))
+
+    def _setDelay(self, delay):
+        self.set(packet.VAR_DELAY, chr(delay & 0xFF))
+
     matrix = property(_getMatrix, _setMatrix, doc='Matriz de leds')
+    paused = property(_getPaused, _setPaused,
+                      doc='Indica si el dispositivo está en pausa')
+    delay  = property(_getDelay, _setDelay,
+                      doc='Timpo de retardo del dibujado')
 
 class DummyServer:
 
-    def __init__(self, host='localhost', port=38437, timeout=3.0):
+    def __init__(self, host='localhost', port=9876, timeout=3.0):
         self._host = host
         self._port = port
         self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -159,23 +179,21 @@ def _stream2Matrix(stream):
     cols = ord(stream[0]) # Obtiene tamaño
     stream = stream[1:1+cols*_LED_BYTES] # me quedo con el resto
     matrix = {}
-    for col in xrange(cols-1, -1, -1):
+    for col in xrange(cols):
         for row_byte in xrange(_LED_BYTES):
-            byte = ord(stream[(cols-col-1)*_LED_BYTES+row_byte])
+            byte = ord(stream[col*_LED_BYTES+_LED_BYTES-row_byte-1])
             for i in xrange(8):
-                shift = 8 - i - 1
-                matrix[row_byte*8+i,col] = (byte >> shift) & 1
+                matrix[row_byte*8+i,col] = (byte >> i) & 1
     return matrix
 
 def _matrix2Stream(matrix):
     cols = len(matrix) / (_LED_BYTES*8)
     stream = chr(cols) # primero va el tamaño
-    for col in xrange(cols-1, -1, -1):
-        for i in xrange(_LED_BYTES):
+    for col in xrange(cols):
+        for i in xrange(_LED_BYTES-1, -1, -1):
             byte = 0
-            for row in xrange(8):
-                shift = 8 - row - 1
-                byte += matrix[row+i*8,col] << shift
+            for row in xrange(7, -1, -1):
+                byte += matrix[row+i*8,col] << row
             stream += chr(byte)
     return stream
 
@@ -271,6 +289,15 @@ if __name__ == '__main__':
         _print_matrix(dev.matrix)
         # Verifico resultado
         assert matrix == dev.matrix
+        # Probamos con otras cositas
+        dev.paused = True
+        assert dev.paused == True
+        dev.paused = False
+        assert dev.paused == False
+        dev.delay = 0x40
+        assert dev.delay == 0x40
+        dev.delay = 0xff
+        assert dev.delay == 0xff
         # Matamos al servidor
         os.kill(pid, 15)
     else: