--- /dev/null
+#!/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!"
+
--- /dev/null
+#!/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!"
+