changeset 9:d147529ad2db

Switch to only reading RX pkts and writing TX pkts to make things simpler. Add AT reply test case.
author darius@inchoate.localdomain
date Mon, 05 Nov 2007 23:35:52 +1030
parents 9f0808b13454
children 4c91fdfc862e
files zb.py
diffstat 1 files changed, 104 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/zb.py	Sun Nov 04 21:10:21 2007 +1030
+++ b/zb.py	Mon Nov 05 23:35:52 2007 +1030
@@ -1,22 +1,46 @@
 import serial, inspect
 
+def easyord(i):
+    if (type(i) != type(str())):
+        return i
+    else:
+        return ord(i)
+
 class PktBase(object):
+    PKT_MAXLEN = 2 ** 16
+    
     def __init__(self, data = []):
-        self.data = data
+        self._data = data
         print "Constructing " + self.__class__.__name__
 
-    def __repr__(self):
-        return str(self.__class__) + "(" + str([self.PKT_TYPE] + self.data) + ")"
-
     def Encapsulate(self):
         return Packets.Encapsulate([self.PKT_TYPE] + self.data)
 
     def resize(self, dlen):
         """Ensure the data list can hold at least len elements (0 fill)"""
-        if (len(self.data) < dlen):
-            self.data = (self.data + [0] * dlen)[0:dlen]
+        if (len(self._data) < dlen):
+            self._data = (self._data + [0] * dlen)[0:dlen]
+
+    def _checklist(list, min = 0, max = 255, maxlen = None):
+        if (maxlen != None and len(list) > maxlen):
+            raise ValueError("must have %d elements" % (maxlen))
+            
+        for i in xrange(len(list)):
+            if (easyord(list[i]) < min or easyord(list[i]) > max):
+                raise ValueError("element %d (= %d) out of range must be between %d and %d inclusive" %
+                                 (i, ord(list[i]), min, max))
+    _checklist = staticmethod(_checklist)
+
+class TXPkts(PktBase):
+    """Base class for all packets that go to the module"""
+    def setframeid(self, value):
+        if (value < 0 or value > 255):
+            raise ValueError("FrameID must be 0-255")
+        self._frameid = value
+    frameid =  property(lambda s: s._frameid, setframeid)
     
-class AT_Cmd(PktBase):
+    
+class AT_Cmd(TXPkts):
     PKT_TYPE = 0x08
     PKT_DESC = "AT Command"
 
@@ -33,22 +57,25 @@
             self.cmdarg = args[1]
         else:
             raise TypeError("__init__ takes 1 list of ordinals, 1 string, or 2 strings")
-
-        self.data[0] = 1 # XXX: could be smarter about Frame ID
-
+        self.frameid = 0
+        self.cmdarg = []
+        
     def setcmd(self, value):
-        self.resize(3)
-        self.data[1] = ord(value[0])
-        self.data[2] = ord(value[1])
-    cmd = property(lambda s: chr(s.data[1]) + chr(s.data[2]), setcmd)
+        if (len(value) != 2):
+            raise ValueError("must have 2 elements")
+        self._checklist(value, ord('0'), ord('z'))
+        self._cmd = value
+    cmd = property(lambda s: s._cmd, setcmd)
 
     def setcmdarg(self, value):
-        self.resize(3 + len(value))
-        self.data = self.data[0:3] + map(ord, value)
-    def delcmdarg(self):
-        self.data = self.data[0:3]
-    cmdarg = property(lambda s: map(chr, s.data[3:]), setcmdarg, delcmdarg)
-        
+        self._checklist(value, maxlen = self.PKT_MAXLEN - 3)
+        self._cmdarg = value
+    cmdarg = property(lambda s: s._cmdarg, setcmdarg)
+
+    def getdata(self):
+        return([self.frameid] + map(ord, self.cmd) + map(easyord, self.cmdarg))
+    data = property(getdata)
+
 class AT_Cmd_Queue(AT_Cmd):
     PKT_TYPE = 0x09
     PKT_DESC = "AT Command (queued)"
@@ -56,10 +83,10 @@
 class AT_Response(PktBase):
     PKT_TYPE = 0x88
     PKT_DESC = "AT Command response"
