odxtools 5.3.1__py3-none-any.whl → 6.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odxtools/__init__.py +1 -1
- odxtools/basicstructure.py +76 -91
- odxtools/cli/_parser_utils.py +12 -9
- odxtools/cli/_print_utils.py +7 -7
- odxtools/cli/browse.py +94 -73
- odxtools/cli/find.py +42 -59
- odxtools/cli/list.py +21 -17
- odxtools/cli/snoop.py +19 -18
- odxtools/communicationparameterref.py +6 -3
- odxtools/companydocinfo.py +2 -2
- odxtools/companyrevisioninfo.py +1 -1
- odxtools/comparamsubset.py +6 -6
- odxtools/complexcomparam.py +1 -1
- odxtools/compumethods/compumethod.py +6 -9
- odxtools/compumethods/createanycompumethod.py +11 -9
- odxtools/compumethods/identicalcompumethod.py +5 -4
- odxtools/compumethods/limit.py +9 -9
- odxtools/compumethods/linearcompumethod.py +25 -17
- odxtools/compumethods/scalelinearcompumethod.py +6 -5
- odxtools/compumethods/tabintpcompumethod.py +30 -9
- odxtools/compumethods/texttablecompumethod.py +22 -24
- odxtools/database.py +5 -5
- odxtools/dataobjectproperty.py +10 -23
- odxtools/decodestate.py +1 -1
- odxtools/determinenumberofitems.py +37 -8
- odxtools/diagcodedtype.py +14 -9
- odxtools/diagdatadictionaryspec.py +60 -37
- odxtools/diaglayer.py +30 -21
- odxtools/diaglayercontainer.py +40 -40
- odxtools/diaglayerraw.py +92 -63
- odxtools/diagnostictroublecode.py +2 -2
- odxtools/diagservice.py +53 -35
- odxtools/docrevision.py +1 -1
- odxtools/dopbase.py +14 -3
- odxtools/dtcdop.py +15 -9
- odxtools/dynamiclengthfield.py +6 -4
- odxtools/endofpdufield.py +22 -23
- odxtools/environmentdata.py +2 -5
- odxtools/environmentdatadescription.py +6 -4
- odxtools/field.py +3 -8
- odxtools/isotp_state_machine.py +52 -38
- odxtools/leadinglengthinfotype.py +9 -7
- odxtools/load_file.py +2 -1
- odxtools/load_odx_d_file.py +2 -5
- odxtools/load_pdx_file.py +2 -6
- odxtools/message.py +11 -3
- odxtools/minmaxlengthtype.py +107 -78
- odxtools/modification.py +2 -2
- odxtools/multiplexer.py +23 -21
- odxtools/multiplexerswitchkey.py +37 -8
- odxtools/nameditemlist.py +59 -58
- odxtools/odxlink.py +4 -2
- odxtools/odxtypes.py +4 -3
- odxtools/parameterinfo.py +6 -6
- odxtools/parameters/codedconstparameter.py +15 -25
- odxtools/parameters/createanyparameter.py +1 -1
- odxtools/parameters/dynamicparameter.py +6 -5
- odxtools/parameters/lengthkeyparameter.py +2 -1
- odxtools/parameters/matchingrequestparameter.py +8 -11
- odxtools/parameters/nrcconstparameter.py +11 -21
- odxtools/parameters/parameter.py +4 -18
- odxtools/parameters/parameterwithdop.py +14 -29
- odxtools/parameters/physicalconstantparameter.py +7 -9
- odxtools/parameters/reservedparameter.py +17 -38
- odxtools/parameters/systemparameter.py +6 -5
- odxtools/parameters/tableentryparameter.py +6 -5
- odxtools/parameters/tablekeyparameter.py +8 -15
- odxtools/parameters/tablestructparameter.py +11 -12
- odxtools/parameters/valueparameter.py +9 -24
- odxtools/paramlengthinfotype.py +11 -9
- odxtools/physicaldimension.py +1 -1
- odxtools/physicaltype.py +2 -2
- odxtools/response.py +7 -3
- odxtools/singleecujob.py +48 -22
- odxtools/standardlengthtype.py +11 -6
- odxtools/uds.py +1 -1
- odxtools/unit.py +5 -5
- odxtools/unitgroup.py +1 -1
- odxtools/unitspec.py +2 -2
- odxtools/version.py +13 -3
- odxtools/write_pdx_file.py +7 -4
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/METADATA +7 -5
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/RECORD +87 -88
- odxtools/positioneddataobjectproperty.py +0 -74
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/LICENSE +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/WHEEL +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/entry_points.txt +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@ from ..decodestate import DecodeState
|
|
6
6
|
from ..encodestate import EncodeState
|
7
7
|
from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
|
8
8
|
from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
9
|
+
from ..odxtypes import ParameterValue
|
9
10
|
from .parameter import Parameter, ParameterType
|
10
11
|
|
11
12
|
if TYPE_CHECKING:
|
@@ -94,14 +95,6 @@ class TableKeyParameter(Parameter):
|
|
94
95
|
def is_settable(self) -> bool:
|
95
96
|
return True
|
96
97
|
|
97
|
-
def get_coded_value(self, physical_value=None) -> Any:
|
98
|
-
key_dop = self.table.key_dop
|
99
|
-
if key_dop is None:
|
100
|
-
raise EncodeError(f"Table '{self.table.short_name}' does not define "
|
101
|
-
f"a KEY-DOP, but is used in TABLE-KEY parameter "
|
102
|
-
f"'{self.short_name}'")
|
103
|
-
return key_dop.convert_physical_to_internal(physical_value)
|
104
|
-
|
105
98
|
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
|
106
99
|
tr_short_name = encode_state.parameter_values.get(self.short_name)
|
107
100
|
|
@@ -116,7 +109,7 @@ class TableKeyParameter(Parameter):
|
|
116
109
|
f"a KEY-DOP, but is used in TABLE-KEY parameter "
|
117
110
|
f"'{self.short_name}'")
|
118
111
|
|
119
|
-
byte_len = (key_dop.
|
112
|
+
byte_len = (odxrequire(key_dop.get_static_bit_length()) + 7) // 8
|
120
113
|
if self.bit_position is not None and self.bit_position > 0:
|
121
114
|
byte_len += 1
|
122
115
|
|
@@ -142,21 +135,21 @@ class TableKeyParameter(Parameter):
|
|
142
135
|
def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
|
143
136
|
return super().encode_into_pdu(encode_state)
|
144
137
|
|
145
|
-
def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[
|
146
|
-
if self.byte_position is not None and self.byte_position != decode_state.
|
147
|
-
|
138
|
+
def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
|
139
|
+
if self.byte_position is not None and self.byte_position != decode_state.cursor_position:
|
140
|
+
cursor_position = self.byte_position
|
148
141
|
|
149
142
|
# update the decode_state's table key
|
150
143
|
if self.table_row is not None:
|
151
144
|
# the table row to be used is statically specified -> no
|
152
145
|
# need to decode anything!
|
153
146
|
phys_val = self.table_row.short_name
|
154
|
-
|
147
|
+
cursor_position = decode_state.cursor_position
|
155
148
|
else:
|
156
149
|
# Use DOP to decode
|
157
150
|
key_dop = odxrequire(self.table.key_dop)
|
158
151
|
bit_position_int = self.bit_position if self.bit_position is not None else 0
|
159
|
-
key_dop_val,
|
152
|
+
key_dop_val, cursor_position = key_dop.convert_bytes_to_physical(
|
160
153
|
decode_state, bit_position=bit_position_int)
|
161
154
|
|
162
155
|
table_row_candidates = [x for x in self.table.table_rows if x.key == key_dop_val]
|
@@ -167,4 +160,4 @@ class TableKeyParameter(Parameter):
|
|
167
160
|
f"Multiple rows exhibiting key '{str(key_dop_val)}' found in table")
|
168
161
|
phys_val = table_row_candidates[0].short_name
|
169
162
|
|
170
|
-
return phys_val,
|
163
|
+
return phys_val, cursor_position
|
@@ -2,12 +2,13 @@
|
|
2
2
|
import warnings
|
3
3
|
from copy import copy
|
4
4
|
from dataclasses import dataclass
|
5
|
-
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
|
5
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, cast
|
6
6
|
|
7
7
|
from ..decodestate import DecodeState
|
8
8
|
from ..encodestate import EncodeState
|
9
9
|
from ..exceptions import EncodeError, OdxWarning, odxraise
|
10
10
|
from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
11
|
+
from ..odxtypes import ParameterValue
|
11
12
|
from .parameter import Parameter, ParameterType
|
12
13
|
from .tablekeyparameter import TableKeyParameter
|
13
14
|
|
@@ -44,24 +45,22 @@ class TableStructParameter(Parameter):
|
|
44
45
|
if self.table_key_snref is not None:
|
45
46
|
warnings.warn(
|
46
47
|
"Table keys cannot yet be defined using SNREFs"
|
47
|
-
" in TableStructParameters.",
|
48
|
+
" in TableStructParameters.",
|
49
|
+
OdxWarning,
|
50
|
+
stacklevel=1)
|
48
51
|
|
49
52
|
@property
|
50
53
|
def table_key(self) -> TableKeyParameter:
|
51
54
|
return self._table_key
|
52
55
|
|
53
56
|
@property
|
54
|
-
def is_required(self):
|
57
|
+
def is_required(self) -> bool:
|
55
58
|
return True
|
56
59
|
|
57
60
|
@property
|
58
|
-
def is_settable(self):
|
61
|
+
def is_settable(self) -> bool:
|
59
62
|
return True
|
60
63
|
|
61
|
-
def get_coded_value(self, physical_value=None):
|
62
|
-
raise EncodeError("TableStructParameters cannot be converted to "
|
63
|
-
"internal values without a table row.")
|
64
|
-
|
65
64
|
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
|
66
65
|
physical_value = encode_state.parameter_values.get(self.short_name)
|
67
66
|
|
@@ -131,11 +130,11 @@ class TableStructParameter(Parameter):
|
|
131
130
|
def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
|
132
131
|
return super().encode_into_pdu(encode_state)
|
133
132
|
|
134
|
-
def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[
|
135
|
-
if self.byte_position is not None and self.byte_position != decode_state.
|
133
|
+
def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
|
134
|
+
if self.byte_position is not None and self.byte_position != decode_state.cursor_position:
|
136
135
|
next_pos = self.byte_position if self.byte_position is not None else 0
|
137
136
|
decode_state = copy(decode_state)
|
138
|
-
decode_state.
|
137
|
+
decode_state.cursor_position = next_pos
|
139
138
|
|
140
139
|
# find the selected table row
|
141
140
|
key_name = self.table_key.short_name
|
@@ -160,4 +159,4 @@ class TableStructParameter(Parameter):
|
|
160
159
|
else:
|
161
160
|
# the table row associated with the key neither defines a
|
162
161
|
# DOP not a structure -> ignore it
|
163
|
-
return (table_row.short_name, None), decode_state.
|
162
|
+
return (table_row.short_name, cast(int, None)), decode_state.cursor_position
|
@@ -31,17 +31,15 @@ class ValueParameter(ParameterWithDOP):
|
|
31
31
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
32
32
|
super()._resolve_snrefs(diag_layer)
|
33
33
|
|
34
|
-
self._physical_default_value: Optional[AtomicOdxType]
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
base_data_type = dop.physical_type.base_data_type
|
44
|
-
self._physical_default_value = base_data_type.from_string(pdvr)
|
34
|
+
self._physical_default_value: Optional[AtomicOdxType] = None
|
35
|
+
if self.physical_default_value_raw is not None:
|
36
|
+
dop = odxrequire(self.dop)
|
37
|
+
if not isinstance(dop, DataObjectProperty):
|
38
|
+
odxraise("Value parameters can only define a physical default "
|
39
|
+
"value if they use a simple DOP")
|
40
|
+
base_data_type = dop.physical_type.base_data_type
|
41
|
+
self._physical_default_value = base_data_type.from_string(
|
42
|
+
self.physical_default_value_raw)
|
45
43
|
|
46
44
|
@property
|
47
45
|
def physical_default_value(self) -> Optional[AtomicOdxType]:
|
@@ -55,15 +53,6 @@ class ValueParameter(ParameterWithDOP):
|
|
55
53
|
def is_settable(self) -> bool:
|
56
54
|
return True
|
57
55
|
|
58
|
-
def get_coded_value(self, physical_value: Optional[AtomicOdxType] = None):
|
59
|
-
if physical_value is not None:
|
60
|
-
dop = odxrequire(self.dop)
|
61
|
-
if not isinstance(dop, DataObjectProperty):
|
62
|
-
odxraise()
|
63
|
-
return dop.convert_physical_to_internal(physical_value)
|
64
|
-
else:
|
65
|
-
return self.physical_default_value
|
66
|
-
|
67
56
|
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
|
68
57
|
physical_value = encode_state.parameter_values.get(self.short_name,
|
69
58
|
self.physical_default_value)
|
@@ -77,7 +66,3 @@ class ValueParameter(ParameterWithDOP):
|
|
77
66
|
bit_position_int = self.bit_position if self.bit_position is not None else 0
|
78
67
|
return dop.convert_physical_to_bytes(
|
79
68
|
physical_value, encode_state=encode_state, bit_position=bit_position_int)
|
80
|
-
|
81
|
-
def get_valid_physical_values(self):
|
82
|
-
if isinstance(self.dop, DataObjectProperty):
|
83
|
-
return self.dop.get_valid_physical_values()
|
odxtools/paramlengthinfotype.py
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import TYPE_CHECKING
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, Tuple
|
4
4
|
|
5
5
|
from .decodestate import DecodeState
|
6
6
|
from .diagcodedtype import DctType, DiagCodedType
|
7
7
|
from .encodestate import EncodeState
|
8
8
|
from .exceptions import odxraise
|
9
|
-
from .odxlink import OdxLinkDatabase, OdxLinkRef
|
10
|
-
from .odxtypes import DataType
|
9
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
10
|
+
from .odxtypes import AtomicOdxType, DataType
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
13
13
|
from .diaglayer import DiagLayer
|
@@ -23,7 +23,7 @@ class ParamLengthInfoType(DiagCodedType):
|
|
23
23
|
def dct_type(self) -> DctType:
|
24
24
|
return "PARAM-LENGTH-INFO-TYPE"
|
25
25
|
|
26
|
-
def _build_odxlinks(self):
|
26
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
27
27
|
return super()._build_odxlinks()
|
28
28
|
|
29
29
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
@@ -40,7 +40,7 @@ class ParamLengthInfoType(DiagCodedType):
|
|
40
40
|
def length_key(self) -> "LengthKeyParameter":
|
41
41
|
return self._length_key
|
42
42
|
|
43
|
-
def convert_internal_to_bytes(self, internal_value, encode_state: EncodeState,
|
43
|
+
def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
|
44
44
|
bit_position: int) -> bytes:
|
45
45
|
bit_length = encode_state.parameter_values.get(self.length_key.short_name, None)
|
46
46
|
|
@@ -50,9 +50,9 @@ class ParamLengthInfoType(DiagCodedType):
|
|
50
50
|
DataType.A_ASCIISTRING,
|
51
51
|
DataType.A_UTF8STRING,
|
52
52
|
]:
|
53
|
-
bit_length = 8 * len(internal_value)
|
53
|
+
bit_length = 8 * len(internal_value) # type: ignore[arg-type]
|
54
54
|
if self.base_data_type in [DataType.A_UNICODE2STRING]:
|
55
|
-
bit_length = 16 * len(internal_value)
|
55
|
+
bit_length = 16 * len(internal_value) # type: ignore[arg-type]
|
56
56
|
|
57
57
|
if self.base_data_type in [DataType.A_INT32, DataType.A_UINT32]:
|
58
58
|
bit_length = int(internal_value).bit_length()
|
@@ -74,7 +74,9 @@ class ParamLengthInfoType(DiagCodedType):
|
|
74
74
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
75
75
|
)
|
76
76
|
|
77
|
-
def convert_bytes_to_internal(self,
|
77
|
+
def convert_bytes_to_internal(self,
|
78
|
+
decode_state: DecodeState,
|
79
|
+
bit_position: int = 0) -> Tuple[AtomicOdxType, int]:
|
78
80
|
# Find length key with matching ID.
|
79
81
|
bit_length = None
|
80
82
|
for parameter_name, value in decode_state.parameter_values.items():
|
@@ -91,7 +93,7 @@ class ParamLengthInfoType(DiagCodedType):
|
|
91
93
|
# Extract the internal value and return.
|
92
94
|
return self._extract_internal(
|
93
95
|
decode_state.coded_message,
|
94
|
-
decode_state.
|
96
|
+
decode_state.cursor_position,
|
95
97
|
bit_position,
|
96
98
|
bit_length,
|
97
99
|
self.base_data_type,
|
odxtools/physicaldimension.py
CHANGED
@@ -58,7 +58,7 @@ class PhysicalDimension(IdentifiableElement):
|
|
58
58
|
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
59
59
|
oid = et_element.get("OID")
|
60
60
|
|
61
|
-
def read_optional_int(element, name):
|
61
|
+
def read_optional_int(element: ElementTree.Element, name: str) -> int:
|
62
62
|
if val_str := element.findtext(name):
|
63
63
|
return int(val_str)
|
64
64
|
else:
|
odxtools/physicaltype.py
CHANGED
@@ -51,13 +51,13 @@ class PhysicalType:
|
|
51
51
|
The precision is only applicable if the base data type is A_FLOAT32 or A_FLOAT64.
|
52
52
|
"""
|
53
53
|
|
54
|
-
def __post_init__(self):
|
54
|
+
def __post_init__(self) -> None:
|
55
55
|
self.base_data_type = DataType(self.base_data_type)
|
56
56
|
if self.display_radix is not None:
|
57
57
|
self.display_radix = Radix(self.display_radix)
|
58
58
|
|
59
59
|
@staticmethod
|
60
|
-
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]):
|
60
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "PhysicalType":
|
61
61
|
base_data_type_str = et_element.get("BASE-DATA-TYPE")
|
62
62
|
if base_data_type_str not in DataType.__members__:
|
63
63
|
odxraise(f"Encountered unknown base data type '{base_data_type_str}'")
|
odxtools/response.py
CHANGED
@@ -3,17 +3,21 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import Optional
|
4
4
|
|
5
5
|
from .basicstructure import BasicStructure
|
6
|
+
from .odxtypes import ParameterValue
|
7
|
+
from .parameters.matchingrequestparameter import MatchingRequestParameter
|
6
8
|
|
7
9
|
|
8
10
|
@dataclass
|
9
11
|
class Response(BasicStructure):
|
10
12
|
response_type: str # "POS-RESPONSE" or "NEG-RESPONSE"
|
11
13
|
|
12
|
-
def encode(self, coded_request: Optional[bytes] = None, **params) -> bytes:
|
14
|
+
def encode(self, coded_request: Optional[bytes] = None, **params: ParameterValue) -> bytes:
|
13
15
|
if coded_request is not None:
|
14
|
-
# Extract MATCHING-REQUEST-PARAMs from the coded
|
16
|
+
# Extract MATCHING-REQUEST-PARAMs from the coded
|
17
|
+
# request. TODO: this should be done by
|
18
|
+
# MatchingRequestParam itself!
|
15
19
|
for param in self.parameters:
|
16
|
-
if param
|
20
|
+
if isinstance(param, MatchingRequestParameter):
|
17
21
|
byte_pos = param.request_byte_position
|
18
22
|
byte_length = param.byte_length
|
19
23
|
|
odxtools/singleecujob.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from enum import Enum
|
4
|
-
from itertools import chain
|
5
4
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
|
6
5
|
from xml.etree import ElementTree
|
7
6
|
|
@@ -17,7 +16,7 @@ from .message import Message
|
|
17
16
|
from .nameditemlist import NamedItemList
|
18
17
|
from .negoutputparam import NegOutputParam
|
19
18
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
20
|
-
from .odxtypes import odxstr_to_bool
|
19
|
+
from .odxtypes import ParameterValue, odxstr_to_bool
|
21
20
|
from .outputparam import OutputParam
|
22
21
|
from .progcode import ProgCode
|
23
22
|
from .specialdatagroup import SpecialDataGroup
|
@@ -71,21 +70,21 @@ class SingleEcuJob(IdentifiableElement):
|
|
71
70
|
sdgs: List[SpecialDataGroup]
|
72
71
|
|
73
72
|
@property
|
74
|
-
def is_mandatory(self):
|
75
|
-
return self.is_mandatory_raw
|
73
|
+
def is_mandatory(self) -> bool:
|
74
|
+
return self.is_mandatory_raw is True
|
76
75
|
|
77
76
|
@property
|
78
|
-
def is_executable(self):
|
77
|
+
def is_executable(self) -> bool:
|
79
78
|
return self.is_executable_raw in (None, True)
|
80
79
|
|
81
80
|
@property
|
82
|
-
def is_final(self):
|
83
|
-
return self.is_final_raw
|
81
|
+
def is_final(self) -> bool:
|
82
|
+
return self.is_final_raw is True
|
84
83
|
|
85
84
|
def __post_init__(self) -> None:
|
86
85
|
if not self.functional_class_refs:
|
87
86
|
self.functional_class_refs = []
|
88
|
-
self._functional_classes:
|
87
|
+
self._functional_classes: NamedItemList[FunctionalClass]
|
89
88
|
|
90
89
|
# Replace None attributes by empty lists
|
91
90
|
if not self.input_params:
|
@@ -164,7 +163,7 @@ class SingleEcuJob(IdentifiableElement):
|
|
164
163
|
**kwargs)
|
165
164
|
|
166
165
|
@property
|
167
|
-
def functional_classes(self) ->
|
166
|
+
def functional_classes(self) -> NamedItemList[FunctionalClass]:
|
168
167
|
"""The functional classes referenced by this job.
|
169
168
|
This is None iff the references were not resolved.
|
170
169
|
"""
|
@@ -173,9 +172,16 @@ class SingleEcuJob(IdentifiableElement):
|
|
173
172
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
174
173
|
result = {self.odx_id: self}
|
175
174
|
|
176
|
-
for
|
177
|
-
|
178
|
-
|
175
|
+
for prog_code in self.prog_codes:
|
176
|
+
result.update(prog_code._build_odxlinks())
|
177
|
+
for input_param in self.input_params:
|
178
|
+
result.update(input_param._build_odxlinks())
|
179
|
+
for output_param in self.output_params:
|
180
|
+
result.update(output_param._build_odxlinks())
|
181
|
+
for neg_output_param in self.neg_output_params:
|
182
|
+
result.update(neg_output_param._build_odxlinks())
|
183
|
+
for sdg in self.sdgs:
|
184
|
+
result.update(sdg._build_odxlinks())
|
179
185
|
|
180
186
|
if self.admin_data:
|
181
187
|
result.update(self.admin_data._build_odxlinks())
|
@@ -198,9 +204,16 @@ class SingleEcuJob(IdentifiableElement):
|
|
198
204
|
else:
|
199
205
|
logger.warning(f"Functional class ID {fc_ref!r} resolved to {fc!r}.")
|
200
206
|
|
201
|
-
for
|
202
|
-
|
203
|
-
|
207
|
+
for prog_code in self.prog_codes:
|
208
|
+
prog_code._resolve_odxlinks(odxlinks)
|
209
|
+
for input_param in self.input_params:
|
210
|
+
input_param._resolve_odxlinks(odxlinks)
|
211
|
+
for output_param in self.output_params:
|
212
|
+
output_param._resolve_odxlinks(odxlinks)
|
213
|
+
for neg_output_param in self.neg_output_params:
|
214
|
+
neg_output_param._resolve_odxlinks(odxlinks)
|
215
|
+
for sdg in self.sdgs:
|
216
|
+
sdg._resolve_odxlinks(odxlinks)
|
204
217
|
|
205
218
|
# Resolve references of admin data
|
206
219
|
if self.admin_data:
|
@@ -211,9 +224,16 @@ class SingleEcuJob(IdentifiableElement):
|
|
211
224
|
self.audience._resolve_odxlinks(odxlinks)
|
212
225
|
|
213
226
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
214
|
-
for
|
215
|
-
|
216
|
-
|
227
|
+
for prog_code in self.prog_codes:
|
228
|
+
prog_code._resolve_snrefs(diag_layer)
|
229
|
+
for input_param in self.input_params:
|
230
|
+
input_param._resolve_snrefs(diag_layer)
|
231
|
+
for output_param in self.output_params:
|
232
|
+
output_param._resolve_snrefs(diag_layer)
|
233
|
+
for neg_output_param in self.neg_output_params:
|
234
|
+
neg_output_param._resolve_snrefs(diag_layer)
|
235
|
+
for sdg in self.sdgs:
|
236
|
+
sdg._resolve_snrefs(diag_layer)
|
217
237
|
|
218
238
|
# Resolve references of admin data
|
219
239
|
if self.admin_data:
|
@@ -231,7 +251,7 @@ class SingleEcuJob(IdentifiableElement):
|
|
231
251
|
f"Single ECU jobs are completely executed on the tester and thus cannot be decoded."
|
232
252
|
f" You tried to decode a response for the job {self.odx_id}.")
|
233
253
|
|
234
|
-
def encode_request(self, **params):
|
254
|
+
def encode_request(self, **params: ParameterValue) -> bytes:
|
235
255
|
"""This function's signature matches `DiagService.encode_request`
|
236
256
|
and only raises an informative error.
|
237
257
|
"""
|
@@ -239,7 +259,10 @@ class SingleEcuJob(IdentifiableElement):
|
|
239
259
|
f"Single ECU jobs are completely executed on the tester and thus cannot be encoded."
|
240
260
|
f" You tried to encode a request for the job {self.odx_id}.")
|
241
261
|
|
242
|
-
def encode_positive_response(self,
|
262
|
+
def encode_positive_response(self,
|
263
|
+
coded_request: bytes,
|
264
|
+
response_index: int = 0,
|
265
|
+
**params: ParameterValue) -> bytes:
|
243
266
|
"""This function's signature matches `DiagService.encode_positive_response`
|
244
267
|
and only raises an informative error.
|
245
268
|
"""
|
@@ -247,7 +270,10 @@ class SingleEcuJob(IdentifiableElement):
|
|
247
270
|
f"Single ECU jobs are completely executed on the tester and thus cannot be encoded."
|
248
271
|
f" You tried to encode a response for the job {self.odx_id}.")
|
249
272
|
|
250
|
-
def encode_negative_response(self,
|
273
|
+
def encode_negative_response(self,
|
274
|
+
coded_request: bytes,
|
275
|
+
response_index: int = 0,
|
276
|
+
**params: ParameterValue) -> bytes:
|
251
277
|
"""This function's signature matches `DiagService.encode_negative_response`
|
252
278
|
and only raises an informative error.
|
253
279
|
"""
|
@@ -255,7 +281,7 @@ class SingleEcuJob(IdentifiableElement):
|
|
255
281
|
f"Single ECU jobs are completely executed on the tester and thus cannot be encoded."
|
256
282
|
f" You tried to encode the job {self.odx_id}.")
|
257
283
|
|
258
|
-
def __call__(self, **params) -> bytes:
|
284
|
+
def __call__(self, **params: ParameterValue) -> bytes:
|
259
285
|
raise EncodeError(
|
260
286
|
f"Single ECU jobs are completely executed on the tester and thus cannot be encoded."
|
261
287
|
f" You tried to call the job {self.odx_id}.")
|
odxtools/standardlengthtype.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Optional
|
3
|
+
from typing import Optional, Tuple
|
4
4
|
|
5
5
|
from .decodestate import DecodeState
|
6
6
|
from .diagcodedtype import DctType, DiagCodedType
|
@@ -20,7 +20,7 @@ class StandardLengthType(DiagCodedType):
|
|
20
20
|
def dct_type(self) -> DctType:
|
21
21
|
return "STANDARD-LENGTH-TYPE"
|
22
22
|
|
23
|
-
def __post_init__(self):
|
23
|
+
def __post_init__(self) -> None:
|
24
24
|
if self.bit_mask is not None:
|
25
25
|
maskable_types = (DataType.A_UINT32, DataType.A_INT32, DataType.A_BYTEFIELD)
|
26
26
|
odxassert(
|
@@ -43,6 +43,9 @@ class StandardLengthType(DiagCodedType):
|
|
43
43
|
odxraise(f'Can not apply a bit_mask on a value of type {type(internal_value)}')
|
44
44
|
return internal_value
|
45
45
|
|
46
|
+
def get_static_bit_length(self) -> Optional[int]:
|
47
|
+
return self.bit_length
|
48
|
+
|
46
49
|
def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
|
47
50
|
bit_position: int) -> bytes:
|
48
51
|
return self._to_bytes(
|
@@ -53,14 +56,16 @@ class StandardLengthType(DiagCodedType):
|
|
53
56
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
54
57
|
)
|
55
58
|
|
56
|
-
def convert_bytes_to_internal(self,
|
57
|
-
|
59
|
+
def convert_bytes_to_internal(self,
|
60
|
+
decode_state: DecodeState,
|
61
|
+
bit_position: int = 0) -> Tuple[AtomicOdxType, int]:
|
62
|
+
internal_value, cursor_position = self._extract_internal(
|
58
63
|
decode_state.coded_message,
|
59
|
-
decode_state.
|
64
|
+
decode_state.cursor_position,
|
60
65
|
bit_position,
|
61
66
|
self.bit_length,
|
62
67
|
self.base_data_type,
|
63
68
|
self.is_highlow_byte_order,
|
64
69
|
)
|
65
70
|
internal_value = self.__apply_mask(internal_value)
|
66
|
-
return internal_value,
|
71
|
+
return internal_value, cursor_position
|
odxtools/uds.py
CHANGED
@@ -49,7 +49,7 @@ class UDSSID(IntEnum):
|
|
49
49
|
|
50
50
|
|
51
51
|
# add the OBD SIDs to the ones from UDS
|
52
|
-
SID = IntEnum("UdsSID", ((i.name, i.value) for i in chain(obd.SID, UDSSID))) # type: ignore
|
52
|
+
SID = IntEnum("UdsSID", ((i.name, i.value) for i in chain(obd.SID, UDSSID))) # type: ignore[misc]
|
53
53
|
|
54
54
|
|
55
55
|
class NegativeResponseCodes(IntEnum):
|
odxtools/unit.py
CHANGED
@@ -60,7 +60,7 @@ class Unit(IdentifiableElement):
|
|
60
60
|
offset_si_to_unit: Optional[float]
|
61
61
|
physical_dimension_ref: Optional[OdxLinkRef]
|
62
62
|
|
63
|
-
def __post_init__(self):
|
63
|
+
def __post_init__(self) -> None:
|
64
64
|
self._physical_dimension = None
|
65
65
|
|
66
66
|
@staticmethod
|
@@ -69,9 +69,9 @@ class Unit(IdentifiableElement):
|
|
69
69
|
oid = et_element.get("OID")
|
70
70
|
display_name = odxrequire(et_element.findtext("DISPLAY-NAME"))
|
71
71
|
|
72
|
-
def read_optional_float(element, name):
|
73
|
-
if element.findtext(name):
|
74
|
-
return float(
|
72
|
+
def read_optional_float(element: ElementTree.Element, name: str) -> Optional[float]:
|
73
|
+
if (elem_str := element.findtext(name)) is not None:
|
74
|
+
return float(elem_str)
|
75
75
|
else:
|
76
76
|
return None
|
77
77
|
|
@@ -89,7 +89,7 @@ class Unit(IdentifiableElement):
|
|
89
89
|
**kwargs)
|
90
90
|
|
91
91
|
@property
|
92
|
-
def physical_dimension(self) -> PhysicalDimension:
|
92
|
+
def physical_dimension(self) -> Optional[PhysicalDimension]:
|
93
93
|
return self._physical_dimension
|
94
94
|
|
95
95
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
odxtools/unitgroup.py
CHANGED
odxtools/unitspec.py
CHANGED
@@ -33,13 +33,13 @@ class UnitSpec:
|
|
33
33
|
physical_dimensions: Union[NamedItemList[PhysicalDimension], List[PhysicalDimension]]
|
34
34
|
sdgs: List[SpecialDataGroup]
|
35
35
|
|
36
|
-
def __post_init__(self):
|
36
|
+
def __post_init__(self) -> None:
|
37
37
|
self.unit_groups = NamedItemList(self.unit_groups)
|
38
38
|
self.units = NamedItemList(self.units)
|
39
39
|
self.physical_dimensions = NamedItemList(self.physical_dimensions)
|
40
40
|
|
41
41
|
@staticmethod
|
42
|
-
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]):
|
42
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "UnitSpec":
|
43
43
|
|
44
44
|
unit_groups = [
|
45
45
|
UnitGroup.from_et(el, doc_frags) for el in et_element.iterfind("UNIT-GROUPS/UNIT-GROUP")
|
odxtools/version.py
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
# file generated by setuptools_scm
|
2
2
|
# don't change, don't track in version control
|
3
|
-
|
3
|
+
TYPE_CHECKING = False
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from typing import Tuple, Union
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
7
|
+
else:
|
8
|
+
VERSION_TUPLE = object
|
4
9
|
|
5
|
-
|
6
|
-
|
10
|
+
version: str
|
11
|
+
__version__: str
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
13
|
+
version_tuple: VERSION_TUPLE
|
14
|
+
|
15
|
+
__version__ = version = '6.0.1'
|
16
|
+
__version_tuple__ = version_tuple = (6, 0, 1)
|
odxtools/write_pdx_file.py
CHANGED
@@ -42,7 +42,7 @@ __templates_dir = os.path.sep.join([os.path.dirname(__module_filename), "templat
|
|
42
42
|
def write_pdx_file(
|
43
43
|
output_file_name: str,
|
44
44
|
database: Database,
|
45
|
-
auxiliary_content_specifiers: List[Tuple[str, bytes]] =
|
45
|
+
auxiliary_content_specifiers: Optional[List[Tuple[str, bytes]]] = None,
|
46
46
|
templates_dir: str = __templates_dir,
|
47
47
|
) -> bool:
|
48
48
|
"""
|
@@ -50,13 +50,16 @@ def write_pdx_file(
|
|
50
50
|
"""
|
51
51
|
global odxdatabase
|
52
52
|
|
53
|
+
if auxiliary_content_specifiers is None:
|
54
|
+
auxiliary_content_specifiers = []
|
55
|
+
|
53
56
|
odxdatabase = database
|
54
57
|
|
55
|
-
file_index =
|
58
|
+
file_index = []
|
56
59
|
with zipfile.ZipFile(output_file_name, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
|
57
60
|
|
58
61
|
# write all files in the templates directory
|
59
|
-
for root,
|
62
|
+
for root, _, files in os.walk(templates_dir):
|
60
63
|
for template_file_name in files:
|
61
64
|
# we are not interested in the autosave garbage of
|
62
65
|
# editors...
|
@@ -81,7 +84,7 @@ def write_pdx_file(
|
|
81
84
|
elif template_file_name.endswith(".odx-d"):
|
82
85
|
template_file_mime_type = "application/x-asam.odx.odx-d"
|
83
86
|
|
84
|
-
in_path =
|
87
|
+
in_path = [root]
|
85
88
|
in_path.append(template_file_name)
|
86
89
|
in_file_name = os.path.sep.join(in_path)
|
87
90
|
|