Source code for uds.can.addressing.addressing_information
"""
Implementation of Addressing Information for CAN bus.
This module contains helper class for managing :ref:`Addressing Information <knowledge-base-n-ai>` on CAN bus.
"""
__all__ = ["CanAddressingInformation"]
from typing import Dict, Optional, Type
from uds.addressing import AddressingType
from uds.utilities import InconsistencyError, RawBytesAlias, validate_raw_bytes
from ..frame import CanIdHandler
from .abstract_addressing_information import AbstractCanAddressingInformation, CANAddressingParams
from .addressing_format import CanAddressingFormat
from .extended_addressing import ExtendedCanAddressingInformation
from .mixed_addressing import Mixed11BitCanAddressingInformation, Mixed29BitCanAddressingInformation
from .normal_addressing import NormalCanAddressingInformation, NormalFixedCanAddressingInformation
[docs]
class CanAddressingInformation:
"""CAN Entity (either server or client) Addressing Information."""
ADDRESSING_INFORMATION_MAPPING: Dict[CanAddressingFormat, Type[AbstractCanAddressingInformation]] = {
CanAddressingFormat.NORMAL_ADDRESSING: NormalCanAddressingInformation,
CanAddressingFormat.NORMAL_FIXED_ADDRESSING: NormalFixedCanAddressingInformation,
CanAddressingFormat.EXTENDED_ADDRESSING: ExtendedCanAddressingInformation,
CanAddressingFormat.MIXED_11BIT_ADDRESSING: Mixed11BitCanAddressingInformation,
CanAddressingFormat.MIXED_29BIT_ADDRESSING: Mixed29BitCanAddressingInformation,
}
"""Dictionary with CAN Addressing Formats mapped to Addressing Information handler classes."""
def __new__(cls, # type: ignore
addressing_format: CanAddressingFormat,
rx_physical_params: AbstractCanAddressingInformation.InputAIParams,
tx_physical_params: AbstractCanAddressingInformation.InputAIParams,
rx_functional_params: AbstractCanAddressingInformation.InputAIParams,
tx_functional_params: AbstractCanAddressingInformation.InputAIParams
) -> AbstractCanAddressingInformation:
"""
Create UDS Addressing Information for a CAN node.
:param addressing_format: CAN Addressing format used by CAN node.
:param rx_physical_params: Addressing Information parameters used for incoming physically
addressed communication.
:param tx_physical_params: Addressing Information parameters used for outgoing physically
addressed communication.
:param rx_functional_params: Addressing Information parameters used for incoming functionally
addressed communication.
:param tx_functional_params: Addressing Information parameters used for outgoing functionally
addressed communication.
"""
ai_class = cls.ADDRESSING_INFORMATION_MAPPING[addressing_format]
return ai_class(rx_physical_params=rx_physical_params,
tx_physical_params=tx_physical_params,
rx_functional_params=rx_functional_params,
tx_functional_params=tx_functional_params)
[docs]
@classmethod
def get_ai_data_bytes_number(cls, addressing_format: CanAddressingFormat) -> int:
"""
Get number of data bytes that are used to carry Addressing Information.
:param addressing_format: CAN Addressing Format used.
:return: Number of data bytes in a CAN Packet that are used to carry Addressing Information for provided
CAN Addressing Format.
"""
return cls.ADDRESSING_INFORMATION_MAPPING[addressing_format].AI_DATA_BYTES_NUMBER
[docs]
@classmethod
def validate_ai_data_bytes(cls, addressing_format: CanAddressingFormat, ai_data_bytes: RawBytesAlias) -> None:
"""
Validate Addressing Information stored in CAN data bytes.
:param addressing_format: CAN Addressing Format used.
:param ai_data_bytes: Data bytes to validate.
:raise InconsistencyError: Provided number of Addressing Information data bytes does not match
CAN Addressing Format used.
"""
CanAddressingFormat.validate_member(addressing_format)
validate_raw_bytes(ai_data_bytes, allow_empty=True)
expected_ai_bytes_number = cls.get_ai_data_bytes_number(addressing_format)
if expected_ai_bytes_number != len(ai_data_bytes):
raise InconsistencyError("Number of Addressing Information data bytes does not match provided "
f"CAN Addressing Format. CAN Addressing Format: {addressing_format}. "
f"Provided AI Data Bytes number: {len(ai_data_bytes)}. "
f"Expected AI Data Bytes number: {expected_ai_bytes_number}.")
[docs]
@classmethod
def validate_addressing_params(cls,
addressing_format: CanAddressingFormat,
addressing_type: AddressingType,
can_id: Optional[int] = None,
target_address: Optional[int] = None,
source_address: Optional[int] = None,
address_extension: Optional[int] = None) -> CANAddressingParams:
"""
Validate Addressing Information parameters of a CAN packet.
:param addressing_format: CAN addressing format used.
:param addressing_type: Addressing type to validate.
:param can_id: CAN Identifier value to validate.
:param target_address: Target Address value to validate.
:param source_address: Source Address value to validate.
:param address_extension: Address Extension value to validate.
:return: Normalized dictionary with the provided Addressing Information.
"""
CanAddressingFormat.validate_member(addressing_format)
return cls.ADDRESSING_INFORMATION_MAPPING[addressing_format].validate_addressing_params(
addressing_format=addressing_format,
addressing_type=addressing_type,
can_id=can_id,
target_address=target_address,
source_address=source_address,
address_extension=address_extension)
[docs]
@classmethod
def is_compatible_can_id(cls,
addressing_format: CanAddressingFormat,
can_id: int,
addressing_type: Optional[AddressingType] = None) -> bool:
"""
Check whether provided CAN ID is consistent the provided CAN Addressing Format.
:param addressing_format: Addressing format used.
:param can_id: CAN ID value to check.
:param addressing_type: Addressing type for which consistency to be performed.
Leave None to skip crosscheck between CAN Identifier and Addressing Type.
:return: True if CAN ID value is compatible with this CAN Addressing Format, False otherwise.
"""
CanAddressingFormat.validate_member(addressing_format)
return cls.ADDRESSING_INFORMATION_MAPPING[addressing_format].is_compatible_can_id(
can_id=can_id, addressing_type=addressing_type)
[docs]
@classmethod
def decode_can_id_ai_params(cls,
addressing_format: CanAddressingFormat,
can_id: int) -> AbstractCanAddressingInformation.CanIdAIParams:
"""
Decode Addressing Information parameters from CAN Identifier.
:param addressing_format: Addressing format used.
:param can_id: Value of a CAN Identifier.
:return: Decoded Addressing Information parameters.
"""
CanAddressingFormat.validate_member(addressing_format)
return cls.ADDRESSING_INFORMATION_MAPPING[addressing_format].decode_can_id_ai_params(can_id)
[docs]
@classmethod
def decode_data_bytes_ai_params(cls,
addressing_format: CanAddressingFormat,
ai_data_bytes: RawBytesAlias
) -> AbstractCanAddressingInformation.DataBytesAIParamsAlias:
"""
Decode Addressing Information parameters from CAN data bytes.
:param addressing_format: Addressing format used.
:param ai_data_bytes: Data bytes containing Addressing Information.
:return: Decoded Addressing Information parameters.
"""
CanAddressingFormat.validate_member(addressing_format)
return cls.ADDRESSING_INFORMATION_MAPPING[addressing_format].decode_data_bytes_ai_params(ai_data_bytes)
[docs]
@classmethod
def decode_frame_ai_params(cls,
addressing_format: CanAddressingFormat,
can_id: int,
raw_frame_data: RawBytesAlias) -> AbstractCanAddressingInformation.DecodedAIParamsAlias:
"""
Decode Addressing Information parameters from a CAN Frame.
:param addressing_format: Addressing format used.
:param can_id: CAN Identifier value of a CAN frame.
:param raw_frame_data: Raw data bytes of a CAN frame
:return: Decoded Addressing Information parameters.
"""
CanAddressingFormat.validate_member(addressing_format)
return cls.ADDRESSING_INFORMATION_MAPPING[addressing_format].decode_frame_ai_params(
can_id=can_id, raw_frame_data=raw_frame_data)
[docs]
@classmethod
def encode_can_id(cls,
addressing_format: CanAddressingFormat,
addressing_type: AddressingType,
target_address: int,
source_address: int,
priority: int = CanIdHandler.DEFAULT_PRIORITY_VALUE) -> int:
"""
Generate CAN ID value for Normal Fixed CAN Addressing format.
:param addressing_format: Addressing format used.
:param addressing_type: Addressing type used.
:param target_address: Target Address value to use.
:param source_address: Source Address value to use.
:param priority: Priority parameter value to use.
:raise ValueError: CAN ID cannot be encoded for provided CAN Addressing Format.
:return: Value of CAN ID that is compatible with provided CAN Addressing Format and was generated out of
the provided values.
"""
if addressing_format == CanAddressingFormat.NORMAL_FIXED_ADDRESSING:
return NormalFixedCanAddressingInformation.encode_can_id(addressing_type=addressing_type,
target_address=target_address,
source_address=source_address,
priority=priority)
if addressing_format == CanAddressingFormat.MIXED_29BIT_ADDRESSING:
return Mixed29BitCanAddressingInformation.encode_can_id(addressing_type=addressing_type,
target_address=target_address,
source_address=source_address,
priority=priority)
raise ValueError("Provided CAN Addressing Format does not offer utility of CAN ID encoding. "
f"Actual value: {addressing_format}")
[docs]
@classmethod
def encode_ai_data_bytes(cls,
addressing_format: CanAddressingFormat,
target_address: Optional[int] = None,
address_extension: Optional[int] = None) -> bytearray:
"""
Generate data bytes that carry Addressing Information.
:param addressing_format: CAN Addressing Format used.
:param target_address: Target Address value used.
:param address_extension: Source Address value used.
:return: Data bytes that carry Addressing Information in a CAN frame Data field.
"""
CanAddressingFormat.validate_member(addressing_format)
return cls.ADDRESSING_INFORMATION_MAPPING[addressing_format].encode_ai_data_bytes(
target_address=target_address, address_extension=address_extension)