示例#1
0
    def make_request(cls, routine_id, control_type, data=None):
        u"""
        Generates a request for RoutineControl

        :param routine_id: The routine ID. Value should be between 0 and 0xFFFF
        :type routine_id: int

        :param control_type: Service subfunction. Allowed values are from 0 to 0x7F
        :type control_type: bytes

        :param data: Optional additional data to provide to the server
        :type data: bytes

        :raises ValueError: If parameters are out of range, missing or wrong type
        """		
        from udsoncan import Request

        ServiceHelper.validate_int(routine_id, min=0, max=0xFFFF, name=u'Routine ID')
        ServiceHelper.validate_int(control_type, min=0, max=0x7F, name=u'Routine control type')

        if data is not None:
            if not isinstance(data, str):
                raise ValueError(u'data must be a valid bytes object')

        request = Request(service=cls, subfunction=control_type)
        request.data = struct.pack(u'>H', routine_id)
        if data is not None:
            request.data += data

        return request
	def make_request(cls, memory_location, dfi=None):
		"""
		Generate a request for RequestDownload

		:param memory_location: The address and the size of the memory block to be written.
		:type memory_location: :ref:`MemoryLocation <HelperClass_MemoryLocation>`

		:param dfi: Optional dataFormatIdentifier defining the compression and encryption scheme of the data. 
			If not specified, the default value of 00 will be used, specifying no encryption and no compression
		:type dfi: :ref:`DataFormatIdentifier <HelperClass_DataFormatIdentifier>`	

		:raises ValueError: If parameters are out of range or missing
		"""				
		from udsoncan import Request, MemoryLocation
		
		dfi = cls.normalize_data_format_identifier(dfi)

		if not isinstance(memory_location, MemoryLocation):
			raise ValueError('memory_location must be an instance of MemoryLocation')

		request = Request(service=cls)
		request.data=b""
		request.data += dfi.get_byte()	# Data Format Identifier
		request.data += memory_location.alfid.get_byte()	# AddressAndLengthFormatIdentifier
		request.data += memory_location.get_address_bytes()
		request.data += memory_location.get_memorysize_bytes()

		return request
    def make_request(cls, control_type, communication_type):
        """
		Generate a request for CommunicationControl

		:param control_type: Service subfunction. Allowed values are from 0 to 0x7F
		:type control_type: int

		:param communication_type: The communication type requested.
		:type communication_type: :ref:`CommunicationType <CommunicationType>`

		:raises ValueError: If parameters are out of range, missing or wrong type
		"""
        from udsoncan import Request

        ServiceHelper.validate_int(control_type,
                                   min=0,
                                   max=0x7F,
                                   name='Control type')

        communication_type = cls.normalize_communication_type(
            communication_type)
        request = Request(service=cls, subfunction=control_type)
        request.data = communication_type.get_byte()

        return request
示例#4
0
	def make_request(cls, memory_location, data):
		"""
		Generate a request for ReadMemoryByAddress

		:param memory_location: The address and the size of the memory block to write.
		:type memory_location: :ref:`MemoryLocation <HelperClass_MemoryLocation>`

		:param data: The data to write into memory.
		:type data: bytes

		:raises ValueError: If parameters are out of range or missing
		"""				
		from udsoncan import Request, MemoryLocation

		if not isinstance(memory_location, MemoryLocation):
			raise ValueError('Given memory location must be an instance of MemoryLocation')

		if not isinstance(data, bytes):
			raise ValueError('data must be a bytes object')
		request =  Request(service=cls)
		
		request.data = b''
		request.data += memory_location.alfid.get_byte() # AddressAndLengthFormatIdentifier
		request.data += memory_location.get_address_bytes()
		request.data += memory_location.get_memorysize_bytes()
		request.data += data

		return request
    def make_request(cls, did, value, didconfig):
        """
        Generates a request for WriteDataByIdentifier

        :param did: The data identifier to write
        :type did: int

        :param value: Value given to the :ref:`DidCodec <DidCodec>`.encode method. If involved codec is defined with a pack string (default codec), multiple values may be passed with a tuple.
        :type value: object

        :param didconfig: Definition of DID codecs. Dictionary mapping a DID (int) to a valid :ref:`DidCodec <DidCodec>` class or pack/unpack string 
        :type didconfig: dict[int] = :ref:`DidCodec <DidCodec>`

        :raises ValueError: If parameters are out of range, missing or wrong type
        :raises ConfigError: If ``didlist`` contains a DID not defined in ``didconfig``
        """

        from udsoncan import Request, DidCodec
        ServiceHelper.validate_int(did,
                                   min=0,
                                   max=0xFFFF,
                                   name='Data Identifier')
        req = Request(cls)
        didconfig = ServiceHelper.check_did_config(
            did, didconfig=didconfig
        )  # Make sure all DIDs are correctly defined in client config
        req.data = struct.pack('>H', did)  # encode DID number
        codec = DidCodec.from_config(didconfig[did])
        if codec.__class__ == DidCodec and isinstance(value, tuple):
            req.data += codec.encode(*value)  # Fixes issue #29
        else:
            req.data += codec.encode(value)

        return req
