odxtools 5.3.0__py3-none-any.whl → 5.3.1__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.
Files changed (44) hide show
  1. odxtools/determinenumberofitems.py +18 -0
  2. odxtools/diagdatadictionaryspec.py +14 -4
  3. odxtools/diaglayer.py +54 -59
  4. odxtools/dynamiclengthfield.py +59 -0
  5. odxtools/element.py +2 -4
  6. odxtools/endofpdufield.py +6 -77
  7. odxtools/field.py +94 -0
  8. odxtools/multiplexer.py +6 -18
  9. odxtools/multiplexerswitchkey.py +8 -43
  10. odxtools/odxtypes.py +1 -1
  11. odxtools/parameters/lengthkeyparameter.py +1 -1
  12. odxtools/parameters/parameterwithdop.py +6 -1
  13. odxtools/positioneddataobjectproperty.py +74 -0
  14. odxtools/tablerow.py +1 -2
  15. odxtools/templates/macros/printAudience.xml.jinja2 +3 -9
  16. odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -27
  17. odxtools/templates/macros/printComparam.xml.jinja2 +4 -18
  18. odxtools/templates/macros/printDOP.xml.jinja2 +3 -9
  19. odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +22 -0
  20. odxtools/templates/macros/printElementID.xml.jinja2 +6 -6
  21. odxtools/templates/macros/printEndOfPdu.xml.jinja2 +3 -2
  22. odxtools/templates/macros/printEnvData.xml.jinja2 +2 -2
  23. odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +3 -2
  24. odxtools/templates/macros/printFunctionalClass.xml.jinja2 +3 -9
  25. odxtools/templates/macros/printMux.xml.jinja2 +5 -6
  26. odxtools/templates/macros/printParam.xml.jinja2 +2 -7
  27. odxtools/templates/macros/printRequest.xml.jinja2 +2 -9
  28. odxtools/templates/macros/printResponse.xml.jinja2 +2 -9
  29. odxtools/templates/macros/printService.xml.jinja2 +2 -9
  30. odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
  31. odxtools/templates/macros/printState.xml.jinja2 +3 -9
  32. odxtools/templates/macros/printStateChart.xml.jinja2 +2 -9
  33. odxtools/templates/macros/printStateTransition.xml.jinja2 +3 -9
  34. odxtools/templates/macros/printStructure.xml.jinja2 +2 -4
  35. odxtools/templates/macros/printTable.xml.jinja2 +2 -7
  36. odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -3
  37. odxtools/templates/macros/printVariant.xml.jinja2 +10 -9
  38. odxtools/version.py +4 -2
  39. {odxtools-5.3.0.dist-info → odxtools-5.3.1.dist-info}/METADATA +1 -1
  40. {odxtools-5.3.0.dist-info → odxtools-5.3.1.dist-info}/RECORD +44 -39
  41. {odxtools-5.3.0.dist-info → odxtools-5.3.1.dist-info}/LICENSE +0 -0
  42. {odxtools-5.3.0.dist-info → odxtools-5.3.1.dist-info}/WHEEL +0 -0
  43. {odxtools-5.3.0.dist-info → odxtools-5.3.1.dist-info}/entry_points.txt +0 -0
  44. {odxtools-5.3.0.dist-info → odxtools-5.3.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,18 @@
1
+ from dataclasses import dataclass
2
+ from typing import List
3
+ from xml.etree import ElementTree
4
+
5
+ from .odxlink import OdxDocFragment
6
+ from .positioneddataobjectproperty import PositionedDataObjectProperty
7
+ from .utils import dataclass_fields_asdict
8
+
9
+
10
+ @dataclass
11
+ class DetermineNumberOfItems(PositionedDataObjectProperty):
12
+
13
+ @staticmethod
14
+ def from_et(et_element: ElementTree.Element,
15
+ doc_frags: List[OdxDocFragment]) -> "DetermineNumberOfItems":
16
+ kwargs = dataclass_fields_asdict(
17
+ PositionedDataObjectProperty.from_et(et_element, doc_frags))
18
+ return DetermineNumberOfItems(**kwargs)
@@ -9,6 +9,7 @@ from .createanystructure import create_any_structure_from_et
9
9
  from .createsdgs import create_sdgs_from_et
10
10
  from .dataobjectproperty import DataObjectProperty
11
11
  from .dtcdop import DtcDop
12
+ from .dynamiclengthfield import DynamicLengthField
12
13
  from .endofpdufield import EndOfPduField
13
14
  from .environmentdata import EnvironmentData
14
15
  from .environmentdatadescription import EnvironmentDataDescription
@@ -31,6 +32,7 @@ class DiagDataDictionarySpec:
31
32
  data_object_props: NamedItemList[DataObjectProperty]
32
33
  structures: NamedItemList[BasicStructure]
33
34
  end_of_pdu_fields: NamedItemList[EndOfPduField]
35
+ dynamic_length_fields: NamedItemList[DynamicLengthField]
34
36
  tables: NamedItemList[Table]
35
37
  env_data_descs: NamedItemList[EnvironmentDataDescription]
36
38
  env_datas: NamedItemList[EnvironmentData]
@@ -44,6 +46,7 @@ class DiagDataDictionarySpec:
44
46
  self.data_object_props,
45
47
  self.structures,
46
48
  self.end_of_pdu_fields,
49
+ self.dynamic_length_fields,
47
50
  self.dtc_dops,
48
51
  self.tables,
49
52
  ),)
