Example #1
0
    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 APIFrame.ESCAPE_BYTES:
                escaped_data += APIFrame.ESCAPE_BYTE
                escaped_data += intToByte(0x20 ^ byteToInt(byte))
            else:
                escaped_data += intToByte(byteToInt(byte))
                    
        return escaped_data
Example #2
0
    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 == APIFrame.ESCAPE_BYTE:
            self._unescape_next_byte = True
            return

        self.raw_data += intToByte(byteToInt(byte))
Example #3
0
    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.
        """
        self.escaped = True
        if self._unescape_next_byte:
            byte = intToByte(byteToInt(byte) ^ 0x20)
            self._unescape_next_byte = False
        elif self.escaped and byte == APIFrame.ESCAPE_BYTE:
            self._unescape_next_byte = True
            return

        self.raw_data += intToByte(byteToInt(byte))
Example #4
0
    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 APIFrame.ESCAPE_BYTES:
                escaped_data += APIFrame.ESCAPE_BYTE
                escaped_data += intToByte(0x20 ^ byteToInt(byte))
            else:
                escaped_data += intToByte(byteToInt(byte))
                    
        return escaped_data
Example #5
0
    def _parse_samples_header(self, io_bytes):
        """
        _parse_samples_header: binary data in XBee ZB 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

        _parse_samples_header is overloaded here to support the additional
        IO lines offered by the XBee ZB
        """
        header_size = 4

        # number of samples (always 1?) is the first byte
        sample_count = byteToInt(io_bytes[0])

        # bytes 1 and 2 are the DIO mask; bits 9 and 8 aren't used
        dio_mask = (byteToInt(io_bytes[1]) << 8 |
                    byteToInt(io_bytes[2])) & 0x1CFF

        # byte 3 is the AIO mask
        aio_mask = byteToInt(io_bytes[3])

        # sorted lists of enabled channels; value is position of bit in mask
        dio_chans = []
        aio_chans = []

        for i in range(0, 13):
            if dio_mask & (1 << i):
                dio_chans.append(i)

        dio_chans.sort()

        for i in range(0, 8):
            if aio_mask & (1 << i):
                aio_chans.append(i)

        aio_chans.sort()

        return (sample_count, dio_chans, aio_chans, dio_mask, header_size)
Example #6
0
    def _parse_samples_header(self, io_bytes):
        """
        _parse_samples_header: binary data in XBee ZB 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

        _parse_samples_header is overloaded here to support the additional
        IO lines offered by the XBee ZB
        """
        header_size = 4

        # number of samples (always 1?) is the first byte
        sample_count = byteToInt(io_bytes[0])

        # bytes 1 and 2 are the DIO mask; bits 9 and 8 aren't used
        dio_mask = (byteToInt(io_bytes[1]) << 8
                    | byteToInt(io_bytes[2])) & 0x1CFF

        # byte 3 is the AIO mask
        aio_mask = byteToInt(io_bytes[3])

        # sorted lists of enabled channels; value is position of bit in mask
        dio_chans = []
        aio_chans = []

        for i in range(0, 13):
            if dio_mask & (1 << i):
                dio_chans.append(i)

        dio_chans.sort()

        for i in range(0, 8):
            if aio_mask & (1 << i):
                aio_chans.append(i)

        aio_chans.sort()

        return (sample_count, dio_chans, aio_chans, dio_mask, header_size)
Example #7
0
    def test_invalid_checksum(self):
        """
        when an invalid frame is read, an exception must be raised
        """
        api_frame = APIFrame()
        frame = b'\x7E\x00\x01\x00\xF6'
        
        for byte in frame:
            api_frame.fill(intToByte(byteToInt(byte)))

        self.assertRaises(ValueError, api_frame.parse)
Example #8
0
    def test_invalid_checksum(self):
        """
        when an invalid frame is read, an exception must be raised
        """
        api_frame = APIFrame()
        frame = b'\x7E\x00\x01\x00\xF6'

        for byte in frame:
            api_frame.fill(intToByte(byteToInt(byte)))

        self.assertRaises(ValueError, api_frame.parse)
Example #9
0
 def verify(self, chksum):
     """
     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)
             
     # Add checksum too
     total += byteToInt(chksum)
     
     # Only keep low bits
     total &= 0xFF
     
     # Check result
     return total == 0xFF
Example #10
0
    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 = 3

        # 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[1]) & 0xFE) >> 1

        # 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)
Example #11
0
    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 = 3

        # 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[1]) & 0xFE) >> 1

        # 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)
Example #12
0
 def verify(self, chksum):
     """
     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)
             
     # Add checksum too
     total += byteToInt(chksum)
     
     # Only keep low bits
     total &= 0xFF
     
     # Check result
     return total == 0xFF
Example #13
0
    def test_single_byte(self):
        """
        read a frame containing a single byte
        """
        api_frame = APIFrame()

        frame = b'\x7E\x00\x01\x00\xFF'
        expected_data = b'\x00'

        for byte in frame:
            api_frame.fill(intToByte(byteToInt(byte)))
        api_frame.parse()

        self.assertEqual(api_frame.data, expected_data)
Example #14
0
    def test_single_byte(self):
        """
        read a frame containing a single byte
        """
        api_frame = APIFrame()

        frame = b'\x7E\x00\x01\x00\xFF'
        expected_data = b'\x00'
        
        for byte in frame:
            api_frame.fill(intToByte(byteToInt(byte)))
        api_frame.parse()

        self.assertEqual(api_frame.data, expected_data)
Example #15
0
 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)
Example #16
0
 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)
Example #17
0
    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
        sample_bytes = [byteToInt(c) for c in io_bytes[header_size:]]

        # repeat for every sample provided
        for sample_ind in range(0, sample_count):
            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:
                # only first 10 bits are significant
                analog_sample = (sample_bytes.pop(0) << 8
                                 | sample_bytes.pop(0)) & 0x03FF
                tmp_samples['adc-{0}'.format(i)] = analog_sample

            samples.append(tmp_samples)

        return samples
Example #18
0
    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
        sample_bytes = [byteToInt(c) for c in io_bytes[header_size:]]
        
        if len( sample_bytes ) > 0:
	  # repeat for every sample provided
	  for sample_ind in range(0, sample_count):
            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:
                # only first 10 bits are significant
                analog_sample = (sample_bytes.pop(0) << 8 | sample_bytes.pop(0)) & 0x03FF
                tmp_samples['adc-{0}'.format(i)] = analog_sample
            
            samples.append(tmp_samples)
        
        return samples