-    frameid = property(lambda s: s.data[0], None)
-    cmd = property(lambda s: chr(s.data[1]) + chr(s.data[2]), None)
-    statusOK = property(lambda s: s.data[3] == 0, None)
-    payload = property(lambda s: s.data[4:], None)
+    frameid = property(lambda s: s._data[0], None)
+    cmd = property(lambda s: chr(s._data[1]) + chr(s._data[2]), None)
+    statusOK = property(lambda s: s._data[3] == 0, None)
+    payload = property(lambda s: s._data[4:], None)
 
 class Modem_Status(PktBase):
     PKT_TYPE = 0x8a
@@ -77,15 +104,15 @@
     def getsender(self):
         value = 0
         for i, j in zip(reversed(range(self.ADDR_SIZE)), range(0, self.ADDR_SIZE * 8, 8)):
-            value |= self.data[i] << j
+            value |= self._data[i] << j
         return value
     sender = property(getsender, None)
 
-    rssi = property(lambda s: -1 * s.data[s.ADDR_SIZE], None)
+    rssi = property(lambda s: -1 * s._data[s.ADDR_SIZE], None)
 
-    flags = property(lambda s: s.data[s.ADDR_SIZE + 1], None)
+    flags = property(lambda s: s._data[s.ADDR_SIZE + 1], None)
 
-    payload = property(lambda s: s.data[s.ADDR_SIZE + 2:], None)
+    payload = property(lambda s: s._data[s.ADDR_SIZE + 2:], None)
                        
 class RX_64_Bit(RX_16_Bit):
     PKT_TYPE = 0x80
@@ -96,27 +123,17 @@
     PKT_TYPE = 0x83
     PKT_DESC = "RXIO Packet: 16 bit address"
 
-    def setnsamples(self, value):
-        self.resize(self.ADDR_SIZE + 3)
-        self.data[self.ADDR_SIZE + 2] = value
+    nsamples = property(lambda s: s._data[s.ADDR_SIZE + 2])
 
-    def getnsamples(self):
-        return self.data[self.ADDR_SIZE + 2]
-    nsamples = property(getnsamples, setnsamples)
-
-    def setmask(self, value):
-        self.resize(self.ADDR_SIZE + 5)
-        self.data[self.ADDR_SIZE + 3] = (value & 0xff00) >> 8
-        self.data[self.ADDR_SIZE + 4] = value & 0xff
-    mask = property(lambda s: s.data[s.ADDR_SIZE + 3] << 8 | s.data[s.ADDR_SIZE + 4], setmask)
+    mask = property(lambda s: s._data[s.ADDR_SIZE + 3] << 8 | s._data[s.ADDR_SIZE + 4])
 
     def __str__(self):
         rtn = "0x%0*x (%ddBm) -> %d samples, mask 0x%04x" % (self.ADDR_SIZE * 2, self.sender,
                                                              self.rssi, self.nsamples, self.mask)
         # Any DIO lines enabled?
         if (self.mask | 0x01ff):
-            rtn = rtn + ", DIO - 0x%03x" % (self.data[self.ADDR_SIZE + 5] << 8 |
-                                            self.data[self.ADDR_SIZE + 6])
+            rtn = rtn + ", DIO - 0x%03x" % (self._data[self.ADDR_SIZE + 5] << 8 |
+                                            self._data[self.ADDR_SIZE + 6])
             offs = self.ADDR_SIZE + 7
         else:
             offs = self.ADDR_SIZE + 5
@@ -125,10 +142,9 @@
         if (self.mask | 0x7e00):
             for i in range(6):
                 if (self.mask & 1 << (i + 9)):
-                    rtn = rtn + ", ADC%d - 0x%02x" % (i, self.data[offs] << 8 |
-                                                      self.data[offs + 1])
+                    rtn = rtn + ", ADC%d - 0x%02x" % (i, self._data[offs] << 8 |
+                                                      self._data[offs + 1])
                     offs = offs + 2
-
         return rtn
 
 class RXIO_64_Bit(RXIO_16_Bit):
@@ -136,18 +152,18 @@
     PKT_DESC = "RXIO Packet: 64 bit address"
     ADDR_SIZE = 8
     
-class TX_16_Bit(RX_64_Bit):
+class TX_16_Bit(TXPkts):
     PKT_TYPE = 0x01
     PKT_DESC = "TX Packet: 16 bit address"
     ADDR_SIZE = 2
-
+    FLG_DISABLE_ACK = 0x01
+    FLG_BCAST_PANID = 0x04
+    PKT_MAX_PAYLOAD = 100
+    
     def __init__(self, *args):
         if (len(args) == 1):