示例#6
0
    def make_request(cls, level, mode, key=None):
        u"""
        Generates a request for SecurityAccess

        :param level: Service subfunction. The security level to unlock. Value ranging from 0 to 7F 
                For mode=``RequestSeed`` (0), level must be an even value. For mode=``SendKey`` (1), level must be an odd value.
                If the even/odd constraint is not respected, the level value will be corrected to properly set the LSB.
        :type level: int

        :param mode: Type of request to perform. ``SecurityAccess.Mode.RequestSeed`` or ``SecurityAccess.Mode.SendKey`` 
        :type mode: SecurityAccess.Mode, int

        :param key: When mode=``SendKey``, this value must be provided.
        :type key: bytes

        :raises ValueError: If parameters are out of range, missing or wrong type
        """
        from udsoncan import Request
        cls.validate_mode(mode)

        ServiceHelper.validate_int(level,
                                   min=0,
                                   max=0x7F,
                                   name=u'Security level')
        req = Request(service=cls,
                      subfunction=cls.normalize_level(mode=mode, level=level))

        if mode == cls.Mode.SendKey:
            if not isinstance(key, str):
                raise ValueError(u'key must be a valid bytes object')
            req.data = key

        return req
示例#7
0
	def make_request(cls, did, value, didconfig):
		"""
		Generate a request for WriteDataByIdentifier

		:param did: The data identifier to write
		:type did: int

		:param value: Value given to the :ref:`DidCodec <HelperClass_DidCodec>`.encode method
		:type value: object

		:param didconfig: Definition of DID codecs. Dictionary mapping a DID (int) to a valid :ref:`DidCodec <HelperClass_DidCodec>` class or pack/unpack string 
		:type didconfig: dict[int] = :ref:`DidCodec <HelperClass_DidCodec>`

		:raises ValueError: If parameters are out of range or missing
		:raises ConfigError: If didlist contains a DID not defined in didconfig
		"""	

		from udsoncan import Request, DidCodec
		ServiceHelper.validate_int(did, min=0, max=0xFFFF, name='Data Identifier')
		req = Request(cls)
		didconfig = ServiceHelper.check_did_config(did, didconfig=didconfig)	# Make sure all DID are correctly defined in client config
		req.data = struct.pack('>H', did)	# encode DID number
		codec = DidCodec.from_config(didconfig[did])
		req.data += codec.encode(value)

		return req
    def make_request(cls, sequence_number, data=None):
        """
		Generates a request for TransferData

		:param sequence_number: Corresponds to an 8bit counter that should increment for each new block transferred.
			Allowed values are from 0 to 0xFF
		:type sequence_number: int

		:param data: Optional additional data to send to the server
		:type data: bytes

		:raises ValueError: If parameters are out of range, missing or wrong type
		"""
        from udsoncan import Request, MemoryLocation

        ServiceHelper.validate_int(
            sequence_number, min=0, max=0xFF,
            name='Block sequence counter')  # Not a subfunction!

        if data is not None and not isinstance(data, bytes):
            raise ValueError('data must be a bytes object')

        request = Request(service=cls)
        request.data = struct.pack('B', sequence_number)

        if data is not None:
            request.data += data
        return request
