odxtools 6.7.0__py3-none-any.whl → 9.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odxtools/__init__.py +6 -4
- odxtools/additionalaudience.py +3 -5
- odxtools/admindata.py +5 -7
- odxtools/audience.py +10 -13
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +55 -240
- odxtools/cli/_parser_utils.py +1 -1
- odxtools/cli/_print_utils.py +168 -134
- odxtools/cli/browse.py +111 -92
- odxtools/cli/compare.py +90 -71
- odxtools/cli/list.py +24 -15
- odxtools/cli/snoop.py +28 -5
- 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 +7 -9
- 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 +93 -12
- odxtools/compumethods/compuphystointernal.py +56 -0
- odxtools/compumethods/compurationalcoeffs.py +20 -9
- odxtools/compumethods/compuscale.py +30 -35
- odxtools/compumethods/createanycompumethod.py +28 -161
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/linearcompumethod.py +69 -189
- 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 +119 -99
- odxtools/compumethods/texttablecompumethod.py +107 -43
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +167 -87
- odxtools/dataobjectproperty.py +15 -25
- odxtools/decodestate.py +9 -15
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +36 -106
- odxtools/diagcomm.py +24 -12
- odxtools/diagdatadictionaryspec.py +33 -34
- 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/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} +209 -448
- 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 +56 -43
- odxtools/diagvariable.py +113 -0
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +15 -17
- odxtools/dtcdop.py +168 -50
- odxtools/dynamicendmarkerfield.py +134 -0
- odxtools/dynamiclengthfield.py +41 -37
- 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 -38
- odxtools/exceptions.py +11 -2
- odxtools/field.py +10 -10
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -12
- 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 +128 -73
- odxtools/multiplexercase.py +13 -14
- odxtools/multiplexerdefaultcase.py +15 -12
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/nameditemlist.py +29 -5
- odxtools/negoutputparam.py +3 -5
- odxtools/odxcategory.py +83 -0
- odxtools/odxlink.py +60 -51
- odxtools/odxtypes.py +37 -5
- odxtools/outputparam.py +4 -15
- odxtools/parameterinfo.py +218 -67
- odxtools/parameters/codedconstparameter.py +16 -24
- odxtools/parameters/dynamicparameter.py +5 -4
- odxtools/parameters/lengthkeyparameter.py +60 -26
- odxtools/parameters/matchingrequestparameter.py +23 -11
- odxtools/parameters/nrcconstparameter.py +45 -46
- odxtools/parameters/parameter.py +54 -56
- odxtools/parameters/parameterwithdop.py +15 -25
- odxtools/parameters/physicalconstantparameter.py +15 -18
- odxtools/parameters/reservedparameter.py +6 -2
- odxtools/parameters/systemparameter.py +55 -11
- odxtools/parameters/tableentryparameter.py +3 -2
- odxtools/parameters/tablekeyparameter.py +103 -49
- odxtools/parameters/tablestructparameter.py +47 -48
- odxtools/parameters/valueparameter.py +16 -20
- odxtools/paramlengthinfotype.py +52 -32
- odxtools/parentref.py +16 -2
- 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 +3 -3
- 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 +7 -17
- odxtools/staticfield.py +31 -25
- odxtools/subcomponent.py +288 -0
- odxtools/swvariable.py +21 -0
- odxtools/table.py +7 -8
- odxtools/tablerow.py +19 -11
- 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 -133
- 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 +4 -3
- odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
- odxtools/templates/macros/printParam.xml.jinja2 +11 -12
- 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 +1 -1
- 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} +20 -10
- odxtools/xdoc.py +3 -5
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/METADATA +20 -21
- odxtools-9.3.0.dist-info/RECORD +228 -0
- {odxtools-6.7.0.dist-info → odxtools-9.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/templates/macros/printVariant.xml.jinja2 +0 -216
- odxtools-6.7.0.dist-info/RECORD +0 -182
- /odxtools/{diaglayertype.py → diaglayers/diaglayertype.py} +0 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
@@ -1,84 +1,96 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import re
|
3
3
|
import warnings
|
4
|
+
from copy import deepcopy
|
4
5
|
from dataclasses import dataclass
|
5
6
|
from functools import cached_property
|
6
|
-
from
|
7
|
-
|
7
|
+
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Tuple, TypeVar,
|
8
|
+
Union, cast)
|
8
9
|
from xml.etree import ElementTree
|
9
10
|
|
10
|
-
from
|
11
|
-
|
12
|
-
from
|
13
|
-
from
|
14
|
-
from
|
15
|
-
from
|
16
|
-
from
|
17
|
-
from
|
18
|
-
from
|
19
|
-
from
|
20
|
-
from
|
21
|
-
from
|
22
|
-
from
|
23
|
-
from
|
24
|
-
from
|
25
|
-
from
|
26
|
-
from
|
27
|
-
from
|
28
|
-
from .
|
29
|
-
from .
|
30
|
-
|
31
|
-
|
32
|
-
from .
|
33
|
-
from .
|
34
|
-
from .specialdatagroup import SpecialDataGroup
|
35
|
-
from .statechart import StateChart
|
36
|
-
from .table import Table
|
37
|
-
from .unitgroup import UnitGroup
|
38
|
-
from .unitspec import UnitSpec
|
11
|
+
from ..additionalaudience import AdditionalAudience
|
12
|
+
from ..admindata import AdminData
|
13
|
+
from ..comparaminstance import ComparamInstance
|
14
|
+
from ..diagcomm import DiagComm
|
15
|
+
from ..diagdatadictionaryspec import DiagDataDictionarySpec
|
16
|
+
from ..diagservice import DiagService
|
17
|
+
from ..exceptions import OdxWarning, odxassert, odxraise
|
18
|
+
from ..functionalclass import FunctionalClass
|
19
|
+
from ..nameditemlist import NamedItemList, OdxNamed
|
20
|
+
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
21
|
+
from ..parentref import ParentRef
|
22
|
+
from ..response import Response
|
23
|
+
from ..singleecujob import SingleEcuJob
|
24
|
+
from ..snrefcontext import SnRefContext
|
25
|
+
from ..specialdatagroup import SpecialDataGroup
|
26
|
+
from ..statechart import StateChart
|
27
|
+
from ..unitgroup import UnitGroup
|
28
|
+
from ..unitspec import UnitSpec
|
29
|
+
from .diaglayer import DiagLayer
|
30
|
+
from .hierarchyelementraw import HierarchyElementRaw
|
31
|
+
|
32
|
+
if TYPE_CHECKING:
|
33
|
+
from .database import Database
|
34
|
+
from .protocol import Protocol
|
39
35
|
|
40
36
|
TNamed = TypeVar("TNamed", bound=OdxNamed)
|
41
37
|
|
42
|
-
PrefixTree = Dict[int, Union[List[DiagService], "PrefixTree"]]
|
43
|
-
|
44
38
|
|
45
39
|
@dataclass
|
46
|
-
class DiagLayer:
|
47
|
-
"""This class
|
48
|
-
according to the ODX standard.
|
49
|
-
|
50
|
-
i.e. it handles the value inheritance, communication parameters,
|
51
|
-
encoding/decoding of data, etc.
|
40
|
+
class HierarchyElement(DiagLayer):
|
41
|
+
"""This is the base class for diagnostic layers that may be involved in value inheritance
|
52
42
|
"""
|
53
43
|
|
54
|
-
|
44
|
+
@property
|
45
|
+
def hierarchy_element_raw(self) -> HierarchyElementRaw:
|
46
|
+
return cast(HierarchyElementRaw, self.diag_layer_raw)
|
47
|
+
|
48
|
+
@staticmethod
|
49
|
+
def from_et(et_element: ElementTree.Element,
|
50
|
+
doc_frags: List[OdxDocFragment]) -> "HierarchyElement":
|
51
|
+
hierarchy_element_raw = HierarchyElementRaw.from_et(et_element, doc_frags)
|
52
|
+
|
53
|
+
return HierarchyElement(diag_layer_raw=hierarchy_element_raw)
|
55
54
|
|
56
55
|
def __post_init__(self) -> None:
|
57
|
-
|
56
|
+
super().__post_init__()
|
58
57
|
|
59
|
-
|
60
|
-
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagLayer":
|
61
|
-
diag_layer_raw = DiagLayerRaw.from_et(et_element, doc_frags)
|
58
|
+
self._global_negative_responses: NamedItemList[Response]
|
62
59
|
|
63
|
-
|
64
|
-
|
60
|
+
odxassert(
|
61
|
+
isinstance(self.diag_layer_raw, HierarchyElementRaw),
|
62
|
+
"The raw diagnostic layer passed to HierarchyElement "
|
63
|
+
"must be a HierarchyElementRaw")
|
65
64
|
|
66
65
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
67
|
-
|
68
|
-
result = self.diag_layer_raw._build_odxlinks()
|
69
|
-
|
70
|
-
# we want to get the full diag layer, not just the raw layer
|
71
|
-
# when referencing...
|
72
|
-
result[self.odx_id] = self
|
66
|
+
result = super()._build_odxlinks()
|
73
67
|
|
74
68
|
return result
|
75
69
|
|
76
70
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
77
|
-
|
71
|
+
super()._resolve_odxlinks(odxlinks)
|
72
|
+
|
73
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
74
|
+
super()._resolve_snrefs(context)
|
75
|
+
|
76
|
+
def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
|
77
|
+
"""Create a deep copy of the hierarchy element
|
78
|
+
|
79
|
+
Note that the copied diagnostic layer is not fully
|
80
|
+
initialized, so `_finalize_init()` should to be called on it
|
81
|
+
before it can be used normally.
|
82
|
+
"""
|
83
|
+
|
84
|
+
new_he = super().__deepcopy__(memo)
|
85
|
+
|
86
|
+
# note that the self.hierarchy_element_raw object is *not*
|
87
|
+
# copied at this place because the attribute points to the
|
88
|
+
# same object as self.diag_layer_raw.
|
89
|
+
new_he.hierarchy_element_raw = deepcopy(self.hierarchy_element_raw)
|
78
90
|
|
79
|
-
|
91
|
+
return new_he
|
80
92
|
|
81
|
-
def _finalize_init(self, odxlinks: OdxLinkDatabase) -> None:
|
93
|
+
def _finalize_init(self, database: "Database", odxlinks: OdxLinkDatabase) -> None:
|
82
94
|
"""This method deals with everything inheritance related and
|
83
95
|
-- after the final set of objects covered by the diagnostic
|
84
96
|
layer is determined -- resolves any short name references in
|
@@ -98,27 +110,7 @@ class DiagLayer:
|
|
98
110
|
# fill in all applicable objects that use value inheritance
|
99
111
|
#####
|
100
112
|
|
101
|
-
|
102
|
-
diag_comms = self._compute_available_diag_comms(odxlinks)
|
103
|
-
self._diag_comms = NamedItemList(diag_comms)
|
104
|
-
|
105
|
-
# filter the diag comms for services and single-ECU jobs
|
106
|
-
services = [dc for dc in diag_comms if isinstance(dc, DiagService)]
|
107
|
-
single_ecu_jobs = [dc for dc in diag_comms if isinstance(dc, SingleEcuJob)]
|
108
|
-
self._services = NamedItemList(services)
|
109
|
-
self._single_ecu_jobs = NamedItemList(single_ecu_jobs)
|
110
|
-
|
111
|
-
global_negative_responses = self._compute_available_global_neg_responses(odxlinks)
|
112
|
-
self._global_negative_responses = NamedItemList(global_negative_responses)
|
113
|
-
|
114
|
-
functional_classes = self._compute_available_functional_classes()
|
115
|
-
self._functional_classes = NamedItemList(functional_classes)
|
116
|
-
|
117
|
-
additional_audiences = self._compute_available_additional_audiences()
|
118
|
-
self._additional_audiences = NamedItemList(additional_audiences)
|
119
|
-
|
120
|
-
state_charts = self._compute_available_state_charts()
|
121
|
-
self._state_charts = NamedItemList(state_charts)
|
113
|
+
self._compute_value_inheritance(odxlinks)
|
122
114
|
|
123
115
|
############
|
124
116
|
# create a new unit_spec object. This is necessary because
|
@@ -167,10 +159,18 @@ class DiagLayer:
|
|
167
159
|
lambda ddd_spec: ddd_spec.dtc_dops,
|
168
160
|
lambda parent_ref: parent_ref.not_inherited_dops,
|
169
161
|
)
|
162
|
+
static_fields = self._compute_available_ddd_spec_items(
|
163
|
+
lambda ddd_spec: ddd_spec.static_fields,
|
164
|
+
lambda parent_ref: parent_ref.not_inherited_dops,
|
165
|
+
)
|
170
166
|
end_of_pdu_fields = self._compute_available_ddd_spec_items(
|
171
167
|
lambda ddd_spec: ddd_spec.end_of_pdu_fields,
|
172
168
|
lambda parent_ref: parent_ref.not_inherited_dops,
|
173
169
|
)
|
170
|
+
dynamic_endmarker_fields = self._compute_available_ddd_spec_items(
|
171
|
+
lambda ddd_spec: ddd_spec.dynamic_endmarker_fields,
|
172
|
+
lambda parent_ref: parent_ref.not_inherited_dops,
|
173
|
+
)
|
174
174
|
dynamic_length_fields = self._compute_available_ddd_spec_items(
|
175
175
|
lambda ddd_spec: ddd_spec.dynamic_length_fields,
|
176
176
|
lambda parent_ref: parent_ref.not_inherited_dops,
|
@@ -185,22 +185,24 @@ class DiagLayer:
|
|
185
185
|
lambda ddd_spec: ddd_spec.muxs, lambda parent_ref: parent_ref.not_inherited_dops)
|
186
186
|
tables = self._compute_available_ddd_spec_items(
|
187
187
|
lambda ddd_spec: ddd_spec.tables, lambda parent_ref: parent_ref.not_inherited_tables)
|
188
|
-
|
188
|
+
|
189
|
+
ddds_admin_data: Optional[AdminData] = None
|
190
|
+
ddds_sdgs: List[SpecialDataGroup] = []
|
189
191
|
if self.diag_layer_raw.diag_data_dictionary_spec:
|
192
|
+
ddds_admin_data = self.diag_layer_raw.diag_data_dictionary_spec.admin_data
|
190
193
|
ddds_sdgs = self.diag_layer_raw.diag_data_dictionary_spec.sdgs
|
191
|
-
else:
|
192
|
-
ddds_sdgs = []
|
193
194
|
|
194
195
|
# create a DiagDataDictionarySpec which includes all the
|
195
196
|
# inherited objects. To me, this seems rather inelegant, but
|
196
197
|
# hey, it's described like this in the standard.
|
197
198
|
self._diag_data_dictionary_spec = DiagDataDictionarySpec(
|
198
|
-
admin_data=
|
199
|
+
admin_data=ddds_admin_data,
|
199
200
|
data_object_props=dops,
|
200
201
|
dtc_dops=dtc_dops,
|
201
202
|
structures=structures,
|
202
|
-
static_fields=
|
203
|
+
static_fields=static_fields,
|
203
204
|
end_of_pdu_fields=end_of_pdu_fields,
|
205
|
+
dynamic_endmarker_fields=dynamic_endmarker_fields,
|
204
206
|
dynamic_length_fields=dynamic_length_fields,
|
205
207
|
tables=tables,
|
206
208
|
env_data_descs=env_data_descs,
|
@@ -217,7 +219,7 @@ class DiagLayer:
|
|
217
219
|
# scheme, cf the docstring of
|
218
220
|
# _compute_available_commmunication_parameters().
|
219
221
|
#####
|
220
|
-
self.
|
222
|
+
self._comparam_refs = NamedItemList(self._compute_available_commmunication_parameters())
|
221
223
|
|
222
224
|
#####
|
223
225
|
# resolve all SNREFs. TODO: We allow SNREFS to objects that
|
@@ -225,169 +227,40 @@ class DiagLayer:
|
|
225
227
|
# by the spec (So far, I haven't found any definitive
|
226
228
|
# statement...)
|
227
229
|
#####
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
#####
|
233
|
-
@cached_property
|
234
|
-
def service_groups(self) -> ServiceBinner:
|
235
|
-
return ServiceBinner(self.services)
|
236
|
-
|
237
|
-
#####
|
238
|
-
# </convenience functionality>
|
239
|
-
#####
|
240
|
-
|
241
|
-
#####
|
242
|
-
# <properties forwarded to the "raw" diag layer>
|
243
|
-
#####
|
244
|
-
@property
|
245
|
-
def variant_type(self) -> DiagLayerType:
|
246
|
-
return self.diag_layer_raw.variant_type
|
247
|
-
|
248
|
-
@property
|
249
|
-
def odx_id(self) -> OdxLinkId:
|
250
|
-
return self.diag_layer_raw.odx_id
|
251
|
-
|
252
|
-
@property
|
253
|
-
def short_name(self) -> str:
|
254
|
-
return self.diag_layer_raw.short_name
|
255
|
-
|
256
|
-
@property
|
257
|
-
def long_name(self) -> Optional[str]:
|
258
|
-
return self.diag_layer_raw.long_name
|
259
|
-
|
260
|
-
@property
|
261
|
-
def description(self) -> Optional[str]:
|
262
|
-
return self.diag_layer_raw.description
|
263
|
-
|
264
|
-
@property
|
265
|
-
def admin_data(self) -> Optional[AdminData]:
|
266
|
-
return self.diag_layer_raw.admin_data
|
267
|
-
|
268
|
-
@property
|
269
|
-
def company_datas(self) -> NamedItemList[CompanyData]:
|
270
|
-
return self.diag_layer_raw.company_datas
|
271
|
-
|
272
|
-
@property
|
273
|
-
def requests(self) -> NamedItemList[Request]:
|
274
|
-
return self.diag_layer_raw.requests
|
275
|
-
|
276
|
-
@property
|
277
|
-
def positive_responses(self) -> NamedItemList[Response]:
|
278
|
-
return self.diag_layer_raw.positive_responses
|
279
|
-
|
280
|
-
@property
|
281
|
-
def negative_responses(self) -> NamedItemList[Response]:
|
282
|
-
return self.diag_layer_raw.negative_responses
|
283
|
-
|
284
|
-
@property
|
285
|
-
def import_refs(self) -> List[OdxLinkRef]:
|
286
|
-
return self.diag_layer_raw.import_refs
|
287
|
-
|
288
|
-
@property
|
289
|
-
def sdgs(self) -> List[SpecialDataGroup]:
|
290
|
-
return self.diag_layer_raw.sdgs
|
291
|
-
|
292
|
-
@property
|
293
|
-
def parent_refs(self) -> List[ParentRef]:
|
294
|
-
return self.diag_layer_raw.parent_refs
|
295
|
-
|
296
|
-
@property
|
297
|
-
def ecu_variant_patterns(self) -> List[EcuVariantPattern]:
|
298
|
-
return self.diag_layer_raw.ecu_variant_patterns
|
299
|
-
|
300
|
-
@property
|
301
|
-
def comparam_spec_ref(self) -> Optional[OdxLinkRef]:
|
302
|
-
return self.diag_layer_raw.comparam_spec_ref
|
303
|
-
|
304
|
-
@property
|
305
|
-
def prot_stack_snref(self) -> Optional[str]:
|
306
|
-
return self.diag_layer_raw.prot_stack_snref
|
307
|
-
|
308
|
-
@property
|
309
|
-
def comparam_spec(self) -> Optional[ComparamSpec]:
|
310
|
-
return self.diag_layer_raw.comparam_spec
|
311
|
-
|
312
|
-
@property
|
313
|
-
def prot_stack(self) -> Optional[ProtStack]:
|
314
|
-
return self.diag_layer_raw.prot_stack
|
230
|
+
context = SnRefContext(database=database)
|
231
|
+
context.diag_layer = self
|
232
|
+
self._resolve_snrefs(context)
|
233
|
+
context.diag_layer = None
|
315
234
|
|
316
235
|
#####
|
317
|
-
#
|
236
|
+
# <value inheritance mechanism helpers>
|
318
237
|
#####
|
238
|
+
def _compute_value_inheritance(self, odxlinks: OdxLinkDatabase) -> None:
|
239
|
+
# diagnostic communication objects with the ODXLINKs resolved
|
240
|
+
diag_comms = self._compute_available_diag_comms(odxlinks)
|
241
|
+
self._diag_comms = NamedItemList[DiagComm](diag_comms)
|
319
242
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
"""All diagnostic communication primitives applicable to this DiagLayer
|
326
|
-
|
327
|
-
Diagnostic communication primitives are diagnostic services as
|
328
|
-
well as single-ECU jobs. This list has all references
|
329
|
-
resolved.
|
330
|
-
"""
|
331
|
-
return self._diag_comms
|
332
|
-
|
333
|
-
@property
|
334
|
-
def services(self) -> NamedItemList[DiagService]:
|
335
|
-
"""All diagnostic services applicable to this DiagLayer
|
336
|
-
|
337
|
-
This is a subset of all diagnostic communication
|
338
|
-
primitives. All references are resolved in the list returned.
|
339
|
-
"""
|
340
|
-
return self._services
|
341
|
-
|
342
|
-
@property
|
343
|
-
def single_ecu_jobs(self) -> NamedItemList[SingleEcuJob]:
|
344
|
-
"""All single-ECU jobs applicable to this DiagLayer
|
345
|
-
|
346
|
-
This is a subset of all diagnostic communication
|
347
|
-
primitives. All references are resolved in the list returned.
|
348
|
-
"""
|
349
|
-
return self._single_ecu_jobs
|
350
|
-
|
351
|
-
@property
|
352
|
-
def global_negative_responses(self) -> NamedItemList[Response]:
|
353
|
-
"""All global negative responses applicable to this DiagLayer"""
|
354
|
-
return self._global_negative_responses
|
355
|
-
|
356
|
-
@property
|
357
|
-
@deprecated(details="use diag_data_dictionary_spec.tables") # type: ignore[misc]
|
358
|
-
def tables(self) -> NamedItemList[Table]:
|
359
|
-
return self.diag_data_dictionary_spec.tables
|
360
|
-
|
361
|
-
@property
|
362
|
-
def functional_classes(self) -> NamedItemList[FunctionalClass]:
|
363
|
-
"""All functional classes applicable to this DiagLayer"""
|
364
|
-
return self._functional_classes
|
243
|
+
# filter the diag comms for services and single-ECU jobs
|
244
|
+
diag_services = [dc for dc in diag_comms if isinstance(dc, DiagService)]
|
245
|
+
single_ecu_jobs = [dc for dc in diag_comms if isinstance(dc, SingleEcuJob)]
|
246
|
+
self._diag_services = NamedItemList(diag_services)
|
247
|
+
self._single_ecu_jobs = NamedItemList(single_ecu_jobs)
|
365
248
|
|
366
|
-
|
367
|
-
|
368
|
-
"""All state charts applicable to this DiagLayer"""
|
369
|
-
return self._state_charts
|
249
|
+
global_negative_responses = self._compute_available_global_neg_responses(odxlinks)
|
250
|
+
self._global_negative_responses = NamedItemList(global_negative_responses)
|
370
251
|
|
371
|
-
|
372
|
-
|
373
|
-
"""All audiences applicable to this DiagLayer"""
|
374
|
-
return self._additional_audiences
|
252
|
+
functional_classes = self._compute_available_functional_classes()
|
253
|
+
self._functional_classes = NamedItemList(functional_classes)
|
375
254
|
|
376
|
-
|
377
|
-
|
378
|
-
"""The DiagDataDictionarySpec applicable to this DiagLayer"""
|
379
|
-
return self._diag_data_dictionary_spec
|
255
|
+
additional_audiences = self._compute_available_additional_audiences()
|
256
|
+
self._additional_audiences = NamedItemList(additional_audiences)
|
380
257
|
|
381
|
-
|
382
|
-
|
383
|
-
#######
|
258
|
+
state_charts = self._compute_available_state_charts()
|
259
|
+
self._state_charts = NamedItemList(state_charts)
|
384
260
|
|
385
|
-
#####
|
386
|
-
# <value inheritance mechanism helpers>
|
387
|
-
#####
|
388
261
|
def _get_parent_refs_sorted_by_priority(self, reverse: bool = False) -> Iterable[ParentRef]:
|
389
262
|
return sorted(
|
390
|
-
self.diag_layer_raw
|
263
|
+
getattr(self.diag_layer_raw, "parent_refs", []),
|
391
264
|
key=lambda pr: pr.layer.variant_type.inheritance_priority,
|
392
265
|
reverse=reverse)
|
393
266
|
|
@@ -481,41 +354,6 @@ class DiagLayer:
|
|
481
354
|
|
482
355
|
return [x[0] for x in result_dict.values()]
|
483
356
|
|
484
|
-
def _get_local_diag_comms(self, odxlinks: OdxLinkDatabase) -> Iterable[DiagComm]:
|
485
|
-
"""Return the list of locally defined diagnostic communications.
|
486
|
-
|
487
|
-
This is not completely trivial as it requires to resolving the
|
488
|
-
references specified in the <DIAG-COMMS> XML tag.
|
489
|
-
"""
|
490
|
-
result_dict: Dict[str, DiagComm] = {}
|
491
|
-
|
492
|
-
# TODO (?): add objects from the import-refs
|
493
|
-
|
494
|
-
for dc_proxy in self.diag_layer_raw.diag_comms:
|
495
|
-
if isinstance(dc_proxy, OdxLinkRef):
|
496
|
-
dc = odxlinks.resolve(dc_proxy)
|
497
|
-
else:
|
498
|
-
dc = dc_proxy
|
499
|
-
|
500
|
-
odxassert(isinstance(dc, DiagComm))
|
501
|
-
odxassert(
|
502
|
-
dc.short_name not in result_dict,
|
503
|
-
f"Multiple definitions of DIAG-COMM '{dc.short_name}' in "
|
504
|
-
f"layer '{self.short_name}'")
|
505
|
-
result_dict[dc.short_name] = dc
|
506
|
-
|
507
|
-
return result_dict.values()
|
508
|
-
|
509
|
-
def _get_local_unit_groups(self) -> Iterable[UnitGroup]:
|
510
|
-
if self.diag_layer_raw.diag_data_dictionary_spec is None:
|
511
|
-
return []
|
512
|
-
|
513
|
-
unit_spec = self.diag_layer_raw.diag_data_dictionary_spec.unit_spec
|
514
|
-
if unit_spec is None:
|
515
|
-
return []
|
516
|
-
|
517
|
-
return unit_spec.unit_groups
|
518
|
-
|
519
357
|
def _compute_available_diag_comms(self, odxlinks: OdxLinkDatabase) -> Iterable[DiagComm]:
|
520
358
|
|
521
359
|
def get_local_objects_fn(dl: DiagLayer) -> Iterable[DiagComm]:
|
@@ -595,6 +433,70 @@ class DiagLayer:
|
|
595
433
|
# </value inheritance mechanism helpers>
|
596
434
|
#####
|
597
435
|
|
436
|
+
#######
|
437
|
+
# <properties subject to value inheritance>
|
438
|
+
#######
|
439
|
+
@property
|
440
|
+
def diag_data_dictionary_spec(self) -> DiagDataDictionarySpec:
|
441
|
+
return self._diag_data_dictionary_spec
|
442
|
+
|
443
|
+
@property
|
444
|
+
def diag_comms(self) -> NamedItemList[DiagComm]:
|
445
|
+
"""All diagnostic communication primitives applicable to this DiagLayer
|
446
|
+
|
447
|
+
Diagnostic communication primitives are diagnostic services as
|
448
|
+
well as single-ECU jobs. This list has all references
|
449
|
+
resolved.
|
450
|
+
"""
|
451
|
+
return self._diag_comms
|
452
|
+
|
453
|
+
@property
|
454
|
+
def services(self) -> NamedItemList[DiagService]:
|
455
|
+
"""This property is an alias for `.diag_services`"""
|
456
|
+
return self._diag_services
|
457
|
+
|
458
|
+
@property
|
459
|
+
def diag_services(self) -> NamedItemList[DiagService]:
|
460
|
+
"""All diagnostic services applicable to this DiagLayer
|
461
|
+
|
462
|
+
This is a subset of all diagnostic communication
|
463
|
+
primitives. All references are resolved in the list returned.
|
464
|
+
"""
|
465
|
+
return self._diag_services
|
466
|
+
|
467
|
+
@property
|
468
|
+
def single_ecu_jobs(self) -> NamedItemList[SingleEcuJob]:
|
469
|
+
"""All single-ECU jobs applicable to this DiagLayer
|
470
|
+
|
471
|
+
This is a subset of all diagnostic communication
|
472
|
+
primitives. All references are resolved in the list returned.
|
473
|
+
"""
|
474
|
+
return self._single_ecu_jobs
|
475
|
+
|
476
|
+
@property
|
477
|
+
def global_negative_responses(self) -> NamedItemList[Response]:
|
478
|
+
"""All global negative responses applicable to this DiagLayer"""
|
479
|
+
return self._global_negative_responses
|
480
|
+
|
481
|
+
@property
|
482
|
+
def functional_classes(self) -> NamedItemList[FunctionalClass]:
|
483
|
+
"""All functional classes applicable to this DiagLayer"""
|
484
|
+
return self._functional_classes
|
485
|
+
|
486
|
+
@property
|
487
|
+
def state_charts(self) -> NamedItemList[StateChart]:
|
488
|
+
"""All state charts applicable to this DiagLayer"""
|
489
|
+
return self._state_charts
|
490
|
+
|
491
|
+
@property
|
492
|
+
def additional_audiences(self) -> NamedItemList[AdditionalAudience]:
|
493
|
+
"""All audiences applicable to this DiagLayer"""
|
494
|
+
return self._additional_audiences
|
495
|
+
|
496
|
+
#######
|
497
|
+
# </properties subject to value inheritance>
|
498
|
+
#######
|
499
|
+
|
598
500
|
#####
|
599
501
|
# <communication parameter handling>
|
600
502
|
#####
|
@@ -628,39 +530,45 @@ class DiagLayer:
|
|
628
530
|
# parameters. First fetch the communication parameters from
|
629
531
|
# low priority parents, then update with increasing priority.
|
630
532
|
for parent_ref in self._get_parent_refs_sorted_by_priority():
|
631
|
-
|
533
|
+
parent_layer = parent_ref.layer
|
534
|
+
if not isinstance(parent_layer, HierarchyElement):
|
535
|
+
continue
|
536
|
+
for cp in parent_layer._compute_available_commmunication_parameters():
|
632
537
|
com_params_dict[(cp.spec_ref.ref_id, cp.protocol_snref)] = cp
|
633
538
|
|
634
539
|
# finally, handle the locally defined communication parameters
|
635
|
-
for cp in self.
|
540
|
+
for cp in getattr(self.hierarchy_element_raw, "comparam_refs", []):
|
636
541
|
com_params_dict[(cp.spec_ref.ref_id, cp.protocol_snref)] = cp
|
637
542
|
|
638
543
|
return list(com_params_dict.values())
|
639
544
|
|
640
545
|
@property
|
641
|
-
def
|
546
|
+
def comparam_refs(self) -> NamedItemList[ComparamInstance]:
|
642
547
|
"""All communication parameters applicable to this DiagLayer
|
643
548
|
|
644
549
|
Note that, although communication parameters use inheritance,
|
645
550
|
it is *not* the "value inheritance" scheme used by e.g. DOPs,
|
646
551
|
tables, state charts, ...
|
647
552
|
"""
|
648
|
-
return self.
|
553
|
+
return self._comparam_refs
|
649
554
|
|
650
555
|
@cached_property
|
651
|
-
def protocols(self) -> NamedItemList["
|
556
|
+
def protocols(self) -> NamedItemList["Protocol"]:
|
652
557
|
"""Return the set of all protocols which are applicable to the diagnostic layer
|
653
558
|
|
654
|
-
Note that protocols are *not* explicitly
|
655
|
-
but the parent
|
559
|
+
Note that protocols are *not* explicitly defined by the XML,
|
560
|
+
but they are the parent layers of variant type "PROTOCOL".
|
561
|
+
|
656
562
|
"""
|
657
|
-
|
563
|
+
from .protocol import Protocol
|
658
564
|
|
659
|
-
|
660
|
-
|
565
|
+
result_dict: Dict[str, Protocol] = {}
|
566
|
+
|
567
|
+
for parent_ref in getattr(self, "parent_refs", []):
|
568
|
+
for prot in getattr(parent_ref.layer, "protocols", []):
|
661
569
|
result_dict[prot.short_name] = prot
|
662
570
|
|
663
|
-
if self
|
571
|
+
if isinstance(self, Protocol):
|
664
572
|
result_dict[self.diag_layer_raw.short_name] = self
|
665
573
|
|
666
574
|
return NamedItemList(result_dict.values())
|
@@ -669,20 +577,22 @@ class DiagLayer:
|
|
669
577
|
self,
|
670
578
|
cp_short_name: str,
|
671
579
|
*,
|
672
|
-
protocol: Optional[Union[str, "
|
580
|
+
protocol: Optional[Union[str, "Protocol"]] = None,
|
673
581
|
) -> Optional[ComparamInstance]:
|
674
582
|
"""Find a specific communication parameter according to some criteria.
|
675
583
|
|
676
584
|
Setting a given parameter to `None` means "don't care"."""
|
677
585
|
|
586
|
+
from .protocol import Protocol
|
587
|
+
|
678
588
|
protocol_name: Optional[str]
|
679
|
-
if isinstance(protocol,
|
589
|
+
if isinstance(protocol, Protocol):
|
680
590
|
protocol_name = protocol.short_name
|
681
591
|
else:
|
682
592
|
protocol_name = protocol
|
683
593
|
|
684
594
|
# determine the set of applicable communication parameters
|
685
|
-
cps = [cp for cp in self.
|
595
|
+
cps = [cp for cp in self.comparam_refs if cp.short_name == cp_short_name]
|
686
596
|
if protocol_name is not None:
|
687
597
|
cps = [cp for cp in cps if cp.protocol_snref in (None, protocol_name)]
|
688
598
|
|
@@ -700,7 +610,7 @@ class DiagLayer:
|
|
700
610
|
|
701
611
|
def get_max_can_payload_size(self,
|
702
612
|
protocol: Optional[Union[str,
|
703
|
-
"
|
613
|
+
"Protocol"]] = None) -> Optional[int]:
|
704
614
|
"""Return the maximum size of a CAN frame payload that can be
|
705
615
|
transmitted in bytes.
|
706
616
|
|
@@ -730,13 +640,13 @@ class DiagLayer:
|
|
730
640
|
# unexpected format of parameter value
|
731
641
|
return 8
|
732
642
|
|
733
|
-
def uses_can(self, protocol: Optional[Union[str, "
|
643
|
+
def uses_can(self, protocol: Optional[Union[str, "Protocol"]] = None) -> bool:
|
734
644
|
"""
|
735
645
|
Check if CAN ought to be used as the link layer protocol.
|
736
646
|
"""
|
737
647
|
return self.get_can_receive_id(protocol=protocol) is not None
|
738
648
|
|
739
|
-
def uses_can_fd(self, protocol: Optional[Union[str, "
|
649
|
+
def uses_can_fd(self, protocol: Optional[Union[str, "Protocol"]] = None) -> bool:
|
740
650
|
"""Check if CAN-FD ought to be used.
|
741
651
|
|
742
652
|
If the ECU is not using CAN-FD for the specified protocol, `False`
|
@@ -755,7 +665,7 @@ class DiagLayer:
|
|
755
665
|
|
756
666
|
return "CANFD" in com_param.value
|
757
667
|
|
758
|
-
def get_can_baudrate(self, protocol: Optional[Union[str, "
|
668
|
+
def get_can_baudrate(self, protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
|
759
669
|
"""Baudrate of the CAN bus which is used by the ECU [bits/s]
|
760
670
|
|
761
671
|
If the ECU is not using CAN for the specified protocol, None
|
@@ -773,7 +683,7 @@ class DiagLayer:
|
|
773
683
|
return int(val)
|
774
684
|
|
775
685
|
def get_can_fd_baudrate(self,
|
776
|
-
protocol: Optional[Union[str, "
|
686
|
+
protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
|
777
687
|
"""Data baudrate of the CAN bus which is used by the ECU [bits/s]
|
778
688
|
|
779
689
|
If the ECU is not using CAN-FD for the specified protocol,
|
@@ -793,7 +703,7 @@ class DiagLayer:
|
|
793
703
|
return int(val)
|
794
704
|
|
795
705
|
def get_can_receive_id(self,
|
796
|
-
protocol: Optional[Union[str, "
|
706
|
+
protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
|
797
707
|
"""CAN ID to which the ECU listens for diagnostic messages"""
|
798
708
|
com_param = self.get_comparam("CP_UniqueRespIdTable", protocol=protocol)
|
799
709
|
if com_param is None:
|
@@ -812,11 +722,7 @@ class DiagLayer:
|
|
812
722
|
|
813
723
|
return int(result)
|
814
724
|
|
815
|
-
|
816
|
-
def get_receive_id(self) -> Optional[int]:
|
817
|
-
return self.get_can_receive_id()
|
818
|
-
|
819
|
-
def get_can_send_id(self, protocol: Optional[Union[str, "DiagLayer"]] = None) -> Optional[int]:
|
725
|
+
def get_can_send_id(self, protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
|
820
726
|
"""CAN ID to which the ECU sends replies to diagnostic messages"""
|
821
727
|
|
822
728
|
# this hopefully resolves to the 'CP_UniqueRespIdTable'
|
@@ -841,12 +747,8 @@ class DiagLayer:
|
|
841
747
|
|
842
748
|
return int(result)
|
843
749
|
|
844
|
-
@deprecated(details="use get_can_send_id()") # type: ignore[misc]
|
845
|
-
def get_send_id(self) -> Optional[int]:
|
846
|
-
return self.get_can_send_id()
|
847
|
-
|
848
750
|
def get_can_func_req_id(self,
|
849
|
-
protocol: Optional[Union[str, "
|
751
|
+
protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
|
850
752
|
"""CAN Functional Request Id."""
|
851
753
|
com_param = self.get_comparam("CP_CanFuncReqId", protocol=protocol)
|
852
754
|
if com_param is None:
|
@@ -860,8 +762,8 @@ class DiagLayer:
|
|
860
762
|
return int(result)
|
861
763
|
|
862
764
|
def get_doip_logical_ecu_address(self,
|
863
|
-
protocol: Optional[Union[str,
|
864
|
-
|
765
|
+
protocol: Optional[Union[str,
|
766
|
+
"Protocol"]] = None) -> Optional[int]:
|
865
767
|
"""Return the address of the ECU when using functional addressing.
|
866
768
|
|
867
769
|
The parameter protocol is used to distinguish between
|
@@ -892,7 +794,7 @@ class DiagLayer:
|
|
892
794
|
return int(ecu_addr)
|
893
795
|
|
894
796
|
def get_doip_logical_gateway_address(self,
|
895
|
-
protocol: Optional[Union[str, "
|
797
|
+
protocol: Optional[Union[str, "Protocol"]] = None
|
896
798
|
) -> Optional[int]:
|
897
799
|
"""The logical gateway address for the diagnosis over IP transport protocol"""
|
898
800
|
|
@@ -910,7 +812,7 @@ class DiagLayer:
|
|
910
812
|
return int(result)
|
911
813
|
|
912
814
|
def get_doip_logical_tester_address(self,
|
913
|
-
protocol: Optional[Union[str, "
|
815
|
+
protocol: Optional[Union[str, "Protocol"]] = None
|
914
816
|
) -> Optional[int]:
|
915
817
|
"""DoIp logical gateway address"""
|
916
818
|
|
@@ -928,7 +830,7 @@ class DiagLayer:
|
|
928
830
|
return int(result)
|
929
831
|
|
930
832
|
def get_doip_logical_functional_address(self,
|
931
|
-
protocol: Optional[Union[str, "
|
833
|
+
protocol: Optional[Union[str, "Protocol"]] = None
|
932
834
|
) -> Optional[int]:
|
933
835
|
"""The logical functional DoIP address of the ECU."""
|
934
836
|
|
@@ -949,7 +851,7 @@ class DiagLayer:
|
|
949
851
|
return int(result)
|
950
852
|
|
951
853
|
def get_doip_routing_activation_timeout(self,
|
952
|
-
protocol: Optional[Union[str, "
|
854
|
+
protocol: Optional[Union[str, "Protocol"]] = None
|
953
855
|
) -> Optional[float]:
|
954
856
|
"""The timout for the DoIP routing activation request in seconds"""
|
955
857
|
|
@@ -967,7 +869,7 @@ class DiagLayer:
|
|
967
869
|
return float(result) / 1e6
|
968
870
|
|
969
871
|
def get_doip_routing_activation_type(self,
|
970
|
-
protocol: Optional[Union[str, "
|
872
|
+
protocol: Optional[Union[str, "Protocol"]] = None
|
971
873
|
) -> Optional[int]:
|
972
874
|
"""The DoIP routing activation type
|
973
875
|
|
@@ -994,7 +896,7 @@ class DiagLayer:
|
|
994
896
|
|
995
897
|
def get_tester_present_time(self,
|
996
898
|
protocol: Optional[Union[str,
|
997
|
-
"
|
899
|
+
"Protocol"]] = None) -> Optional[float]:
|
998
900
|
"""Timeout on inactivity in seconds.
|
999
901
|
|
1000
902
|
This is defined by the communication parameter "CP_TesterPresentTime".
|
@@ -1021,144 +923,3 @@ class DiagLayer:
|
|
1021
923
|
#####
|
1022
924
|
# </communication parameter handling>
|
1023
925
|
#####
|
1024
|
-
|
1025
|
-
#####
|
1026
|
-
# <PDU decoding>
|
1027
|
-
#####
|
1028
|
-
|
1029
|
-
@cached_property
|
1030
|
-
def _prefix_tree(self) -> PrefixTree:
|
1031
|
-
"""Constructs the coded prefix tree of the services.
|
1032
|
-
|
1033
|
-
Each leaf node is a list of `DiagService`s. (This is because
|
1034
|
-
navigating from a service to the request/ responses is easier
|
1035
|
-
than finding the service for a given request/response object.)
|
1036
|
-
|
1037
|
-
Example:
|
1038
|
-
Let there be four services with corresponding requests:
|
1039
|
-
* Request 1 has the coded constant prefix `12 34`.
|
1040
|
-
* Request 2 has the coded constant prefix `12 34`.
|
1041
|
-
* Request 3 has the coded constant prefix `12 56`.
|
1042
|
-
* Request 4 has the coded constant prefix `12 56 00`.
|
1043
|
-
|
1044
|
-
Then, the constructed prefix tree is the dict
|
1045
|
-
```
|
1046
|
-
{0x12: {0x34: {-1: [<Service 1>, <Service 2>]},
|
1047
|
-
0x56: {-1: [<Service 3>],
|
1048
|
-
0x0: {-1: [<Service 4>]}
|
1049
|
-
}}}
|
1050
|
-
```
|
1051
|
-
Note, that the inner `-1` are constant to distinguish them
|
1052
|
-
from possible service IDs.
|
1053
|
-
|
1054
|
-
Also note, that it is actually allowed that
|
1055
|
-
(a) SIDs for different services are the same like for service 1 and 2 (thus each leaf node is a list) and
|
1056
|
-
(b) one SID is the prefix of another SID like for service 3 and 4 (thus the constant `-1` key).
|
1057
|
-
|
1058
|
-
"""
|
1059
|
-
prefix_tree: PrefixTree = {}
|
1060
|
-
for s in self.services:
|
1061
|
-
# Compute prefixes for the service's request and all
|
1062
|
-
# possible responses. We need to consider the global
|
1063
|
-
# negative responses here, because they might contain
|
1064
|
-
# MATCHING-REQUEST parameters. If these global responses
|
1065
|
-
# do not contain such parameters, this will potentially
|
1066
|
-
# result in an enormous amount of decoded messages for
|
1067
|
-
# global negative responses. (I.e., one for each
|
1068
|
-
# service. This can be avoided by specifying the
|
1069
|
-
# corresponding request for `decode_response()`.)
|
1070
|
-
request_prefix = b''
|
1071
|
-
if s.request is not None:
|
1072
|
-
request_prefix = s.request.coded_const_prefix()
|
1073
|
-
prefixes = [request_prefix]
|
1074
|
-
prefixes += [
|
1075
|
-
x.coded_const_prefix(request_prefix=request_prefix) for x in chain(
|
1076
|
-
s.positive_responses, s.negative_responses, self.global_negative_responses)
|
1077
|
-
]
|
1078
|
-
for coded_prefix in prefixes:
|
1079
|
-
self._extend_prefix_tree(prefix_tree, coded_prefix, s)
|
1080
|
-
|
1081
|
-
return prefix_tree
|
1082
|
-
|
1083
|
-
@staticmethod
|
1084
|
-
def _extend_prefix_tree(prefix_tree: PrefixTree, coded_prefix: bytes,
|
1085
|
-
service: DiagService) -> None:
|
1086
|
-
|
1087
|
-
# make sure that tree has an entry for the given prefix
|
1088
|
-
sub_tree = prefix_tree
|
1089
|
-
for b in coded_prefix:
|
1090
|
-
if b not in sub_tree:
|
1091
|
-
sub_tree[b] = {}
|
1092
|
-
sub_tree = cast(PrefixTree, sub_tree[b])
|
1093
|
-
|
1094
|
-
# Store the object as in the prefix tree. This is done by
|
1095
|
-
# assigning the list of possible objects to the key -1 of the
|
1096
|
-
# dictionary (this is quite hacky...)
|
1097
|
-
if sub_tree.get(-1) is None:
|
1098
|
-
sub_tree[-1] = [service]
|
1099
|
-
else:
|
1100
|
-
cast(List[DiagService], sub_tree[-1]).append(service)
|
1101
|
-
|
1102
|
-
def _find_services_for_uds(self, message: bytes) -> List[DiagService]:
|
1103
|
-
prefix_tree = self._prefix_tree
|
1104
|
-
|
1105
|
-
# Find matching service(s) in prefix tree
|
1106
|
-
possible_services: List[DiagService] = []
|
1107
|
-
for b in message:
|
1108
|
-
if b in prefix_tree:
|
1109
|
-
odxassert(isinstance(prefix_tree[b], dict))
|
1110
|
-
prefix_tree = cast(PrefixTree, prefix_tree[b])
|
1111
|
-
else:
|
1112
|
-
break
|
1113
|
-
if -1 in prefix_tree:
|
1114
|
-
possible_services += cast(List[DiagService], prefix_tree[-1])
|
1115
|
-
return possible_services
|
1116
|
-
|
1117
|
-
def _decode(self, message: bytes, candidate_services: Iterable[DiagService]) -> List[Message]:
|
1118
|
-
decoded_messages: List[Message] = []
|
1119
|
-
|
1120
|
-
for service in candidate_services:
|
1121
|
-
try:
|
1122
|
-
decoded_messages.append(service.decode_message(message))
|
1123
|
-
except DecodeError:
|
1124
|
-
# check if the message can be decoded as a global
|
1125
|
-
# negative response for the service
|
1126
|
-
for gnr in self.global_negative_responses:
|
1127
|
-
try:
|
1128
|
-
decoded_gnr = gnr.decode(message)
|
1129
|
-
if not isinstance(decoded_gnr, dict):
|
1130
|
-
raise DecodeError(f"Expected the decoded value of a global "
|
1131
|
-
f"negative response to be a dictionary, "
|
1132
|
-
f"got {type(decoded_gnr)} for {self.short_name}")
|
1133
|
-
|
1134
|
-
decoded_messages.append(
|
1135
|
-
Message(
|
1136
|
-
coded_message=message,
|
1137
|
-
service=service,
|
1138
|
-
coding_object=gnr,
|
1139
|
-
param_dict=decoded_gnr))
|
1140
|
-
except DecodeError:
|
1141
|
-
pass
|
1142
|
-
|
1143
|
-
if len(decoded_messages) == 0:
|
1144
|
-
raise DecodeError(
|
1145
|
-
f"None of the services {[x.short_name for x in candidate_services]} could parse {message.hex()}."
|
1146
|
-
)
|
1147
|
-
|
1148
|
-
return decoded_messages
|
1149
|
-
|
1150
|
-
def decode(self, message: bytes) -> List[Message]:
|
1151
|
-
candidate_services = self._find_services_for_uds(message)
|
1152
|
-
|
1153
|
-
return self._decode(message, candidate_services)
|
1154
|
-
|
1155
|
-
def decode_response(self, response: bytes, request: bytes) -> List[Message]:
|
1156
|
-
candidate_services = self._find_services_for_uds(request)
|
1157
|
-
if candidate_services is None:
|
1158
|
-
raise DecodeError(f"Couldn't find corresponding service for request {request.hex()}.")
|
1159
|
-
|
1160
|
-
return self._decode(response, candidate_services)
|
1161
|
-
|
1162
|
-
#####
|
1163
|
-
# </PDU decoding>
|
1164
|
-
#####
|