def interpret_response(cls, response, tolerate_zero_padding=True):
        """
        Populates the response ``service_data`` property with an instance of :class:`RequestFileTransfer.ResponseData<udsoncan.services.RequestFileTransfer.ResponseData>`

        :param response: The received response to interpret
        :type response: :ref:`Response<Response>`

        :raises InvalidResponseException: If length of ``response.data`` is too short or payload does not respect ISO-14229 specifications
        :raises NotImplementedError: If the MaxNumberOfBlock or fileSizeUncompressedOrDirInfoLength value is encoded over more than 8 bytes.
        """
        from udsoncan import Filesize, DataFormatIdentifier
        response.service_data = cls.ResponseData()
        if len(response.data) < 1:
            raise InvalidResponseException(
                response, 'Response payload must be at least 1 byte long')
        response.service_data.moop_echo = int(response.data[0])

        has_lfid = response.service_data.moop_echo in [
            cls.ModeOfOperation.AddFile, cls.ModeOfOperation.ReplaceFile,
            cls.ModeOfOperation.ReadFile, cls.ModeOfOperation.ReadDir
        ]
        has_dfi = response.service_data.moop_echo in [
            cls.ModeOfOperation.AddFile, cls.ModeOfOperation.ReplaceFile,
            cls.ModeOfOperation.ReadFile, cls.ModeOfOperation.ReadDir
        ]
        has_filesize_length = response.service_data.moop_echo in [
            cls.ModeOfOperation.ReadFile, cls.ModeOfOperation.ReadDir
        ]
        has_uncompressed_filesize = response.service_data.moop_echo in [
            cls.ModeOfOperation.ReadFile, cls.ModeOfOperation.ReadDir
        ]
        has_compressed_filesize = response.service_data.moop_echo in [
            cls.ModeOfOperation.ReadFile
        ]

        cursor = 1
        if has_lfid:
            if len(response.data) < 2:
                raise InvalidResponseException(
                    response,
                    'Response payload must be at least 2 byte long for Mode of operation %d'
                    % response.service_data.moop_echo)
            lfid = int(response.data[1])
            cursor = 2

            if lfid > 8:
                raise NotImplementedError(
                    'This client does not support number bigger than %d bits, but MaxNumberOfBlock is encoded on %d bits'
                    % ((8 * 8), (lfid * 8)))

            if lfid == 0:
                raise InvalidResponseException(
                    response,
                    'Received a MaxNumberOfBlockLength of 0 which is impossible'
                )

            if len(response.data) < 2 + lfid:
                raise InvalidResponseException(
                    response,
                    'Response payload says that MaxNumberOfBlock is encoded on %d bytes, but only %d bytes are present'
                    % (lfid, (len(response.data) - 2)))

            todecode = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
            for i in range(1, lfid + 1):
                todecode[-i] = response.data[cursor + lfid - i]
            response.service_data.max_length = struct.unpack('>q', todecode)[0]
            cursor += lfid

        if has_dfi:
            if len(response.data) < cursor + 1:
                raise InvalidResponseException(
                    response,
                    'Missing DataFormatIdentifier in received response')

            response.service_data.dfi = DataFormatIdentifier.from_byte(
                response.data[cursor])
            cursor += 1
            dfi = response.service_data.dfi.get_byte_as_int()

            if response.service_data.moop_echo == cls.ModeOfOperation.ReadDir and dfi != 0:
                raise InvalidResponseException(
                    response,
                    'DataFormatIdentifier for ReadDir can only be 0x00 as per ISO-14229, but its value was set to 0x%02x'
                    % (dfi))

        if has_filesize_length:
            if len(response.data) < cursor + 2:
                raise InvalidResponseException(
                    response,
                    'Missing or incomplete FileSizeOrDirInfoParameterLength in received response'
                )
            fsodipl = struct.unpack('>H', response.data[cursor:cursor + 2])[0]
            cursor += 2

            if fsodipl > 8:
                raise NotImplementedError(
                    response,
                    'This client does not support number bigger than %d bits, but FileSizeOrDirInfoLength is encoded on %d bits'
                    % ((8 * 8), (fsodipl * 8)))

            if fsodipl == 0:
                raise InvalidResponseException(
                    response,
                    'Received a FileSizeOrDirInfoParameterLength of 0 which is impossible'
                )

            if has_uncompressed_filesize:
                if len(response.data) < cursor + fsodipl:
                    raise InvalidResponseException(
                        response,
                        'Missing or incomplete fileSizeUncompressedOrDirInfoLength in received response'
                    )

                todecode = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
                for i in range(1, lfid + 1):
                    todecode[-i] = response.data[cursor + fsodipl - i]
                uncompressed_size = struct.unpack('>q', todecode)[0]
                cursor += fsodipl
            else:
                uncompressed_size = None

            if has_compressed_filesize:
                if len(response.data) < cursor + fsodipl:
                    raise InvalidResponseException(
                        response,
                        'Missing or incomplete fileSizeCompressed in received response'
                    )

                todecode = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
                for i in range(1, lfid + 1):
                    todecode[-i] = response.data[cursor + fsodipl - i]
                compressed_size = struct.unpack('>q', todecode)[0]
                cursor += fsodipl
            else:
                compressed_size = None

        if has_uncompressed_filesize and response.service_data.moop_echo == cls.ModeOfOperation.ReadDir:
            response.service_data.dirinfo_length = uncompressed_size
        else:
            if has_uncompressed_filesize or has_compressed_filesize:
                response.service_data.filesize = Filesize(
                    uncompressed=uncompressed_size, compressed=compressed_size)

        if len(response.data) > cursor:
            if response.data[cursor:] == b'\x00' * (
                    len(response.data) - cursor) and tolerate_zero_padding:
                pass
            else:
                raise InvalidResponseException(
                    response,
                    'Response payload has extra data that has no meaning')

        response.service_data = response.service_data
Ejemplo n.º 2
0
 def test_from_byte(self):
     dfi = DataFormatIdentifier.from_byte(0xAB)
     self.assertEqual(dfi.compression, 0xA)
     self.assertEqual(dfi.encryption, 0xB)