示例#9
0
    def make_request(cls, level, mode, data=bytes()):
        """
        Generates a request for SecurityAccess

        :param level: Service subfunction. The security level to unlock. Value ranging from 0 to 7F 
                For mode=``RequestSeed`` (0), level must be an even value. For mode=``SendKey`` (1), level must be an odd value.
                If the even/odd constraint is not respected, the level value will be corrected to properly set the LSB.
        :type level: int

        :param mode: Type of request to perform. ``SecurityAccess.Mode.RequestSeed`` or ``SecurityAccess.Mode.SendKey`` 
        :type mode: SecurityAccess.Mode, int

        :param data: securityAccessDataRecord (optional) for the get seed, securityKey (required) for the send key
        :type data: bytes

        :raises ValueError: If parameters are out of range, missing or wrong type
        """
        from udsoncan import Request
        cls.validate_mode(mode)

        ServiceHelper.validate_int(level,
                                   min=0,
                                   max=0x7F,
                                   name='Security level')
        req = Request(service=cls,
                      subfunction=cls.normalize_level(mode=mode, level=level))

        if not isinstance(data, bytes):
            raise ValueError('key must be a valid bytes object')
        req.data = data

        return req
示例#10
0
    def make_request(cls, control_type, baudrate=None):
        u"""
        Generates a request for LinkControl

        :param control_type: Service subfunction. Allowed values are from 0 to 0x7F
        :type control_type: int

        :param baudrate: Required baudrate value when ``control_type`` is either ``verifyBaudrateTransitionWithFixedBaudrate`` (1) or ``verifyBaudrateTransitionWithSpecificBaudrate`` (2)
        :type baudrate: :ref:`Baudrate <Baudrate>`

        :raises ValueError: If parameters are out of range, missing or wrong type
        """
        from udsoncan import Request, Baudrate

        ServiceHelper.validate_int(control_type,
                                   min=0,
                                   max=0x7F,
                                   name=u'Control type')

        if control_type in [
                cls.ControlType.verifyBaudrateTransitionWithSpecificBaudrate,
                cls.ControlType.verifyBaudrateTransitionWithFixedBaudrate
        ]:
            if baudrate is None:
                raise ValueError(
                    u'A Baudrate must be provided with control type : "verifyBaudrateTransitionWithSpecificBaudrate" (0x%02x) or "verifyBaudrateTransitionWithFixedBaudrate" (0x%02x)'
                    % (cls.ControlType.
                       verifyBaudrateTransitionWithSpecificBaudrate, cls.
                       ControlType.verifyBaudrateTransitionWithFixedBaudrate))

            if not isinstance(baudrate, Baudrate):
                raise ValueError(
                    u'Given baudrate must be an instance of the Baudrate class'
                )
        else:
            if baudrate is not None:
                raise ValueError(
                    u'The baudrate parameter is only needed when control type is "verifyBaudrateTransitionWithSpecificBaudrate" (0x%02x) or "verifyBaudrateTransitionWithFixedBaudrate" (0x%02x)'
                    % (cls.ControlType.
                       verifyBaudrateTransitionWithSpecificBaudrate, cls.
                       ControlType.verifyBaudrateTransitionWithFixedBaudrate))

        if control_type == cls.ControlType.verifyBaudrateTransitionWithSpecificBaudrate:
            baudrate = baudrate.make_new_type(Baudrate.Type.Specific)

        if control_type == cls.ControlType.verifyBaudrateTransitionWithFixedBaudrate and baudrate.baudtype == Baudrate.Type.Specific:
            baudrate = baudrate.make_new_type(Baudrate.Type.Fixed)

        request = Request(service=cls, subfunction=control_type)
        if baudrate is not None:
            request.data = baudrate.get_bytes()
        return request
