odxtools 9.4.1__py3-none-any.whl → 9.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import List, Union
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import override
7
+
8
+ from .exceptions import odxassert
9
+ from .matchingbasevariantparameter import MatchingBaseVariantParameter
10
+ from .matchingparameter import MatchingParameter
11
+ from .odxlink import OdxDocFragment
12
+ from .variantpattern import VariantPattern
13
+
14
+
15
+ @dataclass
16
+ class BaseVariantPattern(VariantPattern):
17
+ """Base variant patterns are variant patterns used to identify the
18
+ base variant of an ECU.
19
+ """
20
+ matching_base_variant_parameters: List[MatchingBaseVariantParameter]
21
+
22
+ @override
23
+ def get_matching_parameters(
24
+ self) -> Union[List[MatchingParameter], List[MatchingBaseVariantParameter]]:
25
+ return self.matching_base_variant_parameters
26
+
27
+ @staticmethod
28
+ def from_et(et_element: ElementTree.Element,
29
+ doc_frags: List[OdxDocFragment]) -> "BaseVariantPattern":
30
+
31
+ matching_base_variant_parameters = [
32
+ MatchingBaseVariantParameter.from_et(mbvp_el, doc_frags)
33
+ for mbvp_el in et_element.iterfind("MATCHING-BASE-VARIANT-PARAMETERS/"
34
+ "MATCHING-BASE-VARIANT-PARAMETER")
35
+ ]
36
+
37
+ odxassert(len(matching_base_variant_parameters) > 0) # required by ISO 22901-1 Figure 141
38
+ return BaseVariantPattern(matching_base_variant_parameters=matching_base_variant_parameters)
@@ -17,7 +17,7 @@ from .odxtypes import AtomicOdxType, ParameterValue
17
17
  from .physicaltype import PhysicalType
18
18
  from .snrefcontext import SnRefContext
19
19
  from .unit import Unit
20
- from .utils import dataclass_fields_asdict
20
+ from .utils import BytesTypes, dataclass_fields_asdict
21
21
 
22
22
 
23
23
  @dataclass
@@ -120,7 +120,7 @@ class DataObjectProperty(DopBase):
120
120
  f"The value {repr(physical_value)} of type {type(physical_value).__name__}"
121
121
  f" is not a valid.")
122
122
 
123
- if not isinstance(physical_value, (int, float, str, bytes, bytearray)):
123
+ if not isinstance(physical_value, (int, float, str, BytesTypes)):
124
124
  odxraise(f"Invalid type '{type(physical_value).__name__}' for physical value. "
125
125
  f"(Expect atomic type!)")
126
126
  internal_value = self.compu_method.convert_physical_to_internal(physical_value)
@@ -6,6 +6,7 @@ from xml.etree import ElementTree
6
6
 
7
7
  from typing_extensions import override
8
8
 
9
+ from ..basevariantpattern import BaseVariantPattern
9
10
  from ..diagvariable import DiagVariable
10
11
  from ..dyndefinedspec import DynDefinedSpec
11
12
  from ..exceptions import odxassert
@@ -38,6 +39,10 @@ class BaseVariant(HierarchyElement):
38
39
  def dyn_defined_spec(self) -> Optional[DynDefinedSpec]:
39
40
  return self.base_variant_raw.dyn_defined_spec
40
41
 
42
+ @property
43
+ def base_variant_pattern(self) -> Optional[BaseVariantPattern]:
44
+ return self.base_variant_raw.base_variant_pattern
45
+
41
46
  @property
42
47
  def parent_refs(self) -> List[ParentRef]:
43
48
  return self.base_variant_raw.parent_refs
@@ -3,6 +3,7 @@ from dataclasses import dataclass
3
3
  from typing import Any, Dict, List, Optional, Union
4
4
  from xml.etree import ElementTree
5
5
 
6
+ from ..basevariantpattern import BaseVariantPattern
6
7
  from ..diagvariable import DiagVariable
