Mercurial > ~darius > hgwebdir.cgi > ZigBee
annotate zb.py @ 12:a3ec3f2988ac
Initial commit of zbmux.
This program demultiplexes packets from remote ZB modules to feed into a log
file. Also allows user(s) to telnet to a port to talk to a particular module.
Needs more work. eg - TCP connections should be in raw mode, not line.
- There is no way to access statistics for each module.
author | darius@Inchoate |
---|---|
date | Tue, 13 Jan 2009 12:17:02 +1030 |
parents | 75f785a09e2e |
children | 729f2393f296 |
rev | line source |
---|---|
11 | 1 # |
2 # Code to talk to MaxStream ZigBee modules in API (no escaped | |
3 # characters) | |
4 # | |
5 # Copyright (c) 2009 | |
6 # Daniel O'Connor <darius@dons.net.au>. All rights reserved. | |
7 # | |
8 # Redistribution and use in source and binary forms, with or without | |
9 # modification, are permitted provided that the following conditions | |
10 # are met: | |
11 # 1. Redistributions of source code must retain the above copyright | |
12 # notice, this list of conditions and the following disclaimer. | |
13 # 2. Redistributions in binary form must reproduce the above copyright | |
14 # notice, this list of conditions and the following disclaimer in the | |
15 # documentation and/or other materials provided with the distribution. | |
16 # | |
17 # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
18 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE | |
21 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 # SUCH DAMAGE. | |
28 # | |
29 """MaxStream ZigBee module API interface | |
30 | |
31 This code expects the module to be in API mode 1 (no escape | |
32 characters), ie you have done something like.. | |
33 +++ | |
34 ATAP=1 | |
35 ATWR | |
36 ATCN | |
37 | |
38 See here for details | |
39 http://www.digi.com/products/wireless/point-multipoint/xbee-series1-moduledocs.jsp | |
40 """ | |
41 | |
42 import inspect | |
1 | 43 |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
44 def easyord(i): |
11 | 45 """Helper function to return the ordinal of a string, or just the |
46 passed in value""" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
47 if (type(i) != type(str())): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
48 return i |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
49 else: |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
50 return ord(i) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
51 |
1 | 52 class PktBase(object): |
11 | 53 """Base class for all packet types""" |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
54 PKT_MAXLEN = 2 ** 16 |
11 | 55 PKT_TYPE = None |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
56 |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
57 def __init__(self): |
11 | 58 #print "Constructing " + self.__class__.__name__ |
59 pass | |
60 | |
61 def Encapsulate(self): | |
62 """Encapsulate the packet""" | |
63 return Packets.Encapsulate([self.PKT_TYPE] + self.data) | |
1 | 64 |
11 | 65 def Pack(self): |
66 """Return string version of encapsulated packet""" | |
67 return reduce(lambda x, y: str(x) + str(y), self.Encapsulate()) | |
1 | 68 |
4 | 69 def resize(self, dlen): |
70 """Ensure the data list can hold at least len elements (0 fill)""" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
71 if (len(self._data) < dlen): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
72 self._data = (self._data + [0] * dlen)[0:dlen] |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
73 |
11 | 74 @staticmethod |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
75 def _checklist(list, min = 0, max = 255, maxlen = None): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
76 if (maxlen != None and len(list) > maxlen): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
77 raise ValueError("must have %d elements" % (maxlen)) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
78 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
79 for i in xrange(len(list)): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
80 if (easyord(list[i]) < min or easyord(list[i]) > max): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
81 raise ValueError("element %d (= %d) out of range must be between %d and %d inclusive" % |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
82 (i, ord(list[i]), min, max)) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
83 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
84 class TXPkts(PktBase): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
85 """Base class for all packets that go to the module""" |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
86 |
11 | 87 def __init__(self): |
88 # Frame ID of 0 will prevent TX status pakets being sent | |
89 self._frameid = 1 | |
90 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
91 def setframeid(self, value): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
92 if (value < 0 or value > 255): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
93 raise ValueError("FrameID must be 0-255") |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
94 self._frameid = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
95 frameid = property(lambda s: s._frameid, setframeid) |
4 | 96 |
11 | 97 class AT_Cmd(TXPkts): |
98 """AT command packet""" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
99 |
1 | 100 PKT_TYPE = 0x08 |
101 PKT_DESC = "AT Command" | |
102 | |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
103 def __init__(self, cmd = None, cmdarg = None): |
11 | 104 self.frameid = 1 # XXX: why do I need to dupe this? |
105 self.cmdarg = [] | |
106 | |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
107 super(AT_Cmd, self).__init__() |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
108 if (cmd != None): |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
109 self.cmd = cmd |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
110 if (cmdarg != None): |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
111 self.cmdarg = cmdarg |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
112 |
4 | 113 def setcmd(self, value): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
114 if (len(value) != 2): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
115 raise ValueError("must have 2 elements") |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
116 self._checklist(value, ord('0'), ord('z')) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
117 self._cmd = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
118 cmd = property(lambda s: s._cmd, setcmd) |
4 | 119 |
120 def setcmdarg(self, value): | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
121 self._checklist(value, maxlen = self.PKT_MAXLEN - 3) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
122 self._cmdarg = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
123 cmdarg = property(lambda s: s._cmdarg, setcmdarg) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
124 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
125 def getdata(self): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
126 return([self.frameid] + map(ord, self.cmd) + map(easyord, self.cmdarg)) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
127 data = property(getdata) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
128 |
4 | 129 class AT_Cmd_Queue(AT_Cmd): |
11 | 130 """Queued AT command packet""" |
131 | |
1 | 132 PKT_TYPE = 0x09 |
133 PKT_DESC = "AT Command (queued)" | |
134 | |
135 class AT_Response(PktBase): | |
11 | 136 """Response from an AT command packet""" |
137 | |
1 | 138 PKT_TYPE = 0x88 |
139 PKT_DESC = "AT Command response" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
140 frameid = property(lambda s: s._data[0], None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
141 cmd = property(lambda s: chr(s._data[1]) + chr(s._data[2]), None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
142 statusOK = property(lambda s: s._data[3] == 0, None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
143 payload = property(lambda s: s._data[4:], None) |
1 | 144 |
11 | 145 def __init__(self, data = []): |
146 super(AT_Response, self).__init__() | |
147 self._data = data | |
148 | |
1 | 149 class Modem_Status(PktBase): |
150 PKT_TYPE = 0x8a | |
151 PKT_DESC = "Modem Status" | |
152 | |
153 class RX_16_Bit(PktBase): | |
11 | 154 """RX packet from a remote module (16 bit)""" |
1 | 155 PKT_TYPE = 0x81 |
156 PKT_DESC = "RX Packet: 16 bit address" | |
4 | 157 ADDR_SIZE = 2 |
11 | 158 ADRBCASTMSK = 0x01 |
159 PANBCASTMSK = 0x02 | |
4 | 160 |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
161 def __init__(self, data = []): |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
162 super(RX_16_Bit, self).__init__() |
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
163 self._data = data |
11 | 164 |
8
9f0808b13454
Use non-blocking serial access. Add __str__ method for RX packets.
darius@inchoate.localdomain
parents:
7
diff
changeset
|
165 def __str__(self): |
11 | 166 return "RX_%d_Bit 0x%0*x (%ddBm) -> %s" % (self.ADDR_SIZE * 8, self.ADDR_SIZE * 2, |
167 self.sender, self.rssi, self.payloadstr) | |
4 | 168 def getsender(self): |
169 value = 0 | |
11 | 170 # Done this way so we can reuse the code for the 64 bit version |
4 | 171 for i, j in zip(reversed(range(self.ADDR_SIZE)), range(0, self.ADDR_SIZE * 8, 8)): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
172 value |= self._data[i] << j |
4 | 173 return value |
11 | 174 #: Source module address |
5 | 175 sender = property(getsender, None) |
176 | |
11 | 177 def isAdrBcast(self): |
178 """Is this an address broadcast packet?""" | |
179 return self.flags & self.ADRBCASTMSK | |
180 | |
181 def isPanBcast(self): | |
182 """Is this an PAN broadcast packet?""" | |
183 return self.flags & self.PANBCASTMSK | |
184 | |
185 #: RX signal strength (dBm) | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
186 rssi = property(lambda s: -1 * s._data[s.ADDR_SIZE], None) |
1 | 187 |
11 | 188 #: Return flag byte |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
189 flags = property(lambda s: s._data[s.ADDR_SIZE + 1], None) |
4 | 190 |
11 | 191 #: Payload (list of ords) |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
192 payload = property(lambda s: s._data[s.ADDR_SIZE + 2:], None) |
11 | 193 |
194 #: String version of payload | |
195 def payloadstr(self): | |
196 return reduce(lambda a, b: a + chr(b), self.payload, "") | |
197 payloadstr = property(payloadstr, None) | |
198 | |
199 | |
4 | 200 class RX_64_Bit(RX_16_Bit): |
201 PKT_TYPE = 0x80 | |
202 PKT_DESC = "RX Packet: 64 bit address" | |
203 ADDR_SIZE = 8 | |
204 | |
205 class RXIO_16_Bit(RX_16_Bit): | |
11 | 206 """RX I/O packet from remote module (16 bit). |
207 | |
208 This is sent when a remote module is configured to send data based on its IO or DAC pins | |
209 """ | |
1 | 210 PKT_TYPE = 0x83 |
211 PKT_DESC = "RXIO Packet: 16 bit address" | |
212 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
213 nsamples = property(lambda s: s._data[s.ADDR_SIZE + 2]) |
5 | 214 |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
215 mask = property(lambda s: s._data[s.ADDR_SIZE + 3] << 8 | s._data[s.ADDR_SIZE + 4]) |
4 | 216 |
1 | 217 def __str__(self): |
4 | 218 rtn = "0x%0*x (%ddBm) -> %d samples, mask 0x%04x" % (self.ADDR_SIZE * 2, self.sender, |
219 self.rssi, self.nsamples, self.mask) | |
1 | 220 # Any DIO lines enabled? |
4 | 221 if (self.mask | 0x01ff): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
222 rtn = rtn + ", DIO - 0x%03x" % (self._data[self.ADDR_SIZE + 5] << 8 | |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
223 self._data[self.ADDR_SIZE + 6]) |
4 | 224 offs = self.ADDR_SIZE + 7 |
1 | 225 else: |
4 | 226 offs = self.ADDR_SIZE + 5 |
1 | 227 |
228 # Any ADC lines enabled? | |
4 | 229 if (self.mask | 0x7e00): |
1 | 230 for i in range(6): |
4 | 231 if (self.mask & 1 << (i + 9)): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
232 rtn = rtn + ", ADC%d - 0x%02x" % (i, self._data[offs] << 8 | |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
233 self._data[offs + 1]) |
1 | 234 offs = offs + 2 |
235 return rtn | |
236 | |
5 | 237 class RXIO_64_Bit(RXIO_16_Bit): |
4 | 238 PKT_TYPE = 0x82 |
239 PKT_DESC = "RXIO Packet: 64 bit address" | |
240 ADDR_SIZE = 8 | |
241 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
242 class TX_16_Bit(TXPkts): |
11 | 243 """Transmit to a 16 bit destination""" |
6 | 244 PKT_TYPE = 0x01 |
4 | 245 PKT_DESC = "TX Packet: 16 bit address" |
6 | 246 ADDR_SIZE = 2 |
11 | 247 #: Flag to disable ACK |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
248 FLG_DISABLE_ACK = 0x01 |
11 | 249 #: Send to broadcast PAN ID |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
250 FLG_BCAST_PANID = 0x04 |
11 | 251 #: Maximum size payload we can send |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
252 PKT_MAX_PAYLOAD = 100 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
253 |
6 | 254 def __init__(self, *args): |
11 | 255 """Takes 0 to 2 arguments. First is the recipient, the second is the payload (string)""" |
256 self._flags = 0 | |
257 self.payload = [] | |
258 | |
259 if len(args) == 0: | |
260 pass | |
261 if len(args) == 1: | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
262 super(TX_16_Bit, self).__init__() |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
263 self.recipient = args[0] |
11 | 264 elif len(args) == 2: |
265 super(TX_16_Bit, self).__init__() | |
6 | 266 self.recipient = args[0] |
267 self.payload = args[1] | |
268 else: | |
11 | 269 raise TypeError("incorrect number of arguments"); |
4 | 270 |
11 | 271 def __str__(self): |
272 return "TX_%d_Bit 0x%0*x <- %s" % (self.ADDR_SIZE * 8, self.ADDR_SIZE * 2, self.recipient, | |
273 self.payload) | |
274 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
275 def setrecipient(self, value): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
276 if (value < 0 or value > 2 ** (self.ADDR_SIZE * 8)): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
277 raise ValueError("value out of range must be between 0 and %d" % (2 ** self.ADDR_SIZE)) |
6 | 278 |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
279 self._recipient = value |
11 | 280 |
281 """Destination address of the packet""" | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
282 recipient = property(lambda s: s._recipient, setrecipient) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
283 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
284 def setflags(self, value): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
285 if (value < 0 or value > 255): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
286 raise ValueError("Value must be between 0 and 255 inclusive") |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
287 |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
288 self._flags = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
289 flags = property(lambda s: s._flags, setflags) |
5 | 290 |
6 | 291 def setpayload(self, value): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
292 self._checklist(value, maxlen = self.PKT_MAX_PAYLOAD) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
293 self._payload = value |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
294 payload = property(lambda s: s._payload, setpayload) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
295 |
11 | 296 def payloadstr(self): |
297 return reduce(lambda a, b: a + chr(b), self.payload, "") | |
298 payloadstr = property(payloadstr, None) | |
299 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
300 def getdata(self): |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
301 data = [self.frameid] |
11 | 302 for i, j in zip(reversed(range(self.ADDR_SIZE)), reversed(range(0, self.ADDR_SIZE * 8, 8))): |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
303 data.append((self.recipient & (0xff << j)) >> j) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
304 data.append(self.flags) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
305 data.extend(map(easyord, self.payload)) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
306 return(data) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
307 data = property(getdata) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
308 |
6 | 309 class TX_64_Bit(TX_16_Bit): |
1 | 310 PKT_TYPE = 0x00 |
311 PKT_DESC = "TX Packet: 64 bit address" | |
6 | 312 ADDR_SIZE = 8 |
1 | 313 |
314 class TX_Status(PktBase): | |
315 PKT_TYPE = 0x89 | |
316 PKT_DESC = "TX Status" | |
6 | 317 statusTxt = ['OK', 'No Ack', 'CCA failure', 'Purged'] |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
318 frameid = property(lambda s: s._data[0], None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
319 status = property(lambda s: s._data[1], None) |
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
320 statusMsg = property(lambda s: s.statusTxt[s._data[1]], None) |
11 | 321 |
322 def __init__(self, data = []): | |
323 super(TX_Status, self).__init__() | |
324 self._data = data | |
325 | |
1 | 326 class Packets(object): |
11 | 327 """Packet parsing class (misnamed)""" |
4 | 328 PKT_CLASSES = None |
11 | 329 |
330 @classmethod | |
4 | 331 def Build(self, data): |
11 | 332 """Build a packet from data""" |
4 | 333 if (self.PKT_CLASSES == None): |
334 m = inspect.getmodule(self) | |
335 # Generate list of objects from their names | |
336 mobjs = map(lambda n: m.__dict__[n], m.__dict__) | |
337 # Find all the classes | |
338 pktclasses = filter(inspect.isclass, mobjs) | |
339 # Find all subclasses of PktBase (but not PktBase itself) | |
340 pktclasses = filter(lambda s: issubclass(s, m.PktBase) and s != m.PktBase, pktclasses) | |
341 self.PKT_CLASSES = pktclasses | |
342 | |
343 for p in self.PKT_CLASSES: | |
1 | 344 if (p.PKT_TYPE == data[0]): |
11 | 345 #print "Matched " + str(p.PKT_TYPE) |
1 | 346 return(p(data[1:])) |
4 | 347 |
348 raise ValueError("Unknown packet type 0x%02x" % (data[0])) | |
1 | 349 |
11 | 350 @staticmethod |
1 | 351 def Encapsulate(data): |
11 | 352 """Encapsulate a packet so it can be sent to the module. Calculates checksum etc..""" |
1 | 353 pktsum = reduce(lambda x, y: x + y, data) & 0xff |
354 pkt = [0x7e] + [len(data) >> 8] + [len(data) & 0xff] + data + [0xff - pktsum] | |
355 return(map(chr, pkt)) | |
356 | |
10
4c91fdfc862e
- Further changes to make things cleaner (ie RX packets are "read only" etc).
darius@inchoate.localdomain
parents:
9
diff
changeset
|
357 def __init__(self, s = None): |
11 | 358 """Init class, if s is passed in it is used for reading & writing data""" |
359 #print str(inspect.getmodule(self)) | |
1 | 360 self.buffer = [] |
361 self.state = 'init' | |
362 self.packets = [] | |
363 | |
364 self.bufmsb = 0 | |
9
d147529ad2db
Switch to only reading RX pkts and writing TX pkts to make things simpler.
darius@inchoate.localdomain
parents:
8
diff
changeset
|
365 self._dataleft = 0 |
1 | 366 |
367 self.fr_err = 0 # Framing error | |
368 self.ck_err = 0 # Checksum error | |
369 self.rx_cnt = 0 # Packet count | |
370 | |
371 self.pktq = [] | |
11 | 372 self.s = s # Output handle for convenience methods |
6 | 373 |
374 def writedata(self, data): | |
11 | 375 """Convenience method to write data""" |
6 | 376 self.s.write("".join(map(str, data))) |
5 | 377 |
6 | 378 def getdata(self): |
11 | 379 """Read data until nothing is available (assumes non-blocking) and process it""" |
1 | 380 l = [] |
11 | 381 while 1: |
6 | 382 a = self.s.read() |
11 | 383 if a == '': |
1 | 384 break |
5 | 385 l.append(ord(a)) |
1 | 386 |
387 return self.process(l) | |
388 | |
11 | 389 def processstr(self, data): |
390 """Process a string of data""" | |
391 return self.process(map(ord, data)) | |
392 | |
1 | 393 def process(self, data): |
11 | 394 """Process (ordinal) data through the state machine. |
395 | |
396 Returns the number of packets in the queue when finished. Updates | |
397 various internal counters too. | |
398 """ | |
1 | 399 pktcount = 0 |
400 for d in data: | |
401 if (self.state == 'init'): | |
5 | 402 if (d != 0x7e): |
11 | 403 print "Framing error, got 0x%02x, expected 0x7e" % d |
4 | 404 self.fr_err += 1 |
1 | 405 continue |
406 | |
407 self.state = 'sizemsb' | |
408 elif (self.state == 'sizemsb'): | |
5 | 409 self.bufmsb = d |
1 | 410 self.state = 'sizelsb' |
411 elif (self.state == 'sizelsb'): | |
7
579dedf5a1f1
Rejoin line break, left over from removal of struct.
darius@inchoate.localdomain
parents:
6
diff
changeset
|
412 self.dataleft = self.bufmsb << 8 | d |
1 | 413 self.state = 'data' |
414 elif (self.state == 'data'): | |
5 | 415 self.buffer.append(d) |
1 | 416 self.dataleft = self.dataleft - 1 |
417 if (self.dataleft == 0): | |
418 self.state = 'cksum' | |
419 elif (self.state == 'cksum'): | |
420 pktsum = reduce(lambda x, y: x + y, self.buffer) & 0xff | |
5 | 421 rxcksum = d |
1 | 422 self.state = 'init' |
423 if (pktsum + rxcksum != 0xff): | |
424 self.buffer = [] | |
4 | 425 self.ck_err += 1 |
1 | 426 print "Checksum error, got 0x%02x, expected 0x%02x" % \ |
427 (rxcksum, 0xff - pktsum) | |
428 else: | |
11 | 429 #print "Got a packet - " + str(self.buffer) |
1 | 430 p = Packets.Build(self.buffer) |
431 self.pktq.append(p) | |
432 self.buffer = [] | |
4 | 433 pktcount += 1 |
434 self.rx_cnt += 1 | |
1 | 435 else: |
436 print "Invalid state %s! Resetting" % (self.state) | |
437 self.state = 'init' | |
438 | |
439 return pktcount | |
440 |