@@ -67,6 +70,11 @@ class DiagDataDictionarySpec:
67
70
  for eofp_element in et_element.iterfind("END-OF-PDU-FIELDS/END-OF-PDU-FIELD")
68
71
  ]
69
72
 
73
+ dynamic_length_fields = [
74
+ DynamicLengthField.from_et(dl_element, doc_frags)
75
+ for dl_element in et_element.iterfind("DYNAMIC-LENGTH-FIELDS/DYNAMIC-LENGTH-FIELD")
76
+ ]
77
+
70
78
  dtc_dops = []
71
79
  for dtc_dop_elem in et_element.iterfind("DTC-DOPS/DTC-DOP"):
72
80
  dtc_dop = DtcDop.from_et(dtc_dop_elem, doc_frags)
@@ -123,6 +131,7 @@ class DiagDataDictionarySpec:
123
131
  data_object_props=NamedItemList(data_object_props),
124
132
  structures=NamedItemList(structures),
125
133
  end_of_pdu_fields=NamedItemList(end_of_pdu_fields),
134
+ dynamic_length_fields=NamedItemList(dynamic_length_fields),
126
135
  dtc_dops=NamedItemList(dtc_dops),
127
136
  unit_spec=unit_spec,
128
137
  tables=NamedItemList(tables),
@@ -145,6 +154,7 @@ class DiagDataDictionarySpec:
145
154
  self.sdgs,
146
155
  self.structures,
147
156
  self.end_of_pdu_fields,
157
+ self.dynamic_length_fields,
148
158
  self.tables,
149
159
  ):
150
160
  odxlinks.update(obj._build_odxlinks())
@@ -157,8 +167,8 @@ class DiagDataDictionarySpec:
157
167
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
158
168
 
159
169
  for obj in chain(self.data_object_props, self.dtc_dops, self.end_of_pdu_fields,
160
- self.env_data_descs, self.env_datas, self.muxs, self.sdgs, self.structures,
161
- self.tables):
170
+ self.dynamic_length_fields, self.env_data_descs, self.env_datas, self.muxs,
171
+ self.sdgs, self.structures, self.tables):
162
172
  obj._resolve_odxlinks(odxlinks)
163
173
 
164
174
  if self.unit_spec is not None:
@@ -166,8 +176,8 @@ class DiagDataDictionarySpec:
166
176
 
167
177
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
168
178
  for obj in chain(self.data_object_props, self.dtc_dops, self.end_of_pdu_fields,
169
- self.env_data_descs, self.env_datas, self.muxs, self.sdgs, self.structures,
170
- self.tables):
179
+ self.dynamic_length_fields, self.env_data_descs, self.env_datas, self.muxs,
180
+ self.sdgs, self.structures, self.tables):
171
181
  obj._resolve_snrefs(diag_layer)
172
182
 
173
183
  if self.unit_spec is not None:
odxtools/diaglayer.py CHANGED
@@ -10,24 +10,17 @@ from deprecation import deprecated
10
10
 
11
11
  from .additionalaudience import AdditionalAudience
12
12
  from .admindata import AdminData
13
- from .basicstructure import BasicStructure
14
13
  from .communicationparameterref import CommunicationParameterRef
15
14
  from .companydata import CompanyData
16
- from .dataobjectproperty import DataObjectProperty
17
15
  from .diagdatadictionaryspec import DiagDataDictionarySpec
18
16
  from .diaglayerraw import DiagLayerRaw
19
17
  from .diaglayertype import DiagLayerType
20
18
  from .diagservice import DiagService
