From 41494f92e57663dab1325df1c4e0910217cf7b6a Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 14 Jan 2025 11:53:07 +0100 Subject: [PATCH 1/6] fix en- and decoding of environment data descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit it turns out that the specification only mandates that the base data type of the DOP used by the referenced parameter must be A_UINT32, but the DOP does not need to be a DTC-DOP. Signed-off-by: Andreas Lauser Signed-off-by: Katja Köhler --- odxtools/environmentdatadescription.py | 136 +++++++++++++++++-------- 1 file changed, 93 insertions(+), 43 deletions(-) diff --git a/odxtools/environmentdatadescription.py b/odxtools/environmentdatadescription.py index 1909f07c..b30f1ae4 100644 --- a/odxtools/environmentdatadescription.py +++ b/odxtools/environmentdatadescription.py @@ -6,6 +6,7 @@ from typing_extensions import override from .complexdop import ComplexDop +from .dataobjectproperty import DataObjectProperty from .decodestate import DecodeState from .dtcdop import DtcDop from .encodestate import EncodeState @@ -13,8 +14,9 @@ from .exceptions import odxraise, odxrequire from .nameditemlist import NamedItemList from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef -from .odxtypes import ParameterValue, ParameterValueDict +from .odxtypes import DataType, ParameterValue, ParameterValueDict from .parameters.parameter import Parameter +from .parameters.valueparameter import ValueParameter from .snrefcontext import SnRefContext from .utils import dataclass_fields_asdict @@ -116,40 +118,64 @@ def encode_into_pdu(self, physical_value: Optional[ParameterValue], """Convert a physical value into bytes and emplace them into a PDU. """ - # retrieve the relevant DTC parameter which must be located in - # front of the environment data description. + # retrieve the DTC as a numerical value from the referenced + # parameter (which must be located somewhere before the + # parameter using the environment data description) if self.param_snref is None: odxraise("Specifying the DTC parameter for environment data " "descriptions via SNPATHREF is not supported yet") return None - dtc_param: Optional[Parameter] = None - dtc_dop: Optional[DtcDop] = None - dtc_param_value: Optional[ParameterValue] = None + numerical_dtc_value: Optional[ParameterValue] = None for prev_param, prev_param_value in reversed(encode_state.journal): if prev_param.short_name == self.param_snref: - dtc_param = prev_param - prev_dop = getattr(prev_param, "dop", None) - if not isinstance(prev_dop, DtcDop): - odxraise(f"The DOP of the parameter referenced by environment data " - f"descriptions must be a DTC-DOP (is '{type(prev_dop).__name__}')") + if not isinstance(prev_param, ValueParameter): + odxraise( + f"The parameter referenced by environment data descriptions " + f"must use a VALUE-PARAMETER (encountered {type(prev_param).__name__} " + f"for reference '{self.param_snref}' of ENV-DATA-DESC '{self.short_name}')") return - dtc_dop = prev_dop - dtc_param_value = prev_param_value + + prev_dop = prev_param.dop + if isinstance(prev_dop, DtcDop): + if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: + odxraise(f"The data type used by the DOP of the parameter referenced " + f"by environment data descriptions must be A_UINT32 " + f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") + return + + if prev_param_value is None: + odxraise() + return + + numerical_dtc_value = prev_dop.convert_to_numerical_trouble_code( + prev_param_value) + elif isinstance(prev_dop, DataObjectProperty): + if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: + odxraise(f"The data type used by the DOP of the parameter referenced " + f"by environment data descriptions must be A_UINT32 " + f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") + return + + if not isinstance(prev_param_value, (int, str)): + odxraise() + return + + numerical_dtc_value = prev_dop.compu_method.convert_physical_to_internal( + prev_param_value) + else: + odxraise(f"The DOP of the parameter referenced " + f"by environment data descriptions must use a simple DOP or a DTC-DOP " + f"(encountered '{type(prev_dop).__name__}')") + return + break - if dtc_param is None: + if numerical_dtc_value is None: odxraise("Environment data description parameters are only allowed following " "the referenced value parameter.") return - if dtc_param_value is None or dtc_dop is None: - # this should never happen - odxraise() - return - - numerical_dtc = dtc_dop.convert_to_numerical_trouble_code(dtc_param_value) - # deal with the "all value" environment data. This holds # parameters that are common to all DTCs. Be aware that the # specification mandates that there is at most one such @@ -165,7 +191,7 @@ def encode_into_pdu(self, physical_value: Optional[ParameterValue], # find the environment data corresponding to the given trouble # code for env_data in self.env_datas: - if numerical_dtc in env_data.dtc_values: + if numerical_dtc_value in env_data.dtc_values: tmp = encode_state.allow_unknown_parameters encode_state.allow_unknown_parameters = True env_data.encode_into_pdu(physical_value, encode_state) @@ -177,40 +203,64 @@ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue: """Extract the bytes from a PDU and convert them to a physical value. """ - # retrieve the relevant DTC parameter which must be located in - # front of the environment data description. + # retrieve the DTC as a numerical value from the referenced + # parameter (which must be located somewhere before the + # parameter using the environment data description) if self.param_snref is None: odxraise("Specifying the DTC parameter for environment data " "descriptions via SNPATHREF is not supported yet") return None - dtc_param: Optional[Parameter] = None - dtc_dop: Optional[DtcDop] = None - dtc_param_value: Optional[ParameterValue] = None + numerical_dtc_value: Optional[ParameterValue] = None for prev_param, prev_param_value in reversed(decode_state.journal): if prev_param.short_name == self.param_snref: - dtc_param = prev_param - prev_dop = getattr(prev_param, "dop", None) - if not isinstance(prev_dop, DtcDop): - odxraise(f"The DOP of the parameter referenced by environment data " - f"descriptions must be a DTC-DOP (is '{type(prev_dop).__name__}')") + if not isinstance(prev_param, ValueParameter): + odxraise( + f"The parameter referenced by environment data descriptions " + f"must use a VALUE-PARAMETER (encountered {type(prev_param).__name__} " + f"for reference '{self.param_snref}' of ENV-DATA-DESC '{self.short_name}')") return - dtc_dop = prev_dop - dtc_param_value = prev_param_value + + prev_dop = prev_param.dop + if isinstance(prev_dop, DtcDop): + if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: + odxraise(f"The data type used by the DOP of the parameter referenced " + f"by environment data descriptions must be A_UINT32 " + f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") + return + + if prev_param_value is None: + odxraise() + return + + numerical_dtc_value = prev_dop.convert_to_numerical_trouble_code( + prev_param_value) + elif isinstance(prev_dop, DataObjectProperty): + if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: + odxraise(f"The data type used by the DOP of the parameter referenced " + f"by environment data descriptions must be A_UINT32 " + f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") + return + + if not isinstance(prev_param_value, (int, str)): + odxraise() + return + + numerical_dtc_value = prev_dop.compu_method.convert_physical_to_internal( + prev_param_value) + else: + odxraise(f"The DOP of the parameter referenced " + f"by environment data descriptions must use a simple DOP or a DTC-DOP " + f"(encountered '{type(prev_dop).__name__}')") + return + break - if dtc_param is None: + if numerical_dtc_value is None: odxraise("Environment data description parameters are only allowed following " "the referenced value parameter.") return - if dtc_param_value is None or dtc_dop is None: - # this should never happen - odxraise() - return - - numerical_dtc = dtc_dop.convert_to_numerical_trouble_code(dtc_param_value) - result: ParameterValueDict = {} # deal with the "all value" environment data. This holds @@ -228,7 +278,7 @@ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue: # find the environment data corresponding to the given trouble # code for env_data in self.env_datas: - if numerical_dtc in env_data.dtc_values: + if numerical_dtc_value in env_data.dtc_values: tmp = env_data.decode_from_pdu(decode_state) if not isinstance(tmp, dict): odxraise() From 672ff475ff9227b280ff25b58ff2854dd7b5f99d Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 15 Jan 2025 13:55:36 +0100 Subject: [PATCH 2/6] EnvironmentDataDescription: handle default values and constant parameters for the referenced parameter IMO both do not make any sense, but they probably are not explicitly forbidden by the spec, so let's cover our bases. thanks to [at]kayoub5 for the nudge. Signed-off-by: Andreas Lauser --- odxtools/environmentdatadescription.py | 50 +++++++++++++++----------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/odxtools/environmentdatadescription.py b/odxtools/environmentdatadescription.py index b30f1ae4..5008f328 100644 --- a/odxtools/environmentdatadescription.py +++ b/odxtools/environmentdatadescription.py @@ -15,9 +15,13 @@ from .nameditemlist import NamedItemList from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef from .odxtypes import DataType, ParameterValue, ParameterValueDict +from .parameters.codedconstparameter import CodedConstParameter from .parameters.parameter import Parameter +from .parameters.parameterwithdop import ParameterWithDOP +from .parameters.physicalconstantparameter import PhysicalConstantParameter from .parameters.valueparameter import ValueParameter from .snrefcontext import SnRefContext +from .standardlengthtype import StandardLengthType from .utils import dataclass_fields_asdict @@ -129,38 +133,44 @@ def encode_into_pdu(self, physical_value: Optional[ParameterValue], numerical_dtc_value: Optional[ParameterValue] = None for prev_param, prev_param_value in reversed(encode_state.journal): if prev_param.short_name == self.param_snref: - if not isinstance(prev_param, ValueParameter): + if not isinstance(prev_param, ParameterWithDOP): odxraise( f"The parameter referenced by environment data descriptions " - f"must use a VALUE-PARAMETER (encountered {type(prev_param).__name__} " + f"must use a parameter that specifies a DOP (encountered {type(prev_param).__name__} " f"for reference '{self.param_snref}' of ENV-DATA-DESC '{self.short_name}')") return prev_dop = prev_param.dop - if isinstance(prev_dop, DtcDop): - if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: - odxraise(f"The data type used by the DOP of the parameter referenced " - f"by environment data descriptions must be A_UINT32 " - f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") - return + if not isinstance(prev_dop, (StandardLengthType, DtcDop)): + odxraise( + f"The DOP of the parameter referenced by environment data descriptions " + f"must use either be StandardLengthType or a DtcDop (encountered " + f"{type(prev_param).__name__} for reference '{self.param_snref}' " + f"of ENV-DATA-DESC '{self.short_name}')") + return - if prev_param_value is None: - odxraise() + if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: + odxraise(f"The data type used by the DOP of the parameter referenced " + f"by environment data descriptions must be A_UINT32 " + f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") + return + + if prev_param_value is None: + if isinstance(prev_param, ValueParameter): + prev_param_value = prev_param.physical_default_value + elif isinstance(prev_param, CodedConstParameter): + prev_param_value = prev_param.coded_value + elif isinstance(prev_param, PhysicalConstantParameter): + prev_param_value = prev_param.physical_constant_value + else: + odxraise() # make mypy happy... return + if isinstance(prev_dop, DtcDop): + assert isinstance(prev_param_value, (int, str)) numerical_dtc_value = prev_dop.convert_to_numerical_trouble_code( prev_param_value) elif isinstance(prev_dop, DataObjectProperty): - if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: - odxraise(f"The data type used by the DOP of the parameter referenced " - f"by environment data descriptions must be A_UINT32 " - f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") - return - - if not isinstance(prev_param_value, (int, str)): - odxraise() - return - numerical_dtc_value = prev_dop.compu_method.convert_physical_to_internal( prev_param_value) else: From e6d19762988f00eb22925830a621f5c2e4b34521 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 15 Jan 2025 14:09:15 +0100 Subject: [PATCH 3/6] also adapt `.decode_from_pdu()` Signed-off-by: Andreas Lauser --- odxtools/environmentdatadescription.py | 52 +++++++++++--------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/odxtools/environmentdatadescription.py b/odxtools/environmentdatadescription.py index 5008f328..5a049883 100644 --- a/odxtools/environmentdatadescription.py +++ b/odxtools/environmentdatadescription.py @@ -173,11 +173,6 @@ def encode_into_pdu(self, physical_value: Optional[ParameterValue], elif isinstance(prev_dop, DataObjectProperty): numerical_dtc_value = prev_dop.compu_method.convert_physical_to_internal( prev_param_value) - else: - odxraise(f"The DOP of the parameter referenced " - f"by environment data descriptions must use a simple DOP or a DTC-DOP " - f"(encountered '{type(prev_dop).__name__}')") - return break @@ -224,45 +219,40 @@ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue: numerical_dtc_value: Optional[ParameterValue] = None for prev_param, prev_param_value in reversed(decode_state.journal): if prev_param.short_name == self.param_snref: - if not isinstance(prev_param, ValueParameter): + if not isinstance(prev_param, ParameterWithDOP): odxraise( f"The parameter referenced by environment data descriptions " - f"must use a VALUE-PARAMETER (encountered {type(prev_param).__name__} " + f"must use a parameter that specifies a DOP (encountered {type(prev_param).__name__} " f"for reference '{self.param_snref}' of ENV-DATA-DESC '{self.short_name}')") return prev_dop = prev_param.dop - if isinstance(prev_dop, DtcDop): - if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: - odxraise(f"The data type used by the DOP of the parameter referenced " - f"by environment data descriptions must be A_UINT32 " - f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") - return + if not isinstance(prev_dop, (StandardLengthType, DtcDop)): + odxraise( + f"The DOP of the parameter referenced by environment data descriptions " + f"must use either be StandardLengthType or a DtcDop (encountered " + f"{type(prev_param).__name__} for reference '{self.param_snref}' " + f"of ENV-DATA-DESC '{self.short_name}')") + return - if prev_param_value is None: - odxraise() - return + if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: + odxraise(f"The data type used by the DOP of the parameter referenced " + f"by environment data descriptions must be A_UINT32 " + f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") + return + + if prev_param_value is None: + odxraise() # make mypy happy: during decoding a + # parameter value of None is never + # encountered + return + if isinstance(prev_dop, DtcDop): numerical_dtc_value = prev_dop.convert_to_numerical_trouble_code( prev_param_value) elif isinstance(prev_dop, DataObjectProperty): - if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: - odxraise(f"The data type used by the DOP of the parameter referenced " - f"by environment data descriptions must be A_UINT32 " - f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") - return - - if not isinstance(prev_param_value, (int, str)): - odxraise() - return - numerical_dtc_value = prev_dop.compu_method.convert_physical_to_internal( prev_param_value) - else: - odxraise(f"The DOP of the parameter referenced " - f"by environment data descriptions must use a simple DOP or a DTC-DOP " - f"(encountered '{type(prev_dop).__name__}')") - return break From a9a791f804540f070914559fb391bb0dca4dda4e Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 16 Jan 2025 09:18:42 +0100 Subject: [PATCH 4/6] EnvironmentDataDescription: consolidate the code for converting the value of the DTC parameter to an integer into a method this is surprisingly complicated and led to quite a bit of error-prone copy and pasted code. thanks to [at]kayoub5 for the suggestion! Signed-off-by: Andreas Lauser --- odxtools/environmentdatadescription.py | 125 ++++++++++--------------- 1 file changed, 47 insertions(+), 78 deletions(-) diff --git a/odxtools/environmentdatadescription.py b/odxtools/environmentdatadescription.py index 5a049883..6821ffda 100644 --- a/odxtools/environmentdatadescription.py +++ b/odxtools/environmentdatadescription.py @@ -133,52 +133,13 @@ def encode_into_pdu(self, physical_value: Optional[ParameterValue], numerical_dtc_value: Optional[ParameterValue] = None for prev_param, prev_param_value in reversed(encode_state.journal): if prev_param.short_name == self.param_snref: - if not isinstance(prev_param, ParameterWithDOP): - odxraise( - f"The parameter referenced by environment data descriptions " - f"must use a parameter that specifies a DOP (encountered {type(prev_param).__name__} " - f"for reference '{self.param_snref}' of ENV-DATA-DESC '{self.short_name}')") - return - - prev_dop = prev_param.dop - if not isinstance(prev_dop, (StandardLengthType, DtcDop)): - odxraise( - f"The DOP of the parameter referenced by environment data descriptions " - f"must use either be StandardLengthType or a DtcDop (encountered " - f"{type(prev_param).__name__} for reference '{self.param_snref}' " - f"of ENV-DATA-DESC '{self.short_name}')") - return - - if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: - odxraise(f"The data type used by the DOP of the parameter referenced " - f"by environment data descriptions must be A_UINT32 " - f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") - return - - if prev_param_value is None: - if isinstance(prev_param, ValueParameter): - prev_param_value = prev_param.physical_default_value - elif isinstance(prev_param, CodedConstParameter): - prev_param_value = prev_param.coded_value - elif isinstance(prev_param, PhysicalConstantParameter): - prev_param_value = prev_param.physical_constant_value - else: - odxraise() # make mypy happy... - return - - if isinstance(prev_dop, DtcDop): - assert isinstance(prev_param_value, (int, str)) - numerical_dtc_value = prev_dop.convert_to_numerical_trouble_code( - prev_param_value) - elif isinstance(prev_dop, DataObjectProperty): - numerical_dtc_value = prev_dop.compu_method.convert_physical_to_internal( - prev_param_value) - + numerical_dtc_value = self._get_numerical_dtc_from_parameter( + prev_param, prev_param_value) break if numerical_dtc_value is None: odxraise("Environment data description parameters are only allowed following " - "the referenced value parameter.") + "the referenced parameter.") return # deal with the "all value" environment data. This holds @@ -219,46 +180,13 @@ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue: numerical_dtc_value: Optional[ParameterValue] = None for prev_param, prev_param_value in reversed(decode_state.journal): if prev_param.short_name == self.param_snref: - if not isinstance(prev_param, ParameterWithDOP): - odxraise( - f"The parameter referenced by environment data descriptions " - f"must use a parameter that specifies a DOP (encountered {type(prev_param).__name__} " - f"for reference '{self.param_snref}' of ENV-DATA-DESC '{self.short_name}')") - return - - prev_dop = prev_param.dop - if not isinstance(prev_dop, (StandardLengthType, DtcDop)): - odxraise( - f"The DOP of the parameter referenced by environment data descriptions " - f"must use either be StandardLengthType or a DtcDop (encountered " - f"{type(prev_param).__name__} for reference '{self.param_snref}' " - f"of ENV-DATA-DESC '{self.short_name}')") - return - - if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: - odxraise(f"The data type used by the DOP of the parameter referenced " - f"by environment data descriptions must be A_UINT32 " - f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") - return - - if prev_param_value is None: - odxraise() # make mypy happy: during decoding a - # parameter value of None is never - # encountered - return - - if isinstance(prev_dop, DtcDop): - numerical_dtc_value = prev_dop.convert_to_numerical_trouble_code( - prev_param_value) - elif isinstance(prev_dop, DataObjectProperty): - numerical_dtc_value = prev_dop.compu_method.convert_physical_to_internal( - prev_param_value) - + numerical_dtc_value = self._get_numerical_dtc_from_parameter( + prev_param, prev_param_value) break if numerical_dtc_value is None: odxraise("Environment data description parameters are only allowed following " - "the referenced value parameter.") + "the referenced parameter.") return result: ParameterValueDict = {} @@ -286,3 +214,44 @@ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue: break return result + + def _get_numerical_dtc_from_parameter(self, param: Parameter, + param_value: Optional[ParameterValue]) -> int: + if not isinstance(param, ParameterWithDOP): + odxraise( + f"The parameter referenced by environment data descriptions " + f"must use a parameter that specifies a DOP (encountered {type(param).__name__} " + f"for reference '{self.param_snref}' of ENV-DATA-DESC '{self.short_name}')") + return + + prev_dop = param.dop + if not isinstance(prev_dop, (StandardLengthType, DtcDop)): + odxraise(f"The DOP of the parameter referenced by environment data descriptions " + f"must use either be StandardLengthType or a DtcDop (encountered " + f"{type(param).__name__} for parameter '{self.param.short_name}' " + f"of ENV-DATA-DESC '{self.short_name}')") + return + + if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: + odxraise(f"The data type used by the DOP of the parameter referenced " + f"by environment data descriptions must be A_UINT32 " + f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") + return + + if param_value is None: + if isinstance(param, ValueParameter): + param_value = param.physical_default_value + elif isinstance(param, CodedConstParameter): + param_value = param.coded_value + elif isinstance(param, PhysicalConstantParameter): + param_value = param.physical_constant_value + else: + odxraise() # make mypy happy... + return + + if isinstance(prev_dop, DtcDop): + return prev_dop.convert_to_numerical_trouble_code(odxrequire(param_value)) + elif isinstance(prev_dop, DataObjectProperty): + return prev_dop.compu_method.convert_physical_to_internal(param_value) + + odxraise() # not reachable From ede0fedf7545a13e8444f6b842f61c78cfc3b03b Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 16 Jan 2025 09:37:57 +0100 Subject: [PATCH 5/6] `CodedConstParameter`s do not specify a DOP (only a `DiagCodedType` and the value already is specified using the internal representation.) Signed-off-by: Andreas Lauser --- odxtools/environmentdatadescription.py | 76 +++++++++++++++----------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/odxtools/environmentdatadescription.py b/odxtools/environmentdatadescription.py index 6821ffda..c4d74761 100644 --- a/odxtools/environmentdatadescription.py +++ b/odxtools/environmentdatadescription.py @@ -217,41 +217,51 @@ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue: def _get_numerical_dtc_from_parameter(self, param: Parameter, param_value: Optional[ParameterValue]) -> int: - if not isinstance(param, ParameterWithDOP): - odxraise( - f"The parameter referenced by environment data descriptions " - f"must use a parameter that specifies a DOP (encountered {type(param).__name__} " - f"for reference '{self.param_snref}' of ENV-DATA-DESC '{self.short_name}')") - return - - prev_dop = param.dop - if not isinstance(prev_dop, (StandardLengthType, DtcDop)): - odxraise(f"The DOP of the parameter referenced by environment data descriptions " - f"must use either be StandardLengthType or a DtcDop (encountered " - f"{type(param).__name__} for parameter '{self.param.short_name}' " - f"of ENV-DATA-DESC '{self.short_name}')") - return + if isinstance(param, ParameterWithDOP): + dop = param.dop + if not isinstance(dop, (StandardLengthType, DtcDop)): + odxraise(f"The DOP of the parameter referenced by environment data descriptions " + f"must use either be StandardLengthType or a DtcDop (encountered " + f"{type(param).__name__} for parameter '{self.param.short_name}' " + f"of ENV-DATA-DESC '{self.short_name}')") + return - if prev_dop.diag_coded_type.base_data_type != DataType.A_UINT32: - odxraise(f"The data type used by the DOP of the parameter referenced " - f"by environment data descriptions must be A_UINT32 " - f"(encountered '{prev_dop.diag_coded_type.base_data_type.value}')") - return + if dop.diag_coded_type.base_data_type != DataType.A_UINT32: + odxraise(f"The data type used by the DOP of the parameter referenced " + f"by environment data descriptions must be A_UINT32 " + f"(encountered '{dop.diag_coded_type.base_data_type.value}')") + return - if param_value is None: - if isinstance(param, ValueParameter): - param_value = param.physical_default_value - elif isinstance(param, CodedConstParameter): - param_value = param.coded_value - elif isinstance(param, PhysicalConstantParameter): - param_value = param.physical_constant_value - else: - odxraise() # make mypy happy... + if param_value is None: + if isinstance(param, ValueParameter): + param_value = param.physical_default_value + elif isinstance(param, PhysicalConstantParameter): + param_value = param.physical_constant_value + else: + odxraise() # make mypy happy... + return + + if isinstance(dop, DtcDop): + return dop.convert_to_numerical_trouble_code(odxrequire(param_value)) + elif isinstance(dop, DataObjectProperty): + return dop.compu_method.convert_physical_to_internal(param_value) + + odxraise() # not reachable + + elif isinstance(param, CodedConstParameter): + if param.diag_coded_type.base_data_type != DataType.A_UINT32: + odxraise(f"The data type used by the parameter referenced " + f"by environment data descriptions must be A_UINT32 " + f"(encountered '{param.diag_coded_type.base_data_type.value}')") return - if isinstance(prev_dop, DtcDop): - return prev_dop.convert_to_numerical_trouble_code(odxrequire(param_value)) - elif isinstance(prev_dop, DataObjectProperty): - return prev_dop.compu_method.convert_physical_to_internal(param_value) + assert isinstance(param.coded_value, int) + + return param.coded_value - odxraise() # not reachable + else: + odxraise(f"The parameter referenced by environment data descriptions " + f"must be a parameter that either specifies a DOP or a constant " + f"(encountered {type(param).__name__} for reference '{self.param_snref}' of " + f"ENV-DATA-DESC '{self.short_name}')") + return From c068384049fe08f68987b7581e53765547aecab3 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 16 Jan 2025 13:54:16 +0100 Subject: [PATCH 6/6] fix return policy of `EnvironmentDataDescription._get_numerical_dtc_from_parameter()` also, be less strict about the kind of DOP used by the parameter: any simple DOP is fine, it does not need to be a `StandardLengthType`... Signed-off-by: Andreas Lauser --- odxtools/environmentdatadescription.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/odxtools/environmentdatadescription.py b/odxtools/environmentdatadescription.py index c4d74761..a72f01bc 100644 --- a/odxtools/environmentdatadescription.py +++ b/odxtools/environmentdatadescription.py @@ -21,7 +21,6 @@ from .parameters.physicalconstantparameter import PhysicalConstantParameter from .parameters.valueparameter import ValueParameter from .snrefcontext import SnRefContext -from .standardlengthtype import StandardLengthType from .utils import dataclass_fields_asdict @@ -219,18 +218,17 @@ def _get_numerical_dtc_from_parameter(self, param: Parameter, param_value: Optional[ParameterValue]) -> int: if isinstance(param, ParameterWithDOP): dop = param.dop - if not isinstance(dop, (StandardLengthType, DtcDop)): + if not isinstance(dop, (DataObjectProperty, DtcDop)): odxraise(f"The DOP of the parameter referenced by environment data descriptions " - f"must use either be StandardLengthType or a DtcDop (encountered " + f"must use either be DataObjectProperty or a DtcDop (encountered " f"{type(param).__name__} for parameter '{self.param.short_name}' " f"of ENV-DATA-DESC '{self.short_name}')") - return + return 0 if dop.diag_coded_type.base_data_type != DataType.A_UINT32: odxraise(f"The data type used by the DOP of the parameter referenced " f"by environment data descriptions must be A_UINT32 " f"(encountered '{dop.diag_coded_type.base_data_type.value}')") - return if param_value is None: if isinstance(param, ValueParameter): @@ -244,18 +242,21 @@ def _get_numerical_dtc_from_parameter(self, param: Parameter, if isinstance(dop, DtcDop): return dop.convert_to_numerical_trouble_code(odxrequire(param_value)) elif isinstance(dop, DataObjectProperty): - return dop.compu_method.convert_physical_to_internal(param_value) + return int(dop.compu_method.convert_physical_to_internal( + param_value)) # type: ignore[arg-type] - odxraise() # not reachable + odxraise() # not reachable... elif isinstance(param, CodedConstParameter): if param.diag_coded_type.base_data_type != DataType.A_UINT32: odxraise(f"The data type used by the parameter referenced " f"by environment data descriptions must be A_UINT32 " f"(encountered '{param.diag_coded_type.base_data_type.value}')") - return - assert isinstance(param.coded_value, int) + return param.coded_value + + if not isinstance(param.coded_value, int): + odxraise() return param.coded_value @@ -264,4 +265,4 @@ def _get_numerical_dtc_from_parameter(self, param: Parameter, f"must be a parameter that either specifies a DOP or a constant " f"(encountered {type(param).__name__} for reference '{self.param_snref}' of " f"ENV-DATA-DESC '{self.short_name}')") - return + return 0