示例#11
0
    def make_request(cls, didlist, didconfig):
        """
        Generates a request for ReadDataByIdentifier

        :param didlist: List of data identifier to read.
        :type didlist: list[int]

        :param didconfig: Definition of DID codecs. Dictionary mapping a DID (int) to a valid :ref:`DidCodec<DidCodec>` class or pack/unpack string 
        :type didconfig: dict[int] = :ref:`DidCodec<DidCodec>`

        :raises ValueError: If parameters are out of range, missing or wrong type
        :raises ConfigError: If didlist contains a DID not defined in didconfig
        """
        from udsoncan import Request
        from udsoncan import DidCodec

        didlist = cls.validate_didlist_input(didlist)

        req = Request(cls)
        ServiceHelper.check_did_config(didlist, didconfig)

        did_reading_all_data = None
        for did in didlist:
            if did not in didconfig:  # Already checked in check_did_config. Paranoid check
                raise ConfigError(
                    key=did,
                    msg=
                    'Actual data identifier configuration contains no definition for data identifier 0x%04x'
                    % did)

            codec = DidCodec.from_config(didconfig[did])
            try:
                length = len(codec)
                if did_reading_all_data is not None:
                    raise ValueError(
                        'Did 0x%04X is configured to read the rest of the payload (__len__ raisong ReadAllRemainingData), but a subsequent DID is requested (0x%04x)'
                        % (did_reading_all_data, did))
            except DidCodec.ReadAllRemainingData:
                if did_reading_all_data is not None:
                    raise ValueError(
                        'It is impossible to read 2 DIDs configured to read the rest of the payload (__len__ raising ReadAllRemainingData). Dids are : 0x%04X and 0x%04X'
                        % (did_reading_all_data, did))
                did_reading_all_data = did

        req.data = struct.pack('>' + 'H' * len(didlist),
                               *didlist)  #Encode list of DID

        return req
	def make_request(cls, group=0xFFFFFF):
		"""
		Generate a request for ClearDiagnosticInformation

		:param group: DTC mask ranging from 0 to 0xFFFFFF. 0xFFFFFF means all DTCs
		:type group: int

		:raises ValueError: If parameters are out of range or missing
		"""		
		from udsoncan import Request
		ServiceHelper.validate_int(group, min=0, max=0xFFFFFF, name='Group of DTC')
		request = Request(service=cls)
		hb = (group >> 16) & 0xFF
		mb = (group >> 8) & 0xFF
		lb = (group >> 0) & 0xFF 
		request.data = struct.pack("BBB", hb,mb,lb)
		return request
	def make_request(cls, didlist, didconfig):
		"""
		Generate a request for ReadDataByIdentifier

		:param didlist: List of data identifier to read.
		:type didlist: list[int]

		:param didconfig: Definition of DID codecs. Dictionary mapping a DID (int) to a valid :ref:`DidCodec<DidCodec>` class or pack/unpack string 
		:type didconfig: dict[int] = :ref:`DidCodec<DidCodec>`

		:raises ValueError: If parameters are out of range, missing or wrong type
		:raises ConfigError: If didlist contains a DID not defined in didconfig
		"""		
		from udsoncan import Request
		didlist = cls.validate_didlist_input(didlist)

		req = Request(cls)
		ServiceHelper.check_did_config(didlist, didconfig)
		req.data = struct.pack('>'+'H'*len(didlist), *didlist) #Encode list of DID

		return req
示例#14
0
	def make_request(cls, memory_location):
		"""
		Generates a request for ReadMemoryByAddress

		:param memory_location: The address and the size of the memory block to read.
		:type memory_location: :ref:`MemoryLocation <MemoryLocation>`

		:raises ValueError: If parameters are out of range, missing or wrong type
		"""		
		from udsoncan import Request, MemoryLocation

		if not isinstance(memory_location, MemoryLocation):
			raise ValueError('Given memory location must be an instance of MemoryLocation')

		request =  Request(service=cls)
		request.data = b''
		request.data += memory_location.alfid.get_byte() # AddressAndLengthFormatIdentifier
		request.data += memory_location.get_address_bytes()
		request.data += memory_location.get_memorysize_bytes()

		return request
