odxtools 9.2.0__py3-none-any.whl → 9.4.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 (63) hide show
  1. odxtools/companyrevisioninfo.py +1 -1
  2. odxtools/comparaminstance.py +1 -6
  3. odxtools/comparamspec.py +3 -2
  4. odxtools/comparamsubset.py +3 -5
  5. odxtools/dataobjectproperty.py +12 -6
  6. odxtools/decodestate.py +120 -26
  7. odxtools/description.py +19 -2
  8. odxtools/diagcodedtype.py +9 -2
  9. odxtools/diagcomm.py +4 -4
  10. odxtools/diaglayercontainer.py +2 -2
  11. odxtools/diaglayers/diaglayerraw.py +2 -2
  12. odxtools/diaglayers/hierarchyelement.py +2 -0
  13. odxtools/diagnostictroublecode.py +4 -8
  14. odxtools/diagservice.py +70 -4
  15. odxtools/diagvariable.py +45 -7
  16. odxtools/encodestate.py +147 -51
  17. odxtools/encoding.py +56 -0
  18. odxtools/environmentdatadescription.py +77 -47
  19. odxtools/functionalclass.py +7 -2
  20. odxtools/inputparam.py +9 -3
  21. odxtools/leadinglengthinfotype.py +4 -0
  22. odxtools/minmaxlengthtype.py +57 -36
  23. odxtools/modification.py +3 -2
  24. odxtools/odxcategory.py +2 -2
  25. odxtools/odxlink.py +31 -7
  26. odxtools/odxtypes.py +1 -1
  27. odxtools/outputparam.py +8 -3
  28. odxtools/parameters/matchingrequestparameter.py +1 -0
  29. odxtools/parameters/physicalconstantparameter.py +1 -0
  30. odxtools/parameters/reservedparameter.py +1 -0
  31. odxtools/parameters/tableentryparameter.py +15 -4
  32. odxtools/parameters/tablekeyparameter.py +20 -17
  33. odxtools/paramlengthinfotype.py +5 -3
  34. odxtools/physicaltype.py +2 -1
  35. odxtools/scaleconstr.py +4 -4
  36. odxtools/standardlengthtype.py +98 -22
  37. odxtools/statetransition.py +24 -3
  38. odxtools/structure.py +10 -2
  39. odxtools/swvariable.py +3 -1
  40. odxtools/table.py +55 -3
  41. odxtools/tablerow.py +91 -8
  42. odxtools/templates/macros/printDOP.xml.jinja2 +2 -2
  43. odxtools/templates/macros/printDescription.xml.jinja2 +5 -1
  44. odxtools/templates/macros/printDiagLayer.xml.jinja2 +1 -1
  45. odxtools/templates/macros/printDiagVariable.xml.jinja2 +12 -3
  46. odxtools/templates/macros/printFunctionalClass.xml.jinja2 +4 -0
  47. odxtools/templates/macros/printParentRef.xml.jinja2 +27 -0
  48. odxtools/templates/macros/printProtocol.xml.jinja2 +1 -1
  49. odxtools/templates/macros/printService.xml.jinja2 +30 -0
  50. odxtools/templates/macros/printStructure.xml.jinja2 +2 -1
  51. odxtools/templates/macros/printTable.xml.jinja2 +43 -0
  52. odxtools/templates/macros/printUnitSpec.xml.jinja2 +4 -0
  53. odxtools/uds.py +2 -2
  54. odxtools/unit.py +8 -12
  55. odxtools/unitspec.py +5 -2
  56. odxtools/utils.py +44 -1
  57. odxtools/version.py +2 -2
  58. {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/METADATA +2 -2
  59. {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/RECORD +63 -62
  60. {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/WHEEL +1 -1
  61. {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/LICENSE +0 -0
  62. {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/entry_points.txt +0 -0
  63. {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@ from dataclasses import dataclass
3
3
  from typing import List, Literal, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
- from typing_extensions import override
6
+ from typing_extensions import SupportsBytes, override
7
7
 
8
8
  from .decodestate import DecodeState
9
9
  from .diagcodedtype import DctType, DiagCodedType
@@ -59,7 +59,7 @@ class StandardLengthType(DiagCodedType):
59
59
  'Can not apply a bit_mask on a value of type {self.base_data_type}',
60
60
  )
61
61
 
62
- def __get_raw_mask(self, internal_value: AtomicOdxType) -> Optional[bytes]:
62
+ def __get_used_mask(self, internal_value: AtomicOdxType) -> Optional[bytes]:
63
63
  """Returns a byte field where all bits that are used by the
64
64
  DiagCoded type are set and all unused ones are not set.
65
65
 
@@ -68,10 +68,6 @@ class StandardLengthType(DiagCodedType):
68
68
  if self.bit_mask is None:
69
69
  return None
70
70
 
71
- if self.is_condensed:
72
- odxraise("Condensed bit masks are not yet supported", NotImplementedError)
73
- return
74
-
75
71
  endianness: Literal["little", "big"] = "big"
76
72
  if not self.is_highlow_byte_order and self.base_data_type in [
77
73
  DataType.A_INT32, DataType.A_UINT32, DataType.A_FLOAT32, DataType.A_FLOAT64
@@ -82,52 +78,132 @@ class StandardLengthType(DiagCodedType):
82
78
  # been anticipated by the standard, though...
83
79
  endianness = "little"
84
80
 
81
+ if self.is_condensed:
82
+ # if a condensed bitmask is specified, the number of bits
83
+ # set to one in the bit mask are used in the PDU
84
+
85
+ # TODO: this is pretty slow. replace it by
86
+ # `self.bit_mask.bit_count()` once we require python >=
87
+ # 3.10.
88
+ bit_sz = bin(self.bit_mask).count("1")
89
+ used_mask = (1 << bit_sz) - 1
90
+
91
+ return used_mask.to_bytes((bit_sz + 7) // 8, endianness)
92
+
85
93
  sz: int
86
- if isinstance(internal_value, (bytes, bytearray)):
87
- sz = len(internal_value)
94
+ if isinstance(internal_value, (bytearray, SupportsBytes)):
95
+ sz = len(bytes(internal_value))
88
96
  else:
89
97
  sz = (odxrequire(self.get_static_bit_length()) + 7) // 8
90
98
 
91
99
  max_value = (1 << (sz * 8)) - 1
92
- bit_mask = self.bit_mask & max_value
100
+ used_mask = self.bit_mask & max_value
93
101
 
94
- return bit_mask.to_bytes(sz, endianness)
102
+ return used_mask.to_bytes(sz, endianness)
95
103
 
96
104
  def __apply_mask(self, internal_value: AtomicOdxType) -> AtomicOdxType:
97
105
  if self.bit_mask is None:
98
106
  return internal_value
107
+
99
108
  if self.is_condensed:
100
- odxraise("Serialization of condensed bit mask is not supported", NotImplementedError)
101
- return
109
+ int_value: int
110
+ if isinstance(internal_value, (bytearray, SupportsBytes)):
111
+ int_value = int.from_bytes(internal_value, 'big')
112
+ elif isinstance(internal_value, int):
113
+ int_value = internal_value
114
+ else:
115
+ odxraise("bit masks can only be specified for integers and byte fields")
116
+ return None
117
+
118
+ result = 0
119
+ mask_bit = 0
120
+ result_bit = 0
121
+
122
+ while self.bit_mask >= (1 << mask_bit):
123
+ if self.bit_mask & (1 << mask_bit):
124
+ result |= ((int_value & (1 << mask_bit)) >> mask_bit) << result_bit
125
+ result_bit += 1
126
+
127
+ mask_bit += 1
128
+
129
+ if isinstance(internal_value, (bytearray, SupportsBytes)):
130
+ return result.to_bytes(len(internal_value), 'big')
131
+
132
+ return result
133
+
102
134
  if isinstance(internal_value, int):
103
135
  return internal_value & self.bit_mask
104
- if isinstance(internal_value, bytes):
136
+ if isinstance(internal_value, (bytearray, SupportsBytes)):
105
137
  int_value = int.from_bytes(internal_value, 'big')
106
- int_value = int_value & self.bit_mask
107
- return int_value.to_bytes(len(internal_value), 'big')
138
+ int_value &= self.bit_mask
139
+ return int_value.to_bytes(len(bytes(internal_value)), 'big')
108
140
 
109
141
  odxraise(f'Can not apply a bit_mask on a value of type {type(internal_value)}')
110
142
  return internal_value
111
143
 
144
+ def __unapply_mask(self, raw_value: AtomicOdxType) -> AtomicOdxType:
145
+ if self.bit_mask is None:
146
+ return raw_value
147
+ if self.is_condensed:
148
+ int_value: int
149
+ if isinstance(raw_value, (bytearray, SupportsBytes)):
150
+ int_value = int.from_bytes(raw_value, 'big')
151
+ elif isinstance(raw_value, int):
152
+ int_value = raw_value
153
+ else:
154
+ odxraise("bit masks can only be specified for integers and byte fields")
155
+ return None
156
+
157
+ result = 0
158
+ mask_bit = 0
159
+ input_bit = 0
160
+ while self.bit_mask >= (1 << mask_bit):
161
+ if self.bit_mask & (1 << mask_bit):
162
+ result |= ((int_value & (1 << input_bit)) >> input_bit) << mask_bit
163
+ input_bit += 1
164
+
165
+ mask_bit += 1
166
+
167
+ if isinstance(raw_value, (bytearray, SupportsBytes)):
168
+ return result.to_bytes(len(raw_value), 'big')
169
+
170
+ return result
171
+ if isinstance(raw_value, int):
172
+ return raw_value & self.bit_mask
173
+ if isinstance(raw_value, (bytearray, SupportsBytes)):
174
+ int_value = int.from_bytes(raw_value, 'big')
175
+ int_value &= self.bit_mask
176
+ return int_value
177
+
178
+ odxraise(f'Can not apply a bit_mask on a value of type {type(raw_value)}')
179
+ return raw_value
180
+
112
181
  def get_static_bit_length(self) -> Optional[int]:
182
+ if self.bit_mask is not None and self.is_condensed:
183
+ # TODO: this is pretty slow. replace it by
184
+ # `self.bit_mask.bit_count()` once we require python >=
185
+ # 3.10.
186
+ return bin(self.bit_mask).count("1")
187
+
113
188
  return self.bit_length
114
189
 
115
190
  @override
116
191
  def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
117
192
  encode_state.emplace_atomic_value(
118
193
  internal_value=self.__apply_mask(internal_value),
119
- used_mask=self.__get_raw_mask(internal_value),
194
+ used_mask=self.__get_used_mask(internal_value),
120
195
  bit_length=self.bit_length,
121
196
  base_data_type=self.base_data_type,
197
+ base_type_encoding=self.base_type_encoding,
122
198
  is_highlow_byte_order=self.is_highlow_byte_order)
123
199
 
124
200
  @override
125
201
  def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
126
- internal_value = decode_state.extract_atomic_value(
127
- self.bit_length,
128
- self.base_data_type,
129
- self.is_highlow_byte_order,
130
- )
131
- internal_value = self.__apply_mask(internal_value)
202
+ raw_value = decode_state.extract_atomic_value(
203
+ bit_length=self.bit_length,
204
+ base_data_type=self.base_data_type,
205
+ base_type_encoding=self.base_type_encoding,
206
+ is_highlow_byte_order=self.is_highlow_byte_order)
207
+ internal_value = self.__unapply_mask(raw_value)
132
208
 
133
209
  return internal_value
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List
3
+ from typing import Any, Dict, List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
@@ -11,6 +11,20 @@ from .state import State
11
11
  from .utils import dataclass_fields_asdict
12
12
 
13
13
 
14
+ @dataclass
15
+ class ExternalAccessMethod(IdentifiableElement):
16
+ method: str
17
+
18
+ @staticmethod
19
+ def from_et(et_element: ElementTree.Element,
20
+ doc_frags: List[OdxDocFragment]) -> "ExternalAccessMethod":
21
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
22
+
23
+ method = odxrequire(et_element.findtext("METHOD"))
24
+
25
+ return ExternalAccessMethod(method=method, **kwargs)
26
+
27
+
14
28
  @dataclass
15
29
  class StateTransition(IdentifiableElement):
16
30
  """
@@ -18,7 +32,7 @@ class StateTransition(IdentifiableElement):
18
32
  """
19
33
  source_snref: str
20
34
  target_snref: str
21
- # external_access_method: Optional[ExternalAccessMethod] # TODO
35
+ external_access_method: Optional[ExternalAccessMethod]
22
36
 
23
37
  @property
24
38
  def source_state(self) -> State:
@@ -40,7 +54,14 @@ class StateTransition(IdentifiableElement):
40
54
  target_snref_elem = odxrequire(et_element.find("TARGET-SNREF"))
41
55
  target_snref = odxrequire(target_snref_elem.attrib["SHORT-NAME"])
42
56
 
43
- return StateTransition(source_snref=source_snref, target_snref=target_snref, **kwargs)
57
+ external_access_method = None
58
+ if (eam_elem := et_element.find("EXTERNAL-ACCESS-METHOD")) is not None:
59
+ external_access_method = ExternalAccessMethod.from_et(eam_elem, doc_frags)
60
+ return StateTransition(
61
+ source_snref=source_snref,
62
+ target_snref=target_snref,
63
+ external_access_method=external_access_method,
64
+ **kwargs)
44
65
 
45
66
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
46
67
  return {self.odx_id: self}
odxtools/structure.py CHANGED
@@ -1,19 +1,27 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List
3
+ from typing import List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .basicstructure import BasicStructure
7
7
  from .odxlink import OdxDocFragment
8
+ from .odxtypes import odxstr_to_bool
8
9
  from .utils import dataclass_fields_asdict
9
10
 
10
11
 
11
12
  @dataclass
12
13
  class Structure(BasicStructure):
14
+ is_visible_raw: Optional[bool]
13
15
 
14
16
  @staticmethod
15
17
  def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Structure":
16
18
  """Read a STRUCTURE element from XML."""
17
19
  kwargs = dataclass_fields_asdict(BasicStructure.from_et(et_element, doc_frags))
18
20
 
19
- return Structure(**kwargs)
21
+ is_visible_raw = odxstr_to_bool(et_element.get("IS-VISIBLE"))
22
+
23
+ return Structure(is_visible_raw=is_visible_raw, **kwargs)
24
+
25
+ @property
26
+ def is_visible(self) -> bool:
27
+ return self.is_visible_raw in (True, None)
odxtools/swvariable.py CHANGED
@@ -11,11 +11,13 @@ from .utils import dataclass_fields_asdict
11
11
  @dataclass
12
12
  class SwVariable(NamedElement):
13
13
  origin: Optional[str]
14
+ oid: Optional[str]
14
15
 
15
16
  @staticmethod
16
17
  def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "SwVariable":
17
18
  kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
18
19
 
19
20
  origin = et_element.findtext("ORIGIN")
21
+ oid = et_element.attrib.get("OID")
20
22
 
21
- return SwVariable(origin=origin, **kwargs)
23
+ return SwVariable(origin=origin, oid=oid, **kwargs)
odxtools/table.py CHANGED
@@ -5,16 +5,54 @@ from xml.etree import ElementTree
5
5
 
6
6
  from .admindata import AdminData
7
7
  from .dataobjectproperty import DataObjectProperty
8
+ from .diagcomm import DiagComm
8
9
  from .element import IdentifiableElement
9
- from .exceptions import odxassert
10
+ from .exceptions import odxassert, odxrequire
10
11
  from .nameditemlist import NamedItemList
11
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
12
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
12
13
  from .snrefcontext import SnRefContext
13
14
  from .specialdatagroup import SpecialDataGroup
14
15
  from .tablerow import TableRow
15
16
  from .utils import dataclass_fields_asdict
16
17
 
17
18
 
19
+ @dataclass
20
+ class TableDiagCommConnector:
21
+ semantic: str
22
+
23
+ diag_comm_ref: Optional[OdxLinkRef]
24
+ diag_comm_snref: Optional[str]
25
+
26
+ @property
27
+ def diag_comm(self) -> DiagComm:
28
+ return self._diag_comm
29
+
30
+ @staticmethod
31
+ def from_et(et_element: ElementTree.Element,
32
+ doc_frags: List[OdxDocFragment]) -> "TableDiagCommConnector":
33
+
34
+ semantic = odxrequire(et_element.findtext("SEMANTIC"))
35
+ diag_comm_ref = OdxLinkRef.from_et(et_element.find("DIAG-COMM-REF"), doc_frags)
36
+ diag_comm_snref = None
37
+ if (dc_snref_elem := et_element.find("DIAG-COMM-SNREF")) is not None:
38
+ diag_comm_snref = odxrequire(dc_snref_elem.get("SHORT-NAME"))
39
+
40
+ return TableDiagCommConnector(
41
+ semantic=semantic, diag_comm_ref=diag_comm_ref, diag_comm_snref=diag_comm_snref)
42
+
43
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
44
+ return {}
45
+
46
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
47
+ if self.diag_comm_ref is not None:
48
+ self._diag_comm = odxlinks.resolve(self.diag_comm_ref, DiagComm)
49
+
50
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
51
+ if self.diag_comm_snref is not None:
52
+ dl = odxrequire(context.diag_layer)
53
+ self._diag_comm = resolve_snref(self.diag_comm_snref, dl.diag_comms, DiagComm)
54
+
55
+
18
56
  @dataclass
19
57
  class Table(IdentifiableElement):
20
58
  """This class represents a TABLE."""
@@ -24,7 +62,7 @@ class Table(IdentifiableElement):
24
62
  admin_data: Optional[AdminData]
25
63
  key_dop_ref: Optional[OdxLinkRef]
26
64
  table_rows_raw: List[Union[TableRow, OdxLinkRef]]
27
- # TODO: table_diag_comm_connectors
65
+ table_diag_comm_connectors: List[TableDiagCommConnector]
28
66
  sdgs: List[SpecialDataGroup]
29
67
 
30
68
  @staticmethod
@@ -47,6 +85,10 @@ class Table(IdentifiableElement):
47
85
  elif sub_elem.tag == "TABLE-ROW-REF":
48
86
  table_rows_raw.append(OdxLinkRef.from_et(sub_elem, doc_frags))
49
87
 
88
+ table_diag_comm_connectors = [
89
+ TableDiagCommConnector.from_et(dcc_elem, doc_frags) for dcc_elem in et_element.iterfind(
90
+ "TABLE-DIAG-COMM-CONNECTORS/TABLE-DIAG-COMM-CONNECTOR")
91
+ ]
50
92
  sdgs = [
51
93
  SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
52
94
  ]
@@ -58,6 +100,7 @@ class Table(IdentifiableElement):
58
100
  admin_data=admin_data,
59
101
  key_dop_ref=key_dop_ref,
60
102
  table_rows_raw=table_rows_raw,
103
+ table_diag_comm_connectors=table_diag_comm_connectors,
61
104
  sdgs=sdgs,
62
105
  **kwargs)
63
106
 
@@ -78,6 +121,9 @@ class Table(IdentifiableElement):
78
121
  if isinstance(table_row_wrapper, TableRow):
79
122
  result.update(table_row_wrapper._build_odxlinks())
80
123
 
124
+ for dcc in self.table_diag_comm_connectors:
125
+ result.update(dcc._build_odxlinks())
126
+
81
127
  return result
82
128
 
83
129
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
@@ -98,7 +144,13 @@ class Table(IdentifiableElement):
98
144
 
99
145
  self._table_rows = NamedItemList(table_rows)
100
146
 
147
+ for dcc in self.table_diag_comm_connectors:
148
+ dcc._resolve_odxlinks(odxlinks)
149
+
101
150
  def _resolve_snrefs(self, context: SnRefContext) -> None:
102
151
  for table_row_wrapper in self.table_rows_raw:
103
152
  if isinstance(table_row_wrapper, TableRow):
104
153
  table_row_wrapper._resolve_snrefs(context)
154
+
155
+ for dcc in self.table_diag_comm_connectors:
156
+ dcc._resolve_snrefs(context)
odxtools/tablerow.py CHANGED
@@ -3,15 +3,21 @@ from dataclasses import dataclass, fields
3
3
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
4
4
  from xml.etree import ElementTree
5
5
 
6
+ from .admindata import AdminData
7
+ from .audience import Audience
6
8
  from .basicstructure import BasicStructure
7
9
  from .dataobjectproperty import DataObjectProperty
8
10
  from .dtcdop import DtcDop
9
11
  from .element import IdentifiableElement
10
12
  from .exceptions import odxassert, odxraise, odxrequire
13
+ from .functionalclass import FunctionalClass
14
+ from .nameditemlist import NamedItemList
11
15
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
12
- from .odxtypes import AtomicOdxType
16
+ from .odxtypes import AtomicOdxType, odxstr_to_bool
13
17
  from .snrefcontext import SnRefContext
14
18
  from .specialdatagroup import SpecialDataGroup
19
+ from .state import State
20
+ from .statetransition import StateTransition
15
21
  from .utils import dataclass_fields_asdict
16
22
 
17
23
  if TYPE_CHECKING:
@@ -21,18 +27,52 @@ if TYPE_CHECKING:
21
27
  @dataclass
22
28
  class TableRow(IdentifiableElement):
23
29
  """This class represents a TABLE-ROW."""
24
- table_ref: OdxLinkRef
25
30
  key_raw: str
26
- structure_ref: Optional[OdxLinkRef]
27
- structure_snref: Optional[str]
31
+ table_ref: OdxLinkRef
28
32
 
29
- # the referenced DOP must be a simple DOP (i.e.,
30
- # DataObjectProperty or DtcDop, cf section 7.3.6.11 of the spec)!
33
+ # The spec mandates that either a structure or a non-complex DOP
34
+ # must be referenced here, i.e., exactly one of the four
35
+ # attributes below is not None
31
36
  dop_ref: Optional[OdxLinkRef]
32
37
  dop_snref: Optional[str]
38
+ structure_ref: Optional[OdxLinkRef]
39
+ structure_snref: Optional[str]
33
40
 
34
- semantic: Optional[str]
35
41
  sdgs: List[SpecialDataGroup]
42
+ audience: Optional[Audience]
43
+ functional_class_refs: List[OdxLinkRef]
44
+ state_transition_refs: List[OdxLinkRef]
45
+ pre_condition_state_refs: List[OdxLinkRef]
46
+ admin_data: Optional[AdminData]
47
+
48
+ is_executable_raw: Optional[bool]
49
+ semantic: Optional[str]
50
+ is_mandatory_raw: Optional[bool]
51
+ is_final_raw: Optional[bool]
52
+
53
+ @property
54
+ def functional_classes(self) -> NamedItemList[FunctionalClass]:
55
+ return self._functional_classes
56
+
57
+ @property
58
+ def state_transitions(self) -> NamedItemList[StateTransition]:
59
+ return self._state_transitions
60
+
61
+ @property
62
+ def pre_condition_states(self) -> NamedItemList[State]:
63
+ return self._pre_condition_states
64
+
65
+ @property
66
+ def is_executable(self) -> bool:
67
+ return self.is_executable_raw in (None, True)
68
+
69
+ @property
70
+ def is_mandatory(self) -> bool:
71
+ return self.is_mandatory_raw is True
72
+
73
+ @property
74
+ def is_final(self) -> bool:
75
+ return self.is_final_raw is True
36
76
 
37
77
  def __post_init__(self) -> None:
38
78
  self._structure: Optional[BasicStructure] = None
@@ -73,15 +113,49 @@ class TableRow(IdentifiableElement):
73
113
  SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
74
114
  ]
75
115
 
116
+ audience = None
117
+ if (audience_elem := et_element.find("AUDIENCE")) is not None:
118
+ audience = Audience.from_et(audience_elem, doc_frags)
119
+
120
+ functional_class_refs = [
121
+ odxrequire(OdxLinkRef.from_et(el, doc_frags))
122
+ for el in et_element.iterfind("FUNCT-CLASS-REFS/FUNCT-CLASS-REF")
123
+ ]
124
+
125
+ state_transition_refs = [
126
+ odxrequire(OdxLinkRef.from_et(el, doc_frags))
127
+ for el in et_element.iterfind("STATE-TRANSITION-REFS/STATE-TRANSITION-REF")
128
+ ]
129
+
130
+ pre_condition_state_refs = [
131
+ odxrequire(OdxLinkRef.from_et(el, doc_frags))
132
+ for el in et_element.iterfind("PRE-CONDITION-STATE-REFS/PRE-CONDITION-STATE-REF")
133
+ ]
134
+
135
+ admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
136
+
137
+ is_executable_raw = odxstr_to_bool(et_element.attrib.get("IS-EXECUTABLE"))
138
+ semantic = et_element.attrib.get("SEMANTIC")
139
+ is_mandatory_raw = odxstr_to_bool(et_element.attrib.get("IS-MANDATORY"))
140
+ is_final_raw = odxstr_to_bool(et_element.attrib.get("IS-FINAL"))
141
+
76
142
  return TableRow(
77
143
  table_ref=table_ref,
78
- semantic=semantic,
79
144
  key_raw=key_raw,
80
145
  structure_ref=structure_ref,
81
146
  structure_snref=structure_snref,
82
147
  dop_ref=dop_ref,
83
148
  dop_snref=dop_snref,
84
149
  sdgs=sdgs,
150
+ audience=audience,
151
+ functional_class_refs=functional_class_refs,
152
+ state_transition_refs=state_transition_refs,
153
+ pre_condition_state_refs=pre_condition_state_refs,
154
+ admin_data=admin_data,
155
+ is_executable_raw=is_executable_raw,
156
+ semantic=semantic,
157
+ is_mandatory_raw=is_mandatory_raw,
158
+ is_final_raw=is_final_raw,
85
159
  **kwargs)
86
160
 
87
161
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
@@ -108,6 +182,15 @@ class TableRow(IdentifiableElement):
108
182
  for sdg in self.sdgs:
109
183
  sdg._resolve_odxlinks(odxlinks)
110
184
 
185
+ self._functional_classes = NamedItemList(
186
+ [odxlinks.resolve(fc_ref, FunctionalClass) for fc_ref in self.functional_class_refs])
187
+
188
+ self._state_transitions = NamedItemList(
189
+ [odxlinks.resolve(st_ref, StateTransition) for st_ref in self.state_transition_refs])
190
+
191
+ self._pre_condition_states = NamedItemList(
192
+ [odxlinks.resolve(pcs_ref, State) for pcs_ref in self.pre_condition_state_refs])
193
+
111
194
  def _resolve_snrefs(self, context: SnRefContext) -> None:
112
195
  # convert the raw key into the proper internal
113
196
  # representation. note that we cannot do this earlier because
@@ -12,10 +12,10 @@
12
12
 
13
13
  {%- macro printDiagCodedType(dct) -%}
14
14
  <DIAG-CODED-TYPE {{-make_xml_attrib("BASE-DATA-TYPE", dct.base_data_type.value)}}
15
- {{-make_xml_attrib("BASE-TYPE-ENCODING", dct.base_type_encoding)}}
15
+ {{-make_xml_attrib("BASE-TYPE-ENCODING", dct.base_type_encoding and dct.base_type_encoding.value)}}
16
16
  {{-make_bool_xml_attrib("IS-HIGHLOW-BYTE-ORDER", dct.is_highlow_byte_order_raw)}}
17
17
  {%- if dct.termination is defined %}
18
- {{-make_xml_attrib("TERMINATION", dct.termination)}}
18
+ {{-make_xml_attrib("TERMINATION", dct.termination.value)}}
19
19
  {%- endif %}
20
20
  {#- #} xsi:type="{{dct.dct_type}}">
21
21
  {%- if dct.dct_type in ("STANDARD-LENGTH-TYPE", "LEADING-LENGTH-INFO-TYPE") %}
@@ -9,7 +9,11 @@
9
9
  {%- if desc.external_docs %}
10
10
  <EXTERNAL-DOCS>
11
11
  {%- for ed in desc.external_docs %}
12
- <EXTERNAL-DOC HREF="{{ed}}" />
12
+ {%- if ed.description is none %}}
13
+ <EXTERNAL-DOC HREF="{{ed.href}}" />
14
+ {%- else %}}
15
+ <EXTERNAL-DOC HREF="{{ed.href}}">{{ed.description}}</EXTERNAL-DOC>
16
+ {%- endif %}}
13
17
  {%- endfor %}
14
18
  </EXTERNAL-DOCS>
15
19
  {%- endif -%}
@@ -134,8 +134,8 @@
134
134
  {{ pt.printTable(table)|indent(3) }}
135
135
  {%- endfor %}
136
136
  </TABLES>
137
- {{- psd.printSpecialDataGroups(ddds.sdgs)|indent(2, first=True) }}
138
137
  {%- endif %}
138
+ {{- psd.printSpecialDataGroups(ddds.sdgs)|indent(2, first=True) }}
139
139
  </DIAG-DATA-DICTIONARY-SPEC>
140
140
  {%- endif %}
141
141
  {%- if dlr.diag_comms_raw %}
@@ -5,18 +5,20 @@
5
5
  {%- import('macros/printElementId.xml.jinja2') as peid %}
6
6
  {%- import('macros/printAdminData.xml.jinja2') as pad %}
7
7
  {%- import('macros/printDescription.xml.jinja2') as pd %}
8
+ {%- import('macros/printSpecialData.xml.jinja2') as psd %}
8
9
 
9
10
  {%- macro printDiagVariable(diag_var) -%}
10
- <DIAG-VARIABLE {{-peid.printElementIdAttribs(diag_var)}}>
11
+ <DIAG-VARIABLE {{-peid.printElementIdAttribs(diag_var)}}
12
+ {{-make_bool_xml_attrib("IS-READ-BEFORE-WRITE", diag_var.is_read_before_write_raw}}>
11
13
  {{ peid.printElementIdSubtags(diag_variable)|indent(2) }}
12
14
  {%- if diag_variable.admin_data is not none %}
13
15
  {{ pad.printAdminData(diag_variable.admin_data)|indent(2) }}
14
16
  {%- endif %}
15
17
  <VARIABLE-GROUP-REF ID-REF="{{ diag_var.ref_id }}" />
16
18
  {%- if diag_variable.sw_variables %}
17
- <SW-VARIABLES>
19
+ <SW-VARIABLES>
18
20
  {%- for sw_var in diag_variable.sw_variables %}
19
- <SW-VARIABLE {{-peid.printElementIdAttribs(sw_var)}}>
21
+ <SW-VARIABLE {{-peid.printElementIdAttribs(sw_var)}} {{- make_xml_attrib("OID", sw_var.oid) }}>
20
22
  {{ peid.printElementIdSubtags(sw_var)|indent(6) }}
21
23
  {%- if sw_var.origin is not none %}
22
24
  <ORIGIN>{{ sw_var.origin }}</ORIGIN>
@@ -56,6 +58,13 @@
56
58
  {%- endfor %}
57
59
  </COMM-RELATIONS>
58
60
  {%- endif %}
61
+ {%- if diag_variable.table_snref is not none %}
62
+ <SNREF-TO-TABLE-ROW>
63
+ <TABLE-SNREF SHORT-NAME="{{diag_variable.table_snref}}" />
64
+ <TABLE-ROW-SNREF SHORT-NAME="{{diag_variable.table_row_snref}}" />
65
+ </SNREF-TO-TABLE-ROW>
66
+ {%- endif %}
67
+ {{- psd.printSpecialDataGroups(ddds.sdgs)|indent(2, first=True) }}
59
68
  </DIAG-VARIABLE>
60
69
  {%- endmacro -%}
61
70
 
@@ -5,9 +5,13 @@
5
5
  -#}
6
6
 
7
7
  {%- import('macros/printElementId.xml.jinja2') as peid %}
8
+ {%- import('macros/printAdminData.xml.jinja2') as pad %}
8
9
 
9
10
  {%- macro printFunctionalClass(fc) -%}
10
11
  <FUNCT-CLASS {{-peid.printElementIdAttribs(fc)}}>
11
12
  {{ peid.printElementIdSubtags(fc)|indent(1) }}
13
+ {%- if fc.admin_data is not none -%}
14
+ {{ pad.printAdminData(fc.admin_data)|indent(1) }}
15
+ {%- endif -%}
12
16
  </FUNCT-CLASS>
13
17
  {%- endmacro -%}
@@ -17,6 +17,15 @@
17
17
  {%- endfor %}
18
18
  </NOT-INHERITED-DIAG-COMMS>
19
19
  {%- endif %}
20
+ {%- if par.not_inherited_variables %}
21
+ <NOT-INHERITED-VARIABLES>
22
+ {%- for nivar in par.not_inherited_variables %}
23
+ <NOT-INHERITED-VARIABLE>
24
+ <DIAG-VARIABLE-SNREF SHORT-NAME="{{nivar}}"/>
25
+ </NOT-INHERITED-VARIABLE>
26
+ {%- endfor %}
27
+ </NOT-INHERITED-VARIABLES>
28
+ {%- endif %}
20
29
  {%- if par.not_inherited_dops %}
21
30
  <NOT-INHERITED-DOPS>
22
31
  {%- for nidop in par.not_inherited_dops %}
@@ -26,6 +35,24 @@
26
35
  {%- endfor %}
27
36
  </NOT-INHERITED-DOPS>
28
37
  {%- endif %}
38
+ {%- if par.not_inherited_tables %}
39
+ <NOT-INHERITED-TABLES>
40
+ {%- for nitable in par.not_inherited_tables %}
41
+ <NOT-INHERITED-TABLE>
42
+ <TABLE-SNREF SHORT-NAME="{{nitable}}"/>
43
+ </NOT-INHERITED-TABLE>
44
+ {%- endfor %}
45
+ </NOT-INHERITED-TABLES>
46
+ {%- endif %}
47
+ {%- if par.not_inherited_global_neg_responses %}
48
+ <NOT-INHERITED-GLOBAL-NEG-RESPONSES>
49
+ {%- for nignr in par.not_inherited_global_neg_responses %}
50
+ <NOT-INHERITED-GLOBAL-NEG-RESPONSE>
51
+ <GLOBAL-NEG-RESPONSE-SNREF SHORT-NAME="{{nignr}}"/>
52
+ </NOT-INHERITED-GLOBAL-NEG-RESPONSE>
53
+ {%- endfor %}
54
+ </NOT-INHERITED-GLOBAL-NEG-RESPONSES>
55
+ {%- endif %}
29
56
  </PARENT-REF>
30
57
  {%- endmacro -%}
31
58
 
@@ -13,7 +13,7 @@
13
13
  {%- set dlr = protocol.protocol_raw %}
14
14
 
15
15
  {%- set cps_docfrag = dlr.comparam_spec_ref.ref_docs[-1] %}
16
- <COMPARAM-SPEC-REF ID-REF="{{dlr.comparam_spec_ref.ref_id}}" DOCREF="{{cps_docfrag.doc_name}}" DOCTYPE="{{cps_docfrag.doc_type}}" />
16
+ <COMPARAM-SPEC-REF ID-REF="{{dlr.comparam_spec_ref.ref_id}}" DOCREF="{{cps_docfrag.doc_name}}" DOCTYPE="{{cps_docfrag.doc_type.value}}" />
17
17
 
18
18
  {%- if dlr.prot_stack_snref is not none %}
19
19
  <PROT-STACK-SNREF SHORT-NAME="{{ dlr.prot_stack_snref }}" />