21
- from .dtcdop import DtcDop
22
19
  from .ecuvariantpattern import EcuVariantPattern
23
- from .endofpdufield import EndOfPduField
24
- from .environmentdata import EnvironmentData
25
- from .environmentdatadescription import EnvironmentDataDescription
26
20
  from .exceptions import DecodeError, OdxWarning, odxassert
27
21
  from .functionalclass import FunctionalClass
28
22
  from .message import Message
29
- from .multiplexer import Multiplexer
30
- from .nameditemlist import NamedItemList
23
+ from .nameditemlist import NamedItemList, OdxNamed
31
24
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
32
25
  from .parentref import ParentRef
33
26
  from .request import Request
@@ -40,6 +33,7 @@ from .unitgroup import UnitGroup
40
33
  from .unitspec import UnitSpec
41
34
 
42
35
  T = TypeVar("T")
36
+ TNamed = TypeVar("TNamed", bound=OdxNamed)
43
37
 
44
38
  PrefixTree = Dict[int, Union[List[DiagService], "PrefixTree"]]
45
39
 
@@ -154,47 +148,58 @@ class DiagLayer:
154
148
  sdgs=[])
155
149
  ############
156
150
 
157
- dops = NamedItemList(self._compute_available_data_object_props())
158
- tables = NamedItemList(self._compute_available_tables())
159
- dtc_dops: NamedItemList[DtcDop]
160
- structures: NamedItemList[BasicStructure]
161
- end_of_pdu_fields: NamedItemList[EndOfPduField]
162
- env_data_descs: NamedItemList[EnvironmentDataDescription]
163
- env_datas: NamedItemList[EnvironmentData]
164
- muxs: NamedItemList[Multiplexer]
151
+ dops = self._compute_available_ddd_spec_items(
152
+ lambda ddd_spec: ddd_spec.data_object_props,
153
+ lambda parent_ref: parent_ref.not_inherited_dops,
154
+ )
155
+ structures = self._compute_available_ddd_spec_items(
156
+ lambda ddd_spec: ddd_spec.structures,
157
+ lambda parent_ref: parent_ref.not_inherited_dops,
158
+ )
159
+ dtc_dops = self._compute_available_ddd_spec_items(
160
+ lambda ddd_spec: ddd_spec.dtc_dops,
161
+ lambda parent_ref: parent_ref.not_inherited_dops,
162
+ )
163
+ end_of_pdu_fields = self._compute_available_ddd_spec_items(
164
+ lambda ddd_spec: ddd_spec.end_of_pdu_fields,
165
+ lambda parent_ref: parent_ref.not_inherited_dops,
166
+ )
167
+ dynamic_length_fields = self._compute_available_ddd_spec_items(
168
+ lambda ddd_spec: ddd_spec.dynamic_length_fields,
169
+ lambda parent_ref: parent_ref.not_inherited_dops,
170
+ )
171
+ env_data_descs = self._compute_available_ddd_spec_items(
172
+ lambda ddd_spec: ddd_spec.env_data_descs,
173
+ lambda parent_ref: parent_ref.not_inherited_dops,
174
+ )
175
+ env_datas = self._compute_available_ddd_spec_items(
176
+ lambda ddd_spec: ddd_spec.env_datas, lambda parent_ref: parent_ref.not_inherited_dops)
177
+ muxs = self._compute_available_ddd_spec_items(
178
+ lambda ddd_spec: ddd_spec.muxs, lambda parent_ref: parent_ref.not_inherited_dops)
179
+ tables = self._compute_available_ddd_spec_items(
180
+ lambda ddd_spec: ddd_spec.tables, lambda parent_ref: parent_ref.not_inherited_tables)
165
181
  ddds_sdgs: List[SpecialDataGroup]
166
182
  if self.diag_layer_raw.diag_data_dictionary_spec:
167
- dtc_dops = self.diag_layer_raw.diag_data_dictionary_spec.dtc_dops
168
- structures = self.diag_layer_raw.diag_data_dictionary_spec.structures
169
- end_of_pdu_fields = self.diag_layer_raw.diag_data_dictionary_spec.end_of_pdu_fields
170
- env_data_descs = self.diag_layer_raw.diag_data_dictionary_spec.env_data_descs
171
- env_datas = self.diag_layer_raw.diag_data_dictionary_spec.env_datas
172
- muxs = self.diag_layer_raw.diag_data_dictionary_spec.muxs
173
183
  ddds_sdgs = self.diag_layer_raw.diag_data_dictionary_spec.sdgs
174
184
  else:
