odxtools 7.1.0__py3-none-any.whl → 7.2.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.
Files changed (131) hide show
  1. odxtools/__init__.py +6 -4
  2. odxtools/additionalaudience.py +3 -5
  3. odxtools/admindata.py +5 -7
  4. odxtools/audience.py +3 -5
  5. odxtools/basecomparam.py +3 -5
  6. odxtools/basicstructure.py +10 -17
  7. odxtools/cli/_parser_utils.py +1 -1
  8. odxtools/cli/_print_utils.py +3 -2
  9. odxtools/cli/compare.py +1 -1
  10. odxtools/companydata.py +5 -7
  11. odxtools/companydocinfo.py +7 -8
  12. odxtools/companyrevisioninfo.py +3 -5
  13. odxtools/companyspecificinfo.py +8 -9
  14. odxtools/comparam.py +4 -6
  15. odxtools/comparaminstance.py +6 -8
  16. odxtools/comparamspec.py +14 -13
  17. odxtools/comparamsubset.py +17 -16
  18. odxtools/complexcomparam.py +5 -7
  19. odxtools/compumethods/compuconst.py +31 -0
  20. odxtools/compumethods/compudefaultvalue.py +27 -0
  21. odxtools/compumethods/compuinternaltophys.py +39 -0
  22. odxtools/compumethods/compuinversevalue.py +7 -0
  23. odxtools/compumethods/compumethod.py +67 -12
  24. odxtools/compumethods/compuphystointernal.py +39 -0
  25. odxtools/compumethods/compuscale.py +15 -26
  26. odxtools/compumethods/createanycompumethod.py +14 -160
  27. odxtools/compumethods/identicalcompumethod.py +31 -6
  28. odxtools/compumethods/linearcompumethod.py +69 -189
  29. odxtools/compumethods/linearsegment.py +193 -0
  30. odxtools/compumethods/scalelinearcompumethod.py +132 -26
  31. odxtools/compumethods/tabintpcompumethod.py +119 -99
  32. odxtools/compumethods/texttablecompumethod.py +107 -43
  33. odxtools/createanydiagcodedtype.py +10 -67
  34. odxtools/database.py +68 -62
  35. odxtools/dataobjectproperty.py +10 -19
  36. odxtools/description.py +47 -0
  37. odxtools/determinenumberofitems.py +4 -5
  38. odxtools/diagcodedtype.py +29 -12
  39. odxtools/diagcomm.py +10 -6
  40. odxtools/diagdatadictionaryspec.py +20 -21
  41. odxtools/diaglayer.py +34 -5
  42. odxtools/diaglayercontainer.py +17 -11
  43. odxtools/diaglayerraw.py +20 -21
  44. odxtools/diagnostictroublecode.py +7 -8
  45. odxtools/diagservice.py +9 -7
  46. odxtools/docrevision.py +5 -7
  47. odxtools/dopbase.py +7 -8
  48. odxtools/dtcdop.py +5 -8
  49. odxtools/dynamicendmarkerfield.py +22 -9
  50. odxtools/dynamiclengthfield.py +5 -11
  51. odxtools/element.py +4 -3
  52. odxtools/endofpdufield.py +0 -2
  53. odxtools/environmentdatadescription.py +4 -6
  54. odxtools/exceptions.py +1 -1
  55. odxtools/field.py +9 -9
  56. odxtools/functionalclass.py +3 -5
  57. odxtools/inputparam.py +3 -5
  58. odxtools/leadinglengthinfotype.py +15 -2
  59. odxtools/loadfile.py +64 -0
  60. odxtools/minmaxlengthtype.py +20 -2
  61. odxtools/modification.py +3 -5
  62. odxtools/multiplexer.py +7 -14
  63. odxtools/multiplexercase.py +4 -6
  64. odxtools/multiplexerdefaultcase.py +4 -6
  65. odxtools/multiplexerswitchkey.py +4 -5
  66. odxtools/negoutputparam.py +3 -5
  67. odxtools/outputparam.py +3 -5
  68. odxtools/parameterinfo.py +3 -3
  69. odxtools/parameters/codedconstparameter.py +2 -14
  70. odxtools/parameters/lengthkeyparameter.py +3 -17
  71. odxtools/parameters/nrcconstparameter.py +2 -14
  72. odxtools/parameters/parameter.py +22 -22
  73. odxtools/parameters/parameterwithdop.py +6 -8
  74. odxtools/parameters/physicalconstantparameter.py +5 -8
  75. odxtools/parameters/reservedparameter.py +4 -3
  76. odxtools/parameters/tablekeyparameter.py +6 -9
  77. odxtools/parameters/tablestructparameter.py +6 -8
  78. odxtools/parameters/valueparameter.py +5 -8
  79. odxtools/paramlengthinfotype.py +19 -6
  80. odxtools/parentref.py +15 -1
  81. odxtools/physicaldimension.py +3 -5
  82. odxtools/progcode.py +18 -7
  83. odxtools/protstack.py +3 -5
  84. odxtools/py.typed +0 -0
  85. odxtools/relateddoc.py +7 -9
  86. odxtools/request.py +8 -0
  87. odxtools/response.py +8 -0
  88. odxtools/scaleconstr.py +3 -3
  89. odxtools/singleecujob.py +12 -10
  90. odxtools/snrefcontext.py +29 -0
  91. odxtools/specialdata.py +3 -5
  92. odxtools/specialdatagroup.py +5 -7
  93. odxtools/specialdatagroupcaption.py +3 -6
  94. odxtools/standardlengthtype.py +27 -2
  95. odxtools/state.py +3 -5
  96. odxtools/statechart.py +9 -11
  97. odxtools/statetransition.py +4 -9
  98. odxtools/staticfield.py +4 -8
  99. odxtools/table.py +7 -8
  100. odxtools/tablerow.py +7 -6
  101. odxtools/teammember.py +3 -5
  102. odxtools/templates/comparam-spec.odx-c.xml.jinja2 +2 -5
  103. odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +2 -5
  104. odxtools/templates/macros/printCompanyData.xml.jinja2 +2 -5
  105. odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
  106. odxtools/templates/macros/printCompuMethod.xml.jinja2 +153 -0
  107. odxtools/templates/macros/printDOP.xml.jinja2 +10 -132
  108. odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
  109. odxtools/templates/macros/printElementId.xml.jinja2 +3 -3
  110. odxtools/templates/macros/printMux.xml.jinja2 +3 -2
  111. odxtools/templates/macros/printTable.xml.jinja2 +2 -3
  112. odxtools/unit.py +3 -5
  113. odxtools/unitgroup.py +3 -5
  114. odxtools/unitspec.py +9 -10
  115. odxtools/utils.py +1 -26
  116. odxtools/version.py +2 -2
  117. odxtools/{write_pdx_file.py → writepdxfile.py} +19 -10
  118. odxtools/xdoc.py +3 -5
  119. {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/METADATA +1 -1
  120. odxtools-7.2.0.dist-info/RECORD +192 -0
  121. {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/WHEEL +1 -1
  122. odxtools/createcompanydatas.py +0 -17
  123. odxtools/createsdgs.py +0 -19
  124. odxtools/load_file.py +0 -13
  125. odxtools/load_odx_d_file.py +0 -6
  126. odxtools/load_pdx_file.py +0 -8
  127. odxtools-7.1.0.dist-info/RECORD +0 -185
  128. /odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +0 -0
  129. {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/LICENSE +0 -0
  130. {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/entry_points.txt +0 -0
  131. {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,11 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
3
  from itertools import chain
4
- from typing import Any, Dict, List, Optional, Union
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .admindata import AdminData
8
8
  from .companydata import CompanyData
9
- from .createcompanydatas import create_company_datas_from_et
10
- from .createsdgs import create_sdgs_from_et
11
9
  from .diaglayer import DiagLayer
12
10
  from .element import IdentifiableElement
13
11
  from .exceptions import odxrequire
@@ -16,6 +14,9 @@ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
16
14
  from .specialdatagroup import SpecialDataGroup
17
15
  from .utils import dataclass_fields_asdict
18
16
 
17
+ if TYPE_CHECKING:
18
+ from .database import Database
19
+
19
20
 
20
21
  @dataclass
21
22
  class DiagLayerContainer(IdentifiableElement):
@@ -48,7 +49,10 @@ class DiagLayerContainer(IdentifiableElement):
48
49
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
49
50
 
50
51
  admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
51
- company_datas = create_company_datas_from_et(et_element.find("COMPANY-DATAS"), doc_frags)
52
+ company_datas = NamedItemList([
53
+ CompanyData.from_et(cde, doc_frags)
54
+ for cde in et_element.iterfind("COMPANY-DATAS/COMPANY-DATA")
55
+ ])
52
56
  ecu_shared_datas = NamedItemList([
53
57
  DiagLayer.from_et(dl_element, doc_frags)
54
58
  for dl_element in et_element.iterfind("ECU-SHARED-DATAS/ECU-SHARED-DATA")
@@ -69,7 +73,9 @@ class DiagLayerContainer(IdentifiableElement):
69
73
  DiagLayer.from_et(dl_element, doc_frags)
70
74
  for dl_element in et_element.iterfind("ECU-VARIANTS/ECU-VARIANT")
71
75
  ])
72
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
76
+ sdgs = [
77
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
78
+ ]
73
79
 
74
80
  return DiagLayerContainer(
75
81
  admin_data=admin_data,
@@ -124,17 +130,17 @@ class DiagLayerContainer(IdentifiableElement):
124
130
  for ecu_variant in self.ecu_variants:
125
131
  ecu_variant._resolve_odxlinks(odxlinks)
126
132
 
127
- def _finalize_init(self, odxlinks: OdxLinkDatabase) -> None:
133
+ def _finalize_init(self, database: "Database", odxlinks: OdxLinkDatabase) -> None:
128
134
  for ecu_shared_data in self.ecu_shared_datas:
129
- ecu_shared_data._finalize_init(odxlinks)
135
+ ecu_shared_data._finalize_init(database, odxlinks)
130
136
  for protocol in self.protocols:
131
- protocol._finalize_init(odxlinks)
137
+ protocol._finalize_init(database, odxlinks)
132
138
  for functional_group in self.functional_groups:
133
- functional_group._finalize_init(odxlinks)
139
+ functional_group._finalize_init(database, odxlinks)
134
140
  for base_variant in self.base_variants:
135
- base_variant._finalize_init(odxlinks)
141
+ base_variant._finalize_init(database, odxlinks)
136
142
  for ecu_variant in self.ecu_variants:
137
- ecu_variant._finalize_init(odxlinks)
143
+ ecu_variant._finalize_init(database, odxlinks)
138
144
 
139
145
  @property
140
146
  def diag_layers(self) -> NamedItemList[DiagLayer]:
odxtools/diaglayerraw.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from copy import copy
3
3
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
4
+ from typing import Any, Dict, List, Optional, Union, cast
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .additionalaudience import AdditionalAudience
@@ -10,7 +10,6 @@ from .companydata import CompanyData
10
10
  from .comparaminstance import ComparamInstance
11
11
  from .comparamspec import ComparamSpec
12
12
  from .comparamsubset import ComparamSubset
13
- from .createsdgs import create_sdgs_from_et
14
13
  from .diagcomm import DiagComm
15
14
  from .diagdatadictionaryspec import DiagDataDictionarySpec
16
15
  from .diaglayertype import DiagLayerType
@@ -26,13 +25,11 @@ from .protstack import ProtStack
26
25
  from .request import Request
27
26
  from .response import Response
28
27
  from .singleecujob import SingleEcuJob
28
+ from .snrefcontext import SnRefContext
29
29
  from .specialdatagroup import SpecialDataGroup
30
30
  from .statechart import StateChart
31
31
  from .utils import dataclass_fields_asdict
32
32
 
33
- if TYPE_CHECKING:
34
- from .diaglayer import DiagLayer
35
-
36
33
 
37
34
  @dataclass
38
35
  class DiagLayerRaw(IdentifiableElement):
@@ -153,7 +150,9 @@ class DiagLayerRaw(IdentifiableElement):
153
150
  for el in et_element.iterfind("ADDITIONAL-AUDIENCES/ADDITIONAL-AUDIENCE")
154
151
  ]
155
152
 
156
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
153
+ sdgs = [
154
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
155
+ ]
157
156
 
158
157
  parent_refs = [
159
158
  ParentRef.from_et(pr_el, doc_frags)
@@ -282,7 +281,7 @@ class DiagLayerRaw(IdentifiableElement):
282
281
  for comparam in self.comparams:
283
282
  comparam._resolve_odxlinks(odxlinks)
284
283
 
285
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
284
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
286
285
  self._prot_stack: Optional[ProtStack] = None
287
286
  if self.prot_stack_snref is not None:
288
287
  cp_spec = self.comparam_spec
@@ -292,36 +291,36 @@ class DiagLayerRaw(IdentifiableElement):
292
291
 
293
292
  # do short-name reference resolution
294
293
  if self.admin_data is not None:
295
- self.admin_data._resolve_snrefs(diag_layer)
294
+ self.admin_data._resolve_snrefs(context)
296
295
  if self.diag_data_dictionary_spec is not None:
297
- self.diag_data_dictionary_spec._resolve_snrefs(diag_layer)
296
+ self.diag_data_dictionary_spec._resolve_snrefs(context)
298
297
 
299
298
  for company_data in self.company_datas:
300
- company_data._resolve_snrefs(diag_layer)
299
+ company_data._resolve_snrefs(context)
301
300
  for functional_class in self.functional_classes:
302
- functional_class._resolve_snrefs(diag_layer)
301
+ functional_class._resolve_snrefs(context)
303
302
  for diag_comm in self.diag_comms:
304
303
  if isinstance(diag_comm, OdxLinkRef):
305
304
  continue
306
- diag_comm._resolve_snrefs(diag_layer)
305
+ diag_comm._resolve_snrefs(context)
307
306
  for request in self.requests:
308
- request._resolve_snrefs(diag_layer)
307
+ request._resolve_snrefs(context)
309
308
  for positive_response in self.positive_responses:
310
- positive_response._resolve_snrefs(diag_layer)
309
+ positive_response._resolve_snrefs(context)
311
310
  for negative_response in self.negative_responses:
312
- negative_response._resolve_snrefs(diag_layer)
311
+ negative_response._resolve_snrefs(context)
313
312
  for global_negative_response in self.global_negative_responses:
314
- global_negative_response._resolve_snrefs(diag_layer)
313
+ global_negative_response._resolve_snrefs(context)
315
314
  for state_chart in self.state_charts:
316
- state_chart._resolve_snrefs(diag_layer)
315
+ state_chart._resolve_snrefs(context)
317
316
  for additional_audience in self.additional_audiences:
318
- additional_audience._resolve_snrefs(diag_layer)
317
+ additional_audience._resolve_snrefs(context)
319
318
  for sdg in self.sdgs:
320
- sdg._resolve_snrefs(diag_layer)
319
+ sdg._resolve_snrefs(context)
321
320
  for parent_ref in self.parent_refs:
322
- parent_ref._resolve_snrefs(diag_layer)
321
+ parent_ref._resolve_snrefs(context)
323
322
  for comparam in self.comparams:
324
- comparam._resolve_snrefs(diag_layer)
323
+ comparam._resolve_snrefs(context)
325
324
 
326
325
  @property
327
326
  def comparam_spec(self) -> Optional[Union[ComparamSpec, ComparamSubset]]:
@@ -1,19 +1,16 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
3
+ from typing import Any, Dict, List, Optional, Union
4
4
  from xml.etree import ElementTree
5
5
 
6
- from .createsdgs import create_sdgs_from_et
7
6
  from .element import IdentifiableElement
8
7
  from .exceptions import odxrequire
9
8
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
10
9
  from .odxtypes import odxstr_to_bool
10
+ from .snrefcontext import SnRefContext
11
11
  from .specialdatagroup import SpecialDataGroup
12
12
  from .utils import dataclass_fields_asdict
13
13
 
14
- if TYPE_CHECKING:
15
- from .diaglayer import DiagLayer
16
-
17
14
 
18
15
  @dataclass
19
16
  class DiagnosticTroubleCode(IdentifiableElement):
@@ -43,7 +40,9 @@ class DiagnosticTroubleCode(IdentifiableElement):
43
40
  level = None
44
41
 
45
42
  is_temporary_raw = odxstr_to_bool(et_element.get("IS-TEMPORARY"))
46
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
43
+ sdgs = [
44
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
45
+ ]
47
46
 
48
47
  return DiagnosticTroubleCode(
49
48
  trouble_code=int(odxrequire(et_element.findtext("TROUBLE-CODE"))),
@@ -69,6 +68,6 @@ class DiagnosticTroubleCode(IdentifiableElement):
69
68
  for sdg in self.sdgs:
70
69
  sdg._resolve_odxlinks(odxlinks)
71
70
 
72
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
71
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
73
72
  for sdg in self.sdgs:
74
- sdg._resolve_snrefs(diag_layer)
73
+ sdg._resolve_snrefs(context)
odxtools/diagservice.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
3
  from enum import Enum
4
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
4
+ from typing import Any, Dict, List, Optional, Union, cast
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .comparaminstance import ComparamInstance
@@ -14,11 +14,9 @@ from .odxtypes import ParameterValue, odxstr_to_bool
14
14
  from .parameters.parameter import Parameter
15
15
  from .request import Request
16
16
  from .response import Response
17
+ from .snrefcontext import SnRefContext
17
18
  from .utils import dataclass_fields_asdict
18
19
 
19
- if TYPE_CHECKING:
20
- from .diaglayer import DiagLayer
21
-
22
20
 
23
21
  class Addressing(Enum):
24
22
  FUNCTIONAL = "FUNCTIONAL"
@@ -175,16 +173,20 @@ class DiagService(DiagComm):
175
173
  self._negative_responses = NamedItemList[Response](
176
174
  [odxlinks.resolve(x, Response) for x in self.neg_response_refs])
177
175
 
178
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
179
- super()._resolve_snrefs(diag_layer)
176
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
177
+ context.diag_service = self
178
+
179
+ super()._resolve_snrefs(context)
180
180
 
181
181
  for cpr in self.comparam_refs:
182
- cpr._resolve_snrefs(diag_layer)
182
+ cpr._resolve_snrefs(context)
183
183
 
184
184
  # comparams named list is lazy loaded
185
185
  # since ComparamInstance short_name is only valid after resolution
186
186
  self._comparams = NamedItemList(self.comparam_refs)
187
187
 
188
+ context.diag_service = None
189
+
188
190
  def decode_message(self, raw_message: bytes) -> Message:
189
191
  request_prefix = b''
190
192
  candidate_coding_objects: List[Union[Request, Response]] = [
odxtools/docrevision.py CHANGED
@@ -1,17 +1,15 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
3
+ from typing import Any, Dict, List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .companyrevisioninfo import CompanyRevisionInfo
7
7
  from .exceptions import odxrequire
8
8
  from .modification import Modification
9
9
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
+ from .snrefcontext import SnRefContext
10
11
  from .teammember import TeamMember
11
12
 
12
- if TYPE_CHECKING:
13
- from .diaglayer import DiagLayer
14
-
15
13
 
16
14
  @dataclass
17
15
  class DocRevision:
@@ -75,9 +73,9 @@ class DocRevision:
75
73
  for mod in self.modifications:
76
74
  mod._resolve_odxlinks(odxlinks)
77
75
 
78
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
76
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
79
77
  for cri in self.company_revision_infos:
80
- cri._resolve_snrefs(diag_layer)
78
+ cri._resolve_snrefs(context)
81
79
 
82
80
  for mod in self.modifications:
83
- mod._resolve_snrefs(diag_layer)
81
+ mod._resolve_snrefs(context)
odxtools/dopbase.py CHANGED
@@ -1,21 +1,18 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
3
+ from typing import Any, Dict, List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .admindata import AdminData
7
- from .createsdgs import create_sdgs_from_et
8
7
  from .decodestate import DecodeState
9
8
  from .element import IdentifiableElement
10
9
  from .encodestate import EncodeState
11
10
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
12
11
  from .odxtypes import ParameterValue
12
+ from .snrefcontext import SnRefContext
13
13
  from .specialdatagroup import SpecialDataGroup
14
14
  from .utils import dataclass_fields_asdict
15
15
 
16
- if TYPE_CHECKING:
17
- from .diaglayer import DiagLayer
18
-
19
16
 
20
17
  @dataclass
21
18
  class DopBase(IdentifiableElement):
@@ -37,7 +34,9 @@ class DopBase(IdentifiableElement):
37
34
  if (admin_data_elem := et_element.find("ADMIN-DATA")) is not None:
38
35
  admin_data = AdminData.from_et(admin_data_elem, doc_frags)
39
36
 
40
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
37
+ sdgs = [
38
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
39
+ ]
41
40
 
42
41
  return DopBase(admin_data=admin_data, sdgs=sdgs, **kwargs)
43
42
 
@@ -53,9 +52,9 @@ class DopBase(IdentifiableElement):
53
52
  for sdg in self.sdgs:
54
53
  sdg._resolve_odxlinks(odxlinks)
55
54
 
56
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
55
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
57
56
  for sdg in self.sdgs:
58
- sdg._resolve_snrefs(diag_layer)
57
+ sdg._resolve_snrefs(context)
59
58
 
60
59
  def get_static_bit_length(self) -> Optional[int]:
61
60
  return None
odxtools/dtcdop.py CHANGED
@@ -1,7 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
- # from dataclasses import dataclass, field
3
2
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
3
+ from typing import Any, Dict, List, Optional, Union, cast
5
4
  from xml.etree import ElementTree
6
5
 
7
6
  from typing_extensions import override
@@ -19,11 +18,9 @@ from .nameditemlist import NamedItemList
19
18
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
20
19
  from .odxtypes import ParameterValue, odxstr_to_bool
21
20
  from .physicaltype import PhysicalType
21
+ from .snrefcontext import SnRefContext
22
22
  from .utils import dataclass_fields_asdict
23
23
 
24
- if TYPE_CHECKING:
25
- from .diaglayer import DiagLayer
26
-
27
24
 
28
25
  @dataclass
29
26
  class DtcDop(DopBase):
@@ -177,9 +174,9 @@ class DtcDop(DopBase):
177
174
  linked_dtc_dops = [odxlinks.resolve(x, DtcDop) for x in self.linked_dtc_dop_refs]
178
175
  self._linked_dtc_dops = NamedItemList(linked_dtc_dops)
179
176
 
180
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
181
- super()._resolve_snrefs(diag_layer)
177
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
178
+ super()._resolve_snrefs(context)
182
179
 
183
180
  for dtc_proxy in self.dtcs_raw:
184
181
  if isinstance(dtc_proxy, DiagnosticTroubleCode):
185
- dtc_proxy._resolve_snrefs(diag_layer)
182
+ dtc_proxy._resolve_snrefs(context)
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Sequence
3
+ from typing import Any, Dict, List, Sequence
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -13,11 +13,9 @@ from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequir
13
13
  from .field import Field
14
14
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
15
15
  from .odxtypes import AtomicOdxType, ParameterValue
16
+ from .snrefcontext import SnRefContext
16
17
  from .utils import dataclass_fields_asdict
17
18
 
18
- if TYPE_CHECKING:
19
- from .diaglayer import DiagLayer
20
-
21
19
 
22
20
  @dataclass
23
21
  class DynamicEndmarkerField(Field):
@@ -49,8 +47,8 @@ class DynamicEndmarkerField(Field):
49
47
 
50
48
  self._termination_value = tv_physical
51
49
 
52
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
53
- super()._resolve_snrefs(diag_layer)
50
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
51
+ super()._resolve_snrefs(context)
54
52
 
55
53
  @property
56
54
  def dyn_end_dop(self) -> DataObjectProperty:
@@ -81,8 +79,17 @@ class DynamicEndmarkerField(Field):
81
79
  encode_state.is_end_of_pdu = orig_is_end_of_pdu
82
80
 
83
81
  if not encode_state.is_end_of_pdu:
84
- # only add an endmarker if we are not at the end of the PDU
82
+ # only add an endmarker if we are not at the end of the
83
+ # PDU. note that since section 7.3.6.10.5 of the MCD-2
84
+ # specification states that the data used by the endmarker
85
+ # ought to be considered to be not consumed (why?!), we
86
+ # need to keep the cursor where it is before adding the
87
+ # endmarker. (we still consider its bits to be used
88
+ # "used", in order to produce a warning if it is attempted
89
+ # to be overridden.)
90
+ tmp_cursor = encode_state.cursor_byte_position
85
91
  self.dyn_end_dop.encode_into_pdu(self.termination_value, encode_state)
92
+ encode_state.cursor_byte_position = tmp_cursor
86
93
 
87
94
  @override
88
95
  def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
@@ -91,7 +98,6 @@ class DynamicEndmarkerField(Field):
91
98
  "No bit position can be specified for dynamic endmarker fields!")
92
99
 
93
100
  orig_origin = decode_state.origin_byte_position
94
- orig_cursor = decode_state.cursor_byte_position
95
101
  decode_state.origin_byte_position = decode_state.cursor_byte_position
96
102
 
97
103
  result: List[ParameterValue] = []
@@ -106,6 +112,14 @@ class DynamicEndmarkerField(Field):
106
112
  try:
107
113
  tv_candidate = self.dyn_end_dop.decode_from_pdu(decode_state)
108
114
  if tv_candidate == self.termination_value:
115
+ # note that section 7.3.6.10.5 of the MCD-2
116
+ # specification states that the bytes occupied by
117
+ # the endmarker ought to be considered to be not
118
+ # consumed (why?!), i.e., we need to keep the
119
+ # cursor where it is before adding the
120
+ # endmarker. (we still consider its to be used
121
+ # "used", though.)
122
+ decode_state.cursor_byte_position = tmp_cursor
109
123
  break
110
124
  except DecodeError:
111
125
  pass
@@ -114,6 +128,5 @@ class DynamicEndmarkerField(Field):
114
128
  result.append(self.structure.decode_from_pdu(decode_state))
115
129
 
116
130
  decode_state.origin_byte_position = orig_origin
117
- decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
118
131
 
119
132
  return result
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Sequence
3
+ from typing import Any, Dict, List, Sequence
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -12,11 +12,9 @@ from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequir
12
12
  from .field import Field
13
13
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
14
14
  from .odxtypes import ParameterValue
15
+ from .snrefcontext import SnRefContext
15
16
  from .utils import dataclass_fields_asdict
16
17
 
17
- if TYPE_CHECKING:
18
- from .diaglayer import DiagLayer
19
-
20
18
 
21
19
  @dataclass
22
20
  class DynamicLengthField(Field):
@@ -45,9 +43,9 @@ class DynamicLengthField(Field):
45
43
  super()._resolve_odxlinks(odxlinks)
46
44
  self.determine_number_of_items._resolve_odxlinks(odxlinks)
47
45
 
48
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
49
- super()._resolve_snrefs(diag_layer)
50
- self.determine_number_of_items._resolve_snrefs(diag_layer)
46
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
47
+ super()._resolve_snrefs(context)
48
+ self.determine_number_of_items._resolve_snrefs(context)
51
49
 
52
50
  @override
53
51
  def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
@@ -61,7 +59,6 @@ class DynamicLengthField(Field):
61
59
  f"got {type(physical_value)}", EncodeError)
62
60
 
63
61
  # move the origin to the cursor position
64
- orig_cursor = encode_state.cursor_byte_position
65
62
  orig_origin = encode_state.origin_byte_position
66
63
  encode_state.origin_byte_position = encode_state.cursor_byte_position
67
64
 
@@ -92,7 +89,6 @@ class DynamicLengthField(Field):
92
89
 
93
90
  # move cursor and origin positions
94
91
  encode_state.origin_byte_position = orig_origin
95
- encode_state.cursor_byte_position = max(orig_cursor, encode_state.cursor_byte_position)
96
92
 
97
93
  @override
98
94
  def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
@@ -101,7 +97,6 @@ class DynamicLengthField(Field):
101
97
  "No bit position can be specified for dynamic length fields!")
102
98
 
103
99
  orig_origin = decode_state.origin_byte_position
104
- orig_cursor = decode_state.cursor_byte_position
105
100
 
106
101
  det_num_items = self.determine_number_of_items
107
102
  decode_state.origin_byte_position = decode_state.cursor_byte_position
@@ -125,6 +120,5 @@ class DynamicLengthField(Field):
125
120
  result.append(self.structure.decode_from_pdu(decode_state))
126
121
 
127
122
  decode_state.origin_byte_position = orig_origin
128
- decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
129
123
 
130
124
  return result
odxtools/element.py CHANGED
@@ -2,16 +2,17 @@ from dataclasses import dataclass
2
2
  from typing import List, Optional
3
3
  from xml.etree import ElementTree
4
4
 
5
+ from .description import Description
5
6
  from .exceptions import odxrequire
6
7
  from .odxlink import OdxDocFragment, OdxLinkId
7
- from .utils import create_description_from_et, dataclass_fields_asdict
8
+ from .utils import dataclass_fields_asdict
8
9
 
9
10
 
10
11
  @dataclass
11
12
  class NamedElement:
12
13
  short_name: str
13
14
  long_name: Optional[str]
14
- description: Optional[str]
15
+ description: Optional[Description]
15
16
 
16
17
  @staticmethod
17
18
  def from_et(
@@ -22,7 +23,7 @@ class NamedElement:
22
23
  return NamedElement(
23
24
  short_name=odxrequire(et_element.findtext("SHORT-NAME")),
24
25
  long_name=et_element.findtext("LONG-NAME"),
25
- description=create_description_from_et(et_element.find("DESC")),
26
+ description=Description.from_et(et_element.find("DESC"), doc_frags),
26
27
  )
27
28
 
28
29
 
odxtools/endofpdufield.py CHANGED
@@ -72,7 +72,6 @@ class EndOfPduField(Field):
72
72
  "No bit position can be specified for end-of-pdu fields!")
73
73
 
74
74
  orig_origin = decode_state.origin_byte_position
75
- orig_cursor = decode_state.cursor_byte_position
76
75
  decode_state.origin_byte_position = decode_state.cursor_byte_position
77
76
 
78
77
  result: List[ParameterValue] = []
@@ -84,6 +83,5 @@ class EndOfPduField(Field):
84
83
  result.append(self.structure.decode_from_pdu(decode_state))
85
84
 
86
85
  decode_state.origin_byte_position = orig_origin
87
- decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
88
86
 
89
87
  return result
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
3
+ from typing import Any, Dict, List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -12,11 +12,9 @@ from .environmentdata import EnvironmentData
12
12
  from .exceptions import odxraise, odxrequire
13
13
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
14
14
  from .odxtypes import ParameterValue
15
+ from .snrefcontext import SnRefContext
15
16
  from .utils import dataclass_fields_asdict
16
17
 
17
- if TYPE_CHECKING:
18
- from .diaglayer import DiagLayer
19
-
20
18
 
21
19
  @dataclass
22
20
  class EnvironmentDataDescription(ComplexDop):
@@ -87,12 +85,12 @@ class EnvironmentDataDescription(ComplexDop):
87
85
  for ed in self.env_datas:
88
86
  ed._resolve_odxlinks(odxlinks)
89
87
 
90
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
88
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
91
89
  # ODX 2.0 specifies environment data objects here, ODX 2.2
92
90
  # uses references
93
91
  if self.env_data_refs:
94
92
  for ed in self.env_datas:
95
- ed._resolve_snrefs(diag_layer)
93
+ ed._resolve_snrefs(context)
96
94
 
97
95
  @override
98
96
  def encode_into_pdu(self, physical_value: Optional[ParameterValue],
odxtools/exceptions.py CHANGED
@@ -40,7 +40,7 @@ def odxraise(message: Optional[str] = None, error_type: Type[Exception] = OdxErr
40
40
  else:
41
41
  raise error_type(message)
42
42
  elif message is not None:
43
- logger.warn(message)
43
+ logger.warning(message)
44
44
 
45
45
 
46
46
  def odxassert(condition: bool,
odxtools/field.py CHANGED
@@ -1,5 +1,6 @@
1
+ # SPDX-License-Identifier: MIT
1
2
  from dataclasses import dataclass
2
- from typing import TYPE_CHECKING, List, Optional
3
+ from typing import List, Optional
3
4
  from xml.etree import ElementTree
4
5
 
5
6
  from .basicstructure import BasicStructure
@@ -8,11 +9,9 @@ from .environmentdatadescription import EnvironmentDataDescription
8
9
  from .exceptions import odxassert, odxrequire
9
10
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef, resolve_snref
10
11
  from .odxtypes import odxstr_to_bool
12
+ from .snrefcontext import SnRefContext
11
13
  from .utils import dataclass_fields_asdict
12
14
 
13
- if TYPE_CHECKING:
14
- from .diaglayer import DiagLayer
15
-
16
15
 
17
16
  @dataclass
18
17
  class Field(ComplexDop):
@@ -80,13 +79,14 @@ class Field(ComplexDop):
80
79
  self._env_data_desc = odxlinks.resolve(self.env_data_desc_ref,
81
80
  EnvironmentDataDescription)
82
81
 
83
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
82
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
84
83
  """Recursively resolve any short-name references"""
84
+ ddd_spec = odxrequire(context.diag_layer).diag_data_dictionary_spec
85
+
85
86
  if self.structure_snref is not None:
86
- structures = diag_layer.diag_data_dictionary_spec.structures
87
- self._structure = resolve_snref(self.structure_snref, structures, BasicStructure)
87
+ self._structure = resolve_snref(self.structure_snref, ddd_spec.structures,
88
+ BasicStructure)
88
89
 
89
90
  if self.env_data_desc_snref is not None:
90
- env_data_descs = diag_layer.diag_data_dictionary_spec.env_data_descs
91
- self._env_data_desc = resolve_snref(self.env_data_desc_snref, env_data_descs,
91
+ self._env_data_desc = resolve_snref(self.env_data_desc_snref, ddd_spec.env_data_descs,
92
92
  EnvironmentDataDescription)