7
8
  from ..dyndefinedspec import DynDefinedSpec
8
9
  from ..exceptions import odxraise
@@ -23,7 +24,7 @@ class BaseVariantRaw(HierarchyElementRaw):
23
24
  diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]]
24
25
  variable_groups: NamedItemList[VariableGroup]
25
26
  dyn_defined_spec: Optional[DynDefinedSpec]
26
- # TODO: base_variant_pattern: Optional[BaseVariantPattern]
27
+ base_variant_pattern: Optional[BaseVariantPattern]
27
28
  parent_refs: List[ParentRef]
28
29
 
29
30
  @property
@@ -63,6 +64,10 @@ class BaseVariantRaw(HierarchyElementRaw):
63
64
  if (dds_elem := et_element.find("DYN-DEFINED-SPEC")) is not None:
64
65
  dyn_defined_spec = DynDefinedSpec.from_et(dds_elem, doc_frags)
65
66
 
67
+ base_variant_pattern = None
68
+ if (bvp_elem := et_element.find("BASE-VARIANT-PATTERN")) is not None:
69
+ base_variant_pattern = BaseVariantPattern.from_et(bvp_elem, doc_frags)
70
+
66
71
  parent_refs = [
67
72
  ParentRef.from_et(pr_elem, doc_frags)
68
73
  for pr_elem in et_element.iterfind("PARENT-REFS/PARENT-REF")
@@ -72,6 +77,7 @@ class BaseVariantRaw(HierarchyElementRaw):
72
77
  diag_variables_raw=diag_variables_raw,
73
78
  variable_groups=variable_groups,
74
79
  dyn_defined_spec=dyn_defined_spec,
80
+ base_variant_pattern=base_variant_pattern,
75
81
  parent_refs=parent_refs,
76
82
  **kwargs)
77
83
 
@@ -14,6 +14,7 @@ from ..nameditemlist import NamedItemList
14
14
  from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
15
15
  from ..parentref import ParentRef
16
16
  from ..variablegroup import HasVariableGroups, VariableGroup
17
+ from .basevariant import BaseVariant
17
18
  from .diaglayer import DiagLayer
18
19
  from .ecuvariantraw import EcuVariantRaw
19
20
  from .hierarchyelement import HierarchyElement
@@ -38,6 +39,21 @@ class EcuVariant(HierarchyElement):
38
39
  def parent_refs(self) -> List[ParentRef]:
39
40
  return self.ecu_variant_raw.parent_refs
40
41
 
42
+ @property
43
+ def base_variant(self) -> Optional[BaseVariant]:
44
+ """Return the base variant for the ECU variant
45
+
46
+ The ODX specification allows at a single base variant for each
47
+ ECU variant, cf checker rule 50 of appendix B.2 of the
48
+ specification document.
49
+
50
+ """
51
+ for pr in self.ecu_variant_raw.parent_refs:
52
+ if isinstance(pr.layer, BaseVariant):
53
+ return pr.layer
54
+
55
+ return None
56
+
41
57
  @property
42
58
  def ecu_variant_patterns(self) -> List[EcuVariantPattern]:
43
59
  return self.ecu_variant_raw.ecu_variant_patterns
@@ -1,27 +1,38 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List
3
+ from typing import List, Union
4
4
  from xml.etree import ElementTree
5
5
 
6
- from .exceptions import odxassert, odxrequire
6
+ from typing_extensions import override
7
+
8
+ from .exceptions import odxassert
9
+ from .matchingbasevariantparameter import MatchingBaseVariantParameter
7
10
  from .matchingparameter import MatchingParameter
8
11
  from .odxlink import OdxDocFragment
12
+ from .variantpattern import VariantPattern
9
13
 
10
14
 
11
15
  @dataclass
12
- class EcuVariantPattern:
16
+ class EcuVariantPattern(VariantPattern):
17
+ """ECU variant patterns are variant patterns used to identify the
18
+ concrete variant of an ECU.
19
+ """
13
20
  matching_parameters: List[MatchingParameter]