175
- dtc_dops = NamedItemList()
176
- structures = NamedItemList()
177
- end_of_pdu_fields = NamedItemList()
178
- env_data_descs = NamedItemList()
179
- env_datas = NamedItemList()
180
- muxs = NamedItemList()
181
185
  ddds_sdgs = []
182
186
 
183
187
  # create a DiagDataDictionarySpec which includes all the
184
188
  # inherited objects. To me, this seems rather inelegant, but
185
189
  # hey, it's described like this in the standard.
186
- self._diag_data_dictionary_spec = \
187
- DiagDataDictionarySpec(
188
- data_object_props=dops,
189
- dtc_dops=dtc_dops,
190
- structures=structures,
191
- end_of_pdu_fields=end_of_pdu_fields,
192
- tables=tables,
193
- env_data_descs=env_data_descs,
194
- env_datas=env_datas,
195
- muxs=muxs,
196
- unit_spec=unit_spec,
197
- sdgs=ddds_sdgs)
190
+ self._diag_data_dictionary_spec = DiagDataDictionarySpec(
191
+ data_object_props=dops,
192
+ dtc_dops=dtc_dops,
193
+ structures=structures,
194
+ end_of_pdu_fields=end_of_pdu_fields,
195
+ dynamic_length_fields=dynamic_length_fields,
196
+ tables=tables,
197
+ env_data_descs=env_data_descs,
198
+ env_datas=env_datas,
199
+ muxs=muxs,
200
+ unit_spec=unit_spec,
201
+ sdgs=ddds_sdgs,
202
+ )
198
203
 
199
204
  #####
200
205
  # compute the communication parameters applicable to the
@@ -457,29 +462,19 @@ class DiagLayer:
457
462
 
458
463
  return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
459
464
 
460
- def _compute_available_data_object_props(self) -> Iterable[DataObjectProperty]:
461
-
462
- def get_local_objects_fn(dl):
463
- if dl.diag_layer_raw.diag_data_dictionary_spec is None:
464
- return []
465
- return dl.diag_layer_raw.diag_data_dictionary_spec.data_object_props
466
-
467
- def not_inherited_fn(parent_ref):
468
- return parent_ref.not_inherited_dops
469
-
470
- return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
471
-
472
- def _compute_available_tables(self) -> Iterable[Table]:
465
+ def _compute_available_ddd_spec_items(
466
+ self,
467
+ include: Callable[[DiagDataDictionarySpec], Iterable[TNamed]],
468
+ exclude: Callable[["ParentRef"], List[str]],
469
+ ) -> NamedItemList[TNamed]:
473
470
 
474
- def get_local_objects_fn(dl):
471
+ def get_local_objects_fn(dl: "DiagLayer"):
475
472
  if dl.diag_layer_raw.diag_data_dictionary_spec is None:
476
473
  return []
477
- return dl.diag_layer_raw.diag_data_dictionary_spec.tables
474
+ return include(dl.diag_layer_raw.diag_data_dictionary_spec)
478
475
 
479
- def not_inherited_fn(parent_ref):
480
- return parent_ref.not_inherited_tables
481
-
482
- return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
476
+ found = self._compute_available_objects(get_local_objects_fn, exclude)
477
+ return NamedItemList(found)
483
478
 
484
479
  def _compute_available_functional_classes(self) -> Iterable[FunctionalClass]:
485
480
 
