odxtools 6.7.0__py3-none-any.whl → 9.3.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 (213) hide show
  1. odxtools/__init__.py +6 -4
  2. odxtools/additionalaudience.py +3 -5
  3. odxtools/admindata.py +5 -7
  4. odxtools/audience.py +10 -13
  5. odxtools/basecomparam.py +3 -5
  6. odxtools/basicstructure.py +55 -240
  7. odxtools/cli/_parser_utils.py +1 -1
  8. odxtools/cli/_print_utils.py +168 -134
  9. odxtools/cli/browse.py +111 -92
  10. odxtools/cli/compare.py +90 -71
  11. odxtools/cli/list.py +24 -15
  12. odxtools/cli/snoop.py +28 -5
  13. odxtools/codec.py +211 -0
  14. odxtools/commrelation.py +122 -0
  15. odxtools/companydata.py +5 -7
  16. odxtools/companydocinfo.py +7 -8
  17. odxtools/companyrevisioninfo.py +3 -5
  18. odxtools/companyspecificinfo.py +8 -9
  19. odxtools/comparam.py +4 -6
  20. odxtools/comparaminstance.py +7 -9
  21. odxtools/comparamspec.py +16 -54
  22. odxtools/comparamsubset.py +22 -62
  23. odxtools/complexcomparam.py +5 -7
  24. odxtools/compumethods/compucodecompumethod.py +63 -0
  25. odxtools/compumethods/compuconst.py +31 -0
  26. odxtools/compumethods/compudefaultvalue.py +27 -0
  27. odxtools/compumethods/compuinternaltophys.py +56 -0
  28. odxtools/compumethods/compuinversevalue.py +7 -0
  29. odxtools/compumethods/compumethod.py +93 -12
  30. odxtools/compumethods/compuphystointernal.py +56 -0
  31. odxtools/compumethods/compurationalcoeffs.py +20 -9
  32. odxtools/compumethods/compuscale.py +30 -35
  33. odxtools/compumethods/createanycompumethod.py +28 -161
  34. odxtools/compumethods/identicalcompumethod.py +31 -6
  35. odxtools/compumethods/linearcompumethod.py +69 -189
  36. odxtools/compumethods/linearsegment.py +190 -0
  37. odxtools/compumethods/ratfunccompumethod.py +106 -0
  38. odxtools/compumethods/ratfuncsegment.py +87 -0
  39. odxtools/compumethods/scalelinearcompumethod.py +132 -26
  40. odxtools/compumethods/scaleratfunccompumethod.py +113 -0
  41. odxtools/compumethods/tabintpcompumethod.py +119 -99
  42. odxtools/compumethods/texttablecompumethod.py +107 -43
  43. odxtools/createanydiagcodedtype.py +10 -67
  44. odxtools/database.py +167 -87
  45. odxtools/dataobjectproperty.py +15 -25
  46. odxtools/decodestate.py +9 -15
  47. odxtools/description.py +47 -0
  48. odxtools/determinenumberofitems.py +4 -5
  49. odxtools/diagcodedtype.py +36 -106
  50. odxtools/diagcomm.py +24 -12
  51. odxtools/diagdatadictionaryspec.py +33 -34
  52. odxtools/diaglayercontainer.py +46 -54
  53. odxtools/diaglayers/basevariant.py +128 -0
  54. odxtools/diaglayers/basevariantraw.py +123 -0
  55. odxtools/diaglayers/diaglayer.py +432 -0
  56. odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
  57. odxtools/diaglayers/ecushareddata.py +96 -0
  58. odxtools/diaglayers/ecushareddataraw.py +87 -0
  59. odxtools/diaglayers/ecuvariant.py +124 -0
  60. odxtools/diaglayers/ecuvariantraw.py +129 -0
  61. odxtools/diaglayers/functionalgroup.py +110 -0
  62. odxtools/diaglayers/functionalgroupraw.py +106 -0
  63. odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +209 -448
  64. odxtools/diaglayers/hierarchyelementraw.py +58 -0
  65. odxtools/diaglayers/protocol.py +64 -0
  66. odxtools/diaglayers/protocolraw.py +91 -0
  67. odxtools/diagnostictroublecode.py +8 -9
  68. odxtools/diagservice.py +56 -43
  69. odxtools/diagvariable.py +113 -0
  70. odxtools/docrevision.py +5 -7
  71. odxtools/dopbase.py +15 -17
  72. odxtools/dtcdop.py +168 -50
  73. odxtools/dynamicendmarkerfield.py +134 -0
  74. odxtools/dynamiclengthfield.py +41 -37
  75. odxtools/dyndefinedspec.py +177 -0
  76. odxtools/dynenddopref.py +38 -0
  77. odxtools/ecuvariantmatcher.py +6 -7
  78. odxtools/element.py +13 -15
  79. odxtools/encodestate.py +199 -22
  80. odxtools/endofpdufield.py +31 -18
  81. odxtools/environmentdata.py +8 -1
  82. odxtools/environmentdatadescription.py +198 -38
  83. odxtools/exceptions.py +11 -2
  84. odxtools/field.py +10 -10
  85. odxtools/functionalclass.py +3 -5
  86. odxtools/inputparam.py +3 -12
  87. odxtools/leadinglengthinfotype.py +37 -18
  88. odxtools/library.py +66 -0
  89. odxtools/loadfile.py +64 -0
  90. odxtools/matchingparameter.py +3 -3
  91. odxtools/message.py +0 -7
  92. odxtools/minmaxlengthtype.py +61 -33
  93. odxtools/modification.py +3 -5
  94. odxtools/multiplexer.py +128 -73
  95. odxtools/multiplexercase.py +13 -14
  96. odxtools/multiplexerdefaultcase.py +15 -12
  97. odxtools/multiplexerswitchkey.py +4 -5
  98. odxtools/nameditemlist.py +29 -5
  99. odxtools/negoutputparam.py +3 -5
  100. odxtools/odxcategory.py +83 -0
  101. odxtools/odxlink.py +60 -51
  102. odxtools/odxtypes.py +37 -5
  103. odxtools/outputparam.py +4 -15
  104. odxtools/parameterinfo.py +218 -67
  105. odxtools/parameters/codedconstparameter.py +16 -24
  106. odxtools/parameters/dynamicparameter.py +5 -4
  107. odxtools/parameters/lengthkeyparameter.py +60 -26
  108. odxtools/parameters/matchingrequestparameter.py +23 -11
  109. odxtools/parameters/nrcconstparameter.py +45 -46
  110. odxtools/parameters/parameter.py +54 -56
  111. odxtools/parameters/parameterwithdop.py +15 -25
  112. odxtools/parameters/physicalconstantparameter.py +15 -18
  113. odxtools/parameters/reservedparameter.py +6 -2
  114. odxtools/parameters/systemparameter.py +55 -11
  115. odxtools/parameters/tableentryparameter.py +3 -2
  116. odxtools/parameters/tablekeyparameter.py +103 -49
  117. odxtools/parameters/tablestructparameter.py +47 -48
  118. odxtools/parameters/valueparameter.py +16 -20
  119. odxtools/paramlengthinfotype.py +52 -32
  120. odxtools/parentref.py +16 -2
  121. odxtools/physicaldimension.py +3 -8
  122. odxtools/progcode.py +26 -11
  123. odxtools/protstack.py +3 -5
  124. odxtools/py.typed +0 -0
  125. odxtools/relateddoc.py +7 -9
  126. odxtools/request.py +120 -10
  127. odxtools/response.py +123 -23
  128. odxtools/scaleconstr.py +3 -3
  129. odxtools/servicebinner.py +1 -1
  130. odxtools/singleecujob.py +12 -10
  131. odxtools/snrefcontext.py +29 -0
  132. odxtools/specialdata.py +3 -5
  133. odxtools/specialdatagroup.py +7 -9
  134. odxtools/specialdatagroupcaption.py +3 -6
  135. odxtools/standardlengthtype.py +80 -14
  136. odxtools/state.py +3 -5
  137. odxtools/statechart.py +13 -19
  138. odxtools/statetransition.py +7 -17
  139. odxtools/staticfield.py +31 -25
  140. odxtools/subcomponent.py +288 -0
  141. odxtools/swvariable.py +21 -0
  142. odxtools/table.py +7 -8
  143. odxtools/tablerow.py +19 -11
  144. odxtools/teammember.py +3 -5
  145. odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
  146. odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
  147. odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
  148. odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
  149. odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
  150. odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
  151. odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
  152. odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
  153. odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
  154. odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
  155. odxtools/templates/macros/printDOP.xml.jinja2 +27 -133
  156. odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
  157. odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
  158. odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
  159. odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
  160. odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
  161. odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
  162. odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
  163. odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
  164. odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
  165. odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
  166. odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
  167. odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
  168. odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
  169. odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
  170. odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
  171. odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
  172. odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
  173. odxtools/templates/macros/printMux.xml.jinja2 +4 -3
  174. odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
  175. odxtools/templates/macros/printParam.xml.jinja2 +11 -12
  176. odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
  177. odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
  178. odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
  179. odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
  180. odxtools/templates/macros/printService.xml.jinja2 +3 -2
  181. odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
  182. odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
  183. odxtools/templates/macros/printState.xml.jinja2 +1 -1
  184. odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
  185. odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
  186. odxtools/templates/macros/printStaticField.xml.jinja2 +1 -1
  187. odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
  188. odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
  189. odxtools/templates/macros/printTable.xml.jinja2 +4 -5
  190. odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
  191. odxtools/uds.py +2 -10
  192. odxtools/unit.py +4 -8
  193. odxtools/unitgroup.py +3 -5
  194. odxtools/unitspec.py +17 -17
  195. odxtools/utils.py +38 -20
  196. odxtools/variablegroup.py +32 -0
  197. odxtools/version.py +2 -2
  198. odxtools/{write_pdx_file.py → writepdxfile.py} +20 -10
  199. odxtools/xdoc.py +3 -5
  200. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/METADATA +20 -21
  201. odxtools-9.3.0.dist-info/RECORD +228 -0
  202. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
  203. odxtools/createcompanydatas.py +0 -17
  204. odxtools/createsdgs.py +0 -19
  205. odxtools/load_file.py +0 -13
  206. odxtools/load_odx_d_file.py +0 -6
  207. odxtools/load_pdx_file.py +0 -8
  208. odxtools/templates/macros/printVariant.xml.jinja2 +0 -216
  209. odxtools-6.7.0.dist-info/RECORD +0 -182
  210. /odxtools/{diaglayertype.py → diaglayers/diaglayertype.py} +0 -0
  211. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
  212. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
  213. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
