def fill(self, byte): """ fill: byte -> None Adds the given raw byte to this APIFrame. If this APIFrame is marked as escaped and this byte is an escape byte, the next byte in a call to fill() will be unescaped. """ if self._unescape_next_byte: byte = intToByte(byteToInt(byte) ^ 0x20) self._unescape_next_byte = False elif self.escaped and byte == Frame.ESCAPE_BYTE: self._unescape_next_byte = True return self.raw_data += intToByte(byteToInt(byte))
def escape(data): """ escape: byte string -> byte string When a 'special' byte is encountered in the given data string, it is preceded by an escape byte and XORed with 0x20. """ escaped_data = b"" for byte in data: if intToByte(byteToInt(byte)) in Frame.ESCAPE_BYTES: escaped_data += Frame.ESCAPE_BYTE escaped_data += intToByte(0x20 ^ byteToInt(byte)) else: escaped_data += intToByte(byteToInt(byte)) return escaped_data
def _parse_samples_header(self, io_bytes): """ _parse_samples_header: binary data in XBee IO data format -> (int, [int ...], [int ...], int, int) _parse_samples_header will read the first three bytes of the binary data given and will return the number of samples which follow, a list of enabled digital inputs, a list of enabled analog inputs, the dio_mask, and the size of the header in bytes """ header_size = 4 # number of samples (always 1?) is the first byte sample_count = byteToInt(io_bytes[0]) # part of byte 1 and byte 2 are the DIO mask ( 9 bits ) dio_mask = ( byteToInt(io_bytes[1]) << 8 | byteToInt(io_bytes[2])) & 0x01FF # upper 7 bits of byte 1 is the AIO mask aio_mask = byteToInt(io_bytes[3]) & 0xFE >> 1 # print(byteToInt(io_bytes[3]) & 0xFE >> 1) # print(aio_mask) # sorted lists of enabled channels; value is position of bit in mask dio_chans = [] aio_chans = [] for i in range(0, 9): if dio_mask & (1 << i): dio_chans.append(i) dio_chans.sort() for i in range(0, 7): if aio_mask & (1 << i): aio_chans.append(i) aio_chans.sort() return (sample_count, dio_chans, aio_chans, dio_mask, header_size)
def _parse_samples(self, io_bytes): """ _parse_samples: binary data in XBee IO data format -> [ {"dio-0":True, "dio-1":False, "adc-0":100"}, ...] _parse_samples reads binary data from an XBee device in the IO data format specified by the API. It will then return a dictionary indicating the status of each enabled IO port. """ sample_count, dio_chans, aio_chans, dio_mask, header_size = \ self._parse_samples_header(io_bytes) samples = [] # split the sample data into a list, so it can be pop()'d # self.log.debug('%r' % io_bytes) sample_bytes = [byteToInt(c) for c in io_bytes[header_size:]] # self.log.debug('%r' % sample_bytes) # self.log.debug('%r' % aio_chans) # repeat for every sample provided for sample_ind in range(0, sample_count): # @UnusedVariable tmp_samples = {} if dio_chans: # we have digital data digital_data_set = ( sample_bytes.pop(0) << 8 | sample_bytes.pop(0)) digital_values = dio_mask & digital_data_set for i in dio_chans: tmp_samples['dio-{0}'.format(i)] = True if ( digital_values >> i) & 1 else False for i in aio_chans: analog_sample = ( sample_bytes.pop(0) << 8 | sample_bytes.pop(0)) tmp_samples['adc-{0}'.format(i)] = int( (analog_sample * 1200.0) / 1023.0) samples.append(tmp_samples) return samples
def checksum(self): """ checksum: None -> single checksum byte checksum adds all bytes of the binary, unescaped data in the frame, saves the last byte of the result, and subtracts it from 0xFF. The final result is the checksum """ total = 0 # Add together all bytes for byte in self.data: total += byteToInt(byte) # Only keep the last byte total = total & 0xFF return intToByte(0xFF - total)
def verify(self): """ verify: 1 byte -> boolean verify checksums the frame, adds the expected checksum, and determines whether the result is correct. The result should be 0xFF. """ total = 0 # Add together all bytes for byte in self.data: total += byteToInt(byte) # total += byteToInt(self.data[-1]) # Only keep low bits total &= 0xFF # Check result return total == 0xFF