odxtools 6.6.1__py3-none-any.whl → 9.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odxtools/__init__.py +7 -5
- odxtools/additionalaudience.py +3 -5
- odxtools/admindata.py +5 -7
- odxtools/audience.py +10 -13
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +55 -241
- odxtools/cli/_parser_utils.py +16 -1
- odxtools/cli/_print_utils.py +169 -134
- odxtools/cli/browse.py +127 -103
- odxtools/cli/compare.py +114 -87
- odxtools/cli/decode.py +2 -1
- odxtools/cli/dummy_sub_parser.py +3 -1
- odxtools/cli/find.py +2 -1
- odxtools/cli/list.py +26 -16
- odxtools/cli/main.py +1 -0
- odxtools/cli/snoop.py +32 -6
- odxtools/codec.py +211 -0
- odxtools/commrelation.py +122 -0
- 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 +14 -14
- odxtools/comparamspec.py +16 -54
- odxtools/comparamsubset.py +22 -62
- odxtools/complexcomparam.py +5 -7
- odxtools/compumethods/compucodecompumethod.py +63 -0
- odxtools/compumethods/compuconst.py +31 -0
- odxtools/compumethods/compudefaultvalue.py +27 -0
- odxtools/compumethods/compuinternaltophys.py +56 -0
- odxtools/compumethods/compuinversevalue.py +7 -0
- odxtools/compumethods/compumethod.py +94 -15
- odxtools/compumethods/compuphystointernal.py +56 -0
- odxtools/compumethods/compurationalcoeffs.py +20 -9
- odxtools/compumethods/compuscale.py +67 -32
- odxtools/compumethods/createanycompumethod.py +31 -172
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/limit.py +70 -36
- odxtools/compumethods/linearcompumethod.py +70 -181
- odxtools/compumethods/linearsegment.py +190 -0
- odxtools/compumethods/ratfunccompumethod.py +106 -0
- odxtools/compumethods/ratfuncsegment.py +87 -0
- odxtools/compumethods/scalelinearcompumethod.py +132 -26
- odxtools/compumethods/scaleratfunccompumethod.py +113 -0
- odxtools/compumethods/tabintpcompumethod.py +123 -92
- odxtools/compumethods/texttablecompumethod.py +117 -57
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +167 -87
- odxtools/dataobjectproperty.py +25 -32
- odxtools/decodestate.py +14 -17
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +37 -106
- odxtools/diagcomm.py +24 -12
- odxtools/diagdatadictionaryspec.py +120 -96
- odxtools/diaglayercontainer.py +46 -54
- odxtools/diaglayers/basevariant.py +128 -0
- odxtools/diaglayers/basevariantraw.py +123 -0
- odxtools/diaglayers/diaglayer.py +432 -0
- odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
- odxtools/diaglayers/diaglayertype.py +42 -0
- odxtools/diaglayers/ecushareddata.py +96 -0
- odxtools/diaglayers/ecushareddataraw.py +87 -0
- odxtools/diaglayers/ecuvariant.py +124 -0
- odxtools/diaglayers/ecuvariantraw.py +129 -0
- odxtools/diaglayers/functionalgroup.py +110 -0
- odxtools/diaglayers/functionalgroupraw.py +106 -0
- odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +273 -472
- odxtools/diaglayers/hierarchyelementraw.py +58 -0
- odxtools/diaglayers/protocol.py +64 -0
- odxtools/diaglayers/protocolraw.py +91 -0
- odxtools/diagnostictroublecode.py +8 -9
- odxtools/diagservice.py +57 -44
- odxtools/diagvariable.py +113 -0
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +15 -15
- odxtools/dtcdop.py +170 -50
- odxtools/dynamicendmarkerfield.py +134 -0
- odxtools/dynamiclengthfield.py +47 -42
- odxtools/dyndefinedspec.py +177 -0
- odxtools/dynenddopref.py +38 -0
- odxtools/ecuvariantmatcher.py +6 -7
- odxtools/element.py +13 -15
- odxtools/encodestate.py +199 -22
- odxtools/endofpdufield.py +31 -18
- odxtools/environmentdata.py +8 -1
- odxtools/environmentdatadescription.py +198 -36
- odxtools/exceptions.py +11 -2
- odxtools/field.py +10 -10
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -12
- odxtools/internalconstr.py +14 -5
- odxtools/isotp_state_machine.py +14 -6
- odxtools/leadinglengthinfotype.py +37 -18
- odxtools/library.py +66 -0
- odxtools/loadfile.py +64 -0
- odxtools/matchingparameter.py +3 -3
- odxtools/message.py +0 -7
- odxtools/minmaxlengthtype.py +61 -33
- odxtools/modification.py +3 -5
- odxtools/multiplexer.py +135 -75
- odxtools/multiplexercase.py +39 -18
- odxtools/multiplexerdefaultcase.py +15 -12
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/nameditemlist.py +33 -8
- odxtools/negoutputparam.py +3 -5
- odxtools/odxcategory.py +83 -0
- odxtools/odxlink.py +62 -53
- odxtools/odxtypes.py +93 -8
- odxtools/outputparam.py +5 -16
- odxtools/parameterinfo.py +219 -61
- odxtools/parameters/codedconstparameter.py +45 -32
- odxtools/parameters/createanyparameter.py +19 -193
- odxtools/parameters/dynamicparameter.py +25 -4
- odxtools/parameters/lengthkeyparameter.py +83 -25
- odxtools/parameters/matchingrequestparameter.py +48 -18
- odxtools/parameters/nrcconstparameter.py +76 -54
- odxtools/parameters/parameter.py +97 -73
- odxtools/parameters/parameterwithdop.py +41 -38
- odxtools/parameters/physicalconstantparameter.py +41 -20
- odxtools/parameters/reservedparameter.py +36 -18
- odxtools/parameters/systemparameter.py +74 -7
- odxtools/parameters/tableentryparameter.py +47 -7
- odxtools/parameters/tablekeyparameter.py +142 -55
- odxtools/parameters/tablestructparameter.py +79 -58
- odxtools/parameters/valueparameter.py +39 -21
- odxtools/paramlengthinfotype.py +56 -33
- odxtools/parentref.py +20 -3
- odxtools/physicaldimension.py +3 -8
- odxtools/progcode.py +26 -11
- odxtools/protstack.py +3 -5
- odxtools/py.typed +0 -0
- odxtools/relateddoc.py +7 -9
- odxtools/request.py +120 -10
- odxtools/response.py +123 -23
- odxtools/scaleconstr.py +14 -8
- odxtools/servicebinner.py +1 -1
- odxtools/singleecujob.py +12 -10
- odxtools/snrefcontext.py +29 -0
- odxtools/specialdata.py +3 -5
- odxtools/specialdatagroup.py +7 -9
- odxtools/specialdatagroupcaption.py +3 -6
- odxtools/standardlengthtype.py +80 -14
- odxtools/state.py +3 -5
- odxtools/statechart.py +13 -19
- odxtools/statetransition.py +8 -18
- odxtools/staticfield.py +107 -0
- odxtools/subcomponent.py +288 -0
- odxtools/swvariable.py +21 -0
- odxtools/table.py +9 -9
- odxtools/tablerow.py +30 -15
- odxtools/teammember.py +3 -5
- odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
- odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
- odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
- odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
- odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
- odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
- odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
- odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
- odxtools/templates/macros/printDOP.xml.jinja2 +27 -137
- odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
- odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
- odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
- odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
- odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
- odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
- odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
- odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
- odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
- odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
- odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
- odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
- odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
- odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
- odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
- odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
- odxtools/templates/macros/printMux.xml.jinja2 +5 -3
- odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
- odxtools/templates/macros/printParam.xml.jinja2 +18 -19
- odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
- odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
- odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
- odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
- odxtools/templates/macros/printService.xml.jinja2 +3 -2
- odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
- odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
- odxtools/templates/macros/printState.xml.jinja2 +1 -1
- odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
- odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
- odxtools/templates/macros/printStaticField.xml.jinja2 +15 -0
- odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
- odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
- odxtools/templates/macros/printTable.xml.jinja2 +4 -5
- odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
- odxtools/uds.py +2 -10
- odxtools/unit.py +4 -8
- odxtools/unitgroup.py +3 -5
- odxtools/unitspec.py +17 -17
- odxtools/utils.py +38 -20
- odxtools/variablegroup.py +32 -0
- odxtools/version.py +2 -2
- odxtools/{write_pdx_file.py → writepdxfile.py} +22 -12
- odxtools/xdoc.py +3 -5
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/METADATA +44 -33
- odxtools-9.3.0.dist-info/RECORD +228 -0
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
- odxtools/createcompanydatas.py +0 -17
- odxtools/createsdgs.py +0 -19
- odxtools/diaglayertype.py +0 -30
- odxtools/load_file.py +0 -13
- odxtools/load_odx_d_file.py +0 -6
- odxtools/load_pdx_file.py +0 -8
- odxtools/templates/macros/printVariant.xml.jinja2 +0 -208
- odxtools-6.6.1.dist-info/RECORD +0 -180
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
odxtools/dataobjectproperty.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, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .compumethods.compumethod import CompuMethod
|
@@ -10,17 +10,15 @@ from .decodestate import DecodeState
|
|
10
10
|
from .diagcodedtype import DiagCodedType
|
11
11
|
from .dopbase import DopBase
|
12
12
|
from .encodestate import EncodeState
|
13
|
-
from .exceptions import DecodeError, EncodeError,
|
13
|
+
from .exceptions import DecodeError, EncodeError, odxraise, odxrequire
|
14
14
|
from .internalconstr import InternalConstr
|
15
15
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
16
16
|
from .odxtypes import AtomicOdxType, ParameterValue
|
17
17
|
from .physicaltype import PhysicalType
|
18
|
+
from .snrefcontext import SnRefContext
|
18
19
|
from .unit import Unit
|
19
20
|
from .utils import dataclass_fields_asdict
|
20
21
|
|
21
|
-
if TYPE_CHECKING:
|
22
|
-
from .diaglayer import DiagLayer
|
23
|
-
|
24
22
|
|
25
23
|
@dataclass
|
26
24
|
class DataObjectProperty(DopBase):
|
@@ -59,20 +57,20 @@ class DataObjectProperty(DopBase):
|
|
59
57
|
compu_method = create_any_compu_method_from_et(
|
60
58
|
odxrequire(et_element.find("COMPU-METHOD")),
|
61
59
|
doc_frags,
|
62
|
-
diag_coded_type.base_data_type,
|
63
|
-
physical_type.base_data_type,
|
60
|
+
internal_type=diag_coded_type.base_data_type,
|
61
|
+
physical_type=physical_type.base_data_type,
|
64
62
|
)
|
65
63
|
unit_ref = OdxLinkRef.from_et(et_element.find("UNIT-REF"), doc_frags)
|
66
64
|
|
67
65
|
internal_constr = None
|
68
66
|
if (internal_constr_elem := et_element.find("INTERNAL-CONSTR")) is not None:
|
69
|
-
internal_constr = InternalConstr.
|
70
|
-
internal_constr_elem,
|
67
|
+
internal_constr = InternalConstr.constr_from_et(
|
68
|
+
internal_constr_elem, doc_frags, value_type=diag_coded_type.base_data_type)
|
71
69
|
|
72
70
|
physical_constr = None
|
73
71
|
if (physical_constr_elem := et_element.find("PHYS-CONSTR")) is not None:
|
74
|
-
physical_constr = InternalConstr.
|
75
|
-
physical_constr_elem,
|
72
|
+
physical_constr = InternalConstr.constr_from_et(
|
73
|
+
physical_constr_elem, doc_frags, value_type=physical_type.base_data_type)
|
76
74
|
|
77
75
|
return DataObjectProperty(
|
78
76
|
diag_coded_type=diag_coded_type,
|
@@ -86,6 +84,7 @@ class DataObjectProperty(DopBase):
|
|
86
84
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
87
85
|
result = super()._build_odxlinks()
|
88
86
|
result.update(self.diag_coded_type._build_odxlinks())
|
87
|
+
result.update(self.compu_method._build_odxlinks())
|
89
88
|
return result
|
90
89
|
|
91
90
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
@@ -93,15 +92,17 @@ class DataObjectProperty(DopBase):
|
|
93
92
|
super()._resolve_odxlinks(odxlinks)
|
94
93
|
|
95
94
|
self.diag_coded_type._resolve_odxlinks(odxlinks)
|
95
|
+
self.compu_method._resolve_odxlinks(odxlinks)
|
96
96
|
|
97
97
|
self._unit: Optional[Unit] = None
|
98
98
|
if self.unit_ref:
|
99
99
|
self._unit = odxlinks.resolve(self.unit_ref, Unit)
|
100
100
|
|
101
|
-
def _resolve_snrefs(self,
|
102
|
-
super()._resolve_snrefs(
|
101
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
102
|
+
super()._resolve_snrefs(context)
|
103
103
|
|
104
|
-
self.diag_coded_type._resolve_snrefs(
|
104
|
+
self.diag_coded_type._resolve_snrefs(context)
|
105
|
+
self.compu_method._resolve_snrefs(context)
|
105
106
|
|
106
107
|
@property
|
107
108
|
def unit(self) -> Optional[Unit]:
|
@@ -110,28 +111,20 @@ class DataObjectProperty(DopBase):
|
|
110
111
|
def get_static_bit_length(self) -> Optional[int]:
|
111
112
|
return self.diag_coded_type.get_static_bit_length()
|
112
113
|
|
113
|
-
def
|
114
|
-
"""
|
115
|
-
Convert a physical representation of a parameter to its internal counterpart
|
116
|
-
"""
|
117
|
-
odxassert(
|
118
|
-
self.physical_type.base_data_type.isinstance(physical_value),
|
119
|
-
f"Expected {self.physical_type.base_data_type.value}, got {type(physical_value)}")
|
120
|
-
|
121
|
-
return self.compu_method.convert_physical_to_internal(physical_value)
|
122
|
-
|
123
|
-
def convert_physical_to_bytes(self, physical_value: Any, encode_state: EncodeState,
|
124
|
-
bit_position: int) -> bytes:
|
114
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
125
115
|
"""
|
126
116
|
Convert a physical representation of a parameter to a string bytes that can be send over the wire
|
127
117
|
"""
|
128
118
|
if not self.is_valid_physical_value(physical_value):
|
129
|
-
raise EncodeError(
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
119
|
+
raise EncodeError(
|
120
|
+
f"The value {repr(physical_value)} of type {type(physical_value).__name__}"
|
121
|
+
f" is not a valid.")
|
122
|
+
|
123
|
+
if not isinstance(physical_value, (int, float, str, bytes, bytearray)):
|
124
|
+
odxraise(f"Invalid type '{type(physical_value).__name__}' for physical value. "
|
125
|
+
f"(Expect atomic type!)")
|
126
|
+
internal_value = self.compu_method.convert_physical_to_internal(physical_value)
|
127
|
+
self.diag_coded_type.encode_into_pdu(internal_value, encode_state)
|
135
128
|
|
136
129
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
137
130
|
"""
|
odxtools/decodestate.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass, field
|
3
|
-
from typing import TYPE_CHECKING, Dict
|
3
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, cast
|
4
4
|
|
5
5
|
import odxtools.exceptions as exceptions
|
6
6
|
|
7
7
|
from .exceptions import DecodeError
|
8
|
-
from .odxtypes import AtomicOdxType, DataType
|
8
|
+
from .odxtypes import AtomicOdxType, DataType, ParameterValue
|
9
9
|
|
10
10
|
try:
|
11
11
|
import bitstruct.c as bitstruct
|
@@ -13,20 +13,9 @@ except ImportError:
|
|
13
13
|
import bitstruct
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
16
|
+
from .parameters.parameter import Parameter
|
16
17
|
from .tablerow import TableRow
|
17
18
|
|
18
|
-
# format specifiers for the data type using the bitstruct module
|
19
|
-
ODX_TYPE_TO_FORMAT_LETTER = {
|
20
|
-
DataType.A_INT32: "s",
|
21
|
-
DataType.A_UINT32: "u",
|
22
|
-
DataType.A_FLOAT32: "f",
|
23
|
-
DataType.A_FLOAT64: "f",
|
24
|
-
DataType.A_BYTEFIELD: "r",
|
25
|
-
DataType.A_UNICODE2STRING: "r", # UTF-16 strings must be converted explicitly
|
26
|
-
DataType.A_ASCIISTRING: "r",
|
27
|
-
DataType.A_UTF8STRING: "r",
|
28
|
-
}
|
29
|
-
|
30
19
|
|
31
20
|
@dataclass
|
32
21
|
class DecodeState:
|
@@ -58,6 +47,11 @@ class DecodeState:
|
|
58
47
|
#: values of the table key parameters decoded so far
|
59
48
|
table_keys: Dict[str, "TableRow"] = field(default_factory=dict)
|
60
49
|
|
50
|
+
#: List of parameters that have been decoded so far. The journal
|
51
|
+
#: is used by some types of parameters which depend on the values of
|
52
|
+
#: other parameters; i.e., environment data description parameters
|
53
|
+
journal: List[Tuple["Parameter", Optional[ParameterValue]]] = field(default_factory=list)
|
54
|
+
|
61
55
|
def extract_atomic_value(
|
62
56
|
self,
|
63
57
|
bit_length: int,
|
@@ -72,7 +66,7 @@ class DecodeState:
|
|
72
66
|
"""
|
73
67
|
# If the bit length is zero, return "empty" values of each type
|
74
68
|
if bit_length == 0:
|
75
|
-
return base_data_type.
|
69
|
+
return base_data_type.python_type()
|
76
70
|
|
77
71
|
byte_length = (bit_length + self.cursor_bit_position + 7) // 8
|
78
72
|
if self.cursor_byte_position + byte_length > len(self.coded_message):
|
@@ -94,12 +88,13 @@ class DecodeState:
|
|
94
88
|
|
95
89
|
padding = (8 - (bit_length + self.cursor_bit_position) % 8) % 8
|
96
90
|
internal_value, = bitstruct.unpack_from(
|
97
|
-
f"{
|
91
|
+
f"{base_data_type.bitstruct_format_letter}{bit_length}",
|
98
92
|
extracted_bytes,
|
99
93
|
offset=padding)
|
100
94
|
|
101
95
|
text_errors = 'strict' if exceptions.strict_mode else 'replace'
|
102
96
|
if base_data_type == DataType.A_ASCIISTRING:
|
97
|
+
assert isinstance(internal_value, (bytes, bytearray))
|
103
98
|
# The spec says ASCII, meaning only byte values 0-127.
|
104
99
|
# But in practice, vendors use iso-8859-1, aka latin-1
|
105
100
|
# reason being iso-8859-1 never fails since it has a valid
|
@@ -107,9 +102,11 @@ class DecodeState:
|
|
107
102
|
text_encoding = 'iso-8859-1'
|
108
103
|
internal_value = internal_value.decode(encoding=text_encoding, errors=text_errors)
|
109
104
|
elif base_data_type == DataType.A_UTF8STRING:
|
105
|
+
assert isinstance(internal_value, (bytes, bytearray))
|
110
106
|
text_encoding = "utf-8"
|
111
107
|
internal_value = internal_value.decode(encoding=text_encoding, errors=text_errors)
|
112
108
|
elif base_data_type == DataType.A_UNICODE2STRING:
|
109
|
+
assert isinstance(internal_value, (bytes, bytearray))
|
113
110
|
# For UTF-16, we need to manually decode the extracted
|
114
111
|
# bytes to a string
|
115
112
|
text_encoding = "utf-16-be" if is_highlow_byte_order else "utf-16-le"
|
@@ -118,4 +115,4 @@ class DecodeState:
|
|
118
115
|
self.cursor_byte_position += byte_length
|
119
116
|
self.cursor_bit_position = 0
|
120
117
|
|
121
|
-
return internal_value
|
118
|
+
return cast(AtomicOdxType, internal_value)
|
odxtools/description.py
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from typing import List, Optional
|
3
|
+
from xml.etree import ElementTree
|
4
|
+
|
5
|
+
from .exceptions import odxrequire
|
6
|
+
from .odxlink import OdxDocFragment
|
7
|
+
|
8
|
+
|
9
|
+
@dataclass
|
10
|
+
class Description:
|
11
|
+
text: str
|
12
|
+
external_docs: List[str]
|
13
|
+
text_identifier: Optional[str]
|
14
|
+
|
15
|
+
@staticmethod
|
16
|
+
def from_et(et_element: Optional[ElementTree.Element],
|
17
|
+
doc_frags: List[OdxDocFragment]) -> Optional["Description"]:
|
18
|
+
if et_element is None:
|
19
|
+
return None
|
20
|
+
|
21
|
+
# Extract the contents of the tag as a XHTML string.
|
22
|
+
raw_string = et_element.text or ""
|
23
|
+
for e in et_element:
|
24
|
+
if e.tag == "EXTERNAL-DOCS":
|
25
|
+
break
|
26
|
+
raw_string += ElementTree.tostring(e, encoding="unicode")
|
27
|
+
|
28
|
+
# remove white spaces at the beginning and at the end of all
|
29
|
+
# extracted lines
|
30
|
+
stripped_lines = [x.strip() for x in raw_string.split("\n")]
|
31
|
+
|
32
|
+
text = "\n".join(stripped_lines).strip()
|
33
|
+
|
34
|
+
text_identifier = et_element.get("TI")
|
35
|
+
|
36
|
+
external_docs = \
|
37
|
+
[
|
38
|
+
odxrequire(ed.get("HREF")) for ed in et_element.iterfind("EXTERNAL-DOCS/EXTERNAL-DOC")
|
39
|
+
]
|
40
|
+
return Description(text=text, text_identifier=text_identifier, external_docs=external_docs)
|
41
|
+
|
42
|
+
@staticmethod
|
43
|
+
def from_string(text: str) -> "Description":
|
44
|
+
return Description(text=text, external_docs=[], text_identifier=None)
|
45
|
+
|
46
|
+
def __str__(self) -> str:
|
47
|
+
return self.text
|
@@ -1,13 +1,12 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
1
2
|
from dataclasses import dataclass
|
2
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
3
4
|
from xml.etree import ElementTree
|
4
5
|
|
5
6
|
from .dataobjectproperty import DataObjectProperty
|
6
7
|
from .exceptions import odxrequire
|
7
8
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
from ..diaglayer import DiagLayer
|
9
|
+
from .snrefcontext import SnRefContext
|
11
10
|
|
12
11
|
|
13
12
|
@dataclass
|
@@ -39,7 +38,7 @@ class DetermineNumberOfItems:
|
|
39
38
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
40
39
|
self._dop = odxlinks.resolve(self.dop_ref, DataObjectProperty)
|
41
40
|
|
42
|
-
def _resolve_snrefs(self,
|
41
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
43
42
|
pass
|
44
43
|
|
45
44
|
@property
|
odxtools/diagcodedtype.py
CHANGED
@@ -1,21 +1,14 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
-
import abc
|
3
2
|
from dataclasses import dataclass
|
4
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Literal, Optional, Union, cast
|
4
|
+
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .decodestate import
|
6
|
+
from .decodestate import DecodeState
|
7
7
|
from .encodestate import EncodeState
|
8
|
-
from .exceptions import
|
9
|
-
from .odxlink import OdxLinkDatabase, OdxLinkId
|
10
|
-
from .odxtypes import AtomicOdxType, DataType
|
11
|
-
|
12
|
-
try:
|
13
|
-
import bitstruct.c as bitstruct
|
14
|
-
except ImportError:
|
15
|
-
import bitstruct
|
16
|
-
|
17
|
-
if TYPE_CHECKING:
|
18
|
-
from .diaglayer import DiagLayer
|
8
|
+
from .exceptions import odxassert, odxraise, odxrequire
|
9
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
10
|
+
from .odxtypes import AtomicOdxType, DataType, odxstr_to_bool
|
11
|
+
from .snrefcontext import SnRefContext
|
19
12
|
|
20
13
|
# Allowed diag-coded types
|
21
14
|
DctType = Literal[
|
@@ -27,12 +20,30 @@ DctType = Literal[
|
|
27
20
|
|
28
21
|
|
29
22
|
@dataclass
|
30
|
-
class DiagCodedType
|
23
|
+
class DiagCodedType:
|
31
24
|
|
32
25
|
base_data_type: DataType
|
33
26
|
base_type_encoding: Optional[str]
|
34
27
|
is_highlow_byte_order_raw: Optional[bool]
|
35
28
|
|
29
|
+
@staticmethod
|
30
|
+
def from_et(et_element: ElementTree.Element,
|
31
|
+
doc_frags: List[OdxDocFragment]) -> "DiagCodedType":
|
32
|
+
base_data_type_str = odxrequire(et_element.get("BASE-DATA-TYPE"))
|
33
|
+
try:
|
34
|
+
base_data_type = DataType(base_data_type_str)
|
35
|
+
except ValueError:
|
36
|
+
odxraise(f"Unknown base data type {base_data_type_str}")
|
37
|
+
base_data_type = cast(DataType, None)
|
38
|
+
|
39
|
+
base_type_encoding = et_element.get("BASE-TYPE-ENCODING")
|
40
|
+
is_highlow_byte_order_raw = odxstr_to_bool(et_element.get("IS-HIGHLOW-BYTE-ORDER"))
|
41
|
+
|
42
|
+
return DiagCodedType(
|
43
|
+
base_data_type=base_data_type,
|
44
|
+
base_type_encoding=base_type_encoding,
|
45
|
+
is_highlow_byte_order_raw=is_highlow_byte_order_raw)
|
46
|
+
|
36
47
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]: # noqa: B027
|
37
48
|
return {}
|
38
49
|
|
@@ -40,7 +51,7 @@ class DiagCodedType(abc.ABC):
|
|
40
51
|
"""Recursively resolve any odxlinks references"""
|
41
52
|
pass
|
42
53
|
|
43
|
-
def _resolve_snrefs(self,
|
54
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None: # noqa: B027
|
44
55
|
"""Recursively resolve any short-name references"""
|
45
56
|
pass
|
46
57
|
|
@@ -48,100 +59,20 @@ class DiagCodedType(abc.ABC):
|
|
48
59
|
return None
|
49
60
|
|
50
61
|
@property
|
51
|
-
@abc.abstractmethod
|
52
62
|
def dct_type(self) -> DctType:
|
53
|
-
|
63
|
+
odxraise(f"Class {type(self).__name__} does not override required method "
|
64
|
+
f"dct_type()", NotImplementedError)
|
65
|
+
return cast(DctType, None)
|
54
66
|
|
55
67
|
@property
|
56
68
|
def is_highlow_byte_order(self) -> bool:
|
57
69
|
return self.is_highlow_byte_order_raw in [None, True]
|
58
70
|
|
59
|
-
@staticmethod
|
60
|
-
def _encode_internal_value(
|
61
|
-
internal_value: AtomicOdxType,
|
62
|
-
bit_position: int,
|
63
|
-
bit_length: int,
|
64
|
-
base_data_type: DataType,
|
65
|
-
is_highlow_byte_order: bool,
|
66
|
-
) -> bytes:
|
67
|
-
"""Convert the internal_value to bytes."""
|
68
|
-
# Check that bytes and strings actually fit into the bit length
|
69
|
-
if base_data_type == DataType.A_BYTEFIELD:
|
70
|
-
if isinstance(internal_value, bytearray):
|
71
|
-
internal_value = bytes(internal_value)
|
72
|
-
if not isinstance(internal_value, bytes):
|
73
|
-
odxraise()
|
74
|
-
if 8 * len(internal_value) > bit_length:
|
75
|
-
raise EncodeError(f"The bytefield {internal_value.hex()} is too large "
|
76
|
-
f"({len(internal_value)} bytes)."
|
77
|
-
f" The maximum length is {bit_length//8}.")
|
78
|
-
if base_data_type == DataType.A_ASCIISTRING:
|
79
|
-
if not isinstance(internal_value, str):
|
80
|
-
odxraise()
|
81
|
-
|
82
|
-
# The spec says ASCII, meaning only byte values 0-127.
|
83
|
-
# But in practice, vendors use iso-8859-1, aka latin-1
|
84
|
-
# reason being iso-8859-1 never fails since it has a valid
|
85
|
-
# character mapping for every possible byte sequence.
|
86
|
-
internal_value = internal_value.encode("iso-8859-1")
|
87
|
-
|
88
|
-
if 8 * len(internal_value) > bit_length:
|
89
|
-
raise EncodeError(f"The string {repr(internal_value)} is too large."
|
90
|
-
f" The maximum number of characters is {bit_length//8}.")
|
91
|
-
elif base_data_type == DataType.A_UTF8STRING:
|
92
|
-
if not isinstance(internal_value, str):
|
93
|
-
odxraise()
|
94
|
-
|
95
|
-
internal_value = internal_value.encode("utf-8")
|
96
|
-
|
97
|
-
if 8 * len(internal_value) > bit_length:
|
98
|
-
raise EncodeError(f"The string {repr(internal_value)} is too large."
|
99
|
-
f" The maximum number of bytes is {bit_length//8}.")
|
100
|
-
|
101
|
-
elif base_data_type == DataType.A_UNICODE2STRING:
|
102
|
-
if not isinstance(internal_value, str):
|
103
|
-
odxraise()
|
104
|
-
|
105
|
-
text_encoding = "utf-16-be" if is_highlow_byte_order else "utf-16-le"
|
106
|
-
internal_value = internal_value.encode(text_encoding)
|
107
|
-
|
108
|
-
if 8 * len(internal_value) > bit_length:
|
109
|
-
raise EncodeError(f"The string {repr(internal_value)} is too large."
|
110
|
-
f" The maximum number of characters is {bit_length//16}.")
|
111
|
-
|
112
|
-
# If the bit length is zero, return empty bytes
|
113
|
-
if bit_length == 0:
|
114
|
-
if (base_data_type in [
|
115
|
-
DataType.A_INT32, DataType.A_UINT32, DataType.A_FLOAT32, DataType.A_FLOAT64
|
116
|
-
] and base_data_type != 0):
|
117
|
-
raise EncodeError(
|
118
|
-
f"The number {repr(internal_value)} cannot be encoded into {bit_length} bits.")
|
119
|
-
return b''
|
120
|
-
|
121
|
-
char = ODX_TYPE_TO_FORMAT_LETTER[base_data_type]
|
122
|
-
padding = (8 - ((bit_length + bit_position) % 8)) % 8
|
123
|
-
odxassert((0 <= padding and padding < 8 and (padding + bit_length + bit_position) % 8 == 0),
|
124
|
-
f"Incorrect padding {padding}")
|
125
|
-
left_pad = f"p{padding}" if padding > 0 else ""
|
126
|
-
|
127
|
-
# actually encode the value
|
128
|
-
coded = bitstruct.pack(f"{left_pad}{char}{bit_length}", internal_value)
|
129
|
-
|
130
|
-
# apply byte order for numeric objects
|
131
|
-
if not is_highlow_byte_order and base_data_type in [
|
132
|
-
DataType.A_INT32,
|
133
|
-
DataType.A_UINT32,
|
134
|
-
DataType.A_FLOAT32,
|
135
|
-
DataType.A_FLOAT64,
|
136
|
-
]:
|
137
|
-
coded = coded[::-1]
|
138
|
-
|
139
|
-
return coded
|
140
|
-
|
141
71
|
def _minimal_byte_length_of(self, internal_value: Union[bytes, str]) -> int:
|
142
72
|
"""Helper method to get the minimal byte length.
|
143
73
|
(needed for LeadingLength- and MinMaxLengthType)
|
144
74
|
"""
|
75
|
+
byte_length: int = -1
|
145
76
|
# A_BYTEFIELD, A_ASCIISTRING, A_UNICODE2STRING, A_UTF8STRING
|
146
77
|
if self.base_data_type == DataType.A_BYTEFIELD:
|
147
78
|
byte_length = len(internal_value)
|
@@ -159,11 +90,10 @@ class DiagCodedType(abc.ABC):
|
|
159
90
|
odxassert(
|
160
91
|
byte_length % 2 == 0, f"The bit length of A_UNICODE2STRING must"
|
161
92
|
f" be a multiple of 16 but is {8*byte_length}")
|
93
|
+
|
162
94
|
return byte_length
|
163
95
|
|
164
|
-
|
165
|
-
def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
|
166
|
-
bit_position: int) -> bytes:
|
96
|
+
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
167
97
|
"""Encode the internal value.
|
168
98
|
|
169
99
|
Parameters
|
@@ -176,9 +106,9 @@ class DiagCodedType(abc.ABC):
|
|
176
106
|
mapping from ID (of the length key) to bit length
|
177
107
|
(only needed for ParamLengthInfoType)
|
178
108
|
"""
|
179
|
-
|
109
|
+
raise NotImplementedError(
|
110
|
+
f".encode_into_pdu() is not implemented by the class {type(self).__name__}")
|
180
111
|
|
181
|
-
@abc.abstractmethod
|
182
112
|
def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
183
113
|
"""Decode the parameter value from the coded message.
|
184
114
|
|
@@ -194,4 +124,5 @@ class DiagCodedType(abc.ABC):
|
|
194
124
|
int
|
195
125
|
the next byte position after the extracted parameter
|
196
126
|
"""
|
197
|
-
|
127
|
+
raise NotImplementedError(
|
128
|
+
f".decode_from_pdu() is not implemented by the class {type(self).__name__}")
|
odxtools/diagcomm.py
CHANGED
@@ -6,20 +6,20 @@ from xml.etree import ElementTree
|
|
6
6
|
|
7
7
|
from .admindata import AdminData
|
8
8
|
from .audience import Audience
|
9
|
-
from .createsdgs import create_sdgs_from_et
|
10
9
|
from .element import IdentifiableElement
|
11
10
|
from .exceptions import odxraise, odxrequire
|
12
11
|
from .functionalclass import FunctionalClass
|
13
12
|
from .nameditemlist import NamedItemList
|
14
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
13
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
15
14
|
from .odxtypes import odxstr_to_bool
|
15
|
+
from .snrefcontext import SnRefContext
|
16
16
|
from .specialdatagroup import SpecialDataGroup
|
17
17
|
from .state import State
|
18
18
|
from .statetransition import StateTransition
|
19
19
|
from .utils import dataclass_fields_asdict
|
20
20
|
|
21
21
|
if TYPE_CHECKING:
|
22
|
-
from .
|
22
|
+
from .diaglayers.protocol import Protocol
|
23
23
|
|
24
24
|
|
25
25
|
class DiagClassType(Enum):
|
@@ -76,7 +76,9 @@ class DiagComm(IdentifiableElement):
|
|
76
76
|
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
77
77
|
|
78
78
|
admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
|
79
|
-
sdgs =
|
79
|
+
sdgs = [
|
80
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
81
|
+
]
|
80
82
|
|
81
83
|
functional_class_refs = [
|
82
84
|
odxrequire(OdxLinkRef.from_et(el, doc_frags))
|
@@ -141,7 +143,7 @@ class DiagComm(IdentifiableElement):
|
|
141
143
|
return self._functional_classes
|
142
144
|
|
143
145
|
@property
|
144
|
-
def protocols(self) -> NamedItemList["
|
146
|
+
def protocols(self) -> NamedItemList["Protocol"]:
|
145
147
|
return self._protocols
|
146
148
|
|
147
149
|
@property
|
@@ -201,15 +203,25 @@ class DiagComm(IdentifiableElement):
|
|
201
203
|
self._state_transitions = NamedItemList(
|
202
204
|
[odxlinks.resolve(stt_ref, StateTransition) for stt_ref in self.state_transition_refs])
|
203
205
|
|
204
|
-
def _resolve_snrefs(self,
|
206
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
205
207
|
if self.admin_data:
|
206
|
-
self.admin_data._resolve_snrefs(
|
208
|
+
self.admin_data._resolve_snrefs(context)
|
207
209
|
|
208
210
|
if self.audience:
|
209
|
-
self.audience._resolve_snrefs(
|
211
|
+
self.audience._resolve_snrefs(context)
|
210
212
|
|
211
213
|
for sdg in self.sdgs:
|
212
|
-
sdg._resolve_snrefs(
|
213
|
-
|
214
|
-
|
215
|
-
|
214
|
+
sdg._resolve_snrefs(context)
|
215
|
+
|
216
|
+
if TYPE_CHECKING:
|
217
|
+
diag_layer = odxrequire(context.diag_layer)
|
218
|
+
self._protocols = NamedItemList([
|
219
|
+
resolve_snref(prot_snref, getattr(diag_layer, "protocols", []), Protocol)
|
220
|
+
for prot_snref in self.protocol_snrefs
|
221
|
+
])
|
222
|
+
else:
|
223
|
+
diag_layer = odxrequire(context.diag_layer)
|
224
|
+
self._protocols = NamedItemList([
|
225
|
+
resolve_snref(prot_snref, getattr(diag_layer, "protocols", []))
|
226
|
+
for prot_snref in self.protocol_snrefs
|
227
|
+
])
|