@@ -1,22 +1,20 @@
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
6
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
7
+ from .snrefcontext import SnRefContext
7
8
  from .specialdata import SpecialData
8
9
  from .specialdatagroupcaption import SpecialDataGroupCaption
9
10
 
10
- if TYPE_CHECKING:
11
- from .diaglayer import DiagLayer
12
-
13
11
 
14
12
  @dataclass
15
13
  class SpecialDataGroup:
16
14
  sdg_caption: Optional[SpecialDataGroupCaption]
17
15
  sdg_caption_ref: Optional[OdxLinkRef]
18
- semantic_info: Optional[str] # the "SI" attribute
19
16
  values: List[Union["SpecialDataGroup", SpecialData]]
17
+ semantic_info: Optional[str] # the "SI" attribute
20
18
 
21
19
  @staticmethod
22
20
  def from_et(et_element: ElementTree.Element,
@@ -53,7 +51,7 @@ class SpecialDataGroup:
53
51
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
54
52
  result = {}
55
53
 
56
- if self.sdg_caption is not None:
54
+ if self.sdg_caption_ref is None and self.sdg_caption is not None:
57
55
  result.update(self.sdg_caption._build_odxlinks())
58
56
 
59
57
  for val in self.values:
@@ -72,11 +70,11 @@ class SpecialDataGroup:
72
70
  for val in self.values:
73
71
  val._resolve_odxlinks(odxlinks)
74
72
 
75
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
73
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
76
74
  # resolve the SNREFs of the caption, but only if the caption
77
75
  # was specified by value, not by reference
78
76
  if self.sdg_caption is not None and self.sdg_caption_ref is None:
79
- self.sdg_caption._resolve_snrefs(diag_layer)
77
+ self.sdg_caption._resolve_snrefs(context)
80
78
 
81
79
  for val in self.values:
82
- val._resolve_snrefs(diag_layer)
80
+ val._resolve_snrefs(context)
@@ -1,16 +1,13 @@
1
1
  # SPDX-License-Identifier: MIT
2
- # import warnings
3
2
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, Dict, List
3
+ from typing import Any, Dict, List
5
4
  from xml.etree import ElementTree
6
5
 
7
6
  from .element import IdentifiableElement
8
7
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
8
+ from .snrefcontext import SnRefContext
9
9
  from .utils import dataclass_fields_asdict
10
10
 
11
- if TYPE_CHECKING:
12
- from .diaglayer import DiagLayer
13
-
14
11
 
15
12
  @dataclass
16
13
  class SpecialDataGroupCaption(IdentifiableElement):
@@ -32,5 +29,5 @@ class SpecialDataGroupCaption(IdentifiableElement):
32
29
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
33
30
  pass
34
31
 
35
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
32
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
36
33
  pass
@@ -1,12 +1,17 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Optional
3
+ from typing import List, Literal, Optional
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import override
4
7
 
5
8
  from .decodestate import DecodeState
6
9
  from .diagcodedtype import DctType, DiagCodedType
7
10
  from .encodestate import EncodeState
8
- from .exceptions import odxassert, odxraise
9
- from .odxtypes import AtomicOdxType, DataType
11
+ from .exceptions import odxassert, odxraise, odxrequire
12
+ from .odxlink import OdxDocFragment
13
+ from .odxtypes import AtomicOdxType, DataType, odxstr_to_bool
14
+ from .utils import dataclass_fields_asdict
10
15
 
11
16
 
12
17
  @dataclass
@@ -16,10 +21,36 @@ class StandardLengthType(DiagCodedType):
16
21
  bit_mask: Optional[int]
17
22
  is_condensed_raw: Optional[bool]
18
23
 
24
+ @staticmethod
25
+ @override
26
+ def from_et(et_element: ElementTree.Element,
27
+ doc_frags: List[OdxDocFragment]) -> "StandardLengthType":
28
+ kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
29
+
30
+ bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
31
+ bit_mask = None
32
+ if (bit_mask_str := et_element.findtext("BIT-MASK")) is not None:
33
+ # The XSD uses the type xsd:hexBinary
34
+ # xsd:hexBinary allows for leading/trailing whitespace, empty strings, and it only allows an even
35
+ # number of hex digits, while some of the examples shown in the ODX specification exhibit an
36
+ # odd number of hex digits.
37
+ # This causes a validation paradox, so we try to be flexible
38
+ bit_mask_str = bit_mask_str.strip()
39
+ if len(bit_mask_str):
40
+ bit_mask = int(bit_mask_str, 16)
41
+ is_condensed_raw = odxstr_to_bool(et_element.get("IS-CONDENSED"))
42
+
43
+ return StandardLengthType(
44
+ bit_length=bit_length, bit_mask=bit_mask, is_condensed_raw=is_condensed_raw, **kwargs)
45
+
19
46
  @property
20
47
  def dct_type(self) -> DctType:
21
48
  return "STANDARD-LENGTH-TYPE"
22
49
 
50
+ @property
51
+ def is_condensed(self) -> bool:
52
+ return self.is_condensed_raw is True
53
+
23
54
  def __post_init__(self) -> None:
24
55
  if self.bit_mask is not None:
25
56
  maskable_types = (DataType.A_UINT32, DataType.A_INT32, DataType.A_BYTEFIELD)
@@ -28,11 +59,46 @@ class StandardLengthType(DiagCodedType):
28
59
  'Can not apply a bit_mask on a value of type {self.base_data_type}',
29
60
  )
