odxtools 9.4.1__py3-none-any.whl → 9.5.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.
@@ -1,18 +1,19 @@
1
+
1
2
  {#- -*- mode: sgml; tab-width: 1; indent-tabs-mode: nil -*-
2
3
  #
3
4
  # SPDX-License-Identifier: MIT
4
5
  -#}
5
6
 
6
- {%- import('macros/printElementId.xml.jinja2') as peid %}
7
- {%- import('macros/printState.xml.jinja2') as ps %}
8
- {%- import('macros/printStateTransition.xml.jinja2') as pst %}
9
-
10
7
  {%- macro printMatchingParameter(mp) -%}
11
8
  <MATCHING-PARAMETER>
12
9
  <EXPECTED-VALUE>{{mp.expected_value | e}}</EXPECTED-VALUE>
13
10
  <DIAG-COMM-SNREF SHORT-NAME="{{mp.diag_comm_snref}}" />
14
- {#- TODO: OUT-PARAM-IF-SNPATHREF #}
15
- <OUT-PARAM-IF-SNREF SHORT-NAME="{{mp.out_param_if}}" />
11
+ {%- if mp.out_param_if_snref is not none %}
12
+ <OUT-PARAM-IF-SNREF SHORT-NAME="{{mp.out_param_if_snref}}" />
13
+ {%- endif %}
14
+ {%- if mp.out_param_if_snpathref is not none %}
15
+ <OUT-PARAM-IF-SNPATHREF SHORT-NAME-PATH="{{mp.out_param_if_snpathref}}" />
16
+ {%- endif %}
16
17
  </MATCHING-PARAMETER>
17
18
  {%- endmacro -%}
18
19
 
odxtools/utils.py CHANGED
@@ -3,11 +3,15 @@ import dataclasses
3
3
  import re
4
4
  from typing import TYPE_CHECKING, Any, Dict, Optional
5
5
 
6
+ from typing_extensions import SupportsBytes
7
+
6
8
  if TYPE_CHECKING:
7
9
  from .database import Database
8
10
  from .diaglayers.diaglayer import DiagLayer
9
11
  from .snrefcontext import SnRefContext
10
12
 
13
+ BytesTypes = (bytearray, bytes, SupportsBytes)
14
+
11
15
 
12
16
  def retarget_snrefs(database: "Database",
13
17
  diag_layer: "DiagLayer",
@@ -0,0 +1,209 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from copy import copy
3
+ from enum import Enum
4
+ from typing import Dict, Generator, List, Optional, Tuple, Union
5
+
6
+ from .diaglayers.basevariant import BaseVariant
7
+ from .diaglayers.ecuvariant import EcuVariant
8
+ from .exceptions import DecodeError, odxraise
9
+ from .matchingparameter import MatchingParameter
10
+ from .response import Response
11
+
12
+
13
+ class VariantMatcher:
14
+ """VariantMatcher implements the matching algorithm of ECU and
15
+ base variants according to their ECU-VARIANT-PATTERNs or
16
+ BASE-VARIANT-PATTERNs according to ISO 22901-1.
17
+
18
+ Usage (example):
19
+
20
+ ```python
21
+
22
+ # initialize the matcher with a list of base or ECU variants
23
+ matcher = VariantMatcher(candidates=[...], use_cache=use_cache)
24
+
25
+ # run the request loop to obtain responses for every request
26
+ for use_physical_addressing, encoded_request in matcher.request_loop():
27
+ if use_physical_addressing:
28
+ resp = send_to_ecu_using_physical_addressing(encoded_request)
29
+ else:
30
+ resp = send_to_ecu_using_functional_addressing(encoded_request)
31
+ matcher.evaluate(resp)
32
+
33
+ # result
34
+ if matcher.has_match():
35
+ if isinstance(matcher.matching_variant, BaseVariant):
36
+ print(f"Match found for base variant "
37
+ f"{matcher.matching_variant.short_name}")
38
+ elif isinstance(matcher.matching_variant, EcuVariant):
39
+ print(f"Match found for ECU variant "
40
+ f"{matcher.matching_variant.short_name}")
41
+ else:
42
+ print(f"Match found for unknown diag layer type "
43
+ f"{type(matcher.matching_variant).__name__}")
44
+ else:
45
+ print("No matching base- or ECU variant found")
46
+ ```
47
+
48
+ TODO: Note that only patterns that exclusivly reference diagnostic
49
+ services (i.e., no single-ECU jobs) in their matching parameters
50
+ are currently supported.
51
+ """
52
+
53
+ class State(Enum):
54
+ PENDING = 0
55
+ NO_MATCH = 1
56
+ MATCH = 2
57
+
58
+ def __init__(self,
59
+ variant_candidates: Union[List[EcuVariant], List[BaseVariant]],
60
+ use_cache: bool = True):
61
+
62
+ self.variant_candidates = variant_candidates
63
+ self.use_cache = use_cache
64
+ self.req_resp_cache: Dict[bytes, bytes] = {}
65
+ self._recent_ident_response: Optional[bytes] = None
66
+
67
+ self._state = VariantMatcher.State.PENDING
68
+ self._matching_variant: Optional[Union[EcuVariant, BaseVariant]] = None
69
+
70
+ def request_loop(self) -> Generator[Tuple[bool, bytes], None, None]:
71
+ """The request loop yielding tuples of byte sequences of
72
+ requests and the whether physical addressing ought to be used
73
+ to send them
74
+
75
+ Each of these requests needs to be send to the ECU to be
76
+ identified using the specified addressing scheme. The response
77
+ of the ECU is then required to be passed to the matcher using
78
+ the `evaluate()` method.
79
+ """
80
+ from .basevariantpattern import BaseVariantPattern
81
+ from .ecuvariantpattern import EcuVariantPattern
82
+ from .matchingbasevariantparameter import MatchingBaseVariantParameter
83
+
84
+ if not self.is_pending():
85
+ return
86
+
87
+ self._matching_variant = None
88
+ for variant in self.variant_candidates:
89
+ variant_patterns: Union[List[EcuVariantPattern], List[BaseVariantPattern]]
90
+ if isinstance(variant, EcuVariant):
91
+ variant_patterns = variant.ecu_variant_patterns
92
+ elif isinstance(variant, BaseVariant):
93
+ if variant.base_variant_pattern is None:
94
+ variant_patterns = []
95
+ else:
96
+ variant_patterns = [variant.base_variant_pattern]
97
+ else:
98
+ odxraise(f"Only EcuVariant and BaseVariant are supported "
99
+ f"for pattern matching, not {type(self).__name__}")
100
+ self._state = VariantMatcher.State.NO_MATCH
101
+ return
102
+
103
+ any_pattern_matches = False
104
+ for pattern in variant_patterns:
105
+ all_params_match = True
106
+ for matching_param in pattern.get_matching_parameters():
107
+ req_bytes = matching_param.get_ident_service(variant).encode_request()
108
+
109
+ if self.use_cache and req_bytes in self.req_resp_cache:
110
+ resp_values = copy(self.req_resp_cache[req_bytes])
111
+ else:
112
+ if isinstance(matching_param, MatchingBaseVariantParameter):
113
+ yield matching_param.use_physical_addressing, req_bytes
114
+ else:
115
+ yield True, req_bytes
116
+ resp_values = self._get_ident_response()
117
+ self._update_cache(req_bytes, copy(resp_values))
118
+
119
+ cur_response_matches = self._ident_response_matches(
120
+ variant, matching_param, resp_values)
121
+ all_params_match = all_params_match and cur_response_matches
122
+ if not all_params_match:
123
+ break
124
+
125
+ if all_params_match:
126
+ any_pattern_matches = True
127
+ break
128
+
129
+ if any_pattern_matches:
130
+ self._state = VariantMatcher.State.MATCH
131
+ self._matching_variant = variant
132
+ break
133
+
134
+ if self.is_pending():
135
+ # no pattern has matched for any ecu variant
136
+ self._state = VariantMatcher.State.NO_MATCH
137
+
138
+ def evaluate(self, resp_bytes: bytes) -> None:
139
+ """Update the matcher with the response to a requst.
140
+
141
+ Warning: Use this method EXACTLY once within the loop body of the request loop.
142
+ """
143
+ self._recent_ident_response = bytes(resp_bytes)
144
+
145
+ def is_pending(self) -> bool:
146
+ """True iff request loop has not yet been run."""
147
+ return self._state == VariantMatcher.State.PENDING
148
+
149
+ def has_match(self) -> bool:
150
+ """Returns true iff the non-pending matcher found a matching ecu variant.
151
+
152
+ Raises a runtime error if the matcher is pending.
153
+ """
154
+ if self.is_pending():
155
+ raise RuntimeError(
156
+ "EcuVariantMatcher is pending. Run the request_loop to determine the active ecu variant."
157
+ )
158
+ return self._state == VariantMatcher.State.MATCH
159
+
160
+ @property
161
+ def matching_variant(self) -> Optional[Union[EcuVariant, BaseVariant]]:
162
+ """Returns the matched, i.e., active ecu variant if such a variant has been found."""
163
+ return self._matching_variant
164
+
165
+ def _ident_response_matches(
166
+ self,
167
+ variant: Union[EcuVariant, BaseVariant],
168
+ matching_param: MatchingParameter,
169
+ response_bytes: bytes,
170
+ ) -> bool:
171
+ """Decode a binary response and extract the identification string according
172
+ to the snref or snpathref of the matching_param.
173
+ """
174
+ service = matching_param.get_ident_service(variant)
175
+
176
+ # ISO 22901 requires that snref or snpathref is resolvable in
177
+ # at least one POS-RESPONSE or NEG-RESPONSE
178
+ all_responses: List[Response] = []
179
+ all_responses.extend(service.positive_responses)
180
+ all_responses.extend(service.negative_responses)
181
+ all_responses.extend(variant.global_negative_responses)
182
+
183
+ for cur_response in all_responses:
184
+ try:
185
+ decoded_vals = cur_response.decode(response_bytes)
186
+ except DecodeError:
187
+ # the current response object could not decode the received
188
+ # data. Ignore it.
189
+ continue
190
+
191
+ if not matching_param.matches(decoded_vals):
192
+ # This particular response object does not match the
193
+ # expected value of the specified matching
194
+ # parameter. Ignore it.
195
+ continue
196
+
197
+ return True
198
+
199
+ return False
200
+
201
+ def _update_cache(self, req_bytes: bytes, resp_bytes: bytes) -> None:
202
+ if self.use_cache:
203
+ self.req_resp_cache[req_bytes] = resp_bytes
204
+
205
+ def _get_ident_response(self) -> bytes:
206
+ if not self._recent_ident_response:
207
+ raise RuntimeError(
208
+ "No response available. Did you forget to call 'evaluate()' in a loop?")
209
+ return self._recent_ident_response
@@ -0,0 +1,38 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING, List, Union
4
+ from xml.etree import ElementTree
5
+
6
+ from .exceptions import odxraise
7
+ from .matchingparameter import MatchingParameter
8
+ from .odxlink import OdxDocFragment
9
+
10
+ if TYPE_CHECKING:
11
+ from .matchingbasevariantparameter import MatchingBaseVariantParameter
12
+ from .matchingparameter import MatchingParameter
13
+
14
+
15
+ @dataclass
16
+ class VariantPattern:
17
+ """Variant patterns are used to identify the concrete variant of an ECU
18
+
19
+ This is done by observing the responses after sending it some
20
+ requests. There are two kinds of variant patterns:
21
+ `BaseVariantPattern`s which are used to identify the applicable
22
+ base variant and ECU variant patterns which can be used identify
23
+ concrete revisions of the ECU in question. (Both types of pattern
24
+ are optional, i.e., it might not be possible to identify the base-
25
+ or ECU variant present.)
26
+ """
27
+
28
+ def get_matching_parameters(
29
+ self) -> Union[List["MatchingParameter"], List["MatchingBaseVariantParameter"]]:
30
+ odxraise(
31
+ f"VariantPattern subclass `{type(self).__name__}` does not "
32
+ f"implement `.get_match_parameters()`", RuntimeError)
33
+ return []
34
+
35
+ @staticmethod
36
+ def from_et(et_element: ElementTree.Element,
37
+ doc_frags: List[OdxDocFragment]) -> "VariantPattern":
38
+ return VariantPattern()
odxtools/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '9.4.1'
21
- __version_tuple__ = version_tuple = (9, 4, 1)
20
+ __version__ = version = '9.5.0'
21
+ __version_tuple__ = version_tuple = (9, 5, 0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: odxtools
3
- Version: 9.4.1
3
+ Version: 9.5.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>
@@ -4,6 +4,7 @@ odxtools/additionalaudience.py,sha256=Xjy_PhTfTV23oAMvml5XeZw3ex8xssIhgRseSsxKcx
4
4
  odxtools/admindata.py,sha256=ue5MnYJK_jxNq6Okxy4aGtH15uBAnWXIjMBFSh36Q3g,1879
5
5
  odxtools/audience.py,sha256=_O6UPKnrDbwMMKRnCJ5b8M3_I2jU5jMR6Ss5zLzk9yM,3361
6
6
  odxtools/basecomparam.py,sha256=VxpIsTr6-S4e8Vw72hJXSLUFU5TvHy5CM0sweVL-ZWE,2384
7
+ odxtools/basevariantpattern.py,sha256=c6XSjPGNYErOiyl9SrROwShkreoC9tIU3zmoXXAHimo,1462
7
8
  odxtools/basicstructure.py,sha256=GcyZuasaH62JcINgyeD300N297q7_LP6iMfDy8fwzMg,5071
8
9
  odxtools/codec.py,sha256=2aTfrQc0-cNuPAfXeDDyjxXAHjBJ1xy3ELCVeA9UQP0,8108
9
10
  odxtools/commrelation.py,sha256=Gh3CqMuF9rS0X107oxUkISNT2VWDZVPCpOSu54q7hcs,4793
@@ -19,9 +20,8 @@ odxtools/complexcomparam.py,sha256=4EJQXVmi_i3Eas5LAF-sEC5XnAUMZKr2sS6LoDNskk0,3
19
20
  odxtools/complexdop.py,sha256=AHkHMf_IEWfy-_zSiCuZXmdTNCXiCOpg5GRZ5uMVV5A,280
20
21
  odxtools/createanycomparam.py,sha256=RP0XA2Ut4GL2NWy05RvKBrTnpbvvCG2XuNL8OO-9rbI,617
21
22
  odxtools/createanydiagcodedtype.py,sha256=0DVmMj4UCDYIg0zu9pgTplyhHmRedz1KJcchq1uKMjg,1216
22
- odxtools/createecuvariantpatterns.py,sha256=FXHLGR4M791MRkpU2_keck88hoMYkuyPiCUxuGbUasQ,559
23
23
  odxtools/database.py,sha256=VJk-EOzHHtxjkhP7o0h470B0oyg73ZafK3w1UjnwRNM,9945
24
- odxtools/dataobjectproperty.py,sha256=N-8YJUXdvvlmM3IZYu5SokSnST1hbgPRr3sI-VP4Epk,6238
24
+ odxtools/dataobjectproperty.py,sha256=OMbD5P2T1A5b7qoDmRik08jWGSZFvaBn4CQI2fGNMQA,6244
25
25
  odxtools/decodestate.py,sha256=VdYB4Q3yuFqCNV0IK2QfS_120n9dIgyLnPOwc3Hy33U,8292
26
26
  odxtools/description.py,sha256=xaSfOjRCWn7_9BhdTgI0ESPtaU-iUE815Z6WobYYL-A,1952
27
27
  odxtools/determinenumberofitems.py,sha256=DxM9udr1MQfEojiLdc35bbjniQl1fkeHcTMpoo9AfPI,1558
@@ -39,10 +39,9 @@ odxtools/dynamicendmarkerfield.py,sha256=Q7KKOBNSrAg5cehEP3i4YTPiqz5UetEIJDGah60
39
39
  odxtools/dynamiclengthfield.py,sha256=Us0qRvkRPId7xSNKrYdsr1AhVFdTBezRMCBhXl98E00,5273
40
40
  odxtools/dyndefinedspec.py,sha256=efvUG2kMUdwfmMG5qxGHzMSZ5CxdSJhmtzHecopnpHg,7610
41
41
  odxtools/dynenddopref.py,sha256=SYXrmRUQoLWD4UI3WV92oA5rNhxbDuRv6H1MeXeuWhM,1199
42
- odxtools/ecuvariantmatcher.py,sha256=muZfBcHVUQFXn3mBRwLtQ2asGV1oDYkDrBwZ9WYuLKU,6848
43
- odxtools/ecuvariantpattern.py,sha256=Xp2NpS13sVvyOjy2PgxLvXPptAfmYSH_KDIjqYByZHs,863
42
+ odxtools/ecuvariantpattern.py,sha256=HxiDLdZWD2aOtBoCKJQjpTY8Fm1ptFtFK3-CcCj3hQo,1330
44
43
  odxtools/element.py,sha256=nCJkQMIPC26XfD4w1VrTH3l7Cd_BemMM_5lALh5gC7E,1259
45
- odxtools/encodestate.py,sha256=V3AEH3g6TYQIfP-TP2yinBgfCtEbdlbJ1xWgY_zhAVs,13566
44
+ odxtools/encodestate.py,sha256=iufi35usTYoZ58iR6m1cN01VNFGeeGCmWV00dnAvr-o,13558
46
45
  odxtools/encoding.py,sha256=5gn6PDqwU9K2RzEfEO9kcBj1v55TSKI1nCXNcTiZE9s,1994
47
46
  odxtools/endofpdufield.py,sha256=mGkhTj-YGdJ4EHMsK3AlWV6G_KLWI4ymiFKbY8U2W50,3359
48
47
  odxtools/environmentdata.py,sha256=_wSKhV_NuMG6UPMnpOxz6mVjvrXsd2mtqqT7VKSSxHU,1414
@@ -57,9 +56,10 @@ odxtools/isotp_state_machine.py,sha256=O6v29IK7p_4gFqrr-fahnbp9Qmo78EEQfEwRsA_Vf
57
56
  odxtools/leadinglengthinfotype.py,sha256=oD0uwLECssJZDKicKYlOZRbrcCeTfz-CiWa-SROfrIU,4013
58
57
  odxtools/library.py,sha256=E_A6K6jcEoUdXh4h4sQILj7_JM-6X5OUUWSM47G69CU,2067
59
58
  odxtools/loadfile.py,sha256=hi8jVE7B1G9gklsb7MrGrp4aOAI65j0kNwvDeEEEN2k,1744
60
- odxtools/matchingparameter.py,sha256=2cqaxFCZ28GvvUKCIM-jas3ae4eYGbULbkBlb3aH5RY,2232
59
+ odxtools/matchingbasevariantparameter.py,sha256=hpxAWJQfEGsChEkSos5K8pJDgblu4gbBsEG6f2VhMg8,1287
60
+ odxtools/matchingparameter.py,sha256=nwdshZUUMc13XIKPyalMRYa-F64edg89UPIp-WjfTZU,5762
61
61
  odxtools/message.py,sha256=PDY-CDgTmJA6dyNacCV57M-p_ETtR8MdU7WZGEew4zM,855
62
- odxtools/minmaxlengthtype.py,sha256=tL_PSvDrZ1JZAnqup5RJWTyj9nuueQ_OjlowG-ZWciU,9710
62
+ odxtools/minmaxlengthtype.py,sha256=TPDHQbHqOJTWNr7NGZSgtoEoYW1Cijilz8LL_smSLVA,9718
63
63
  odxtools/modification.py,sha256=9EJJUCNTzM05HGT6UZ7r2yLDm7voBFPO-hldBC9D4aw,875
64
64
  odxtools/multiplexer.py,sha256=flHlorv9grEMj8pNAWQJNrxhptnDxSsFERO7gpKYRME,10235
65
65
  odxtools/multiplexercase.py,sha256=qfxWJzfJLYAsnhFGheUWDsUaIte2dSRJ0nadiN9SXDw,3176
@@ -70,7 +70,7 @@ odxtools/negoutputparam.py,sha256=u2IhJvy6n19t3nyADGG0H0XTm7JNdQ0-95FawGAdtiE,13
70
70
  odxtools/obd.py,sha256=WsUbhdonqQJtxf972CUdYL78AMMTVjMQhoCJbaZhRIo,1860
71
71
  odxtools/odxcategory.py,sha256=5R5vUTcYnsgfzGD3c0TM6qFYXpbW-d-t1VpUmgZYHWU,3221
72
72
  odxtools/odxlink.py,sha256=WVPOXNIldLdz0YnwVpRVe42ugC6G1WX4Ic2Khhz-ito,10194
73
- odxtools/odxtypes.py,sha256=XJ6OR2_fsILR_cA3lDK47XAtYQ5U6KsH7r4Qn5nSNH8,8094
73
+ odxtools/odxtypes.py,sha256=DUZSjmdeHSq0XCBgU4CvZTM7TvmcB2WtpE25QWm3NMs,8100
74
74
  odxtools/outputparam.py,sha256=VqD7jJ5yDvIq9KAatGWD17CWfuSqySQl142FXZYeQHE,1468
75
75
  odxtools/parameterinfo.py,sha256=PzhIZyYINNxykecPQWnRH1Ywrfgxx31DVax2VNN__CY,11183
76
76
  odxtools/paramlengthinfotype.py,sha256=zJ3gRweIEngl2C4wJoT2uuTf05NRNSJ4RlDmC2L3UBE,4859
@@ -90,13 +90,13 @@ odxtools/snrefcontext.py,sha256=5ZbrZiX1gw6LC6o62RPuxzSo4tDR2LqRQ9w9Vi57FA8,953
90
90
  odxtools/specialdata.py,sha256=5-1D_6dcEmi-TodCzCoBZK7gqeDY-cuRe1ybkTh4rBY,1033
91
91
  odxtools/specialdatagroup.py,sha256=9pNCA89kGwhBiVN9nC_03ASHZT6q3FL8l2CxRf9jwU0,3070
92
92
  odxtools/specialdatagroupcaption.py,sha256=pIa1l6EXVDXBisUtRkn6hTzAcUXFnXJoWstesayLfME,987
93
- odxtools/standardlengthtype.py,sha256=-Zi4yNXSiEPj0wk3mOQLaWhw15JkxCaZjBDcxqf9NzA,8190
93
+ odxtools/standardlengthtype.py,sha256=nT0s-cvPKaOxxRnWPocY9rqHRgxnzFlW3iuCe_B8w2Y,8107
94
94
  odxtools/state.py,sha256=d--nCbV1qBmOmiTmUrQjDC_naispOM0a-H1AvCe5_xI,898
95
95
  odxtools/statechart.py,sha256=ZT8amPjec25pexwnOAPiifGEmKa68rliOILLnB6tNJc,2956
96
96
  odxtools/statetransition.py,sha256=Q7UaDksJmiKVGnO4GsxPH9pFIKt12ZOkDeJn4KICiGk,2599
97
97
  odxtools/staticfield.py,sha256=NBZa5UZD4BD4UekpqF5bq_IzFjxUafmvpCHIEptOl-s,4434
98
98
  odxtools/structure.py,sha256=kRBP00MDN1w9w3Vc2XnhZDC_SBBVCZwayGGJoKJ6U_c,866
99
- odxtools/subcomponent.py,sha256=OPnpbliL7OTfYy9dR0hpQclyqQiTUEa2e2n7GmIoZVE,10401
99
+ odxtools/subcomponent.py,sha256=Sn4CH0FelZ8j_Ny9w7B7qeK6YJFs3FGcYSMPawmHUmc,10498
100
100
  odxtools/swvariable.py,sha256=7vmVTAqtKIn81UpAZrboIZpbbD4jqwW0MEiK0m5bZNo,697
101
101
  odxtools/table.py,sha256=UTNQMVuQOj3uOYos_V1eH3QgWXncv6MPRXUhTYUxiMA,6030
102
102
  odxtools/tablerow.py,sha256=QWOQxd83EN6jnOXDAc3ogaFxCz9R416vZSXpmZKjjnQ,9759
@@ -105,9 +105,11 @@ odxtools/uds.py,sha256=ttjCaqwfAU-DlM5peFpZCcwzq60QjBMxVpaOyUyY48k,5567
105
105
  odxtools/unit.py,sha256=n2aJdXWvtZbqvoGUXDRA6nXc5QwM25suZZg8dQG6y1A,3408
106
106
  odxtools/unitgroup.py,sha256=_WNpnYFmkiHQnOY8XhIviiQLXziY3kplH--7ufAWXyI,2067
107
107
  odxtools/unitspec.py,sha256=IAmDSLscVb4QUGgbQT-xXhtywW3qs0o9FfelnVYvimk,3074
108
- odxtools/utils.py,sha256=FaNlouR0FvO4W4nk3Wr7Ogx1wC4tYfuMOo07KF2QxbE,2764
108
+ odxtools/utils.py,sha256=VBURockmg0E9NgaPZu177zA64QV6TxYRth1tpn7MfI0,2857
109
109
  odxtools/variablegroup.py,sha256=--5sMDOMUg4I3hFyoVOFcGCTOrctrrMADdmH3Mn4wHk,848
110
- odxtools/version.py,sha256=rCNnPeguHEaZ5sV4KzLUwq9jwQUlDxbmYLFI4XE-Jkk,511
110
+ odxtools/variantmatcher.py,sha256=a1c1NtZgxRBA7OsRhEBM-3mgKUHqSywYXggT7Q5alJo,8355
111
+ odxtools/variantpattern.py,sha256=f3t1KXXjrNkJLGmEc7iGG86Nj4h6Fdh9loFKTt-1Ffc,1431
112
+ odxtools/version.py,sha256=EXUTEMMlT-sZN4IxnqEb0scBddeq3W9TNIgCRDO-ezE,511
111
113
  odxtools/writepdxfile.py,sha256=ZEoNkSxheqnXm836-HOpd04O6ipMXYKFzXZy6PM0YLc,7813
112
114
  odxtools/xdoc.py,sha256=gDq-8l8x-Tj1ZJOttPxZchcO5_jEPwcXxMgT29VgTS0,1433
113
115
  odxtools/cli/__init__.py,sha256=T7ano_FIyzBASxYpmcA5VJXU5bQLIy_Qk0HE_SDKelY,106
@@ -141,14 +143,14 @@ odxtools/compumethods/scalelinearcompumethod.py,sha256=o_Re17G4XlCn-XbStnvlTs9XV
141
143
  odxtools/compumethods/scaleratfunccompumethod.py,sha256=3imvLJgN3gkc7pILPIwAILHw0Ie5Ih_vysVGA8wYRgk,4457
142
144
  odxtools/compumethods/tabintpcompumethod.py,sha256=Gyf2qV5y53SHIcum5d5HQVGszoZCdu47jxMCQ6yKdQk,8065
143
145
  odxtools/compumethods/texttablecompumethod.py,sha256=vDYafbM0YaipjfN0NSlCFMiIPvp8waMNj3thNcwuYgk,5929
144
- odxtools/diaglayers/basevariant.py,sha256=mXS-SF-tcwtdV-rxZZCH6_yqdA-yhtMX1SGo1MS8w7k,4452
145
- odxtools/diaglayers/basevariantraw.py,sha256=A9h6p2Ri0sPPTW217rtEdNpAt5g6t0MwP9sgXqZ8pFo,4673
146
+ odxtools/diaglayers/basevariant.py,sha256=x9WW-gpYl517la1nc2CJMj08-S_KGFb_8nwIOQN9Vg0,4645
147
+ odxtools/diaglayers/basevariantraw.py,sha256=ujW58iFFWFSL9dzgUM4f5bPOA8oTZ7BjbH5lUHtLMJQ,4970
146
148
  odxtools/diaglayers/diaglayer.py,sha256=xs529IVG_18Fi8kbEZkD2vHjHRr57jeE4A_2lDLRKCQ,16254
147
149
  odxtools/diaglayers/diaglayerraw.py,sha256=ihOq51PWZUi4L7k0R5k06aLxZ8ppUkRYFym4hK_gXq8,12985
148
150
  odxtools/diaglayers/diaglayertype.py,sha256=FXL-EVBdrAURuHSHo9OthFuEYEybQO66aMB1o2GxVdI,1340
149
151
  odxtools/diaglayers/ecushareddata.py,sha256=QoV50wQIrJ2-faLtU1P7RrjdacbhPY2BfUtVtIyWqCM,3347
150
152
  odxtools/diaglayers/ecushareddataraw.py,sha256=cvkBhqTLOAey7Pt1bjQ3Jkkenp-D3Zh5djpB4ZWWUvw,3375
151
- odxtools/diaglayers/ecuvariant.py,sha256=OsLWg-dx6OJ_OPuYcgoTJuDXEReaJclDRbU5YKqrtBA,4368
153
+ odxtools/diaglayers/ecuvariant.py,sha256=SE1JYUo3F67WO1wu8hYekW34s6OV7ORdoKkL62fZchU,4863
152
154
  odxtools/diaglayers/ecuvariantraw.py,sha256=bKlqFnBWkKdkM9EY5rFGOno3ClIVQNhgBoU5DseNnRc,4944
153
155
  odxtools/diaglayers/functionalgroup.py,sha256=o5bUGGDYisr7CjHZZh83fokA80f0s0zpHC_501cg-f0,4093
154
156
  odxtools/diaglayers/functionalgroupraw.py,sha256=H0-_XXlFTV8POGnh0S7CVBOKKQuyKHMB9lEyoAHuG_A,3963
@@ -177,7 +179,8 @@ odxtools/templates/diag_layer_container.odx-d.xml.jinja2,sha256=fDH9K2IdGfX-dVV0
177
179
  odxtools/templates/index.xml.jinja2,sha256=Z1bcntvvky7NoH0Q0CRJgUORwf3g96PFPGeOpi7VATw,647
178
180
  odxtools/templates/macros/printAdminData.xml.jinja2,sha256=YTB_CbhnALF6RIbE0XKQHRbC6c0PSZ_DxdrRHgqMD_s,2590
179
181
  odxtools/templates/macros/printAudience.xml.jinja2,sha256=vaYL_-GPVdlLc7WAQTbkVZZbFuQptnCbOhXJb3tsHv8,1286
180
- odxtools/templates/macros/printBaseVariant.xml.jinja2,sha256=l4Jp8v9ZfPXvIisqVLgTpzc5UdYGeIocY4vAcgCiiaE,1480
182
+ odxtools/templates/macros/printBaseVariant.xml.jinja2,sha256=Ovf7NvO5jzy_9w1-iXi_q2dedoEfsQOYCK_WASkcITQ,1451
183
+ odxtools/templates/macros/printBaseVariantPattern.xml.jinja2,sha256=foL_DmH4mWFzPObZ3VjPixdzbomAA5jK7GLpAhJsKVE,1192
181
184
  odxtools/templates/macros/printBasicStructure.xml.jinja2,sha256=V5oWGZesU_U5pvPO3Rtlodc7sxe5mFWY-uURLnQ8OfQ,614
182
185
  odxtools/templates/macros/printCompanyData.xml.jinja2,sha256=jcNmQBOGDw7wcKdam72NIJSOTxthfkPJvrmw24MBiYQ,2981
183
186
  odxtools/templates/macros/printComparam.xml.jinja2,sha256=e3hPCswAymJteQIUNoc7FOwsK_FrbJOvJooWdEx_vuQ,2408
@@ -193,7 +196,7 @@ odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2,sha256=6BmxPKesQ
193
196
  odxtools/templates/macros/printDynamicLengthField.xml.jinja2,sha256=bNvNh_4xyp4L6v7cxJ5cO1yOHYPnSFiaK9oY4l63fDU,784
194
197
  odxtools/templates/macros/printEcuSharedData.xml.jinja2,sha256=WK08eoDFEjj9o3eOuy__H0r9ioo688rTuQp6uXE6Tdo,820
195
198
  odxtools/templates/macros/printEcuVariant.xml.jinja2,sha256=Nsgj2VUWZvSDFPQsn6wv75M9TaryOB-tlQYCVq9hDEM,1594
196
- odxtools/templates/macros/printEcuVariantPattern.xml.jinja2,sha256=uSUrX1n2YVw6tt1-A0Lmlgy_W3dxW4r8bVErdrf_6vk,846
199
+ odxtools/templates/macros/printEcuVariantPattern.xml.jinja2,sha256=YoYyzmxerJLJzUNoQ8H-JBnOqiwpVdODqk2KNRdWKWo,844
197
200
  odxtools/templates/macros/printElementId.xml.jinja2,sha256=Oe4sgtBB_VhTz6uJzaiTCpQki2ulKKfpBq56FVgBC-I,570
198
201
  odxtools/templates/macros/printEndOfPdu.xml.jinja2,sha256=ptjwOxyNTABATOajiitcvlJRKVGP_osh2wSJTCbaa38,679
199
202
  odxtools/templates/macros/printEnvData.xml.jinja2,sha256=2Goatqb66d-pCf9wqJDXn1EZWNhkQ8FJANKQMOnxuJs,609
@@ -221,9 +224,9 @@ odxtools/templates/macros/printStructure.xml.jinja2,sha256=M8p82SlgKaDk6nJA-0wr6
221
224
  odxtools/templates/macros/printSubComponent.xml.jinja2,sha256=ETo-k0K7px5nu9kgVjgDnjPoy8rYRfzq5ojvsZhM3Rw,3416
222
225
  odxtools/templates/macros/printTable.xml.jinja2,sha256=IMAaYNtpxs_6V7VxM_hfOeGkFfkyRjSmwvmr9_K8Ddc,3075
223
226
  odxtools/templates/macros/printUnitSpec.xml.jinja2,sha256=KOjRVwtdIg7uGl4B9cLpvW68-TYeE33Tz9sWZdnwkBE,2878
224
- odxtools-9.4.1.dist-info/LICENSE,sha256=NeGPFQdTa6EKeON3aShVlPAIquJnbbiOfj0suz6rzyQ,1074
225
- odxtools-9.4.1.dist-info/METADATA,sha256=jqTqPdpARQPDAnEyGaDMfjBGoOidZ1AyYZB_DZc5qyE,44055
226
- odxtools-9.4.1.dist-info/WHEEL,sha256=nn6H5-ilmfVryoAQl3ZQ2l8SH5imPWFpm1A5FgEuFV4,91
227
- odxtools-9.4.1.dist-info/entry_points.txt,sha256=_sBDzuNoT8LbbCjfc-OJiUt5WPrtOq_x-rr9txhrPjY,53
228
- odxtools-9.4.1.dist-info/top_level.txt,sha256=pdS02kE5ZdgsaBRZDpX3NBFlaSx3zotsqX4E4V6tXEI,9
229
- odxtools-9.4.1.dist-info/RECORD,,
227
+ odxtools-9.5.0.dist-info/LICENSE,sha256=NeGPFQdTa6EKeON3aShVlPAIquJnbbiOfj0suz6rzyQ,1074
228
+ odxtools-9.5.0.dist-info/METADATA,sha256=TG3F5Qqkpiu1FVtwEkeH1s3eKEyHWR6LR1eyBe6g3N0,44055
229
+ odxtools-9.5.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
230
+ odxtools-9.5.0.dist-info/entry_points.txt,sha256=_sBDzuNoT8LbbCjfc-OJiUt5WPrtOq_x-rr9txhrPjY,53
231
+ odxtools-9.5.0.dist-info/top_level.txt,sha256=pdS02kE5ZdgsaBRZDpX3NBFlaSx3zotsqX4E4V6tXEI,9
232
+ odxtools-9.5.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.1)
2
+ Generator: setuptools (75.8.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,18 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- from typing import List, Optional
3
- from xml.etree import ElementTree
4
-
5
- from .ecuvariantpattern import EcuVariantPattern
6
- from .odxlink import OdxDocFragment
7
-
8
-
9
- def create_ecu_variant_patterns_from_et(et_element: Optional[ElementTree.Element],
10
- doc_frags: List[OdxDocFragment]) -> List[EcuVariantPattern]:
11
-
12
- if et_element is None:
13
- return []
14
-
15
- return [
16
- EcuVariantPattern.from_et(evp_elem, doc_frags)
17
- for evp_elem in et_element.iterfind("ECU-VARIANT-PATTERN")
18
- ]
@@ -1,171 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- from enum import Enum
3
- from typing import Dict, Generator, List, Optional
4
-
5
- from .diaglayers.diaglayer import DiagLayer
6
- from .diaglayers.diaglayertype import DiagLayerType
7
- from .diaglayers.ecuvariant import EcuVariant
8
- from .diagservice import DiagService
9
- from .exceptions import OdxError, odxassert, odxrequire
10
- from .matchingparameter import MatchingParameter
11
- from .odxtypes import ParameterValue
12
- from .response import Response
13
-
14
-
15
- class EcuVariantMatcher:
16
- """EcuVariantMatcher implements the matching algorithm of ecu variants according to their
17
- ECU-VARIANT-PATTERN according to ISO 22901-1.
18
-
19
- Usage (example):
20
-
21
- ```python
22
-
23
- # initialize the matcher with a list of ecu variants,
24
- # i.e., DiagLayer instances of variant_type == DiagLayerType.ECU-VARIANT
25
- matcher = EcuVariantMatcher(ecu_variant_candidates=[...], use_cache=use_cache)
26
-
27
- # run the request loop to obtain responses for every request
28
- for req in matcher.request_loop():
29
- resp = ... # make a UDS request
30
- matcher.evaluate(resp)
31
-
32
- # result
33
- if matcher.has_match()
34
- match = matcher.get_active_ecu_variant()
35
- ```
36
- """
37
-
38
- class State(Enum):
39
- PENDING = 0
40
- NO_MATCH = 1
41
- MATCH = 2
42
-
43
- @staticmethod
44
- def get_ident_service(diag_layer: DiagLayer, matching_param: MatchingParameter) -> DiagService:
45
- service_name = matching_param.diag_comm_snref
46
- service = odxrequire(diag_layer.services.get(service_name))
47
- return service
48
-
49
- @staticmethod
50
- def encode_ident_request(diag_layer: DiagLayer, matching_param: MatchingParameter) -> bytes:
51
- service = EcuVariantMatcher.get_ident_service(diag_layer, matching_param)
52
- return bytes(service.encode_request())
53
-
54
- @staticmethod
55
- def decode_ident_response(
56
- diag_layer: DiagLayer,
57
- matching_param: MatchingParameter,
58
- response_bytes: bytes,
59
- ) -> str:
60
- """Decode a binary response and extract the identification string according
61
- to the snref or snpathref of the matching_param.
62
- """
63
- service = EcuVariantMatcher.get_ident_service(diag_layer, matching_param)
64
-
65
- # ISO 22901 requires that snref or snpathref is resolvable in at least one
66
- # POS-RESPONSE or NEG-RESPONSE
67
- pos_neg_responses: List[Response] = []
68
- if service.positive_responses is not None:
69
- pos_neg_responses.extend(service.positive_responses)
70
- if service.negative_responses is not None:
71
- pos_neg_responses.extend(service.negative_responses)
72
-
73
- for any_response in pos_neg_responses:
74
- decoded_val: Optional[ParameterValue] = any_response.decode(response_bytes)
75
- # disassemble snref / snpathref
76
- path_ref = matching_param.out_param_if.split(".")
77
- for ref in path_ref:
78
- if isinstance(decoded_val, dict) and ref in decoded_val:
79
- decoded_val = decoded_val[ref]
80
- else:
81
- decoded_val = None
82
- break
83
-
84
- if decoded_val is not None:
85
- if isinstance(decoded_val, str) or isinstance(decoded_val, int):
86
- return str(decoded_val)
87
-
88
- raise OdxError(f"The snref or snpathref '{matching_param.out_param_if}' is cannot be \
89
- resolved for any positive or negative response.")
90
-
91
- def __init__(self, ecu_variant_candidates: List[EcuVariant], use_cache: bool = True):
92
-
93
- self.ecus = ecu_variant_candidates
94
- for ecu in self.ecus:
95
- odxassert(ecu.variant_type == DiagLayerType.ECU_VARIANT)
96
-
97
- self.use_cache = use_cache
98
- self.req_resp_cache: Dict[bytes, bytes] = {}
99
- self._recent_ident_response: Optional[bytes] = None
100
-
101
- self._state = EcuVariantMatcher.State.PENDING
102
-
103
- def request_loop(self) -> Generator[bytes, None, None]:
104
- """The request loop yields byte sequences of requests, which shall be executed within the
105
- loop body. It is required to pass the response back to the matcher using the evaluate method.
106
- """
107
- if not self.is_pending():
108
- return
109
-
110
- for ecu in self.ecus:
111
- any_match = False
112
- for pattern in ecu.ecu_variant_patterns:
113
- all_match = True
114
- for matching_param in pattern.matching_parameters:
115
- req_bytes = bytes(EcuVariantMatcher.encode_ident_request(ecu, matching_param))
116
- if self.use_cache and req_bytes in self.req_resp_cache:
117
- resp_bytes = self.req_resp_cache[req_bytes]
118
- else:
119
- yield req_bytes
120
- resp_bytes = self._get_ident_response()
121
- self._update_cache(req_bytes, resp_bytes)
122
- ident_val = EcuVariantMatcher.decode_ident_response(
123
- ecu, matching_param, resp_bytes)
124
- all_match &= matching_param.is_match(ident_val)
125
- if all_match:
126
- any_match = True
127
- break
128
- if any_match:
129
- self._state = EcuVariantMatcher.State.MATCH
130
- self._match = ecu
131
- break
132
- if self.is_pending():
133
- # no pattern has matched for any ecu variant
134
- self._state = EcuVariantMatcher.State.NO_MATCH
135
-
136
- def evaluate(self, resp_bytes: bytes) -> None:
137
- """Update the matcher with the response to a requst.
138
-
139
- Warning: Use this method EXACTLY once within the loop body of the request loop.
140
- """
141
- self._recent_ident_response = bytes(resp_bytes)
142
-
143
- def is_pending(self) -> bool:
144
- """True iff request loop has not yet been run."""
145
- return self._state == EcuVariantMatcher.State.PENDING
146
-
147
- def has_match(self) -> bool:
148
- """Returns true iff the non-pending matcher found a matching ecu variant.
149
-
150
- Raises a runtime error if the matcher is pending.
151
- """
152
- if self.is_pending():
153
- raise RuntimeError(
154
- "EcuVariantMatcher is pending. Run the request_loop to determine the active ecu variant."
155
- )
156
- return self._state == EcuVariantMatcher.State.MATCH
157
-
158
- def get_active_ecu_variant(self) -> DiagLayer:
159
- """Returns the matched, i.e., active ecu variant if such a variant has been found."""
160
- odxassert(self.has_match())
161
- return self._match
162
-
163
- def _update_cache(self, req_bytes: bytes, resp_bytes: bytes) -> None:
164
- if self.use_cache:
165
- self.req_resp_cache[req_bytes] = resp_bytes
166
-
167
- def _get_ident_response(self) -> bytes:
168
- if not self._recent_ident_response:
169
- raise RuntimeError(
170
- "No response available. Did you forget to call 'evaluate()' in a loop?")
171
- return self._recent_ident_response