@@ -0,0 +1,59 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING, Any, Dict, List
4
+ from xml.etree import ElementTree
5
+
6
+ from .decodestate import DecodeState
7
+ from .determinenumberofitems import DetermineNumberOfItems
8
+ from .encodestate import EncodeState
9
+ from .exceptions import odxrequire
10
+ from .field import Field
11
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
12
+ from .odxtypes import ParameterValueDict
13
+ from .utils import dataclass_fields_asdict
14
+
15
+ if TYPE_CHECKING:
16
+ from .diaglayer import DiagLayer
17
+
18
+
19
+ @dataclass
20
+ class DynamicLengthField(Field):
21
+ """Array of structure with length field"""
22
+ offset: int
23
+ determine_number_of_items: DetermineNumberOfItems
24
+
25
+ @staticmethod
26
+ def from_et(et_element: ElementTree.Element,
27
+ doc_frags: List[OdxDocFragment]) -> "DynamicLengthField":
28
+ kwargs = dataclass_fields_asdict(Field.from_et(et_element, doc_frags))
29
+ offset = int(odxrequire(et_element.findtext('OFFSET')))
30
+ determine_number_of_items = DetermineNumberOfItems.from_et(
31
+ odxrequire(et_element.find('DETERMINE-NUMBER-OF-ITEMS')),
32
+ doc_frags,
33
+ )
34
+ return DynamicLengthField(
35
+ offset=offset, determine_number_of_items=determine_number_of_items, **kwargs)
36
+
37
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
38
+ odxlinks = super()._build_odxlinks()
39
+ odxlinks.update(self.determine_number_of_items._build_odxlinks())
40
+ return odxlinks
41
+
42
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
43
+ super()._resolve_odxlinks(odxlinks)
44
+ self.determine_number_of_items._resolve_odxlinks(odxlinks)
45
+
46
+ def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
47
+ super()._resolve_snrefs(diag_layer)
48
+ self.determine_number_of_items._resolve_snrefs(diag_layer)
49
+
50
+ def convert_physical_to_bytes(
51
+ self,
52
+ physical_value: List[ParameterValueDict],
53
+ encode_state: EncodeState,
54
+ bit_position: int = 0,
55
+ ) -> bytes:
56
+ raise NotImplementedError()
57
+
58
+ def convert_bytes_to_physical(self, decode_state: DecodeState, bit_position: int = 0):
59
+ raise NotImplementedError()
odxtools/element.py CHANGED
@@ -2,11 +2,9 @@ from dataclasses import dataclass
2
2
  from typing import List, Optional
3
3
  from xml.etree import ElementTree
4
4
 
5
- from odxtools.exceptions import odxrequire
6
- from odxtools.utils import create_description_from_et
7
-
5
+ from .exceptions import odxrequire
8
6
  from .odxlink import OdxDocFragment, OdxLinkId
9
- from .utils import dataclass_fields_asdict
7
+ from .utils import create_description_from_et, dataclass_fields_asdict
10
8
 
11
9
 
12
10
  @dataclass
odxtools/endofpdufield.py CHANGED
@@ -1,62 +1,28 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from copy import copy
3
3
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, List, Optional
4
+ from typing import List, Optional
5
5
  from xml.etree import ElementTree
6
6
 
7
- from .basicstructure import BasicStructure
8
- from .createsdgs import create_sdgs_from_et
9
7
  from .decodestate import DecodeState
10
- from .dopbase import DopBase
11
- from .element import IdentifiableElement
12
8
  from .encodestate import EncodeState
13
9
  from .exceptions import odxassert
14
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
15
- from .odxtypes import ParameterValueDict, odxstr_to_bool
10
+ from .field import Field
11
+ from .odxlink import OdxDocFragment
12
+ from .odxtypes import ParameterValueDict
16
13
  from .utils import dataclass_fields_asdict
17
14
 
18
- if TYPE_CHECKING:
19
- from .diaglayer import DiagLayer
20
-
21
15
 
22
16
  @dataclass
23
- class EndOfPduField(DopBase):
17
+ class EndOfPduField(Field):
24
18
  """End of PDU fields are structures that are repeated until the end of the PDU"""
25
-
26
- structure_ref: Optional[OdxLinkRef]
27
- structure_snref: Optional[str]
28
- env_data_desc_ref: Optional[OdxLinkRef]
29
- env_data_desc_snref: Optional[str]
30
19
  min_number_of_items: Optional[int]
31
20
  max_number_of_items: Optional[int]
32
21
 
33
- def __post_init__(self) -> None:
34
- num_struct_refs = 0 if self.structure_ref is None else 1
35
- num_struct_refs += 0 if self.structure_snref is None else 1
36
-
37
- num_edd_refs = 0 if self.env_data_desc_ref is None else 1
38
- num_edd_refs += 0 if self.env_data_desc_snref is None else 1
39
-
40
- odxassert(
41
- num_struct_refs + num_edd_refs == 1,
42
- "END-OF-PDU-FIELDs need to specify exactly one reference to a "
43
- "structure or an environment data description")
44
-
45
22
  @staticmethod
46
23
  def from_et(et_element: ElementTree.Element,
47
24
  doc_frags: List[OdxDocFragment]) -> "EndOfPduField":
48
- kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
49
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
50
-
51
- structure_ref = OdxLinkRef.from_et(et_element.find("BASIC-STRUCTURE-REF"), doc_frags)
52
- structure_snref = None
53
- if (edsnr_elem := et_element.find("BASIC-STRUCTURE-SNREF")) is not None:
54
- structure_snref = edsnr_elem.get("SHORT-NAME")
55
-
56
- env_data_desc_ref = OdxLinkRef.from_et(et_element.find("ENV-DATA-DESC-REF"), doc_frags)
57
- env_data_desc_snref = None
58
- if (edsnr_elem := et_element.find("ENV-DATA-DESC-SNREF")) is not None:
59
- env_data_desc_snref = edsnr_elem.get("SHORT-NAME")
25
+ kwargs = dataclass_fields_asdict(Field.from_et(et_element, doc_frags))
60
26
 