30
61
 
62
+ def __get_raw_mask(self, internal_value: AtomicOdxType) -> Optional[bytes]:
63
+ """Returns a byte field where all bits that are used by the
64
+ DiagCoded type are set and all unused ones are not set.
65
+
66
+ If `None` is returned, all bits are used.
67
+ """
68
+ if self.bit_mask is None:
69
+ return None
70
+
71
+ if self.is_condensed:
72
+ odxraise("Condensed bit masks are not yet supported", NotImplementedError)
73
+ return
74
+
75
+ endianness: Literal["little", "big"] = "big"
76
+ if not self.is_highlow_byte_order and self.base_data_type in [
77
+ DataType.A_INT32, DataType.A_UINT32, DataType.A_FLOAT32, DataType.A_FLOAT64
78
+ ]:
79
+ # TODO (?): Technically, little endian A_UNICODE2STRING
80
+ # objects require a byte swap for each 16 bit letter, and
81
+ # thus also for the mask. I somehow doubt that this has
82
+ # been anticipated by the standard, though...
83
+ endianness = "little"
84
+
85
+ sz: int
86
+ if isinstance(internal_value, (bytes, bytearray)):
87
+ sz = len(internal_value)
88
+ else:
89
+ sz = (odxrequire(self.get_static_bit_length()) + 7) // 8
90
+
91
+ max_value = (1 << (sz * 8)) - 1
92
+ bit_mask = self.bit_mask & max_value
93
+
94
+ return bit_mask.to_bytes(sz, endianness)
95
+
31
96
  def __apply_mask(self, internal_value: AtomicOdxType) -> AtomicOdxType:
