From 0517035110c91cfc762b5fc2a123de50d2d2a401 Mon Sep 17 00:00:00 2001 From: schneider Date: Wed, 14 Dec 2011 17:59:32 +0100 Subject: [PATCH] modified system to work with threads --- tools/game/r0ketrem0te/packets.py | 197 ++++++++++++++++++++++++++++++ tools/game/r0ketrem0te/rem0te.py | 167 ++++++++++++++++++++++--- tools/game/simpletest.py | 28 ++++- 3 files changed, 371 insertions(+), 21 deletions(-) create mode 100644 tools/game/r0ketrem0te/packets.py diff --git a/tools/game/r0ketrem0te/packets.py b/tools/game/r0ketrem0te/packets.py new file mode 100644 index 0000000..b5e6e7f --- /dev/null +++ b/tools/game/r0ketrem0te/packets.py @@ -0,0 +1,197 @@ +def inttouint32(v): + return chr(v&0xff)+chr((v>>8)&0xff)+chr((v>>16)&0xff)+chr((v>>24)&0xff) + +def uint32toint(v): + return (ord(v[3])<< 24) + (ord(v[2])<<16) + (ord(v[1])<<8) + (ord(v[0])) + +class Packet: + def __init__(self, command, id=None, ctr=None): + if id == None and ctr == None: + message = command + command = message[2] + id = uint32toint(message[3:7]) + ctr = uint32toint(message[7:11]) + self.length = 32 + self.protocol = 'G' + self.command = command + self.id = id + self.ctr = ctr + + def toMessage(self): + message = chr(self.length) + message += self.protocol + message += self.command + message += inttouint32(self.id) + message += inttouint32(self.ctr) + return message + + def headerString(self): + return "id=%d, ctr=%d"%(self.id, self.ctr) + + def __str__(self): + s = "Packet with protocol=" + self.protocol + s += ", command=" + self.command + s += ", "+ self.headerString() + return s + +class Button(Packet): + def __init__(self, id, ctr=None, button=None): + if ctr != None and button!= None: + Packet.__init__(self, 'B', id, ctr) + else: + message = id + Packet.__init__(self, message) + button = ord(message[11]) + self.button = button + + def toMessage(self): + base = Packet.toMessage(self) + return base + chr(self.button) + '\x00'*18 + + def __str__(self): + s = "Button packet with " + self.headerString() + s += ", button=%d"%self.button + return s + +class Announce(Packet): + def __init__(self, id, ctr, gameMac, gameChannel, gameId, gameFlags, gameTitle): + Packet.__init__(self, 'A', id, ctr) + self.gameMac = gameMac + self.gameChannel = gameChannel + self.gameId = gameId + self.gameFlags = gameFlags + self.gameTitle = gameTitle[0:8] + + def toMessage(self): + message = Packet.toMessage(self) + message += ''.join([chr(x) for x in self.gameMac]) + message += chr(self.gameChannel) + message += inttouint32(self.gameId) + message += chr(self.gameFlags) + message += self.gameTitle + if len(self.gameTitle) < 8: + message += '\x00'*(8-len(self.gameTitle)) + return message + + def __str__(self): + s = "Announce packet with " + self.headerString() + s += ", gameMac="+str(self.gameMac) + s += ", gameChannel=%d"%self.gameChannel + s += ", gameId=%d"%self.gameId + s += ", gameFlags=%d"%self.gameFlags + s += ", gameTitle="+self.gameTitle + return s + +class Join(Packet): + def __init__(self, id, ctr=None, gameId=None): + if ctr != None and gameId != None: + Packet.__init__(self, 'J', id, ctr) + else: + message = id + Packet.__init__(self, message) + gameId = uint32toint(message[11:15]) + self.gameId = gameId + + def toMessage(self): + message = Packet.toMessage(self) + message += inttouint32(self.gameId) + message += '\x00'*15 + return message + + def __str__(self): + s = "Join packet with " + self.headerString() + s += ", gameId=%d"%self.gameId + return s + +class Ack(Packet): + def __init__(self, id, ctr=None, flags=None): + if ctr != None and flags != None: + Packet.__init__(self, 'a', id, ctr) + else: + message = id + Packet.__init__(self, message) + flags = ord(message[11]) + self.flags = flags + + def toMessage(self): + message = Packet.toMessage(self) + message += chr(self.flags) + message += '\x00'*18 + return message + + def __str__(self): + s = "Ack packet with " + self.headerString() + s += ", flags=%d"%self.flags + return s + +class Nickrequest(Packet): + def __init__(self, id, ctr): + Packet.__init__(self, 'N', id, ctr) + + def __str__(self): + s = "Nickrequest packet with " + self.headerString() + return s + +class Nick(Packet): + def __init__(self, id, ctr=None, flags=None, nick=None): + if ctr != None and flags != None and nick != None: + Packet.__init__(self, 'n', id, ctr) + else: + message = id + Packet.__init__(self, message) + flags = ord(message[11]) + nick = message[12:30].rstrip(' \t\r\n\0') + self.flags = flags + self.nick = nick + + def toMessage(self): + message = Packet.toMessage(self) + message += chr(self.flags) + message += self.nick + if len(self.nick) < 18: + message += '\x00'*(18-len(self.nick)) + return message + + def __str__(self): + s = "Nick packet with " + self.headerString() + s += ", flags=%d"%self.flags + s += ", nick="+self.nick + return s + +class Text(Packet): + def __init__(self, id, ctr, x, y, flags, text): + Packet.__init__(self, 'T', id, ctr) + self.x = x + self.y = y + self. flags = flags + self.text = text[0:16] + + def toMessage(self): + message = Packet.toMessage(self) + message += chr(self.x) + message += chr(self.y) + message += chr(self.flags) + message += self.text + if len(self.text) < 16: + message += '\x00'*(16-len(self.text)) + return message + + def __str__(self): + s = "Text packet with " + self.headerString() + s += ", x=%d"%self.x + s += ", y=%d"%self.y + s += ", flags=%d"%self.flags + s += ", text="+self.text + return s + +def fromMessage(message): + if len(message) >= 30 and ord(message[0]) == 32 and message[1] == 'G': + if message[2] == 'B': + return Button(message) + if message[2] == 'n': + return Nick(message) + if message[2] == 'J': + return Join(message) + if message[2] == 'a': + return Ack(message) + return None diff --git a/tools/game/r0ketrem0te/rem0te.py b/tools/game/r0ketrem0te/rem0te.py index 080e82b..3882eb1 100644 --- a/tools/game/r0ketrem0te/rem0te.py +++ b/tools/game/r0ketrem0te/rem0te.py @@ -1,25 +1,135 @@ import serialinterface -import thread import threading import Queue import crcmod +import packets -class r0ket: +class QueuePacket: + def __init__(self, channel, mac, acked, packet): + self.channel = channel + self.mac = mac + self.acked = acked + self.packet = packet + self.priority = 5 + self.retriesleft = 5 + self.timeout = 0.1 + self.timer = None + self.timedout = False + self.lock = threading.RLock() + self.isdone = False + + def __cmp__(self, other): + if not isinstance(other,QueuePacket): + return 1 + if self.priority < other.priority: + return -1 + if self.priority > other.priority: + return 1 + return 0 + + def valid(self): + with self.lock: + return self.retriesleft > 0 and not self.acked + + def sent(self, timeoutcallback): + with self.lock: + self.timedout = False + if self.acked: + self.timeoutcallback = timeoutcallback + self.timer = threading.Timer(self.timeout, self.timercallback) + self.timer.start() + + def done(self): + with self.lock: + if self.timer != None: + self.timer.cancel() + self.timer = None + self.isdone = True + + def timercallback(self): + with self.lock: + self.timedout = True + self.timeoutcallback(self) + +class Bridge: def __init__(self, path2device): self.ser = serialinterface.SerialInterface(path2device, 115200, 0) self.free = threading.Lock() - self.packets = Queue.Queue() - thread.start_new_thread(self.readerThread,()) - self.setPacketLength(0x20) + self.queueslock = threading.Lock() + self.packets = Queue.PriorityQueue() + self.outpackets = Queue.Queue() self.crc = crcmod.predefined.mkCrcFun('crc-ccitt-false') + self.queues = {} + + self.reader = threading.Thread(target = self.readerThread) + self.reader.daemon = True + + self.writer = threading.Thread(target = self.writerThread) + self.writer.daemon = True + + self.writer.start() + self.reader.start() - def writeCommand(self, command, data): - crc = self.crc(data) - data += chr(crc>>8); - data += chr(crc&0xFF); - self.free.acquire() - print 'sending command:', command, 'len:', len(data), 'data:', list(data) - self.ser.writeMessage(command,data); + self.setPacketLength(0x20) + self.setTxMAC((1,2,3,2,1)) + self.setRxMAC((1,2,3,2,1)) + self.setChannel(81) + + def registerQueue(self, queue): + if queue not in self.queues: + self.queues[queue] = None + + def putInQueue(self, queue, qp): + if queue in self.queues: + queue.put(qp); + self.checkQueues() + + def processAck(self, ack): + #find the corresponding packet in the queues + found = False + for pq in self.queues.values(): + if pq.packet.id == ack.id and pq.packet.ctr == ack.ctr: + #notify it + pq.done() + found = True + #notify the queue system + if found: + self.checkQueues() + else: + print "got an ack for an unknown packet" + + def packetTimeout(self, qp): + self.checkQueues() + + def checkQueues(self): + with self.queueslock: + for q in self.queues: + #check if a packet has to be resent + #remove it from the packet slot if it has been resent to often + qp = self.queues[q] + if qp != None: + if qp.valid(): + self.queues[q] = None + elif qp.timedout: + print "packet timed out" + qp.packet + self.outpackets.put(qp) + #check if a idle queue has a new packet in line + qp = self.queues[q] + if qp == None and not q.empty(): + qp = q.get() + self.queues[q] = qp + self.outpackets.put(qp) + + def writerThread(self): + while True: + try: + #wait until we have packets to take care of + qp = self.outpackets.get() + #send it and notify the queuepacket + self.sendPacket(qp.packet) + qp.sent(self.packetTimeout) + except Exception as e: + print e def readerThread(self): while True: @@ -36,8 +146,15 @@ class r0ket: print e def newPacket(self, data): - print "received:", list(data) - self.packets.put(data) + #print "received:", list(data) + crc = self.crc(data[:-2]) + if data[-2:] == chr(crc>>8) + chr(crc&0xFF): + packet = packets.fromMessage(data) + print "received:", packet + if isinstance(packet,packets.Ack): + self.ProcessAck(packet) + else: + self.packets.put(packet) def gotPacket(self): return not self.packets.empty() @@ -46,8 +163,28 @@ class r0ket: return self.packets.get() def sendPacket(self, packet): - self.writeCommand('1', packet) + print 'sending', packet + data = packet.toMessage() + crc = self.crc(data) + data += chr(crc>>8); + data += chr(crc&0xFF); + self.free.acquire() + #print 'sending packet: len:', len(data), 'data:', list(data) + self.ser.writeMessage('1',data); def setPacketLength(self, length): self.free.acquire() self.ser.writeMessage('6', '%c'%length) + + def setTxMAC(self, mac): + self.free.acquire() + self.ser.writeMessage('3', ''.join([chr(x) for x in mac])) + + def setRxMAC(self, mac): + self.free.acquire() + self.ser.writeMessage('4', ''.join([chr(x) for x in mac])) + + def setChannel(self, channel): + self.free.acquire() + self.ser.writeMessage('5', '%c'%channel) + diff --git a/tools/game/simpletest.py b/tools/game/simpletest.py index f757cae..cff1019 100644 --- a/tools/game/simpletest.py +++ b/tools/game/simpletest.py @@ -1,11 +1,27 @@ import r0ketrem0te.rem0te +import r0ketrem0te.packets import time -r = r0ketrem0te.rem0te.r0ket('/dev/ttyACM0') +import Queue + +announcequeue = Queue.Queue() +r = r0ketrem0te.rem0te.Bridge('/dev/ttyACM0') + +r.registerQueue(announcequeue) + +a = r0ketrem0te.packets.Announce(0,2,(1,2,3,2,1), 81, 1, 0, "testgame") +aq = r0ketrem0te.rem0te.QueuePacket(81, (1,2,3,2,1), False, a) +aq.priority = 4 while True: - r.sendPacket("\x20GA\x00\x00\x00\x00\x01\x02\x03\x04\x01\x02\x03\x01\x02\x51\x01\x02\x03\x04\x00test\x00\x06\x07\x08") - time.sleep(1) - #packet = r.getPacket() - #print list(packet) - #r.sendPacket('12345678901234567890123456789012') + r.putInQueue(announcequeue, aq) + for i in range(1,1000): + if r.gotPacket(): + packet = r.getPacket() + if isinstance(packet, r0ketrem0te.packets.Join): + r.sendPacket(r0ketrem0te.packets.Ack(packet.id, packet.ctr, 1)) + time.sleep(.001) + + + +