Skip to content

Commit

Permalink
Merge pull request #379 from andlaus/fix_env_data_desc_coding
Browse files Browse the repository at this point in the history
fix en- and decoding of environment data descriptions
  • Loading branch information
andlaus authored Jan 16, 2025
2 parents 30fb09b + c068384 commit ecc4d14
Showing 1 changed file with 77 additions and 47 deletions.
124 changes: 77 additions & 47 deletions odxtools/environmentdatadescription.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@
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
from .environmentdata import EnvironmentData
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.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 .utils import dataclass_fields_asdict

Expand Down Expand Up @@ -116,40 +121,26 @@ 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__}')")
return
dtc_dop = prev_dop
dtc_param_value = prev_param_value
numerical_dtc_value = self._get_numerical_dtc_from_parameter(
prev_param, prev_param_value)
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()
"the referenced parameter.")
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
Expand All @@ -165,7 +156,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)
Expand All @@ -177,40 +168,26 @@ 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__}')")
return
dtc_dop = prev_dop
dtc_param_value = prev_param_value
numerical_dtc_value = self._get_numerical_dtc_from_parameter(
prev_param, prev_param_value)
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.")
"the referenced 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
Expand All @@ -228,11 +205,64 @@ 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()
result.update(tmp)
break

return result

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, (DataObjectProperty, DtcDop)):
odxraise(f"The DOP of the parameter referenced by environment data descriptions "
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 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}')")

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 int(dop.compu_method.convert_physical_to_internal(
param_value)) # type: ignore[arg-type]

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 param.coded_value

if not isinstance(param.coded_value, int):
odxraise()

return param.coded_value

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 0

0 comments on commit ecc4d14

Please sign in to comment.