frame.py
Go to the documentation of this file.
1 """
2 frame.py
3 
4 By Paul Malmsten, 2010
5 pmalmsten@gmail.com
6 
7 Represents an API frame for communicating with an XBee
8 """
9 import struct
10 
11 class APIFrame:
12  """
13  Represents a frame of data to be sent to or which was received
14  from an XBee device
15  """
16 
17  START_BYTE = '\x7E'
18  ESCAPE_BYTE = '\x7D'
19  XON_BYTE = '\x11'
20  XOFF_BYTE = '\x13'
21  ESCAPE_BYTES = (START_BYTE, ESCAPE_BYTE, XON_BYTE, XOFF_BYTE)
22 
23  def __init__(self, data='', escaped=False):
24  self.data = data
25  self.raw_data = ''
26  self.escaped = escaped
27  self._unescape_next_byte = False
28 
29  def checksum(self):
30  """
31  checksum: None -> single checksum byte
32 
33  checksum adds all bytes of the binary, unescaped data in the
34  frame, saves the last byte of the result, and subtracts it from
35  0xFF. The final result is the checksum
36  """
37  total = 0
38 
39  # Add together all bytes
40  for byte in self.data:
41  total += ord(byte)
42 
43  # Only keep the last byte
44  total = total & 0xFF
45 
46  # Subtract from 0xFF
47  return chr(0xFF - total)
48 
49  def verify(self, chksum):
50  """
51  verify: 1 byte -> boolean
52 
53  verify checksums the frame, adds the expected checksum, and
54  determines whether the result is correct. The result should
55  be 0xFF.
56  """
57  total = 0
58 
59  # Add together all bytes
60  for byte in self.data:
61  total += ord(byte)
62 
63  # Add checksum too
64  total += ord(chksum)
65 
66  # Only keep low bits
67  total &= 0xFF
68 
69  # Check result
70  return total == 0xFF
71 
72  def len_bytes(self):
73  """
74  len_data: None -> (MSB, LSB) 16-bit integer length, two bytes
75 
76  len_bytes counts the number of bytes to be sent and encodes the
77  data length in two bytes, big-endian (most significant first).
78  """
79  count = len(self.data)
80  return struct.pack("> h", count)
81 
82  def output(self):
83  """
84  output: None -> valid API frame (binary data)
85 
86  output will produce a valid API frame for transmission to an
87  XBee module.
88  """
89  # start is one byte long, length is two bytes
90  # data is n bytes long (indicated by length)
91  # chksum is one byte long
92  data = self.len_bytes() + self.data + self.checksum()
93 
94  # Only run the escaoe process if it hasn't been already
95  if self.escaped and len(self.raw_data) < 1:
96  self.raw_data = APIFrame.escape(data)
97 
98  if self.escaped:
99  data = self.raw_data
100 
101  # Never escape start byte
102  return APIFrame.START_BYTE + data
103 
104  @staticmethod
105  def escape(data):
106  """
107  escape: byte string -> byte string
108 
109  When a 'special' byte is encountered in the given data string,
110  it is preceded by an escape byte and XORed with 0x20.
111  """
112 
113  escaped_data = ""
114  for byte in data:
115  if byte in APIFrame.ESCAPE_BYTES:
116  escaped_data += APIFrame.ESCAPE_BYTE
117  escaped_data += chr(0x20 ^ ord(byte))
118  else:
119  escaped_data += byte
120 
121  return escaped_data
122 
123  def fill(self, byte):
124  """
125  fill: byte -> None
126 
127  Adds the given raw byte to this APIFrame. If this APIFrame is marked
128  as escaped and this byte is an escape byte, the next byte in a call
129  to fill() will be unescaped.
130  """
131 
132  if self._unescape_next_byte:
133  byte = chr(ord(byte) ^ 0x20)
134  self._unescape_next_byte = False
135  elif self.escaped and byte == APIFrame.ESCAPE_BYTE:
136  self._unescape_next_byte = True
137  return
138 
139  self.raw_data += byte
140 
141  def remaining_bytes(self):
142  remaining = 3
143 
144  if len(self.raw_data) >= 3:
145  # First two bytes are the length of the data
146  raw_len = self.raw_data[1:3]
147  data_len = struct.unpack("> h", raw_len)[0]
148 
149  remaining += data_len
150 
151  # Don't forget the checksum
152  remaining += 1
153 
154  return remaining - len(self.raw_data)
155 
156  def parse(self):
157  """
158  parse: None -> None
159 
160  Given a valid API frame, parse extracts the data contained
161  inside it and verifies it against its checksum
162  """
163  if len(self.raw_data) < 3:
164  ValueError("parse() may only be called on a frame containing at least 3 bytes of raw data (see fill())")
165 
166  # First two bytes are the length of the data
167  raw_len = self.raw_data[1:3]
168 
169  # Unpack it
170  data_len = struct.unpack("> h", raw_len)[0]
171 
172  # Read the data
173  data = self.raw_data[3:3 + data_len]
174  chksum = self.raw_data[-1]
175 
176  # Checksum check
177  self.data = data
178  if not self.verify(chksum):
179  raise ValueError("Invalid checksum")
def fill(self, byte)
Definition: frame.py:123
def escape(data)
Definition: frame.py:105
def parse(self)
Definition: frame.py:156
def output(self)
Definition: frame.py:82
def checksum(self)
Definition: frame.py:29
def verify(self, chksum)
Definition: frame.py:49
def len_bytes(self)
Definition: frame.py:72
def remaining_bytes(self)
Definition: frame.py:141
def __init__(self, data='', escaped=False)
Definition: frame.py:23


rosserial_xbee
Author(s): Adam Stambler
autogenerated on Fri Jun 7 2019 22:03:11