odxtools 9.6.0__py3-none-any.whl → 9.7.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/addressing.py +8 -0
- odxtools/basecomparam.py +2 -15
- odxtools/basicstructure.py +4 -3
- odxtools/codec.py +1 -184
- odxtools/commrelation.py +1 -8
- odxtools/commrelationvaluetype.py +9 -0
- odxtools/compositecodec.py +191 -0
- odxtools/compumethods/compucategory.py +13 -0
- odxtools/compumethods/compucodecompumethod.py +2 -1
- odxtools/compumethods/compumethod.py +1 -12
- odxtools/compumethods/intervaltype.py +8 -0
- odxtools/compumethods/limit.py +1 -7
- odxtools/compumethods/linearcompumethod.py +2 -1
- odxtools/compumethods/ratfunccompumethod.py +2 -1
- odxtools/compumethods/scalelinearcompumethod.py +3 -2
- odxtools/compumethods/scaleratfunccompumethod.py +2 -1
- odxtools/compumethods/tabintpcompumethod.py +4 -2
- odxtools/compumethods/texttablecompumethod.py +2 -1
- odxtools/description.py +1 -17
- odxtools/diagclasstype.py +11 -0
- odxtools/diagcomm.py +2 -25
- odxtools/diagservice.py +3 -79
- odxtools/dtcconnector.py +45 -0
- odxtools/dtcdop.py +2 -47
- odxtools/dyndefinedspec.py +3 -155
- odxtools/dyniddefmodeinfo.py +161 -0
- odxtools/envdataconnector.py +49 -0
- odxtools/externalaccessmethod.py +23 -0
- odxtools/externaldoc.py +23 -0
- odxtools/linkeddtcdop.py +62 -0
- odxtools/minmaxlengthtype.py +1 -7
- odxtools/parameterinfo.py +1 -1
- odxtools/parameters/rowfragment.py +7 -0
- odxtools/parameters/tableentryparameter.py +1 -6
- odxtools/physicaltype.py +1 -8
- odxtools/posresponsesuppressible.py +73 -0
- odxtools/radix.py +9 -0
- odxtools/relateddiagcommref.py +23 -0
- odxtools/request.py +5 -3
- odxtools/response.py +5 -3
- odxtools/scaleconstr.py +1 -8
- odxtools/standardizationlevel.py +9 -0
- odxtools/standardlengthtype.py +2 -11
- odxtools/statetransition.py +1 -14
- odxtools/subcomponent.py +8 -241
- odxtools/subcomponentparamconnector.py +103 -0
- odxtools/subcomponentpattern.py +42 -0
- odxtools/table.py +3 -41
- odxtools/tablediagcommconnector.py +47 -0
- odxtools/tablerowconnector.py +46 -0
- odxtools/templates/macros/printService.xml.jinja2 +2 -1
- odxtools/termination.py +8 -0
- odxtools/transmode.py +9 -0
- odxtools/unitgroup.py +1 -6
- odxtools/unitgroupcategory.py +7 -0
- odxtools/usage.py +9 -0
- odxtools/utils.py +29 -0
- odxtools/validtype.py +9 -0
- odxtools/version.py +2 -2
- {odxtools-9.6.0.dist-info → odxtools-9.7.0.dist-info}/METADATA +1 -1
- {odxtools-9.6.0.dist-info → odxtools-9.7.0.dist-info}/RECORD +65 -39
- {odxtools-9.6.0.dist-info → odxtools-9.7.0.dist-info}/WHEEL +0 -0
- {odxtools-9.6.0.dist-info → odxtools-9.7.0.dist-info}/entry_points.txt +0 -0
- {odxtools-9.6.0.dist-info → odxtools-9.7.0.dist-info}/licenses/LICENSE +0 -0
- {odxtools-9.6.0.dist-info → odxtools-9.7.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, List
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .diagservice import DiagService
|
7
|
+
from .element import IdentifiableElement
|
8
|
+
from .exceptions import odxassert, odxraise, odxrequire
|
9
|
+
from .nameditemlist import NamedItemList
|
10
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
|
11
|
+
from .parameters.parameter import Parameter
|
12
|
+
from .snrefcontext import SnRefContext
|
13
|
+
from .utils import dataclass_fields_asdict
|
14
|
+
|
15
|
+
|
16
|
+
@dataclass
|
17
|
+
class SubComponentParamConnector(IdentifiableElement):
|
18
|
+
diag_comm_snref: str
|
19
|
+
|
20
|
+
# TODO: we currently only support SNREFs, not SNPATHREFs
|
21
|
+
out_param_if_refs: List[str]
|
22
|
+
in_param_if_refs: List[str]
|
23
|
+
|
24
|
+
@property
|
25
|
+
def service(self) -> DiagService:
|
26
|
+
return self._service
|
27
|
+
|
28
|
+
@property
|
29
|
+
def out_param_ifs(self) -> NamedItemList[Parameter]:
|
30
|
+
return self._out_param_ifs
|
31
|
+
|
32
|
+
@property
|
33
|
+
def in_param_ifs(self) -> NamedItemList[Parameter]:
|
34
|
+
return self._in_param_ifs
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def from_et(et_element: ElementTree.Element,
|
38
|
+
doc_frags: List[OdxDocFragment]) -> "SubComponentParamConnector":
|
39
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
40
|
+
|
41
|
+
diag_comm_snref = odxrequire(
|
42
|
+
odxrequire(et_element.find("DIAG-COMM-SNREF")).get("SHORT-NAME"))
|
43
|
+
|
44
|
+
out_param_if_refs = []
|
45
|
+
for elem in et_element.find("OUT-PARAM-IF-REFS") or []:
|
46
|
+
if elem.tag != "OUT-PARAM-IF-SNREF":
|
47
|
+
odxraise("Currently, only SNREFS are supported for OUT-PARAM-IF-REFS")
|
48
|
+
continue
|
49
|
+
else:
|
50
|
+
odxassert(elem.tag == "OUT-PARAM-IF-SNREF")
|
51
|
+
out_param_if_refs.append(odxrequire(elem.attrib.get("SHORT-NAME")))
|
52
|
+
|
53
|
+
in_param_if_refs = []
|
54
|
+
for elem in et_element.find("IN-PARAM-IF-REFS") or []:
|
55
|
+
if elem.tag != "IN-PARAM-IF-SNREF":
|
56
|
+
odxraise("Currently, only SNREFS are supported for IN-PARAM-IF-REFS")
|
57
|
+
continue
|
58
|
+
else:
|
59
|
+
odxassert(elem.tag == "IN-PARAM-IF-SNREF")
|
60
|
+
in_param_if_refs.append(odxrequire(elem.attrib.get("SHORT-NAME")))
|
61
|
+
|
62
|
+
return SubComponentParamConnector(
|
63
|
+
diag_comm_snref=diag_comm_snref,
|
64
|
+
out_param_if_refs=out_param_if_refs,
|
65
|
+
in_param_if_refs=in_param_if_refs,
|
66
|
+
**kwargs)
|
67
|
+
|
68
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
69
|
+
return {}
|
70
|
+
|
71
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
72
|
+
pass
|
73
|
+
|
74
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
75
|
+
service = resolve_snref(self.diag_comm_snref,
|
76
|
+
odxrequire(context.diag_layer).diag_comms, DiagService)
|
77
|
+
self._service = service
|
78
|
+
|
79
|
+
if self._service.request is not None:
|
80
|
+
odxraise()
|
81
|
+
return
|
82
|
+
if not self._service.positive_responses:
|
83
|
+
odxraise()
|
84
|
+
return
|
85
|
+
|
86
|
+
request = odxrequire(service.request)
|
87
|
+
in_param_ifs = []
|
88
|
+
for x in self.in_param_if_refs:
|
89
|
+
in_param_ifs.append(resolve_snref(x, request.parameters, Parameter))
|
90
|
+
|
91
|
+
# TODO: The output parameters are probably part of a response
|
92
|
+
# (?). If so, they cannot be resolved ahead of time because
|
93
|
+
# the service in question can have multiple responses
|
94
|
+
# associated with it and each of these has its own set of
|
95
|
+
# parameters. In the meantime, we simply use the first
|
96
|
+
# positive response specified.
|
97
|
+
response = service.positive_responses[0]
|
98
|
+
out_param_ifs = []
|
99
|
+
for x in self.out_param_if_refs:
|
100
|
+
out_param_ifs.append(resolve_snref(x, response.parameters, Parameter))
|
101
|
+
|
102
|
+
self._in_param_ifs = NamedItemList(in_param_ifs)
|
103
|
+
self._out_param_ifs = NamedItemList(out_param_ifs)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, List
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
7
|
+
from .snrefcontext import SnRefContext
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from .matchingparameter import MatchingParameter
|
11
|
+
|
12
|
+
|
13
|
+
@dataclass
|
14
|
+
class SubComponentPattern:
|
15
|
+
matching_parameters: List["MatchingParameter"]
|
16
|
+
|
17
|
+
@staticmethod
|
18
|
+
def from_et(et_element: ElementTree.Element,
|
19
|
+
doc_frags: List[OdxDocFragment]) -> "SubComponentPattern":
|
20
|
+
from .matchingparameter import MatchingParameter
|
21
|
+
|
22
|
+
matching_parameters = [
|
23
|
+
MatchingParameter.from_et(el, doc_frags)
|
24
|
+
for el in et_element.iterfind("MATCHING-PARAMETERS/MATCHING-PARAMETER")
|
25
|
+
]
|
26
|
+
|
27
|
+
return SubComponentPattern(matching_parameters=matching_parameters)
|
28
|
+
|
29
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
30
|
+
result = {}
|
31
|
+
for mp in self.matching_parameters:
|
32
|
+
result.update(mp._build_odxlinks())
|
33
|
+
|
34
|
+
return result
|
35
|
+
|
36
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
37
|
+
for mp in self.matching_parameters:
|
38
|
+
mp._resolve_odxlinks(odxlinks)
|
39
|
+
|
40
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
41
|
+
for mp in self.matching_parameters:
|
42
|
+
mp._resolve_snrefs(context)
|
odxtools/table.py
CHANGED
@@ -5,55 +5,17 @@ from xml.etree import ElementTree
|
|
5
5
|
|
6
6
|
from .admindata import AdminData
|
7
7
|
from .dataobjectproperty import DataObjectProperty
|
8
|
-
from .diagcomm import DiagComm
|
9
8
|
from .element import IdentifiableElement
|
10
|
-
from .exceptions import odxassert
|
9
|
+
from .exceptions import odxassert
|
11
10
|
from .nameditemlist import NamedItemList
|
12
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
11
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
13
12
|
from .snrefcontext import SnRefContext
|
14
13
|
from .specialdatagroup import SpecialDataGroup
|
14
|
+
from .tablediagcommconnector import TableDiagCommConnector
|
15
15
|
from .tablerow import TableRow
|
16
16
|
from .utils import dataclass_fields_asdict
|
17
17
|
|
18
18
|
|
19
|
-
@dataclass
|
20
|
-
class TableDiagCommConnector:
|
21
|
-
semantic: str
|
22
|
-
|
23
|
-
diag_comm_ref: Optional[OdxLinkRef]
|
24
|
-
diag_comm_snref: Optional[str]
|
25
|
-
|
26
|
-
@property
|
27
|
-
def diag_comm(self) -> DiagComm:
|
28
|
-
return self._diag_comm
|
29
|
-
|
30
|
-
@staticmethod
|
31
|
-
def from_et(et_element: ElementTree.Element,
|
32
|
-
doc_frags: List[OdxDocFragment]) -> "TableDiagCommConnector":
|
33
|
-
|
34
|
-
semantic = odxrequire(et_element.findtext("SEMANTIC"))
|
35
|
-
|
36
|
-
diag_comm_ref = OdxLinkRef.from_et(et_element.find("DIAG-COMM-REF"), doc_frags)
|
37
|
-
diag_comm_snref = None
|
38
|
-
if (dc_snref_elem := et_element.find("DIAG-COMM-SNREF")) is not None:
|
39
|
-
diag_comm_snref = odxrequire(dc_snref_elem.get("SHORT-NAME"))
|
40
|
-
|
41
|
-
return TableDiagCommConnector(
|
42
|
-
semantic=semantic, diag_comm_ref=diag_comm_ref, diag_comm_snref=diag_comm_snref)
|
43
|
-
|
44
|
-
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
45
|
-
return {}
|
46
|
-
|
47
|
-
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
48
|
-
if self.diag_comm_ref is not None:
|
49
|
-
self._diag_comm = odxlinks.resolve(self.diag_comm_ref, DiagComm)
|
50
|
-
|
51
|
-
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
52
|
-
if self.diag_comm_snref is not None:
|
53
|
-
dl = odxrequire(context.diag_layer)
|
54
|
-
self._diag_comm = resolve_snref(self.diag_comm_snref, dl.diag_comms, DiagComm)
|
55
|
-
|
56
|
-
|
57
19
|
@dataclass
|
58
20
|
class Table(IdentifiableElement):
|
59
21
|
"""This class represents a TABLE."""
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .diagcomm import DiagComm
|
7
|
+
from .exceptions import odxrequire
|
8
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
9
|
+
from .snrefcontext import SnRefContext
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class TableDiagCommConnector:
|
14
|
+
semantic: str
|
15
|
+
|
16
|
+
diag_comm_ref: Optional[OdxLinkRef]
|
17
|
+
diag_comm_snref: Optional[str]
|
18
|
+
|
19
|
+
@property
|
20
|
+
def diag_comm(self) -> DiagComm:
|
21
|
+
return self._diag_comm
|
22
|
+
|
23
|
+
@staticmethod
|
24
|
+
def from_et(et_element: ElementTree.Element,
|
25
|
+
doc_frags: List[OdxDocFragment]) -> "TableDiagCommConnector":
|
26
|
+
|
27
|
+
semantic = odxrequire(et_element.findtext("SEMANTIC"))
|
28
|
+
|
29
|
+
diag_comm_ref = OdxLinkRef.from_et(et_element.find("DIAG-COMM-REF"), doc_frags)
|
30
|
+
diag_comm_snref = None
|
31
|
+
if (dc_snref_elem := et_element.find("DIAG-COMM-SNREF")) is not None:
|
32
|
+
diag_comm_snref = odxrequire(dc_snref_elem.get("SHORT-NAME"))
|
33
|
+
|
34
|
+
return TableDiagCommConnector(
|
35
|
+
semantic=semantic, diag_comm_ref=diag_comm_ref, diag_comm_snref=diag_comm_snref)
|
36
|
+
|
37
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
38
|
+
return {}
|
39
|
+
|
40
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
41
|
+
if self.diag_comm_ref is not None:
|
42
|
+
self._diag_comm = odxlinks.resolve(self.diag_comm_ref, DiagComm)
|
43
|
+
|
44
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
45
|
+
if self.diag_comm_snref is not None:
|
46
|
+
dl = odxrequire(context.diag_layer)
|
47
|
+
self._diag_comm = resolve_snref(self.diag_comm_snref, dl.diag_comms, DiagComm)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, List
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .element import NamedElement
|
7
|
+
from .exceptions import odxrequire
|
8
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
9
|
+
from .snrefcontext import SnRefContext
|
10
|
+
from .table import Table
|
11
|
+
from .tablerow import TableRow
|
12
|
+
from .utils import dataclass_fields_asdict
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass
|
16
|
+
class TableRowConnector(NamedElement):
|
17
|
+
table_ref: OdxLinkRef
|
18
|
+
table_row_snref: str
|
19
|
+
|
20
|
+
@property
|
21
|
+
def table(self) -> Table:
|
22
|
+
return self._table
|
23
|
+
|
24
|
+
@property
|
25
|
+
def table_row(self) -> TableRow:
|
26
|
+
return self._table_row
|
27
|
+
|
28
|
+
@staticmethod
|
29
|
+
def from_et(et_element: ElementTree.Element,
|
30
|
+
doc_frags: List[OdxDocFragment]) -> "TableRowConnector":
|
31
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
32
|
+
|
33
|
+
table_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-REF"), doc_frags))
|
34
|
+
table_row_snref_el = odxrequire(et_element.find("TABLE-ROW-SNREF"))
|
35
|
+
table_row_snref = odxrequire(table_row_snref_el.get("SHORT-NAME"))
|
36
|
+
|
37
|
+
return TableRowConnector(table_ref=table_ref, table_row_snref=table_row_snref, **kwargs)
|
38
|
+
|
39
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
40
|
+
return {}
|
41
|
+
|
42
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
43
|
+
self._table = odxlinks.resolve(self.table_ref, Table)
|
44
|
+
|
45
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
46
|
+
self._table_row = resolve_snref(self.table_row_snref, self._table.table_rows, TableRow)
|
@@ -8,7 +8,8 @@
|
|
8
8
|
|
9
9
|
{%- macro printPosResponseSuppressible(prs) -%}
|
10
10
|
<POS-RESPONSE-SUPPRESSABLE>
|
11
|
-
|
11
|
+
{%- set num_nibbles = (prs.bit_mask.bit_length() + 7) // 8 * 2 %}
|
12
|
+
<BIT-MASK>{{ ("%%0%dX" | format(num_nibbles | int)) | format(prs.bit_mask | int) }}</BIT-MASK>
|
12
13
|
{%- if prs.coded_const_snref is not none %}
|
13
14
|
<CODED-CONST-SNREF SHORT-NAME="{{ prs.coded_const_snref }}" />
|
14
15
|
{%- endif %}
|
odxtools/termination.py
ADDED
odxtools/transmode.py
ADDED
odxtools/unitgroup.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from enum import Enum
|
4
3
|
from typing import Any, Dict, List, Optional, cast
|
5
4
|
from xml.etree import ElementTree
|
6
5
|
|
@@ -10,14 +9,10 @@ from .nameditemlist import NamedItemList
|
|
10
9
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
11
10
|
from .snrefcontext import SnRefContext
|
12
11
|
from .unit import Unit
|
12
|
+
from .unitgroupcategory import UnitGroupCategory
|
13
13
|
from .utils import dataclass_fields_asdict
|
14
14
|
|
15
15
|
|
16
|
-
class UnitGroupCategory(Enum):
|
17
|
-
COUNTRY = "COUNTRY"
|
18
|
-
EQUIV_UNITS = "EQUIV-UNITS"
|
19
|
-
|
20
|
-
|
21
16
|
@dataclass
|
22
17
|
class UnitGroup(NamedElement):
|
23
18
|
"""A group of units.
|
odxtools/usage.py
ADDED
odxtools/utils.py
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
import dataclasses
|
3
3
|
import re
|
4
4
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
5
|
+
from xml.etree import ElementTree
|
6
|
+
|
7
|
+
from .exceptions import odxraise
|
5
8
|
|
6
9
|
if TYPE_CHECKING:
|
7
10
|
from .database import Database
|
@@ -9,6 +12,32 @@ if TYPE_CHECKING:
|
|
9
12
|
from .snrefcontext import SnRefContext
|
10
13
|
|
11
14
|
|
15
|
+
def read_hex_binary(et_element: Optional[ElementTree.Element]) -> Optional[int]:
|
16
|
+
"""Convert the contents of an xsd:hexBinary to an integer
|
17
|
+
"""
|
18
|
+
if et_element is None:
|
19
|
+
return None
|
20
|
+
|
21
|
+
if (bytes_str := et_element.text) is None:
|
22
|
+
# tag exists but is immediately terminated ("<FOO />"). we
|
23
|
+
# treat this like an empty string.
|
24
|
+
return 0
|
25
|
+
|
26
|
+
# The XSD uses the type xsd:hexBinary and xsd:hexBinary allows for
|
27
|
+
# leading/trailing whitespace and empty strings whilst `int(x,
|
28
|
+
# 16)` raises an exception if one of these things happen.
|
29
|
+
bytes_str = bytes_str.strip()
|
30
|
+
if len(bytes_str) == 0:
|
31
|
+
return 0
|
32
|
+
|
33
|
+
try:
|
34
|
+
return int(bytes_str, 16)
|
35
|
+
except Exception as e:
|
36
|
+
odxraise(f"Caught exception while parsing hex string `{bytes_str}`"
|
37
|
+
f" of {et_element.tag}: {e}")
|
38
|
+
return None
|
39
|
+
|
40
|
+
|
12
41
|
def retarget_snrefs(database: "Database",
|
13
42
|
diag_layer: "DiagLayer",
|
14
43
|
context: Optional["SnRefContext"] = None) -> None:
|
odxtools/validtype.py
ADDED
odxtools/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: odxtools
|
3
|
-
Version: 9.
|
3
|
+
Version: 9.7.0
|
4
4
|
Summary: Utilities to work with the ODX standard for automotive diagnostics
|
5
5
|
Author-email: Katrin Bauer <katrin.bauer@mbition.io>, Andreas Lauser <andreas.lauser@mbition.io>, Ayoub Kaanich <kayoub5@live.com>
|
6
6
|
Maintainer-email: Andreas Lauser <andreas.lauser@mbition.io>, Ayoub Kaanich <kayoub5@live.com>
|