32
97
  if self.bit_mask is None:
33
98
  return internal_value
34
- if self.is_condensed_raw is True:
35
- raise NotImplementedError("Serialization of condensed bit mask is not supported")
99
+ if self.is_condensed:
100
+ odxraise("Serialization of condensed bit mask is not supported", NotImplementedError)
101
+ return
36
102
  if isinstance(internal_value, int):
37
103
  return internal_value & self.bit_mask
38
104
  if isinstance(internal_value, bytes):
@@ -46,16 +112,16 @@ class StandardLengthType(DiagCodedType):
46
112
  def get_static_bit_length(self) -> Optional[int]:
47
113
  return self.bit_length
48
114
 
49
- def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
50
- bit_position: int) -> bytes:
51
- return self._encode_internal_value(
52
- self.__apply_mask(internal_value),
53
- bit_position,
54
- self.bit_length,
55
- self.base_data_type,
56
- is_highlow_byte_order=self.is_highlow_byte_order,
57
- )
115
+ @override
116
+ def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
117
+ encode_state.emplace_atomic_value(
118
+ internal_value=self.__apply_mask(internal_value),
119
+ used_mask=self.__get_raw_mask(internal_value),
120
+ bit_length=self.bit_length,
121
+ base_data_type=self.base_data_type,
122
+ is_highlow_byte_order=self.is_highlow_byte_order)
58
123
 
124
+ @override
59
125
  def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
60
126
  internal_value = decode_state.extract_atomic_value(
61
127
  self.bit_length,
odxtools/state.py CHANGED
@@ -1,15 +1,13 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List
3
+ from typing import Any, Dict, List
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
7
7
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
8
+ from .snrefcontext import SnRefContext
8
9
  from .utils import dataclass_fields_asdict
9
10
 
10
- if TYPE_CHECKING:
11
- from .diaglayer import DiagLayer
12
-
13
11
 
14
12
  @dataclass
15
13
  class State(IdentifiableElement):
@@ -29,5 +27,5 @@ class State(IdentifiableElement):
29
27
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
30
28
  pass
31
29
 
32
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
30
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
33
31
  pass
odxtools/statechart.py CHANGED
@@ -1,19 +1,17 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List
3
+ from typing import Any, Dict, List
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
7
7
  from .exceptions import odxrequire
8
8
  from .nameditemlist import NamedItemList
9
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
9
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
10
+ from .snrefcontext import SnRefContext
10
11
  from .state import State
11
12
  from .statetransition import StateTransition
12
13
  from .utils import dataclass_fields_asdict
13
14
 
14
- if TYPE_CHECKING:
15
- from .diaglayer import DiagLayer
16
-
17
15
 
18
16
  @dataclass
19
17
  class StateChart(IdentifiableElement):
@@ -68,25 +66,21 @@ class StateChart(IdentifiableElement):
68
66
  for st in self.states:
69
67
  st._resolve_odxlinks(odxlinks)
70
68
 
71
- # For now, we assume that the start state short name ref
72
- # points to a state local to the state chart. TODO: The XML
69
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
70
+ context.state_chart = self
71
+
72
+ # For now, we assume that the start state short name reference
73
+ # points to a local state of the state chart. TODO: The XSD
73
74
  # allows to define state charts without any states, yet the
74
75
  # start state SNREF is mandatory. Is this a gap in the spec or
75
76
  # does it allow "foreign" start states? If the latter, what
76
77
  # does that mean?
77
- self._start_state: State
78
- for st in self.states:
79
- if st.short_name == self.start_state_snref:
80
- self._start_state = st
81
- break
78
+ self._start_state = resolve_snref(self.start_state_snref, self.states, State)
82
79
 
83
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
84
80
  for st in self.states:
85
- st._resolve_snrefs(diag_layer)
81
+ st._resolve_snrefs(context)
86
82
 
87
83
  for strans in self.state_transitions:
88
- # note that the signature of the state transition's
89
- # _resolve_snrefs() method is non-standard as the
90
- # namespace of these SNREFs is the state chart, not the
91
- # whole diag layer...
92
- strans._resolve_snrefs(diag_layer, states=self.states)
84
+ strans._resolve_snrefs(context)
85
+
86
+ context.state_chart = None
@@ -1,17 +1,15 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, Iterable, List
3
+ from typing import Any, Dict, List
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
7
7
  from .exceptions import odxrequire
8
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
8
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
9
+ from .snrefcontext import SnRefContext
9
10
  from .state import State
10
11
  from .utils import dataclass_fields_asdict
11
12
 
12
- if TYPE_CHECKING:
13
- from .diaglayer import DiagLayer
14
-
15
13
 
16
14
  @dataclass
17
15
  class StateTransition(IdentifiableElement):
@@ -50,15 +48,7 @@ class StateTransition(IdentifiableElement):
50
48
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
51
49
  pass
52
50
 
53
- # note that the signature of this method is non-standard because
54
- # the namespace of these SNREFs is the corresponding state
55
- # chart. To mitigate this a bit, the non-standard parameters are
56
- # keyword-only...
57
- def _resolve_snrefs(self, diag_layer: "DiagLayer", *, states: Iterable[State]) -> None:
58
- self._source_state: State
59
- self._target_state: State
60
- for st in states:
61
- if st.short_name == self.source_snref:
62
- self._source_state = st
63
- if st.short_name == self.target_snref:
64
- self._target_state = st
51
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
52
+ states = odxrequire(context.state_chart).states
53
+ self._source_state = resolve_snref(self.source_snref, states, State)
54
+ self._target_state = resolve_snref(self.target_snref, states, State)
odxtools/staticfield.py CHANGED
@@ -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
3
+ from typing import Any, Dict, List, Sequence
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -11,11 +11,9 @@ from .exceptions import odxassert, odxraise, odxrequire
11
11
  from .field import Field
12
12
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
13
13
  from .odxtypes import ParameterValue
14
+ from .snrefcontext import SnRefContext
14
15
  from .utils import dataclass_fields_asdict
15
16
 
16
- if TYPE_CHECKING:
17
- from .diaglayer import DiagLayer
18
-
19
17
 
20
18
  @dataclass
21
19
  class StaticField(Field):
@@ -44,40 +42,43 @@ class StaticField(Field):
44
42
  super()._resolve_odxlinks(odxlinks)
45
43
 
46
44
  @override
47
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
48
- super()._resolve_snrefs(diag_layer)
45
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
46
+ super()._resolve_snrefs(context)
49
47
 
50
48
  @override
51
- def convert_physical_to_bytes(
52
- self,
53
- physical_value: ParameterValue,
54
- encode_state: EncodeState,
55
- bit_position: int = 0,
56
- ) -> bytes:
49
+ def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
50
+
57
51
  if not isinstance(physical_value,
58
- (tuple, list)) or len(physical_value) != self.fixed_number_of_items:
52
+ Sequence) or len(physical_value) != self.fixed_number_of_items:
59
53
  odxraise(f"Value for static field '{self.short_name}' "
60
54
  f"must be a list of size {self.fixed_number_of_items}")