示例#15
0
    def make_request(cls,
                     subfunction,
                     status_mask=None,
                     severity_mask=None,
                     dtc=None,
                     snapshot_record_number=None,
                     extended_data_record_number=None):
        u"""
        Generates a request for ReadDTCInformation. 
        Each subfunction uses a subset of parameters. 

        :param subfunction: The service subfunction. Values are defined in :class:`ReadDTCInformation.Subfunction<ReadDTCInformation.Subfunction>`
        :type subfunction: int

        :param status_mask: A DTC status mask used to filter DTC
        :type status_mask: int or :ref:`Dtc.Status <DTC_Status>`

        :param severity_mask: A severity mask used to filter DTC 
        :type severity_mask: int or :ref:`Dtc.Severity <DTC_Severity>`

        :param dtc: A DTC mask used to filter DTC
        :type dtc: int or :ref:`Dtc <DTC>`

        :param snapshot_record_number: Snapshot record number
        :type snapshot_record_number: int

        :param extended_data_record_number: Extended data record number
        :type extended_data_record_number: int

        :raises ValueError: If parameters are out of range, missing or wrong type
        """

        from udsoncan import Request, Dtc

        # Request grouping for subfunctions that have the same request format
        request_subfn_no_param = [
            ReadDTCInformation.Subfunction.reportSupportedDTCs,
            ReadDTCInformation.Subfunction.reportFirstTestFailedDTC,
            ReadDTCInformation.Subfunction.reportFirstConfirmedDTC,
            ReadDTCInformation.Subfunction.reportMostRecentTestFailedDTC,
            ReadDTCInformation.Subfunction.reportMostRecentConfirmedDTC,
            ReadDTCInformation.Subfunction.reportDTCFaultDetectionCounter,
            ReadDTCInformation.Subfunction.reportDTCWithPermanentStatus,

            # Documentation is confusing about reportDTCSnapshotIdentification subfunction.
            # It is presented with reportDTCSnapshotRecordByDTCNumber (2 params) but a footnote says that these 2 parameters
            # are not to be provided for reportDTCSnapshotIdentification. Therefore, it is the same as other no-params subfn
            ReadDTCInformation.Subfunction.reportDTCSnapshotIdentification
        ]

        request_subfn_status_mask = [
            ReadDTCInformation.Subfunction.reportNumberOfDTCByStatusMask,
            ReadDTCInformation.Subfunction.reportDTCByStatusMask,
            ReadDTCInformation.Subfunction.reportMirrorMemoryDTCByStatusMask,
            ReadDTCInformation.Subfunction.
            reportNumberOfMirrorMemoryDTCByStatusMask, ReadDTCInformation.
            Subfunction.reportNumberOfEmissionsRelatedOBDDTCByStatusMask,
            ReadDTCInformation.Subfunction.
            reportEmissionsRelatedOBDDTCByStatusMask
        ]

        request_subfn_mask_record_plus_snapshot_record_number = [
            ReadDTCInformation.Subfunction.reportDTCSnapshotRecordByDTCNumber
        ]

        request_subfn_snapshot_record_number = [
            ReadDTCInformation.Subfunction.
            reportDTCSnapshotRecordByRecordNumber
        ]

        request_subfn_mask_record_plus_extdata_record_number = [
            ReadDTCInformation.Subfunction.
            reportDTCExtendedDataRecordByDTCNumber, ReadDTCInformation.
            Subfunction.reportMirrorMemoryDTCExtendedDataRecordByDTCNumber
        ]

        request_subfn_severity_plus_status_mask = [
            ReadDTCInformation.Subfunction.
            reportNumberOfDTCBySeverityMaskRecord,
            ReadDTCInformation.Subfunction.reportDTCBySeverityMaskRecord
        ]

        request_subfn_mask_record = [
            ReadDTCInformation.Subfunction.reportSeverityInformationOfDTC
        ]

        ServiceHelper.validate_int(subfunction,
                                   min=1,
                                   max=0x15,
                                   name=u'Subfunction')

        if status_mask is not None and isinstance(status_mask, Dtc.Status):
            status_mask = status_mask.get_byte_as_int()

        if severity_mask is not None and isinstance(severity_mask,
                                                    Dtc.Severity):
            severity_mask = severity_mask.get_byte_as_int()

        if dtc is not None and isinstance(dtc, Dtc):
            dtc = dtc.id

        req = Request(service=cls, subfunction=subfunction)

        if subfunction in request_subfn_no_param:  # Service ID + Subfunction
            pass

        elif subfunction in request_subfn_status_mask:
            cls.assert_status_mask(status_mask, subfunction)
            req.data = struct.pack(u'B', status_mask)

        elif subfunction in request_subfn_mask_record_plus_snapshot_record_number:
            cls.assert_dtc(dtc, subfunction)
            cls.assert_snapshot_record_number(snapshot_record_number,
                                              subfunction)
            req.data = cls.pack_dtc(dtc) + struct.pack(u'B',
                                                       snapshot_record_number)

        elif subfunction in request_subfn_snapshot_record_number:
            cls.assert_snapshot_record_number(snapshot_record_number,
                                              subfunction)
            req.data = struct.pack(u'B', snapshot_record_number)

        elif subfunction in request_subfn_mask_record_plus_extdata_record_number:
            cls.assert_dtc(dtc, subfunction)
            cls.assert_extended_data_record_number(extended_data_record_number,
                                                   subfunction)
            req.data = cls.pack_dtc(dtc) + struct.pack(
                u'B', extended_data_record_number)

        elif subfunction in request_subfn_severity_plus_status_mask:
            cls.assert_status_mask(status_mask, subfunction)
            cls.assert_severity_mask(severity_mask, subfunction)
            req.data = struct.pack(u'BB', severity_mask, status_mask)

        elif subfunction in request_subfn_mask_record:
            cls.assert_dtc(dtc, subfunction)
            req.data = cls.pack_dtc(dtc)

        return req