-            if (type(args[0]) == type(int())):
-                super(TX_16_Bit, self).__init__([])
-                self.recipient = args[0]
-            else:
-                super(TX_16_Bit, self).__init__(args[0])
+            super(TX_16_Bit, self).__init__()
+            self.recipient = args[0]
         elif (len(args) == 2):
             super(TX_16_Bit, self).__init__([])
             self.recipient = args[0]
@@ -155,28 +171,38 @@
         else:
             raise TypeError("__init__ takes 1 list of ordinals or 2 strings")
 
-        self.resize(self.ADDR_SIZE + 2)
-        self.data[0] = 1 # XXX: could be smarter about Frame ID
-        self.data[3] = 0 # XXX: should be able to set flags
+        self.frameid = 0
+        self.flags = 0
+        self.payload = []
+        
+    def setrecipient(self, value):
+        if (value < 0 or value > 2 ** (self.ADDR_SIZE * 8)):
+            raise ValueError("value out of range must be between 0 and %d" % (2 ** self.ADDR_SIZE))
         
-    def getrecipient(self):
-        value = 0
-        for i, j in zip(reversed(range(self.ADDR_SIZE)), range(0, self.ADDR_SIZE * 8, 8)):
-            value |= self.data[i + 1] << j
-        return value
-    
-    def setrecipient(self, value):
-        self.resize(self.ADDR_SIZE + 1)
-        self.data[0] = 1 # XXX: could be smarter about Frame ID
-        for i, j in zip(reversed(range(self.ADDR_SIZE)), range(0, self.ADDR_SIZE * 8, 8)):
-            self.data[i + 1] = (value & (0xff << j)) >> j
-    recipient = property(getrecipient, setrecipient)
+        self._recipient = value
+    recipient = property(lambda s: s._recipient, setrecipient)
+
+    def setflags(self, value):
+        if (value < 0 or value > 255):
+            raise ValueError("Value must be between 0 and 255 inclusive")
+
+        self._flags = value
+    flags = property(lambda s: s._flags, setflags)
 
     def setpayload(self, value):
-        self.resize(self.ADDR_SIZE + 2)
-        self.data[self.ADDR_SIZE + 2:] = value
-    payload = property(lambda s: s.data[self.ADDR_SIZE + 2:], setpayload)
-                       
+        self._checklist(value, maxlen = self.PKT_MAX_PAYLOAD)
+        self._payload = value
+    payload = property(lambda s: s._payload, setpayload)
+
+    def getdata(self):
+        data = [self.frameid]
+        for i, j in zip(reversed(range(self.ADDR_SIZE)), range(0, self.ADDR_SIZE * 8, 8)):
+            data.append((self.recipient & (0xff << j)) >> j)
+        data.append(self.flags)
+        data.extend(map(easyord, self.payload))
+        return(data)
+    data = property(getdata)
+
 class TX_64_Bit(TX_16_Bit):
     PKT_TYPE = 0x00
     PKT_DESC = "TX Packet: 64 bit address"
@@ -186,9 +212,9 @@
     PKT_TYPE = 0x89
     PKT_DESC = "TX Status"
     statusTxt = ['OK', 'No Ack', 'CCA failure', 'Purged']
-    frameid = property(lambda s: s.data[0], None)
-    status = property(lambda s: s.data[1], None)
-    statusMsg = property(lambda s: s.statusTxt[s.data[1]], None)
+    frameid = property(lambda s: s._data[0], None)
+    status = property(lambda s: s._data[1], None)
+    statusMsg = property(lambda s: s.statusTxt[s._data[1]], None)
     
 class Packets(object):
     PKT_CLASSES = None
@@ -225,7 +251,7 @@
         self.packets = []
         
         self.bufmsb = 0
-        self.dataleft = 0
+        self._dataleft = 0
         
         self.fr_err = 0 # Framing error
         self.ck_err = 0 # Checksum error
@@ -314,6 +340,9 @@
 # Exception
 badpkttypetest = [126, 0, 3, 10, 86, 76, 83]
 
+# Frame ID = 0, Cmd = 'VL', Status = OK, Value = 'VL Result'
+atreply = [126, 0, 14, 136, 0, 86, 76, 0, 86, 76, 32, 82, 101, 115, 117, 108, 116, 148]
+
 up = Packets(s)
 up.process(goodtest)
 up.process(badtest)