odxtools 7.1.1__py3-none-any.whl → 7.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.
- odxtools/__init__.py +6 -4
- odxtools/additionalaudience.py +3 -5
- odxtools/admindata.py +5 -7
- odxtools/audience.py +3 -5
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +19 -23
- odxtools/cli/_parser_utils.py +1 -1
- odxtools/cli/_print_utils.py +3 -2
- odxtools/cli/compare.py +1 -1
- odxtools/companydata.py +5 -7
- odxtools/companydocinfo.py +7 -8
- odxtools/companyrevisioninfo.py +3 -5
- odxtools/companyspecificinfo.py +8 -9
- odxtools/comparam.py +4 -6
- odxtools/comparaminstance.py +6 -8
- odxtools/comparamspec.py +14 -13
- odxtools/comparamsubset.py +17 -16
- odxtools/complexcomparam.py +5 -7
- odxtools/compumethods/compuconst.py +31 -0
- odxtools/compumethods/compudefaultvalue.py +27 -0
- odxtools/compumethods/compuinternaltophys.py +39 -0
- odxtools/compumethods/compuinversevalue.py +7 -0
- odxtools/compumethods/compumethod.py +67 -12
- odxtools/compumethods/compuphystointernal.py +39 -0
- odxtools/compumethods/compuscale.py +15 -26
- odxtools/compumethods/createanycompumethod.py +14 -160
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/linearcompumethod.py +69 -189
- odxtools/compumethods/linearsegment.py +191 -0
- odxtools/compumethods/scalelinearcompumethod.py +132 -26
- odxtools/compumethods/tabintpcompumethod.py +119 -99
- odxtools/compumethods/texttablecompumethod.py +107 -43
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +84 -72
- odxtools/dataobjectproperty.py +10 -19
- odxtools/decodestate.py +8 -2
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +29 -12
- odxtools/diagcomm.py +10 -6
- odxtools/diagdatadictionaryspec.py +20 -21
- odxtools/diaglayer.py +39 -9
- odxtools/diaglayercontainer.py +17 -11
- odxtools/diaglayerraw.py +20 -21
- odxtools/diagnostictroublecode.py +8 -9
- odxtools/diagservice.py +42 -27
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +7 -8
- odxtools/dtcdop.py +44 -22
- odxtools/dynamicendmarkerfield.py +22 -9
- odxtools/dynamiclengthfield.py +5 -11
- odxtools/element.py +4 -3
- odxtools/encodestate.py +14 -2
- odxtools/endofpdufield.py +0 -2
- odxtools/environmentdatadescription.py +137 -19
- odxtools/exceptions.py +11 -2
- odxtools/field.py +9 -9
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -5
- odxtools/leadinglengthinfotype.py +15 -2
- odxtools/loadfile.py +64 -0
- odxtools/minmaxlengthtype.py +20 -2
- odxtools/modification.py +3 -5
- odxtools/multiplexer.py +98 -69
- odxtools/multiplexercase.py +10 -11
- odxtools/multiplexerdefaultcase.py +11 -12
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/negoutputparam.py +3 -5
- odxtools/odxlink.py +12 -26
- odxtools/odxtypes.py +1 -1
- odxtools/outputparam.py +3 -5
- odxtools/parameterinfo.py +5 -5
- odxtools/parameters/codedconstparameter.py +2 -14
- odxtools/parameters/lengthkeyparameter.py +3 -17
- odxtools/parameters/nrcconstparameter.py +29 -50
- odxtools/parameters/parameter.py +22 -22
- odxtools/parameters/parameterwithdop.py +6 -8
- odxtools/parameters/physicalconstantparameter.py +5 -8
- odxtools/parameters/reservedparameter.py +4 -3
- odxtools/parameters/systemparameter.py +1 -1
- odxtools/parameters/tablekeyparameter.py +6 -9
- odxtools/parameters/tablestructparameter.py +6 -8
- odxtools/parameters/valueparameter.py +5 -8
- odxtools/paramlengthinfotype.py +19 -6
- odxtools/parentref.py +15 -1
- odxtools/physicaldimension.py +3 -5
- odxtools/progcode.py +18 -7
- odxtools/protstack.py +3 -5
- odxtools/relateddoc.py +7 -9
- odxtools/request.py +8 -0
- odxtools/response.py +8 -0
- odxtools/scaleconstr.py +3 -3
- odxtools/singleecujob.py +12 -10
- odxtools/snrefcontext.py +29 -0
- odxtools/specialdata.py +3 -5
- odxtools/specialdatagroup.py +5 -7
- odxtools/specialdatagroupcaption.py +3 -6
- odxtools/standardlengthtype.py +27 -2
- odxtools/state.py +3 -5
- odxtools/statechart.py +9 -11
- odxtools/statetransition.py +4 -9
- odxtools/staticfield.py +4 -8
- odxtools/table.py +7 -8
- odxtools/tablerow.py +7 -6
- odxtools/teammember.py +3 -5
- odxtools/templates/comparam-spec.odx-c.xml.jinja2 +2 -5
- odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +2 -5
- odxtools/templates/macros/printCompanyData.xml.jinja2 +2 -5
- odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +153 -0
- odxtools/templates/macros/printDOP.xml.jinja2 +10 -132
- odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
- odxtools/templates/macros/printElementId.xml.jinja2 +3 -3
- odxtools/templates/macros/printMux.xml.jinja2 +3 -2
- odxtools/templates/macros/printTable.xml.jinja2 +2 -3
- odxtools/unit.py +3 -5
- odxtools/unitgroup.py +3 -5
- odxtools/unitspec.py +9 -10
- odxtools/utils.py +1 -26
- odxtools/version.py +2 -2
- odxtools/{write_pdx_file.py → writepdxfile.py} +19 -10
- odxtools/xdoc.py +3 -5
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/METADATA +1 -1
- odxtools-7.3.0.dist-info/RECORD +192 -0
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/WHEEL +1 -1
- odxtools/createcompanydatas.py +0 -17
- odxtools/createsdgs.py +0 -19
- odxtools/load_file.py +0 -13
- odxtools/load_odx_d_file.py +0 -6
- odxtools/load_pdx_file.py +0 -8
- odxtools-7.1.1.dist-info/RECORD +0 -186
- /odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +0 -0
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/LICENSE +0 -0
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
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,
|
53
|
-
super()._resolve_snrefs(
|
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
|
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
|
odxtools/dynamiclengthfield.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
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,
|
49
|
-
super()._resolve_snrefs(
|
50
|
-
self.determine_number_of_items._resolve_snrefs(
|
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
|
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[
|
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=
|
26
|
+
description=Description.from_et(et_element.find("DESC"), doc_frags),
|
26
27
|
)
|
27
28
|
|
28
29
|
|
odxtools/encodestate.py
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import warnings
|
3
3
|
from dataclasses import dataclass, field
|
4
|
-
from typing import Dict, Optional, SupportsBytes
|
4
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, SupportsBytes, Tuple
|
5
5
|
|
6
6
|
from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
|
7
|
-
from .odxtypes import AtomicOdxType, DataType
|
7
|
+
from .odxtypes import AtomicOdxType, DataType, ParameterValue
|
8
8
|
|
9
9
|
try:
|
10
10
|
import bitstruct.c as bitstruct
|
11
11
|
except ImportError:
|
12
12
|
import bitstruct
|
13
13
|
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from .parameters.parameter import Parameter
|
16
|
+
|
14
17
|
|
15
18
|
@dataclass
|
16
19
|
class EncodeState:
|
@@ -56,6 +59,15 @@ class EncodeState:
|
|
56
59
|
#: (needed for MinMaxLengthType, EndOfPduField, etc.)
|
57
60
|
is_end_of_pdu: bool = True
|
58
61
|
|
62
|
+
#: list of parameters that have been encoded so far. The journal
|
63
|
+
#: is used by some types of parameters which depend on the values of
|
64
|
+
#: other parameters; e.g., environment data description parameters
|
65
|
+
journal: List[Tuple["Parameter", Optional[ParameterValue]]] = field(default_factory=list)
|
66
|
+
|
67
|
+
#: If this is True, specifying unknown parameters for encoding
|
68
|
+
#: will raise an OdxError exception in strict mode.
|
69
|
+
allow_unknown_parameters = False
|
70
|
+
|
59
71
|
def __post_init__(self) -> None:
|
60
72
|
# if a coded message has been specified, but no used_mask, we
|
61
73
|
# assume that all of the bits of the coded message are
|
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,22 +1,22 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
7
7
|
|
8
8
|
from .complexdop import ComplexDop
|
9
9
|
from .decodestate import DecodeState
|
10
|
+
from .dtcdop import DtcDop
|
10
11
|
from .encodestate import EncodeState
|
11
12
|
from .environmentdata import EnvironmentData
|
12
13
|
from .exceptions import odxraise, odxrequire
|
13
14
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
14
|
-
from .odxtypes import ParameterValue
|
15
|
+
from .odxtypes import ParameterValue, ParameterValueDict
|
16
|
+
from .parameters.parameter import Parameter
|
17
|
+
from .snrefcontext import SnRefContext
|
15
18
|
from .utils import dataclass_fields_asdict
|
16
19
|
|
17
|
-
if TYPE_CHECKING:
|
18
|
-
from .diaglayer import DiagLayer
|
19
|
-
|
20
20
|
|
21
21
|
@dataclass
|
22
22
|
class EnvironmentDataDescription(ComplexDop):
|
@@ -29,16 +29,26 @@ class EnvironmentDataDescription(ComplexDop):
|
|
29
29
|
|
30
30
|
"""
|
31
31
|
|
32
|
+
param_snref: Optional[str]
|
33
|
+
param_snpathref: Optional[str]
|
34
|
+
|
32
35
|
# in ODX 2.0.0, ENV-DATAS seems to be a mandatory
|
33
36
|
# sub-element of ENV-DATA-DESC, in ODX 2.2 it is not
|
34
37
|
# present
|
35
38
|
env_datas: List[EnvironmentData]
|
36
39
|
env_data_refs: List[OdxLinkRef]
|
37
|
-
param_snref: Optional[str]
|
38
|
-
param_snpathref: Optional[str]
|
39
40
|
|
40
|
-
|
41
|
-
|
41
|
+
@property
|
42
|
+
def param(self) -> Parameter:
|
43
|
+
# the parameter referenced via SNREF cannot be resolved here
|
44
|
+
# because the relevant list of parameters depends on the
|
45
|
+
# concrete codec object processed, whilst an environment data
|
46
|
+
# description object can be featured in an arbitrary number of
|
47
|
+
# responses. Instead, lookup of the appropriate parameter is
|
48
|
+
# done within the encode and decode methods.
|
49
|
+
odxraise("The parameter of ENV-DATA-DESC objects cannot be resolved "
|
50
|
+
"because it depends on the context")
|
51
|
+
return cast(None, Parameter)
|
42
52
|
|
43
53
|
@staticmethod
|
44
54
|
def from_et(et_element: ElementTree.Element,
|
@@ -87,28 +97,136 @@ class EnvironmentDataDescription(ComplexDop):
|
|
87
97
|
for ed in self.env_datas:
|
88
98
|
ed._resolve_odxlinks(odxlinks)
|
89
99
|
|
90
|
-
def _resolve_snrefs(self,
|
100
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
91
101
|
# ODX 2.0 specifies environment data objects here, ODX 2.2
|
92
102
|
# uses references
|
93
103
|
if self.env_data_refs:
|
94
104
|
for ed in self.env_datas:
|
95
|
-
ed._resolve_snrefs(
|
105
|
+
ed._resolve_snrefs(context)
|
96
106
|
|
97
107
|
@override
|
98
108
|
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
99
109
|
encode_state: EncodeState) -> None:
|
100
110
|
"""Convert a physical value into bytes and emplace them into a PDU.
|
101
|
-
|
102
|
-
Since environmental data is supposed to never appear on the
|
103
|
-
wire, this method just raises an EncodeError exception.
|
104
111
|
"""
|
105
|
-
|
112
|
+
|
113
|
+
# retrieve the relevant DTC parameter which must be located in
|
114
|
+
# front of the environment data description.
|
115
|
+
if self.param_snref is None:
|
116
|
+
odxraise("Specifying the DTC parameter for environment data "
|
117
|
+
"descriptions via SNPATHREF is not supported yet")
|
118
|
+
return None
|
119
|
+
|
120
|
+
dtc_param: Optional[Parameter] = None
|
121
|
+
dtc_dop: Optional[DtcDop] = None
|
122
|
+
dtc_param_value: Optional[ParameterValue] = None
|
123
|
+
for prev_param, prev_param_value in reversed(encode_state.journal):
|
124
|
+
if prev_param.short_name == self.param_snref:
|
125
|
+
dtc_param = prev_param
|
126
|
+
prev_dop = getattr(prev_param, "dop", None)
|
127
|
+
if not isinstance(prev_dop, DtcDop):
|
128
|
+
odxraise(f"The DOP of the parameter referenced by environment data "
|
129
|
+
f"descriptions must be a DTC-DOP (is '{type(prev_dop).__name__}')")
|
130
|
+
return
|
131
|
+
dtc_dop = prev_dop
|
132
|
+
dtc_param_value = prev_param_value
|
133
|
+
break
|
134
|
+
|
135
|
+
if dtc_param is None:
|
136
|
+
odxraise("Environment data description parameters are only allowed following "
|
137
|
+
"the referenced value parameter.")
|
138
|
+
return
|
139
|
+
|
140
|
+
if dtc_param_value is None or dtc_dop is None:
|
141
|
+
# this should never happen
|
142
|
+
odxraise()
|
143
|
+
return
|
144
|
+
|
145
|
+
numerical_dtc = dtc_dop.convert_to_numerical_trouble_code(dtc_param_value)
|
146
|
+
|
147
|
+
# deal with the "all value" environment data. This holds
|
148
|
+
# parameters that are common to all DTCs. Be aware that the
|
149
|
+
# specification mandates that there is at most one such
|
150
|
+
# environment data object
|
151
|
+
for env_data in self.env_datas:
|
152
|
+
if env_data.all_value:
|
153
|
+
tmp = encode_state.allow_unknown_parameters
|
154
|
+
encode_state.allow_unknown_parameters = True
|
155
|
+
env_data.encode_into_pdu(physical_value, encode_state)
|
156
|
+
encode_state.allow_unknown_parameters = tmp
|
157
|
+
break
|
158
|
+
|
159
|
+
# find the environment data corresponding to the given trouble
|
160
|
+
# code
|
161
|
+
for env_data in self.env_datas:
|
162
|
+
if numerical_dtc in env_data.dtc_values:
|
163
|
+
tmp = encode_state.allow_unknown_parameters
|
164
|
+
encode_state.allow_unknown_parameters = True
|
165
|
+
env_data.encode_into_pdu(physical_value, encode_state)
|
166
|
+
encode_state.allow_unknown_parameters = tmp
|
167
|
+
break
|
106
168
|
|
107
169
|
@override
|
108
170
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
109
171
|
"""Extract the bytes from a PDU and convert them to a physical value.
|
110
|
-
|
111
|
-
Since environmental data is supposed to never appear on the
|
112
|
-
wire, this method just raises an DecodeError exception.
|
113
172
|
"""
|
114
|
-
|
173
|
+
|
174
|
+
# retrieve the relevant DTC parameter which must be located in
|
175
|
+
# front of the environment data description.
|
176
|
+
if self.param_snref is None:
|
177
|
+
odxraise("Specifying the DTC parameter for environment data "
|
178
|
+
"descriptions via SNPATHREF is not supported yet")
|
179
|
+
return None
|
180
|
+
|
181
|
+
dtc_param: Optional[Parameter] = None
|
182
|
+
dtc_dop: Optional[DtcDop] = None
|
183
|
+
dtc_param_value: Optional[ParameterValue] = None
|
184
|
+
for prev_param, prev_param_value in reversed(decode_state.journal):
|
185
|
+
if prev_param.short_name == self.param_snref:
|
186
|
+
dtc_param = prev_param
|
187
|
+
prev_dop = getattr(prev_param, "dop", None)
|
188
|
+
if not isinstance(prev_dop, DtcDop):
|
189
|
+
odxraise(f"The DOP of the parameter referenced by environment data "
|
190
|
+
f"descriptions must be a DTC-DOP (is '{type(prev_dop).__name__}')")
|
191
|
+
return
|
192
|
+
dtc_dop = prev_dop
|
193
|
+
dtc_param_value = prev_param_value
|
194
|
+
break
|
195
|
+
|
196
|
+
if dtc_param is None:
|
197
|
+
odxraise("Environment data description parameters are only allowed following "
|
198
|
+
"the referenced value parameter.")
|
199
|
+
return
|
200
|
+
|
201
|
+
if dtc_param_value is None or dtc_dop is None:
|
202
|
+
# this should never happen
|
203
|
+
odxraise()
|
204
|
+
return
|
205
|
+
|
206
|
+
numerical_dtc = dtc_dop.convert_to_numerical_trouble_code(dtc_param_value)
|
207
|
+
|
208
|
+
result: ParameterValueDict = {}
|
209
|
+
|
210
|
+
# deal with the "all value" environment data. This holds
|
211
|
+
# parameters that are common to all DTCs. Be aware that the
|
212
|
+
# specification mandates that there is at most one such
|
213
|
+
# environment data object
|
214
|
+
for env_data in self.env_datas:
|
215
|
+
if env_data.all_value:
|
216
|
+
tmp = env_data.decode_from_pdu(decode_state)
|
217
|
+
if not isinstance(tmp, dict):
|
218
|
+
odxraise()
|
219
|
+
result.update(tmp)
|
220
|
+
break
|
221
|
+
|
222
|
+
# find the environment data corresponding to the given trouble
|
223
|
+
# code
|
224
|
+
for env_data in self.env_datas:
|
225
|
+
if numerical_dtc in env_data.dtc_values:
|
226
|
+
tmp = env_data.decode_from_pdu(decode_state)
|
227
|
+
if not isinstance(tmp, dict):
|
228
|
+
odxraise()
|
229
|
+
result.update(tmp)
|
230
|
+
break
|
231
|
+
|
232
|
+
return result
|
odxtools/exceptions.py
CHANGED
@@ -9,13 +9,22 @@ class OdxError(Exception):
|
|
9
9
|
|
10
10
|
|
11
11
|
class EncodeError(Warning, OdxError):
|
12
|
-
"""Encoding of a message to raw data failed
|
12
|
+
"""Encoding of a message to raw data failed"""
|
13
13
|
|
14
14
|
|
15
15
|
class DecodeError(Warning, OdxError):
|
16
16
|
"""Decoding raw data failed."""
|
17
17
|
|
18
18
|
|
19
|
+
class DecodeMismatch(DecodeError):
|
20
|
+
"""Decoding failed because some parameters exhibit an incorrect value
|
21
|
+
|
22
|
+
This is can happen if NRC-CONST or environment data descriptions
|
23
|
+
are present.
|
24
|
+
|
25
|
+
"""
|
26
|
+
|
27
|
+
|
19
28
|
class OdxWarning(Warning):
|
20
29
|
"""Any warning that happens during interacting with diagnostic objects."""
|
21
30
|
|
@@ -40,7 +49,7 @@ def odxraise(message: Optional[str] = None, error_type: Type[Exception] = OdxErr
|
|
40
49
|
else:
|
41
50
|
raise error_type(message)
|
42
51
|
elif message is not None:
|
43
|
-
logger.
|
52
|
+
logger.warning(message)
|
44
53
|
|
45
54
|
|
46
55
|
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
|
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,
|
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
|
-
|
87
|
-
|
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
|
-
|
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)
|
odxtools/functionalclass.py
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
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 FunctionalClass(IdentifiableElement):
|
@@ -31,5 +29,5 @@ class FunctionalClass(IdentifiableElement):
|
|
31
29
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
32
30
|
pass
|
33
31
|
|
34
|
-
def _resolve_snrefs(self,
|
32
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
35
33
|
pass
|
odxtools/inputparam.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from deprecation import deprecated
|
@@ -9,11 +9,9 @@ from .dopbase import DopBase
|
|
9
9
|
from .element import NamedElement
|
10
10
|
from .exceptions import odxrequire
|
11
11
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
12
|
+
from .snrefcontext import SnRefContext
|
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 InputParam(NamedElement):
|
@@ -44,7 +42,7 @@ class InputParam(NamedElement):
|
|
44
42
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
45
43
|
self._dop_base = odxlinks.resolve(self.dop_base_ref, DopBase)
|
46
44
|
|
47
|
-
def _resolve_snrefs(self,
|
45
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
48
46
|
pass
|
49
47
|
|
50
48
|
@property
|
@@ -1,14 +1,17 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Optional
|
3
|
+
from typing import List, Optional
|
4
|
+
from xml.etree import ElementTree
|
4
5
|
|
5
6
|
from typing_extensions import override
|
6
7
|
|
7
8
|
from .decodestate import DecodeState
|
8
9
|
from .diagcodedtype import DctType, DiagCodedType
|
9
10
|
from .encodestate import EncodeState
|
10
|
-
from .exceptions import EncodeError, odxassert, odxraise
|
11
|
+
from .exceptions import EncodeError, odxassert, odxraise, odxrequire
|
12
|
+
from .odxlink import OdxDocFragment
|
11
13
|
from .odxtypes import AtomicOdxType, DataType
|
14
|
+
from .utils import dataclass_fields_asdict
|
12
15
|
|
13
16
|
|
14
17
|
@dataclass
|
@@ -20,6 +23,16 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
20
23
|
#: object.
|
21
24
|
bit_length: int
|
22
25
|
|
26
|
+
@staticmethod
|
27
|
+
@override
|
28
|
+
def from_et(et_element: ElementTree.Element,
|
29
|
+
doc_frags: List[OdxDocFragment]) -> "LeadingLengthInfoType":
|
30
|
+
kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
|
31
|
+
|
32
|
+
bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
|
33
|
+
|
34
|
+
return LeadingLengthInfoType(bit_length=bit_length, **kwargs)
|
35
|
+
|
23
36
|
def __post_init__(self) -> None:
|
24
37
|
odxassert(self.bit_length > 0,
|
25
38
|
"A Leading length info type with bit length == 0 does not make sense.")
|
odxtools/loadfile.py
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
import os
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import Union
|
5
|
+
|
6
|
+
from .database import Database
|
7
|
+
|
8
|
+
|
9
|
+
def load_pdx_file(pdx_file: Union[str, Path]) -> Database:
|
10
|
+
db = Database()
|
11
|
+
db.add_pdx_file(str(pdx_file))
|
12
|
+
db.refresh()
|
13
|
+
return db
|
14
|
+
|
15
|
+
|
16
|
+
def load_odx_d_file(odx_d_file_name: Union[str, Path]) -> Database:
|
17
|
+
db = Database()
|
18
|
+
db.add_odx_file(str(odx_d_file_name))
|
19
|
+
db.refresh()
|
20
|
+
|
21
|
+
return db
|
22
|
+
|
23
|
+
|
24
|
+
def load_file(file_name: Union[str, Path]) -> Database:
|
25
|
+
if str(file_name).lower().endswith(".pdx"):
|
26
|
+
return load_pdx_file(str(file_name))
|
27
|
+
elif str(file_name).lower().endswith(".odx-d"):
|
28
|
+
return load_odx_d_file(str(file_name))
|
29
|
+
else:
|
30
|
+
raise RuntimeError(f"Could not guess the file format of file '{file_name}'!")
|
31
|
+
|
32
|
+
|
33
|
+
def load_files(*file_names: Union[str, Path]) -> Database:
|
34
|
+
db = Database()
|
35
|
+
for file_name in file_names:
|
36
|
+
p = Path(file_name)
|
37
|
+
if p.suffix.lower() == ".pdx":
|
38
|
+
db.add_pdx_file(str(file_name))
|
39
|
+
elif p.suffix.lower().startswith(".odx"):
|
40
|
+
db.add_odx_file(str(file_name))
|
41
|
+
elif p.name.lower() != "index.xml":
|
42
|
+
db.add_auxiliary_file(str(file_name))
|
43
|
+
|
44
|
+
db.refresh()
|
45
|
+
return db
|
46
|
+
|
47
|
+
|
48
|
+
def load_directory(dir_name: Union[str, Path]) -> Database:
|
49
|
+
db = Database()
|
50
|
+
for file_name in os.listdir(str(dir_name)):
|
51
|
+
p = Path(dir_name) / file_name
|
52
|
+
|
53
|
+
if not p.is_file():
|
54
|
+
continue
|
55
|
+
|
56
|
+
if p.suffix.lower() == ".pdx":
|
57
|
+
db.add_pdx_file(str(p))
|
58
|
+
elif p.suffix.lower().startswith(".odx"):
|
59
|
+
db.add_odx_file(str(p))
|
60
|
+
elif p.name.lower() != "index.xml":
|
61
|
+
db.add_auxiliary_file(p.name, open(str(p), "rb"))
|
62
|
+
|
63
|
+
db.refresh()
|
64
|
+
return db
|