odxtools 7.1.0__py3-none-any.whl → 7.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odxtools/__init__.py +6 -4
- odxtools/additionalaudience.py +3 -5
- odxtools/admindata.py +5 -7
- odxtools/audience.py +3 -5
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +10 -17
- odxtools/cli/_parser_utils.py +1 -1
- odxtools/cli/_print_utils.py +3 -2
- odxtools/cli/compare.py +1 -1
- odxtools/companydata.py +5 -7
- odxtools/companydocinfo.py +7 -8
- odxtools/companyrevisioninfo.py +3 -5
- odxtools/companyspecificinfo.py +8 -9
- odxtools/comparam.py +4 -6
- odxtools/comparaminstance.py +6 -8
- odxtools/comparamspec.py +14 -13
- odxtools/comparamsubset.py +17 -16
- odxtools/complexcomparam.py +5 -7
- odxtools/compumethods/compuconst.py +31 -0
- odxtools/compumethods/compudefaultvalue.py +27 -0
- odxtools/compumethods/compuinternaltophys.py +39 -0
- odxtools/compumethods/compuinversevalue.py +7 -0
- odxtools/compumethods/compumethod.py +67 -12
- odxtools/compumethods/compuphystointernal.py +39 -0
- odxtools/compumethods/compuscale.py +15 -26
- odxtools/compumethods/createanycompumethod.py +14 -160
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/linearcompumethod.py +69 -189
- odxtools/compumethods/linearsegment.py +193 -0
- odxtools/compumethods/scalelinearcompumethod.py +132 -26
- odxtools/compumethods/tabintpcompumethod.py +119 -99
- odxtools/compumethods/texttablecompumethod.py +107 -43
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +68 -62
- odxtools/dataobjectproperty.py +10 -19
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +29 -12
- odxtools/diagcomm.py +10 -6
- odxtools/diagdatadictionaryspec.py +20 -21
- odxtools/diaglayer.py +34 -5
- odxtools/diaglayercontainer.py +17 -11
- odxtools/diaglayerraw.py +20 -21
- odxtools/diagnostictroublecode.py +7 -8
- odxtools/diagservice.py +9 -7
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +7 -8
- odxtools/dtcdop.py +5 -8
- odxtools/dynamicendmarkerfield.py +22 -9
- odxtools/dynamiclengthfield.py +5 -11
- odxtools/element.py +4 -3
- odxtools/endofpdufield.py +0 -2
- odxtools/environmentdatadescription.py +4 -6
- odxtools/exceptions.py +1 -1
- odxtools/field.py +9 -9
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -5
- odxtools/leadinglengthinfotype.py +15 -2
- odxtools/loadfile.py +64 -0
- odxtools/minmaxlengthtype.py +20 -2
- odxtools/modification.py +3 -5
- odxtools/multiplexer.py +7 -14
- odxtools/multiplexercase.py +4 -6
- odxtools/multiplexerdefaultcase.py +4 -6
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/negoutputparam.py +3 -5
- odxtools/outputparam.py +3 -5
- odxtools/parameterinfo.py +3 -3
- odxtools/parameters/codedconstparameter.py +2 -14
- odxtools/parameters/lengthkeyparameter.py +3 -17
- odxtools/parameters/nrcconstparameter.py +2 -14
- odxtools/parameters/parameter.py +22 -22
- odxtools/parameters/parameterwithdop.py +6 -8
- odxtools/parameters/physicalconstantparameter.py +5 -8
- odxtools/parameters/reservedparameter.py +4 -3
- odxtools/parameters/tablekeyparameter.py +6 -9
- odxtools/parameters/tablestructparameter.py +6 -8
- odxtools/parameters/valueparameter.py +5 -8
- odxtools/paramlengthinfotype.py +19 -6
- odxtools/parentref.py +15 -1
- odxtools/physicaldimension.py +3 -5
- odxtools/progcode.py +18 -7
- odxtools/protstack.py +3 -5
- odxtools/py.typed +0 -0
- odxtools/relateddoc.py +7 -9
- odxtools/request.py +8 -0
- odxtools/response.py +8 -0
- odxtools/scaleconstr.py +3 -3
- odxtools/singleecujob.py +12 -10
- odxtools/snrefcontext.py +29 -0
- odxtools/specialdata.py +3 -5
- odxtools/specialdatagroup.py +5 -7
- odxtools/specialdatagroupcaption.py +3 -6
- odxtools/standardlengthtype.py +27 -2
- odxtools/state.py +3 -5
- odxtools/statechart.py +9 -11
- odxtools/statetransition.py +4 -9
- odxtools/staticfield.py +4 -8
- odxtools/table.py +7 -8
- odxtools/tablerow.py +7 -6
- odxtools/teammember.py +3 -5
- odxtools/templates/comparam-spec.odx-c.xml.jinja2 +2 -5
- odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +2 -5
- odxtools/templates/macros/printCompanyData.xml.jinja2 +2 -5
- odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +153 -0
- odxtools/templates/macros/printDOP.xml.jinja2 +10 -132
- odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
- odxtools/templates/macros/printElementId.xml.jinja2 +3 -3
- odxtools/templates/macros/printMux.xml.jinja2 +3 -2
- odxtools/templates/macros/printTable.xml.jinja2 +2 -3
- odxtools/unit.py +3 -5
- odxtools/unitgroup.py +3 -5
- odxtools/unitspec.py +9 -10
- odxtools/utils.py +1 -26
- odxtools/version.py +2 -2
- odxtools/{write_pdx_file.py → writepdxfile.py} +19 -10
- odxtools/xdoc.py +3 -5
- {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/METADATA +1 -1
- odxtools-7.2.0.dist-info/RECORD +192 -0
- {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/WHEEL +1 -1
- odxtools/createcompanydatas.py +0 -17
- odxtools/createsdgs.py +0 -19
- odxtools/load_file.py +0 -13
- odxtools/load_odx_d_file.py +0 -6
- odxtools/load_pdx_file.py +0 -8
- odxtools-7.1.0.dist-info/RECORD +0 -185
- /odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +0 -0
- {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/LICENSE +0 -0
- {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.1.0.dist-info → odxtools-7.2.0.dist-info}/top_level.txt +0 -0
odxtools/database.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from itertools import chain
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import List, Optional
|
4
|
+
from typing import IO, List, Optional, OrderedDict
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
from zipfile import ZipFile
|
7
7
|
|
@@ -26,69 +26,75 @@ class Database:
|
|
26
26
|
*,
|
27
27
|
pdx_zip: Optional[ZipFile] = None,
|
28
28
|
odx_d_file_name: Optional[str] = None) -> None:
|
29
|
-
self.model_version = None
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
29
|
+
self.model_version: Optional[Version] = None
|
30
|
+
self.auxiliary_files: OrderedDict[str, IO[bytes]] = OrderedDict()
|
31
|
+
|
32
|
+
# create an empty database object
|
33
|
+
self._diag_layer_containers = NamedItemList[DiagLayerContainer]()
|
34
|
+
self._comparam_subsets = NamedItemList[ComparamSubset]()
|
35
|
+
self._comparam_specs = NamedItemList[ComparamSpec]()
|
36
|
+
|
37
|
+
def add_pdx_file(self, pdx_file_name: str) -> None:
|
38
|
+
pdx_zip = ZipFile(pdx_file_name)
|
39
|
+
|
40
|
+
for zip_member in pdx_zip.namelist():
|
41
|
+
# The name of ODX files can end with .odx, .odx-d,
|
42
|
+
# .odx-c, .odx-cs, .odx-e, .odx-f, .odx-fd, .odx-m,
|
43
|
+
# .odx-v . We could test for all that, or just make
|
44
|
+
# sure that the file's suffix starts with .odx
|
45
|
+
p = Path(zip_member)
|
46
|
+
if p.suffix.lower().startswith(".odx"):
|
47
|
+
root = ElementTree.parse(pdx_zip.open(zip_member)).getroot()
|
48
|
+
self._process_xml_tree(root)
|
49
|
+
elif p.name.lower() != "index.xml":
|
50
|
+
self.add_auxiliary_file(zip_member, pdx_zip.open(zip_member))
|
51
|
+
|
52
|
+
def add_odx_file(self, odx_file_name: str) -> None:
|
53
|
+
self._process_xml_tree(ElementTree.parse(odx_file_name).getroot())
|
54
|
+
|
55
|
+
def add_auxiliary_file(self,
|
56
|
+
aux_file_name: str,
|
57
|
+
aux_file_obj: Optional[IO[bytes]] = None) -> None:
|
58
|
+
if aux_file_obj is None:
|
59
|
+
aux_file_obj = open(aux_file_name, "rb")
|
60
|
+
|
61
|
+
self.auxiliary_files[aux_file_name] = aux_file_obj
|
62
|
+
|
63
|
+
def _process_xml_tree(self, root: ElementTree.Element) -> None:
|
58
64
|
dlcs: List[DiagLayerContainer] = []
|
59
65
|
comparam_subsets: List[ComparamSubset] = []
|
60
66
|
comparam_specs: List[ComparamSpec] = []
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
self.
|
90
|
-
|
91
|
-
self.
|
67
|
+
|
68
|
+
# ODX spec version
|
69
|
+
model_version = Version(root.attrib.get("MODEL-VERSION", "2.0"))
|
70
|
+
if self.model_version is not None and self.model_version != model_version:
|
71
|
+
odxraise(f"Different ODX versions used for the same database (ODX {model_version} "
|
72
|
+
f"and ODX {self.model_version}")
|
73
|
+
|
74
|
+
self.model_version = model_version
|
75
|
+
|
76
|
+
dlc = root.find("DIAG-LAYER-CONTAINER")
|
77
|
+
if dlc is not None:
|
78
|
+
dlcs.append(DiagLayerContainer.from_et(dlc, []))
|
79
|
+
|
80
|
+
# In ODX 2.0 there was only COMPARAM-SPEC. In ODX 2.2 the
|
81
|
+
# content of COMPARAM-SPEC was moved to COMPARAM-SUBSET
|
82
|
+
# and COMPARAM-SPEC became a container for PROT-STACKS and
|
83
|
+
# a PROT-STACK references a list of COMPARAM-SUBSET
|
84
|
+
cp_subset = root.find("COMPARAM-SUBSET")
|
85
|
+
if cp_subset is not None:
|
86
|
+
comparam_subsets.append(ComparamSubset.from_et(cp_subset, []))
|
87
|
+
|
88
|
+
cp_spec = root.find("COMPARAM-SPEC")
|
89
|
+
if cp_spec is not None:
|
90
|
+
if model_version < Version("2.2"):
|
91
|
+
comparam_subsets.append(ComparamSubset.from_et(cp_spec, []))
|
92
|
+
else: # odx >= 2.2
|
93
|
+
comparam_specs.append(ComparamSpec.from_et(cp_spec, []))
|
94
|
+
|
95
|
+
self._diag_layer_containers.extend(dlcs)
|
96
|
+
self._comparam_subsets.extend(comparam_subsets)
|
97
|
+
self._comparam_specs.extend(comparam_specs)
|
92
98
|
|
93
99
|
def refresh(self) -> None:
|
94
100
|
# Create wrapper objects
|
@@ -125,7 +131,7 @@ class Database:
|
|
125
131
|
# let the diaglayers sort out the inherited objects and the
|
126
132
|
# short name references
|
127
133
|
for dlc in self.diag_layer_containers:
|
128
|
-
dlc._finalize_init(self._odxlinks)
|
134
|
+
dlc._finalize_init(self, self._odxlinks)
|
129
135
|
|
130
136
|
@property
|
131
137
|
def odxlinks(self) -> OdxLinkDatabase:
|
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):
|
@@ -98,10 +96,10 @@ class DataObjectProperty(DopBase):
|
|
98
96
|
if self.unit_ref:
|
99
97
|
self._unit = odxlinks.resolve(self.unit_ref, Unit)
|
100
98
|
|
101
|
-
def _resolve_snrefs(self,
|
102
|
-
super()._resolve_snrefs(
|
99
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
100
|
+
super()._resolve_snrefs(context)
|
103
101
|
|
104
|
-
self.diag_coded_type._resolve_snrefs(
|
102
|
+
self.diag_coded_type._resolve_snrefs(context)
|
105
103
|
|
106
104
|
@property
|
107
105
|
def unit(self) -> Optional[Unit]:
|
@@ -110,16 +108,6 @@ class DataObjectProperty(DopBase):
|
|
110
108
|
def get_static_bit_length(self) -> Optional[int]:
|
111
109
|
return self.diag_coded_type.get_static_bit_length()
|
112
110
|
|
113
|
-
def convert_physical_to_internal(self, physical_value: Any) -> Any:
|
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
111
|
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
124
112
|
"""
|
125
113
|
Convert a physical representation of a parameter to a string bytes that can be send over the wire
|
@@ -129,7 +117,10 @@ class DataObjectProperty(DopBase):
|
|
129
117
|
f"The value {repr(physical_value)} of type {type(physical_value).__name__}"
|
130
118
|
f" is not a valid.")
|
131
119
|
|
132
|
-
|
120
|
+
if not isinstance(physical_value, (int, float, str, bytes, bytearray)):
|
121
|
+
odxraise(f"Invalid type '{type(physical_value).__name__}' for physical value. "
|
122
|
+
f"(Expect atomic type!)")
|
123
|
+
internal_value = self.compu_method.convert_physical_to_internal(physical_value)
|
133
124
|
self.diag_coded_type.encode_into_pdu(internal_value, encode_state)
|
134
125
|
|
135
126
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
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,16 +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
6
|
from .decodestate import DecodeState
|
7
7
|
from .encodestate import EncodeState
|
8
|
-
from .exceptions import odxassert, odxraise
|
9
|
-
from .odxlink import OdxLinkDatabase, OdxLinkId
|
10
|
-
from .odxtypes import AtomicOdxType, DataType
|
11
|
-
|
12
|
-
if TYPE_CHECKING:
|
13
|
-
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
|
14
12
|
|
15
13
|
# Allowed diag-coded types
|
16
14
|
DctType = Literal[
|
@@ -22,12 +20,30 @@ DctType = Literal[
|
|
22
20
|
|
23
21
|
|
24
22
|
@dataclass
|
25
|
-
class DiagCodedType
|
23
|
+
class DiagCodedType:
|
26
24
|
|
27
25
|
base_data_type: DataType
|
28
26
|
base_type_encoding: Optional[str]
|
29
27
|
is_highlow_byte_order_raw: Optional[bool]
|
30
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
|
+
|
31
47
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]: # noqa: B027
|
32
48
|
return {}
|
33
49
|
|
@@ -35,7 +51,7 @@ class DiagCodedType(abc.ABC):
|
|
35
51
|
"""Recursively resolve any odxlinks references"""
|
36
52
|
pass
|
37
53
|
|
38
|
-
def _resolve_snrefs(self,
|
54
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None: # noqa: B027
|
39
55
|
"""Recursively resolve any short-name references"""
|
40
56
|
pass
|
41
57
|
|
@@ -43,9 +59,10 @@ class DiagCodedType(abc.ABC):
|
|
43
59
|
return None
|
44
60
|
|
45
61
|
@property
|
46
|
-
@abc.abstractmethod
|
47
62
|
def dct_type(self) -> DctType:
|
48
|
-
|
63
|
+
odxraise(f"Class {type(self).__name__} does not override required method "
|
64
|
+
f"dct_type()", NotImplementedError)
|
65
|
+
return cast(DctType, None)
|
49
66
|
|
50
67
|
@property
|
51
68
|
def is_highlow_byte_order(self) -> bool:
|
odxtools/diagcomm.py
CHANGED
@@ -6,13 +6,13 @@ 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
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
|
@@ -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))
|
@@ -201,22 +203,24 @@ 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(
|
214
|
+
sdg._resolve_snrefs(context)
|
213
215
|
|
214
216
|
if TYPE_CHECKING:
|
217
|
+
diag_layer = odxrequire(context.diag_layer)
|
215
218
|
self._protocols = NamedItemList([
|
216
219
|
resolve_snref(prot_snref, diag_layer.protocols, DiagLayer)
|
217
220
|
for prot_snref in self.protocol_snrefs
|
218
221
|
])
|
219
222
|
else:
|
223
|
+
diag_layer = odxrequire(context.diag_layer)
|
220
224
|
self._protocols = NamedItemList([
|
221
225
|
resolve_snref(prot_snref, diag_layer.protocols)
|
222
226
|
for prot_snref in self.protocol_snrefs
|
@@ -1,12 +1,11 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from itertools import chain
|
4
|
-
from typing import
|
4
|
+
from typing import Any, Dict, List, Optional
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
7
|
from .admindata import AdminData
|
8
8
|
from .basicstructure import BasicStructure
|
9
|
-
from .createsdgs import create_sdgs_from_et
|
10
9
|
from .dataobjectproperty import DataObjectProperty
|
11
10
|
from .dopbase import DopBase
|
12
11
|
from .dtcdop import DtcDop
|
@@ -19,15 +18,13 @@ from .exceptions import odxraise
|
|
19
18
|
from .multiplexer import Multiplexer
|
20
19
|
from .nameditemlist import NamedItemList
|
21
20
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
21
|
+
from .snrefcontext import SnRefContext
|
22
22
|
from .specialdatagroup import SpecialDataGroup
|
23
23
|
from .staticfield import StaticField
|
24
24
|
from .structure import Structure
|
25
25
|
from .table import Table
|
26
26
|
from .unitspec import UnitSpec
|
27
27
|
|
28
|
-
if TYPE_CHECKING:
|
29
|
-
from .diaglayer import DiagLayer
|
30
|
-
|
31
28
|
|
32
29
|
@dataclass
|
33
30
|
class DiagDataDictionarySpec:
|
@@ -135,7 +132,9 @@ class DiagDataDictionarySpec:
|
|
135
132
|
for table_element in et_element.iterfind("TABLES/TABLE")
|
136
133
|
]
|
137
134
|
|
138
|
-
sdgs =
|
135
|
+
sdgs = [
|
136
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
137
|
+
]
|
139
138
|
|
140
139
|
return DiagDataDictionarySpec(
|
141
140
|
admin_data=admin_data,
|
@@ -219,35 +218,35 @@ class DiagDataDictionarySpec:
|
|
219
218
|
for sdg in self.sdgs:
|
220
219
|
sdg._resolve_odxlinks(odxlinks)
|
221
220
|
|
222
|
-
def _resolve_snrefs(self,
|
221
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
223
222
|
if self.admin_data is not None:
|
224
|
-
self.admin_data._resolve_snrefs(
|
223
|
+
self.admin_data._resolve_snrefs(context)
|
225
224
|
for dtc_dop in self.dtc_dops:
|
226
|
-
dtc_dop._resolve_snrefs(
|
225
|
+
dtc_dop._resolve_snrefs(context)
|
227
226
|
for env_data_desc in self.env_data_descs:
|
228
|
-
env_data_desc._resolve_snrefs(
|
227
|
+
env_data_desc._resolve_snrefs(context)
|
229
228
|
for data_object_prop in self.data_object_props:
|
230
|
-
data_object_prop._resolve_snrefs(
|
229
|
+
data_object_prop._resolve_snrefs(context)
|
231
230
|
for structure in self.structures:
|
232
|
-
structure._resolve_snrefs(
|
231
|
+
structure._resolve_snrefs(context)
|
233
232
|
for static_field in self.static_fields:
|
234
|
-
static_field._resolve_snrefs(
|
233
|
+
static_field._resolve_snrefs(context)
|
235
234
|
for dynamic_length_field in self.dynamic_length_fields:
|
236
|
-
dynamic_length_field._resolve_snrefs(
|
235
|
+
dynamic_length_field._resolve_snrefs(context)
|
237
236
|
for dynamic_endmarker_field in self.dynamic_endmarker_fields:
|
238
|
-
dynamic_endmarker_field._resolve_snrefs(
|
237
|
+
dynamic_endmarker_field._resolve_snrefs(context)
|
239
238
|
for end_of_pdu_field in self.end_of_pdu_fields:
|
240
|
-
end_of_pdu_field._resolve_snrefs(
|
239
|
+
end_of_pdu_field._resolve_snrefs(context)
|
241
240
|
for mux in self.muxs:
|
242
|
-
mux._resolve_snrefs(
|
241
|
+
mux._resolve_snrefs(context)
|
243
242
|
for env_data in self.env_datas:
|
244
|
-
env_data._resolve_snrefs(
|
243
|
+
env_data._resolve_snrefs(context)
|
245
244
|
if self.unit_spec is not None:
|
246
|
-
self.unit_spec._resolve_snrefs(
|
245
|
+
self.unit_spec._resolve_snrefs(context)
|
247
246
|
for table in self.tables:
|
248
|
-
table._resolve_snrefs(
|
247
|
+
table._resolve_snrefs(context)
|
249
248
|
for sdg in self.sdgs:
|
250
|
-
sdg._resolve_snrefs(
|
249
|
+
sdg._resolve_snrefs(context)
|
251
250
|
|
252
251
|
@property
|
253
252
|
def all_data_object_properties(self) -> NamedItemList[DopBase]:
|
odxtools/diaglayer.py
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import re
|
3
3
|
import warnings
|
4
|
-
from copy import copy
|
4
|
+
from copy import copy, deepcopy
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from functools import cached_property
|
7
7
|
from itertools import chain
|
8
|
-
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, TypeVar,
|
8
|
+
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Tuple, TypeVar,
|
9
|
+
Union, cast)
|
9
10
|
from xml.etree import ElementTree
|
10
11
|
|
11
12
|
from deprecation import deprecated
|
@@ -16,6 +17,7 @@ from .companydata import CompanyData
|
|
16
17
|
from .comparaminstance import ComparamInstance
|
17
18
|
from .comparamspec import ComparamSpec
|
18
19
|
from .comparamsubset import ComparamSubset
|
20
|
+
from .description import Description
|
19
21
|
from .diagcomm import DiagComm
|
20
22
|
from .diagdatadictionaryspec import DiagDataDictionarySpec
|
21
23
|
from .diaglayerraw import DiagLayerRaw
|
@@ -33,12 +35,16 @@ from .request import Request
|
|
33
35
|
from .response import Response
|
34
36
|
from .servicebinner import ServiceBinner
|
35
37
|
from .singleecujob import SingleEcuJob
|
38
|
+
from .snrefcontext import SnRefContext
|
36
39
|
from .specialdatagroup import SpecialDataGroup
|
37
40
|
from .statechart import StateChart
|
38
41
|
from .table import Table
|
39
42
|
from .unitgroup import UnitGroup
|
40
43
|
from .unitspec import UnitSpec
|
41
44
|
|
45
|
+
if TYPE_CHECKING:
|
46
|
+
from .database import Database
|
47
|
+
|
42
48
|
TNamed = TypeVar("TNamed", bound=OdxNamed)
|
43
49
|
|
44
50
|
PrefixTree = Dict[int, Union[List[DiagService], "PrefixTree"]]
|
@@ -124,7 +130,22 @@ class DiagLayer:
|
|
124
130
|
|
125
131
|
self.diag_layer_raw._resolve_odxlinks(odxlinks)
|
126
132
|
|
127
|
-
def
|
133
|
+
def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
|
134
|
+
"""Create a deep copy of the diagnostic layer
|
135
|
+
|
136
|
+
Note that the copied diagnostic layer is not fully
|
137
|
+
initialized, so `_finalize_init()` should to be called on it
|
138
|
+
before it can be used normally.
|
139
|
+
"""
|
140
|
+
cls = self.__class__
|
141
|
+
result = cls.__new__(cls)
|
142
|
+
memo[id(self)] = result
|
143
|
+
|
144
|
+
result.diag_layer_raw = deepcopy(self.diag_layer_raw, memo)
|
145
|
+
|
146
|
+
return result
|
147
|
+
|
148
|
+
def _finalize_init(self, database: "Database", odxlinks: OdxLinkDatabase) -> None:
|
128
149
|
"""This method deals with everything inheritance related and
|
129
150
|
-- after the final set of objects covered by the diagnostic
|
130
151
|
layer is determined -- resolves any short name references in
|
@@ -140,6 +161,11 @@ class DiagLayer:
|
|
140
161
|
excessive memory consumption for large databases...
|
141
162
|
"""
|
142
163
|
|
164
|
+
# this attribute may be removed later. it is currently
|
165
|
+
# required to properly deal with auxiliary files within the
|
166
|
+
# diagnostic layer.
|
167
|
+
self._database = database
|
168
|
+
|
143
169
|
#####
|
144
170
|
# fill in all applicable objects that use value inheritance
|
145
171
|
#####
|
@@ -276,7 +302,10 @@ class DiagLayer:
|
|
276
302
|
# by the spec (So far, I haven't found any definitive
|
277
303
|
# statement...)
|
278
304
|
#####
|
279
|
-
|
305
|
+
context = SnRefContext(database=database)
|
306
|
+
context.diag_layer = self
|
307
|
+
self.diag_layer_raw._resolve_snrefs(context)
|
308
|
+
context.diag_layer = None
|
280
309
|
|
281
310
|
#####
|
282
311
|
# <convenience functionality>
|
@@ -309,7 +338,7 @@ class DiagLayer:
|
|
309
338
|
return self.diag_layer_raw.long_name
|
310
339
|
|
311
340
|
@property
|
312
|
-
def description(self) -> Optional[
|
341
|
+
def description(self) -> Optional[Description]:
|
313
342
|
return self.diag_layer_raw.description
|
314
343
|
|
315
344
|
@property
|