14
21
 
22
+ @override
23
+ def get_matching_parameters(
24
+ self) -> Union[List[MatchingParameter], List[MatchingBaseVariantParameter]]:
25
+ return self.matching_parameters
26
+
15
27
  @staticmethod
16
28
  def from_et(et_element: ElementTree.Element,
17
29
  doc_frags: List[OdxDocFragment]) -> "EcuVariantPattern":
18
30
 
19
- mp_collection_el = odxrequire(et_element.find("MATCHING-PARAMETERS"))
20
-
21
- matching_params = [
31
+ matching_parameters = [
22
32
  MatchingParameter.from_et(mp_el, doc_frags)
23
- for mp_el in mp_collection_el.iterfind("MATCHING-PARAMETER")
33
+ for mp_el in et_element.iterfind("MATCHING-PARAMETERS/"
34
+ "MATCHING-PARAMETER")
24
35
  ]
25
36
 
26
- odxassert(len(matching_params) > 0) # required by ISO 22901-1 Figure 141
27
- return EcuVariantPattern(matching_params)
37
+ odxassert(len(matching_parameters) > 0) # required by ISO 22901-1 Figure 141
38
+ return EcuVariantPattern(matching_parameters=matching_parameters)
odxtools/encodestate.py CHANGED
@@ -1,11 +1,12 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import warnings
3
3
  from dataclasses import dataclass, field
4
- from typing import TYPE_CHECKING, Dict, List, Optional, SupportsBytes, Tuple
4
+ from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
5
5
 
6
6
  from .encoding import Encoding, get_string_encoding
7
7
  from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
8
8
  from .odxtypes import AtomicOdxType, DataType, ParameterValue
9
+ from .utils import BytesTypes
9
10
 
10
11
  try:
11
12
  import bitstruct.c as bitstruct
@@ -97,7 +98,7 @@ class EncodeState:
97
98
 
98
99
  # Deal with raw byte fields, ...
99
100
  if base_data_type == DataType.A_BYTEFIELD:
100
- if not isinstance(internal_value, (bytes, bytearray, SupportsBytes)):
101
+ if not isinstance(internal_value, BytesTypes):
101
102
  odxraise(f"{internal_value!r} is not a bytefield", EncodeError)
102
103
  return
103
104
 
@@ -0,0 +1,38 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import List, Optional
4
+ from xml.etree import ElementTree
5
+
6
+ from .matchingparameter import MatchingParameter
7
+ from .odxlink import OdxDocFragment
8
+ from .odxtypes import odxstr_to_bool
9
+ from .utils import dataclass_fields_asdict
10
+
11
+
12
+ @dataclass
13
+ class MatchingBaseVariantParameter(MatchingParameter):
14
+ """A description of a parameter used for base variant matching.
15
+
16
+ This is very similar to `MatchingParameter` for ECU variant
17
+ matching, but `MatchingBaseVariantParameter` features the
18
+ additional subtag `USE-PHYSICAL-ADDRESSING`.
19
+ """
20
+
21
+ use_physical_addressing_raw: Optional[bool]
22
+
23
+ @property
24
+ def use_physical_addressing(self) -> bool:
25
+ return self.use_physical_addressing_raw in [None, True]
26
+
27
+ @staticmethod
28
+ def from_et(et_element: ElementTree.Element,
29
+ doc_frags: List[OdxDocFragment]) -> "MatchingBaseVariantParameter":
30
+
31
+ kwargs = dataclass_fields_asdict(MatchingParameter.from_et(et_element, doc_frags))
32
+
33
+ use_physical_addressing_raw = odxstr_to_bool(et_element.findtext("USE-PHYSICAL-ADDRESSING"))
34
+
35
+ return MatchingBaseVariantParameter(
36
+ use_physical_addressing_raw=use_physical_addressing_raw,
37
+ **kwargs,
38
+ )
@@ -1,58 +1,139 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List
3
+ from typing import Any, Dict, List, Optional, cast
4
4
  from xml.etree import ElementTree
5
5
 
6
- from .exceptions import odxassert, odxraise, odxrequire
7
- from .odxlink import OdxDocFragment
8
- from .utils import is_short_name, is_short_name_path
6
+ from .diaglayers.diaglayer import DiagLayer
7
+ from .diagnostictroublecode import DiagnosticTroubleCode
8
+ from .diagservice import DiagService
9
+ from .exceptions import odxraise, odxrequire
10
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
11
+ from .odxtypes import ParameterValue, ParameterValueDict
12
+ from .snrefcontext import SnRefContext
13
+ from .utils import BytesTypes
9
14
 
10
15
 
11
16
  @dataclass
12
17
  class MatchingParameter:
13
- """According to ISO 22901, a MatchingParameter contains a string value identifying
14
- the active ECU variant. Moreover, it references a DIAG-COMM via snref and one of its
15
- positive response's OUT-PARAM-IF via snref or snpathref.
18
+ """According to ISO 22901, a MatchingParameter contains a string
19
+ value identifying the active ECU or base variant. Moreover, it
20
+ references a DIAG-COMM via snref and one of its positive
21
+ response's OUT-PARAM-IF via snref or snpathref.
22
+
23
+ Unlike other parameters defined in the `parameters` package, a
24
+ MatchingParameter is not transferred over the network.
16
25
 
17
- Unlike other parameters defined in the `parameters` package, a MatchingParameter is
18
- not transferred over the network.
19
26
  """
20
27
 
21
- # datatype according to ISO 22901-1 Figure 141
22
28
  expected_value: str
23
29
  diag_comm_snref: str
24
- out_param_if: str
25
30
 
26
- def __post_init__(self) -> None:
27
- odxassert(is_short_name(self.diag_comm_snref))
28
- odxassert(is_short_name_path(self.out_param_if))
31
+ # be aware that the checker rules 23 and 110 contradict each other
32
+ # here: the first specifies that the referenced parameter must be
33
+ # present in *all* positive responses of the referenced service,
34
+ # whilst the latter mandates it to be present least one positive
35
+ # or negative response. What it probably actually wants to say is
36
+ # that any response that can possibly be received shall exhibit
37
+ # the referenced parameter.
38
+ out_param_if_snref: Optional[str]
39
+ out_param_if_snpathref: Optional[str]
29
40
 
30
41
  @staticmethod
31
42
  def from_et(et_element: ElementTree.Element,
32
43
  doc_frags: List[OdxDocFragment]) -> "MatchingParameter":
33
44
 
34
45
  expected_value = odxrequire(et_element.findtext("EXPECTED-VALUE"))
35
- diag_comm_snref_el = odxrequire(et_element.find("DIAG-COMM-SNREF"))
36
- diag_comm_snref = odxrequire(diag_comm_snref_el.get("SHORT-NAME"))
37
- out_param_snref_el = et_element.find("OUT-PARAM-IF-SNREF")
38
- out_param_snpathref_el = et_element.find("OUT-PARAM-IF-SNPATHREF")
39
- out_param_if = None
40
- if out_param_snref_el is not None:
41
- out_param_if = out_param_snref_el.get("SHORT-NAME")
42
- elif out_param_snpathref_el is not None:
43
- out_param_if = out_param_snpathref_el.get("SHORT-NAME-PATH")
44
- if out_param_if is None:
46
+ diag_comm_snref = odxrequire(
47
+ odxrequire(et_element.find("DIAG-COMM-SNREF")).get("SHORT-NAME"))
48
+
49
+ out_param_if_snref = None
50
+ out_param_if_snpathref = None
51
+ if (out_param_snref_el := et_element.find("OUT-PARAM-IF-SNREF")) is not None:
52
+ out_param_if_snref = odxrequire(out_param_snref_el.get("SHORT-NAME"))
53
+ elif (out_param_snpathref_el := et_element.find("OUT-PARAM-IF-SNPATHREF")) is not None:
54
+ out_param_if_snpathref = odxrequire(out_param_snpathref_el.get("SHORT-NAME-PATH"))
55
+ else:
45
56
  odxraise("Output parameter must not left unspecified")
46
57
 
47
58
  return MatchingParameter(
48
59
  expected_value=expected_value,
49
60
  diag_comm_snref=diag_comm_snref,
50
- out_param_if=out_param_if,
61
+ out_param_if_snref=out_param_if_snref,
62
+ out_param_if_snpathref=out_param_if_snpathref,
51
63
  )
52
64
 
53
- def is_match(self, ident_value: str) -> bool:
65
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
66
+ return {}
67
+
68
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
69
+ pass
70
+
71
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
72
+ # note that we do not resolve the SNREF to the diag layer here
73
+ # because it is a component which changes depending on the
74
+ # execution context and for variant patterns the execution
75
+ # context is highly volatile w.r.t. the diag layer in question
76
+ pass
77
+
78
+ def get_ident_service(self, diag_layer: DiagLayer) -> DiagService:
79
+ return resolve_snref(self.diag_comm_snref, diag_layer.services, DiagService)
80
+
81
+ def matches(self, param_dict: ParameterValueDict) -> bool:
54
82
  """
55
83
  Returns true iff the provided identification value matches this MatchingParameter's
56
84
  expected value.
57
85
  """
58
- return self.expected_value == ident_value
86
+
87
+ if self.out_param_if_snref is not None:
88
+ snpath_chunks = [self.out_param_if_snref]
89
+ elif self.out_param_if_snpathref is not None:
90
+ snpath_chunks = self.out_param_if_snpathref.split(".")
91
+ else:
92
+ odxraise("no out_param_if specified")
93
+ return False
94
+
95
+ return self.__matches(param_dict, snpath_chunks)
96
+
97
+ def __matches(self, param_dict: ParameterValue, snpath_chunks: List[str]) -> bool:
98
+ if len(snpath_chunks) == 0:
99
+ parameter_value = param_dict
100
+ if isinstance(parameter_value, dict):
101
+ odxraise(f"Parameter must not be a structure")
102
+ return False
103
+
104
+ if isinstance(parameter_value, float):
105
+ # allow a slight tolerance if the expected value is
106
+ # floating point
107
+ return abs(float(self.expected_value) - parameter_value) < 1e-8
108
+ elif isinstance(parameter_value, BytesTypes):
109
+ return parameter_value.hex().upper() == self.expected_value.upper()
110
+ elif isinstance(parameter_value, DiagnosticTroubleCode):
111
+ # TODO: what happens if non-numerical DTCs like
112
+ # "U123456" are specified? Is specifying DTCs even
113
+ # allowed in variant patterns?
114
+ return hex(parameter_value.trouble_code).upper() == self.expected_value.upper()
115
+ else:
116
+ return self.expected_value == str(parameter_value)
117
+
118
+ if not isinstance(param_dict, dict):
119
+ odxraise(f"Parameter {snpath_chunks[0]} must be a structure")
120
+ return False
121
+
122
+ sub_value = param_dict.get(snpath_chunks[0])
123
+ if sub_value is None:
124
+ return False
125
+
126
+ if isinstance(sub_value, tuple) and len(sub_value) == 2:
127
+ # table struct parameter
128
+ sub_value = sub_value[1]
129
+
130
+ if isinstance(sub_value, list):
131
+ # the spec mandates that if any item of a field matches,
132
+ # the parameter as a whole matches
133
+ for x in sub_value:
134
+ if self.__matches(x, snpath_chunks[1:]):
135
+ return True
136
+
137
+ return False
138
+
139
+ return self.__matches(cast(ParameterValue, sub_value), snpath_chunks[1:])
@@ -10,10 +10,10 @@ from .decodestate import DecodeState
10
10
  from .diagcodedtype import DctType, DiagCodedType
11
11
  from .encodestate import EncodeState
12
12
  from .encoding import get_string_encoding
13
- from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
13
+ from .exceptions import (DecodeError, EncodeError, odxassert, odxraise, odxrequire)
14
14
  from .odxlink import OdxDocFragment
15
15
  from .odxtypes import AtomicOdxType, DataType
16
- from .utils import dataclass_fields_asdict
16
+ from .utils import BytesTypes, dataclass_fields_asdict
17
17
 
18
18
 
19
19
  class Termination(Enum):
@@ -83,7 +83,7 @@ class MinMaxLengthType(DiagCodedType):
83
83
  @override
84
84
  def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
85
85
 
86
- if not isinstance(internal_value, (bytes, str, bytearray)):
86
+ if not isinstance(internal_value, (str, BytesTypes)):
87
87
  odxraise("MinMaxLengthType is currently only implemented for strings and byte arrays",
88
88
  EncodeError)
89
89
 
odxtools/odxtypes.py CHANGED
@@ -5,6 +5,7 @@ from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Optional, Tupl
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .exceptions import odxassert, odxraise, odxrequire
8
+ from .utils import BytesTypes
8
9
 
9
10
  if TYPE_CHECKING:
10
11
  from odxtools.diagnostictroublecode import DiagnosticTroubleCode
@@ -135,8 +136,8 @@ def compare_odx_values(a: AtomicOdxType, b: AtomicOdxType) -> int:
135
136
 
136
137
  # bytefields are treated like long integers: to pad the shorter
137
138
  # object with zeros and treat the results like strings.
138
- if isinstance(a, (bytes, bytearray)):
139
- if not isinstance(b, (bytes, bytearray)):
139
+ if isinstance(a, BytesTypes):
140
+ if not isinstance(b, BytesTypes):
140
141
  odxraise()
141
142
 
142
143
  obj_len = max(len(a), len(b))
@@ -237,7 +238,7 @@ class DataType(Enum):
237
238
  return True
238
239
  elif expected_type is float and isinstance(value, (int, float)):
239
240
  return True
240
- elif self == DataType.A_BYTEFIELD and isinstance(value, (bytearray, bytes)):
241
+ elif self == DataType.A_BYTEFIELD and isinstance(value, BytesTypes):
241
242
  return True
242
243
  else:
243
244
  return False
@@ -3,7 +3,7 @@ from dataclasses import dataclass
3
3
  from typing import List, Literal, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
- from typing_extensions import SupportsBytes, override
6
+ from typing_extensions import override
7
7
 
8
8
  from .decodestate import DecodeState
9
9
  from .diagcodedtype import DctType, DiagCodedType
@@ -11,7 +11,7 @@ from .encodestate import EncodeState
11
11
  from .exceptions import odxassert, odxraise, odxrequire
12
12
  from .odxlink import OdxDocFragment
13
13
  from .odxtypes import AtomicOdxType, DataType, odxstr_to_bool
14
- from .utils import dataclass_fields_asdict
14
+ from .utils import BytesTypes, dataclass_fields_asdict
15
15
 
16
16
 
17
17
  @dataclass
@@ -91,7 +91,7 @@ class StandardLengthType(DiagCodedType):
91
91
  return used_mask.to_bytes((bit_sz + 7) // 8, endianness)
92
92
 
93
93
  sz: int
94
- if isinstance(internal_value, (bytearray, SupportsBytes)):
94
+ if isinstance(internal_value, BytesTypes):
95
95
  sz = len(bytes(internal_value))
96
96
  else:
97
97
  sz = (odxrequire(self.get_static_bit_length()) + 7) // 8
@@ -107,7 +107,7 @@ class StandardLengthType(DiagCodedType):
107
107
 
108
108
  if self.is_condensed:
109
109
  int_value: int
110
- if isinstance(internal_value, (bytearray, SupportsBytes)):
110
+ if isinstance(internal_value, BytesTypes):
111
111
  int_value = int.from_bytes(internal_value, 'big')
112
112
  elif isinstance(internal_value, int):
113
113
  int_value = internal_value
@@ -126,14 +126,14 @@ class StandardLengthType(DiagCodedType):
126
126
 
127
127
  mask_bit += 1
128
128
 
129
- if isinstance(internal_value, (bytearray, SupportsBytes)):
129
+ if isinstance(internal_value, BytesTypes):
130
130
  return result.to_bytes(len(internal_value), 'big')
131
131
 
132
132
  return result
133
133
 
134
134
  if isinstance(internal_value, int):
135
135
  return internal_value & self.bit_mask
136
- if isinstance(internal_value, (bytearray, SupportsBytes)):
136
+ if isinstance(internal_value, BytesTypes):
137
137
  int_value = int.from_bytes(internal_value, 'big')
138
138
  int_value &= self.bit_mask
139
139
  return int_value.to_bytes(len(bytes(internal_value)), 'big')
@@ -146,7 +146,7 @@ class StandardLengthType(DiagCodedType):
146
146
  return raw_value
147
147
  if self.is_condensed:
148
148
  int_value: int
149
- if isinstance(raw_value, (bytearray, SupportsBytes)):
149
+ if isinstance(raw_value, BytesTypes):
150
150
  int_value = int.from_bytes(raw_value, 'big')
151
151
  elif isinstance(raw_value, int):
152
152
  int_value = raw_value
@@ -164,16 +164,16 @@ class StandardLengthType(DiagCodedType):
164
164
 
165
165
  mask_bit += 1
166
166
 
167
- if isinstance(raw_value, (bytearray, SupportsBytes)):
167
+ if isinstance(raw_value, BytesTypes):
168
168
  return result.to_bytes(len(raw_value), 'big')
169
169
 
170
170
  return result
171
171
  if isinstance(raw_value, int):
172
172
  return raw_value & self.bit_mask
173
- if isinstance(raw_value, (bytearray, SupportsBytes)):
173
+ if isinstance(raw_value, BytesTypes):
174
174
  int_value = int.from_bytes(raw_value, 'big')
175
175
  int_value &= self.bit_mask
176
- return int_value
176
+ return int_value.to_bytes(len(raw_value), 'big')
177
177
 
178
178
  odxraise(f'Can not apply a bit_mask on a value of type {type(raw_value)}')
179
179
  return raw_value
odxtools/subcomponent.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional
3
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .diagnostictroublecode import DiagnosticTroubleCode
@@ -10,7 +10,6 @@ from .element import IdentifiableElement, NamedElement
10
10
  from .environmentdata import EnvironmentData
11
11
  from .environmentdatadescription import EnvironmentDataDescription
12
12
  from .exceptions import odxraise, odxrequire
13
- from .matchingparameter import MatchingParameter
14
13
  from .nameditemlist import NamedItemList
15
14
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
16
15
  from .parameters.parameter import Parameter
@@ -19,14 +18,18 @@ from .table import Table
19
18
  from .tablerow import TableRow
20
19
  from .utils import dataclass_fields_asdict
21
20
 
21
+ if TYPE_CHECKING:
22
+ from .matchingparameter import MatchingParameter
23
+
22
24
 
23
25
  @dataclass
24
26
  class SubComponentPattern:
25
- matching_parameters: List[MatchingParameter]
27
+ matching_parameters: List["MatchingParameter"]
26
28
 
27
29
  @staticmethod
28
30
  def from_et(et_element: ElementTree.Element,
29
31
  doc_frags: List[OdxDocFragment]) -> "SubComponentPattern":
32
+ from .matchingparameter import MatchingParameter
30
33
 
31
34
  matching_parameters = [
32
35
  MatchingParameter.from_et(el, doc_frags)
@@ -6,6 +6,7 @@
6
6
  {%- import('macros/printHierarchyElement.xml.jinja2') as phe %}
7
7
  {%- import('macros/printComparamRef.xml.jinja2') as pcom %}
8
8
  {%- import('macros/printParentRef.xml.jinja2') as pparref %}
9
+ {%- import('macros/printBaseVariantPattern.xml.jinja2') as pbvp %}
9
10
 
10
11
  {%- macro printBaseVariant(base_variant) -%}
11
12
  <BASE-VARIANT {{- phe.printHierarchyElementAttribs(base_variant) -}}>
@@ -29,18 +30,12 @@
29
30
  {%- endif %}
30
31
 
31
32
  {%- if dlr.dyn_defined_spec %}
32
- {{ pdynspec.printPrintDefinedSpec(dlr.dyn_defined_spec)|indent(4) }}
33
+ {{ pdynspec.printPrintDefinedSpec(dlr.dyn_defined_spec)|indent(2) }}
33
34
  {%- endif %}
34
35
 
35
- {#
36
- {%- if dlr.base_variant_patterns %}
37
- <BASE-VARIANT-PATTERNS>
38
- {%- for vp in dlr.base_variant_patterns -%}
39
- {{ pvpat.printBaseVariantPattern(vp)|indent(4) }}
40
- {%- endfor -%}
41
- </BASE-VARIANT-PATTERNS>
36
+ {%- if dlr.base_variant_pattern is not none %}
37
+ {{ pbvp.printBaseVariantPattern(dlr.base_variant_pattern)|indent(2) }}
42
38
  {%- endif %}
43
- #}
44
39
 
45
40
  {%- if dlr.parent_refs %}
46
41
  <PARENT-REFS>
@@ -0,0 +1,32 @@
1
+ {#- -*- mode: sgml; tab-width: 1; indent-tabs-mode: nil -*-
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ -#}
5
+
6
+ {%- macro printMatchingBaseVariantParameter(mbvp) -%}
7
+ <MATCHING-BASE-VARIANT-PARAMETER>
8
+ <EXPECTED-VALUE>{{mbvp.expected_value | e}}</EXPECTED-VALUE>
9
+ {%- if mbvp.use_physical_addressing_raw is true %}
10
+ <USE-PHYSICAL-ADDRESSING>true</USE-PHYSICAL-ADDRESSING>
11
+ {%- elif mbvp.use_physical_addressing_raw is false %}
12
+ <USE-PHYSICAL-ADDRESSING>false</USE-PHYSICAL-ADDRESSING>
13
+ {%- endif %}
14
+ <DIAG-COMM-SNREF SHORT-NAME="{{mbvp.diag_comm_snref}}" />
15
+ {%- if mbvp.out_param_if_snref is not none %}
16
+ <OUT-PARAM-IF-SNREF SHORT-NAME="{{mbvp.out_param_if_snref}}" />
17
+ {%- endif %}
18
+ {%- if mbvp.out_param_if_snpathref is not none %}
19
+ <OUT-PARAM-IF-SNPATHREF SHORT-NAME-PATH="{{mbvp.out_param_if_snpathref}}" />
20
+ {%- endif %}
21
+ </MATCHING-BASE-VARIANT-PARAMETER>
22
+ {%- endmacro -%}
23
+
24
+ {%- macro printBaseVariantPattern(vp) -%}
25
+ <BASE-VARIANT-PATTERN>
26
+ <MATCHING-BASE-VARIANT-PARAMETERS>
27
+ {%- for mbvp in vp.matching_base_variant_parameters %}
28
+ {{ printMatchingBaseVariantParameter(mbvp) | indent(2) }}
29
+ {%- endfor %}
30
+ </MATCHING-BASE-VARIANT-PARAMETERS>
31
+ </BASE-VARIANT-PATTERN>
32
+ {%- endmacro -%}