61
27
  if (min_n_str := et_element.findtext("MIN-NUMBER-OF-ITEMS")) is not None:
62
28
  min_number_of_items = int(min_n_str)
@@ -67,32 +33,13 @@ class EndOfPduField(DopBase):
67
33
  else:
68
34
  max_number_of_items = None
69
35
 
70
- is_visible_raw = odxstr_to_bool(et_element.get("IS-VISIBLE"))
71
36
  eopf = EndOfPduField(
72
- sdgs=sdgs,
73
- structure_ref=structure_ref,
74
- structure_snref=structure_snref,
75
- env_data_desc_ref=env_data_desc_ref,
76
- env_data_desc_snref=env_data_desc_snref,
77
37
  min_number_of_items=min_number_of_items,
78
38
  max_number_of_items=max_number_of_items,
79
- is_visible_raw=is_visible_raw,
80
39
  **kwargs)
81
40
 
82
41
  return eopf
83
42
 
84
- @property
85
- def structure(self) -> "BasicStructure":
86
- """may be a Structure or a env-data-desc"""
87
- return self._structure
88
-
89
- @property
90
- def bit_length(self):
91
- return self.structure.bit_length
92
-
93
- def convert_physical_to_internal(self, physical_value):
94
- return self.structure.convert_physical_to_internal(physical_value)
95
-
96
43
  def convert_physical_to_bytes(
97
44
  self,
98
45
  physical_value: ParameterValueDict,
@@ -133,21 +80,3 @@ class EndOfPduField(DopBase):
133
80
  value.append(new_value)
134
81
 
135
82
  return value, next_byte_position
136
-
137
- def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
138
- """Recursively resolve any odxlinks references"""
139
- if self.structure_ref is not None:
140
- self._structure = odxlinks.resolve(self.structure_ref)
141
-
142
- if self.env_data_desc_ref is not None:
143
- self._env_data_desc = odxlinks.resolve(self.env_data_desc_ref)
144
-
145
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
146
- """Recursively resolve any short-name references"""
147
- if self.structure_snref is not None:
148
- structures = diag_layer.diag_data_dictionary_spec.structures
149
- self._structure = structures[self.structure_snref]
150
-
151
- if self.env_data_desc_snref is not None:
152
- env_data_descs = diag_layer.diag_data_dictionary_spec.env_data_descs
153
- self._env_data_desc = env_data_descs[self.env_data_desc_snref]
odxtools/field.py ADDED
@@ -0,0 +1,94 @@
1
+ from dataclasses import dataclass
2
+ from typing import TYPE_CHECKING, List, Optional
3
+ from xml.etree import ElementTree
4
+
5
+ from .environmentdatadescription import EnvironmentDataDescription
6
+ from .basicstructure import BasicStructure
7
+ from .createsdgs import create_sdgs_from_et
8
+ from .dopbase import DopBase
9
+ from .element import IdentifiableElement
10
+ from .exceptions import odxassert, odxrequire
11
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
12
+ from .odxtypes import odxstr_to_bool
13
+ from .utils import dataclass_fields_asdict
14
+
15
+ if TYPE_CHECKING:
16
+ from .diaglayer import DiagLayer
17
+
18
+
19
+ @dataclass
20
+ class Field(DopBase):
21
+ structure_ref: Optional[OdxLinkRef]
22
+ structure_snref: Optional[str]
23
+ env_data_desc_ref: Optional[OdxLinkRef]
24
+ env_data_desc_snref: Optional[str]
25
+
26
+ def __post_init__(self) -> None:
27
+ self._structure: Optional[BasicStructure] = None
28
+ self._env_data_desc: Optional[EnvironmentDataDescription] = None
29
+ num_struct_refs = 0 if self.structure_ref is None else 1
30
+ num_struct_refs += 0 if self.structure_snref is None else 1
31
+
32
+ num_edd_refs = 0 if self.env_data_desc_ref is None else 1
33
+ num_edd_refs += 0 if self.env_data_desc_snref is None else 1
34
+
35
+ odxassert(
36
+ num_struct_refs + num_edd_refs == 1,
37
+ "FIELDs need to specify exactly one reference to a "
38
+ "structure or an environment data description")
39
+
40
+ @staticmethod
41
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Field":
42
+ kwargs = dataclass_fields_asdict(DopBase.from_et(et_element, doc_frags))
43
+ sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
44
+
45
+ structure_ref = OdxLinkRef.from_et(et_element.find("BASIC-STRUCTURE-REF"), doc_frags)
46
+ structure_snref = None
47
+ if (edsnr_elem := et_element.find("BASIC-STRUCTURE-SNREF")) is not None:
48
+ structure_snref = edsnr_elem.get("SHORT-NAME")
49
+
50
+ env_data_desc_ref = OdxLinkRef.from_et(et_element.find("ENV-DATA-DESC-REF"), doc_frags)
51
+ env_data_desc_snref = None
52
+ if (edsnr_elem := et_element.find("ENV-DATA-DESC-SNREF")) is not None:
53
+ env_data_desc_snref = edsnr_elem.get("SHORT-NAME")
54
+
55
+ is_visible_raw = odxstr_to_bool(et_element.get("IS-VISIBLE"))
56
+ return Field(
57
+ sdgs=sdgs,
58
+ structure_ref=structure_ref,
59
+ structure_snref=structure_snref,
60
+ env_data_desc_ref=env_data_desc_ref,
61
+ env_data_desc_snref=env_data_desc_snref,
62
+ is_visible_raw=is_visible_raw,
63
+ **kwargs)
64
+
65
+ @property
66
+ def structure(self) -> BasicStructure:
67
+ """may be a Structure or a env-data-desc"""
68
+ return odxrequire(self._structure, "EnvironmentDataDescription is not supported")
69
+
70
+ @property
71
+ def bit_length(self):
72
+ return self.structure.bit_length
73
+
74
+ def convert_physical_to_internal(self, physical_value):
75
+ return self.structure.convert_physical_to_internal(physical_value)
76
+
77
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
78
+ """Recursively resolve any odxlinks references"""
79
+ if self.structure_ref is not None:
80
+ self._structure = odxlinks.resolve(self.structure_ref, BasicStructure)
81
+
82
+ if self.env_data_desc_ref is not None:
83
+ self._env_data_desc = odxlinks.resolve(self.env_data_desc_ref,
84
+ EnvironmentDataDescription)
85
+
86
+ def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
87
+ """Recursively resolve any short-name references"""
88
+ if self.structure_snref is not None:
89
+ structures = diag_layer.diag_data_dictionary_spec.structures
90
+ self._structure = odxrequire(structures.get(self.structure_snref))
91
+
92
+ if self.env_data_desc_snref is not None:
93
+ env_data_descs = diag_layer.diag_data_dictionary_spec.env_data_descs
94
+ self._env_data_desc = odxrequire(env_data_descs.get(self.env_data_desc_snref))
odxtools/multiplexer.py CHANGED
@@ -63,7 +63,7 @@ class Multiplexer(DopBase):
63
63
  return None
64
64
 
65
65
  def _get_case_limits(self, case: MultiplexerCase):
66
- key_type = self.switch_key._dop.physical_type.base_data_type
66
+ key_type = self.switch_key.dop.physical_type.base_data_type
67
67
  lower_limit = key_type.make_from(case.lower_limit)
68
68
  upper_limit = key_type.make_from(case.upper_limit)
69
69
  return lower_limit, upper_limit
@@ -80,7 +80,6 @@ class Multiplexer(DopBase):
80
80
  with only one key equal to the desired case""")
81
81
 
82
82
  case_name, case_value = next(iter(physical_value.items()))
83
- key_pos = self.switch_key.byte_position
84
83
  case_pos = self.byte_position
85
84
 
86
85
  for case in self.cases or []:
@@ -92,14 +91,11 @@ class Multiplexer(DopBase):
92
91
  case_bytes = bytes()
93
92
 
94
93
  key_value, _ = self._get_case_limits(case)
95
- sk_bit_position = self.switch_key.bit_position
96
- sk_bit_position = sk_bit_position if sk_bit_position is not None else 0
97
- key_bytes = self.switch_key._dop.convert_physical_to_bytes(
98
- key_value, encode_state, sk_bit_position)
94
+ key_bytes = self.switch_key.convert_physical_to_bytes(key_value, encode_state)
99
95
 
100
- mux_len = max(len(key_bytes) + key_pos, len(case_bytes) + case_pos)
96
+ mux_len = max(len(key_bytes), len(case_bytes) + case_pos)
101
97
  mux_bytes = bytearray(mux_len)
102
- mux_bytes[key_pos:key_pos + len(key_bytes)] = key_bytes
98
+ mux_bytes[:len(key_bytes)] = key_bytes
103
99
  mux_bytes[case_pos:case_pos + len(case_bytes)] = case_bytes
104
100
 
105
101
  return bytes(mux_bytes)
@@ -111,17 +107,9 @@ class Multiplexer(DopBase):
111
107
  if bit_position != 0:
112
108
  raise DecodeError("Multiplexer must be aligned, i.e. bit_position=0, but "
113
109
  f"{self.short_name} was passed the bit position {bit_position}")
114
- byte_code = decode_state.coded_message[decode_state.next_byte_position:]
115
- key_decode_state = DecodeState(
116
- coded_message=byte_code[self.switch_key.byte_position:],
117
- parameter_values=dict(),
118
- next_byte_position=0,
119
- )
120
- bit_position_int = (
121
- self.switch_key.bit_position if self.switch_key.bit_position is not None else 0)
122
- key_value, key_next_byte = self.switch_key._dop.convert_bytes_to_physical(
123
- key_decode_state, bit_position=bit_position_int)
110
+ key_value, key_next_byte = self.switch_key.convert_bytes_to_physical(decode_state)
124
111
 
112
+ byte_code = decode_state.coded_message[decode_state.next_byte_position:]
125
113
  case_decode_state = DecodeState(
126
114
  coded_message=byte_code[self.byte_position:],
127
115
  parameter_values=dict(),
@@ -1,53 +1,18 @@
1
- # SPDX-License-Identifier: MIT
2
1
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
2
+ from typing import List
4
3
  from xml.etree import ElementTree
5
4
 
6
- from .dataobjectproperty import DataObjectProperty
7
- from .exceptions import odxrequire
8
- from .globals import logger
9
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
-
11
- if TYPE_CHECKING:
12
- from .diaglayer import DiagLayer
5
+ from .odxlink import OdxDocFragment
6
+ from .positioneddataobjectproperty import PositionedDataObjectProperty
7
+ from .utils import dataclass_fields_asdict
13
8
 
14
9
 
15
10
  @dataclass
16
- class MultiplexerSwitchKey:
17
- """This class represents a Switch Key, which is used to select one of the cases defined in the Multiplexer."""
18
-
19
- byte_position: int
20
- bit_position: Optional[int]
21
- dop_ref: OdxLinkRef
22
-
23
- def __post_init__(self):
24
- self._dop: DataObjectProperty = None # type: ignore
11
+ class MultiplexerSwitchKey(PositionedDataObjectProperty):
25
12
 
26
13
  @staticmethod
27
14
  def from_et(et_element: ElementTree.Element,
28
15
  doc_frags: List[OdxDocFragment]) -> "MultiplexerSwitchKey":
29
- """Reads a Switch Key for a Multiplexer."""
30
- byte_position = int(odxrequire(et_element.findtext("BYTE-POSITION", "0")))
31
- bit_position_str = et_element.findtext("BIT-POSITION")
32
- bit_position = int(bit_position_str) if bit_position_str is not None else None
33
- dop_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DATA-OBJECT-PROP-REF"), doc_frags))
34
-
35
- return MultiplexerSwitchKey(
36
- byte_position=byte_position,
37
- bit_position=bit_position,
38
- dop_ref=dop_ref,
39
- )
40
-
41
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
42
- return {}
43
-
44
- def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
45
- dop = odxlinks.resolve(self.dop_ref)
46
- if isinstance(dop, DataObjectProperty):
47
- self._dop = dop
48
- else:
49
- logger.warning(
50
- f"DATA-OBJECT-PROP-REF '{self.dop_ref}' could not be resolved in SWITCH-KEY.")
51
-
52
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
53
- pass
16
+ kwargs = dataclass_fields_asdict(
17
+ PositionedDataObjectProperty.from_et(et_element, doc_frags))
18
+ return MultiplexerSwitchKey(**kwargs)
odxtools/odxtypes.py CHANGED
@@ -6,7 +6,7 @@ from xml.etree import ElementTree
6
6
  from .exceptions import odxassert, odxraise, odxrequire
7
7
 
8
8
  if TYPE_CHECKING:
9
- from odxtools.parameters.parameter import Parameter
9
+ from .parameters.parameter import Parameter
10
10
 
11
11
 
12
12
  def bytefield_to_bytearray(bytefield: str) -> bytearray: