Go to the documentation of this file.00001 """
00002 frame.py
00003
00004 By Paul Malmsten, 2010
00005 pmalmsten@gmail.com
00006
00007 Represents an API frame for communicating with an XBee
00008 """
00009 import struct
00010
00011 class APIFrame:
00012 """
00013 Represents a frame of data to be sent to or which was received
00014 from an XBee device
00015 """
00016
00017 START_BYTE = '\x7E'
00018 ESCAPE_BYTE = '\x7D'
00019 XON_BYTE = '\x11'
00020 XOFF_BYTE = '\x13'
00021 ESCAPE_BYTES = (START_BYTE, ESCAPE_BYTE, XON_BYTE, XOFF_BYTE)
00022
00023 def __init__(self, data='', escaped=False):
00024 self.data = data
00025 self.raw_data = ''
00026 self.escaped = escaped
00027 self._unescape_next_byte = False
00028
00029 def checksum(self):
00030 """
00031 checksum: None -> single checksum byte
00032
00033 checksum adds all bytes of the binary, unescaped data in the
00034 frame, saves the last byte of the result, and subtracts it from
00035 0xFF. The final result is the checksum
00036 """
00037 total = 0
00038
00039
00040 for byte in self.data:
00041 total += ord(byte)
00042
00043
00044 total = total & 0xFF
00045
00046
00047 return chr(0xFF - total)
00048
00049 def verify(self, chksum):
00050 """
00051 verify: 1 byte -> boolean
00052
00053 verify checksums the frame, adds the expected checksum, and
00054 determines whether the result is correct. The result should
00055 be 0xFF.
00056 """
00057 total = 0
00058
00059
00060 for byte in self.data:
00061 total += ord(byte)
00062
00063
00064 total += ord(chksum)
00065
00066
00067 total &= 0xFF
00068
00069
00070 return total == 0xFF
00071
00072 def len_bytes(self):
00073 """
00074 len_data: None -> (MSB, LSB) 16-bit integer length, two bytes
00075
00076 len_bytes counts the number of bytes to be sent and encodes the
00077 data length in two bytes, big-endian (most significant first).
00078 """
00079 count = len(self.data)
00080 return struct.pack("> h", count)
00081
00082 def output(self):
00083 """
00084 output: None -> valid API frame (binary data)
00085
00086 output will produce a valid API frame for transmission to an
00087 XBee module.
00088 """
00089
00090
00091
00092 data = self.len_bytes() + self.data + self.checksum()
00093
00094
00095 if self.escaped and len(self.raw_data) < 1:
00096 self.raw_data = APIFrame.escape(data)
00097
00098 if self.escaped:
00099 data = self.raw_data
00100
00101
00102 return APIFrame.START_BYTE + data
00103
00104 @staticmethod
00105 def escape(data):
00106 """
00107 escape: byte string -> byte string
00108
00109 When a 'special' byte is encountered in the given data string,
00110 it is preceded by an escape byte and XORed with 0x20.
00111 """
00112
00113 escaped_data = ""
00114 for byte in data:
00115 if byte in APIFrame.ESCAPE_BYTES:
00116 escaped_data += APIFrame.ESCAPE_BYTE
00117 escaped_data += chr(0x20 ^ ord(byte))
00118 else:
00119 escaped_data += byte
00120
00121 return escaped_data
00122
00123 def fill(self, byte):
00124 """
00125 fill: byte -> None
00126
00127 Adds the given raw byte to this APIFrame. If this APIFrame is marked
00128 as escaped and this byte is an escape byte, the next byte in a call
00129 to fill() will be unescaped.
00130 """
00131
00132 if self._unescape_next_byte:
00133 byte = chr(ord(byte) ^ 0x20)
00134 self._unescape_next_byte = False
00135 elif self.escaped and byte == APIFrame.ESCAPE_BYTE:
00136 self._unescape_next_byte = True
00137 return
00138
00139 self.raw_data += byte
00140
00141 def remaining_bytes(self):
00142 remaining = 3
00143
00144 if len(self.raw_data) >= 3:
00145
00146 raw_len = self.raw_data[1:3]
00147 data_len = struct.unpack("> h", raw_len)[0]
00148
00149 remaining += data_len
00150
00151
00152 remaining += 1
00153
00154 return remaining - len(self.raw_data)
00155
00156 def parse(self):
00157 """
00158 parse: None -> None
00159
00160 Given a valid API frame, parse extracts the data contained
00161 inside it and verifies it against its checksum
00162 """
00163 if len(self.raw_data) < 3:
00164 ValueError("parse() may only be called on a frame containing at least 3 bytes of raw data (see fill())")
00165
00166
00167 raw_len = self.raw_data[1:3]
00168
00169
00170 data_len = struct.unpack("> h", raw_len)[0]
00171
00172
00173 data = self.raw_data[3:3 + data_len]
00174 chksum = self.raw_data[-1]
00175
00176
00177 self.data = data
00178 if not self.verify(chksum):
00179 raise ValueError("Invalid checksum")