示例#16
0
 def test_make_payload_custom_data_no_subfunction(self):
     req = Request(DummyServiceNoSubunction, subfunction=0x44)
     req.data = b"\x12\x34\x56\x78"
     payload = req.get_payload()
     self.assertEqual(b"\x13\x12\x34\x56\x78", payload)
    def make_request(cls, subfunction, did=None, diddef=None):
        """
        Generates a request for DynamicallyDefineDataIdentifier

        :param subfunction: Service subfunction. Allowed values are from 1 to 3
        :type subfunction: int

        :param did: The Data Identifier to define. Values from 0x0000 to 0xFFFF
        :type did: int

        :param diddef: Definition of the DID. Either by source DID or memory address. This parameter is only needed with subfunctions defineByIdentifie (1)` and defineByMemoryAddress (2)
        :type diddef: :ref:`DynamicDidDefinition<DynamicDidDefinition>`

        :raises ValueError: If parameters are out of range, missing or wrong type
        """
        from udsoncan import Request, DynamicDidDefinition
        ServiceHelper.validate_int(subfunction,
                                   min=1,
                                   max=3,
                                   name='Subfunction')
        req = Request(service=cls, subfunction=subfunction)

        if subfunction in [
                cls.Subfunction.defineByIdentifier,
                cls.Subfunction.defineByMemoryAddress
        ]:
            if not isinstance(diddef, DynamicDidDefinition):
                raise ValueError(
                    'A DynamicDidDefinition must be given to define a dynamic did with subfunction %d'
                    % (subfunction))

            if did is None:
                raise ValueError(
                    'A DID number must be given with subfunction %d' %
                    (subfunction))

        if did is not None:
            ServiceHelper.validate_int(did,
                                       min=0,
                                       max=0xFFFF,
                                       name='DID number')

        if subfunction in [
                cls.Subfunction.defineByIdentifier,
                cls.Subfunction.defineByMemoryAddress
        ]:
            if diddef is None:
                raise ValueError(
                    'DynamicDidDefinition must be given for this subfunction')

            diddef_entries = diddef.get()
            if len(diddef_entries) == 0:
                raise ValueError(
                    'DynamicDidDefinition object must have at least one DID specification'
                )
            req.data = struct.pack('>H', did)

        if subfunction == cls.Subfunction.defineByIdentifier:
            if not diddef.is_by_source_did():
                raise ValueError(
                    "DynamicDidDefinition must be defined by source DID when used with subfunction 'defineByIdentifier'"
                )
            for entry in diddef_entries:
                req.data += struct.pack('>HBB', entry.source_did,
                                        entry.position, entry.memorysize)

        elif subfunction == cls.Subfunction.defineByMemoryAddress:
            if not diddef.is_by_memory_address():
                raise ValueError(
                    "DynamicDidDefinition must be defined by memory address when used with subfunction 'defineByMemoryAddress'"
                )

            req.data += diddef.get_alfid().get_byte()
            for entry in diddef_entries:
                req.data += entry.memloc.get_address_bytes()
                req.data += entry.memloc.get_memorysize_bytes()

        elif subfunction == cls.Subfunction.clearDynamicallyDefinedDataIdentifier:
            if did is not None:
                req.data = struct.pack('>H', did)

        return req