61
55
 
62
- result = bytearray()
63
- for val in physical_value:
56
+ orig_is_end_of_pdu = encode_state.is_end_of_pdu
57
+ encode_state.is_end_of_pdu = False
58
+ for i, val in enumerate(physical_value):
64
59
  if not isinstance(val, dict):
65
60
  odxraise(f"The individual parameter values for static field '{self.short_name}' "
66
61
  f"must be dictionaries for structure '{self.structure.short_name}'")
67
62
 
68
- data = self.structure.convert_physical_to_bytes(val, encode_state)
63
+ if i == len(physical_value) - 1:
64
+ encode_state.is_end_of_pdu = orig_is_end_of_pdu
69
65
 
70
- if len(data) > self.item_byte_size:
71
- odxraise(f"Insufficient item byte size for static field {self.short_name}: "
72
- f"Is {self.item_byte_size} bytes, but need at least {len(data)} bytes")
73
- data = data[:self.item_byte_size]
74
- elif len(data) < self.item_byte_size:
75
- # add some padding bytes
76
- data = data.ljust(self.item_byte_size, b'\x00')
66
+ pos_before = encode_state.cursor_byte_position
67
+ self.structure.encode_into_pdu(val, encode_state)
68
+ pos_after = encode_state.cursor_byte_position
77
69
 