示例#18
0
	def make_request(cls, did, control_param=None, values=None, masks=None, ioconfig=None):
		"""
		Generate a request for InputOutputControlByIdentifier

		:param did: Data identifier to read
		:type did: int

		:param control_param: Optional parameter that can be a value from InputOutputControlByIdentifier.ControlParam
		:type control_param: int

		:param values: Optional values to send to the server. This parameter will be given to :ref:`DidCodec<HelperClass_DidCodec>`.encode() method. 
			It can be:
			
				- A list for positional arguments
				- A dict for named arguments
				- An instance of :ref:`IOValues<HelperClass_IOValues>` for mixed arguments

		:type values: list, dict, :ref:`IOValues<HelperClass_IOValues>`

		:param masks: Optional mask record for composite values. The mask definition must be included in ioconfig
			It can be:

				- A list naming the bit mask to set
				- A dict with the mask name as a key and a boolean setting or clearing the mask as the value
				- An instance of :ref:`IOMask<HelperClass_IOMask>`
				- A boolean value to set all mask to the same value.
		:type masks: list, dict, :ref:`IOMask<HelperClass_IOMask>`, bool

		:param ioconfig: Definition of DID codecs. Dictionary mapping a DID (int) to a valid :ref:`DidCodec<HelperClass_DidCodec>` class or pack/unpack string. 
			It is possible to use composite :ref:`DidCodec<HelperClass_DidCodec>` by specifying a dict with entries : codec, mask, mask_size.
		:type ioconfig: dict[int] = :ref:`DidCodec<HelperClass_DidCodec>`, dict

		:raises ValueError: If parameters are out of range or missing
		:raises ConfigError: If given did is not defined within ioconfig
		"""	

		from udsoncan import Request, IOMasks, IOValues, DidCodec
		
		ServiceHelper.validate_int(did, min=0, max=0xffff, name='DID')
		if control_param is not None:
			if not isinstance(control_param, int):
				raise ValueError("control_param  must be a valid integer")

			if control_param < 0 or control_param > 3:
				raise ValueError('control_param must either be returnControlToECU(0), resetToDefault(1), freezeCurrentState(2), shortTermAdjustment(3). %d given.' % control_param)
		
		if values is not None:
			if isinstance(values, list):
				values = IOValues(*values)
			if isinstance(values, dict):
				values = IOValues(**values)

			if not isinstance(values, IOValues):
				raise ValueError("values must be an instance of IOValues")

		if masks is not None:
			if isinstance(masks, list):
				masks = IOMasks(*masks)
			if isinstance(masks, dict):
				masks = IOMasks(**masks)

			if not isinstance(masks, IOMasks) and not isinstance(masks, bool):
				raise ValueError("masks must be an instance of IOMask or a boolean value")

		if values is None and masks is not None:
			raise ValueError('An IOValue must be given if a IOMask is provided.')	

		request = Request(service=cls)		

		request.data = b''
		ioconfig = ServiceHelper.check_io_config(did, ioconfig)	# IO dids are defined in client config.
		request.data += struct.pack('>H', did)

		# This parameters is optional according to standard
		if control_param is not None:
			request.data += struct.pack('B', control_param)
		
		codec = DidCodec.from_config(ioconfig[did])	# Get IO codec from config
		
		if values is not None:
			request.data += codec.encode(*values.args, **values.kwargs)

		if masks is not None: # Skip the masks byte if none is given.
			if isinstance(masks, bool):
				byte = b'\xFF' if masks == True else b'\x00'
				if 'mask_size' in  ioconfig[did]:
					request.data += (byte * ioconfig[did]['mask_size'])
				else:
					raise ConfigError('mask_size', msg='Given mask is boolean value, indicating that all mask should be set to same value, but no mask_size is defined in configuration. Cannot guess how many bits to set.')

			elif isinstance(masks, IOMasks):
				if 'mask' not in ioconfig[did]:
					raise ConfigError('mask', msg='Cannot apply given mask. Input/Output configuration does not define their position (and size).')
				masks_config = ioconfig[did]['mask']
				given_masks = masks.get_dict()

				numeric_val = 0
				for mask_name in given_masks:
					if mask_name not in masks_config:
						raise ConfigError('mask_size', msg='Cannot set mask bit for mask %s. The configuration does not define its position' % (mask_name))	
					
					if given_masks[mask_name] == True:
						numeric_val |= masks_config[mask_name]

				minsize = math.ceil(math.log(numeric_val+1, 2)/8.0)
				size = minsize if 'mask_size' not in ioconfig[did] else ioconfig[did]['mask_size']
				request.data += numeric_val.to_bytes(size, 'big')
		return request
示例#19
0
 def test_make_payload_custom_data(self):
     req = Request(DummyServiceNormal, subfunction=0x44)
     req.data = "\x12\x34\x56\x78"
     payload = req.get_payload()
     self.assertEqual("\x13\x44\x12\x34\x56\x78", payload)