78
- result += data
70
+ if pos_after - pos_before > self.item_byte_size:
71
+ odxraise(
72
+ f"Insufficient item byte size for static field {self.short_name}: "
73
+ f"Is {self.item_byte_size} bytes, but need at least {pos_after - pos_before} bytes"
74
+ )
75
+ encode_state.cursor_byte_position = pos_before + self.item_byte_size
76
+ elif pos_after - pos_before < self.item_byte_size:
77
+ # add some padding bytes
78
+ encode_state.emplace_bytes(b'\x00' * (self.item_byte_size -
79
+ (pos_after - pos_before)))
79
80
 
80
- return result
81
+ encode_state.is_end_of_pdu = orig_is_end_of_pdu
81
82
 
82
83
  @override
83
84
  def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
@@ -85,6 +86,9 @@ class StaticField(Field):
85
86
  odxassert(decode_state.cursor_bit_position == 0,
86
87
  "No bit position can be specified for static length fields!")
87
88
 
89
+ orig_origin = decode_state.origin_byte_position
90
+ decode_state.origin_byte_position = decode_state.cursor_byte_position
91
+
88
92
  result: List[ParameterValue] = []
89
93
  for _ in range(self.fixed_number_of_items):
90
94
  orig_cursor = decode_state.cursor_byte_position
@@ -98,4 +102,6 @@ class StaticField(Field):
98
102
 
99
103
  decode_state.cursor_byte_position = orig_cursor + self.item_byte_size
100
104
 
105
+ decode_state.origin_byte_position = orig_origin
106
+
101
107
  return result