pyxcp 0.21.10__cp39-cp39-win_amd64.whl → 0.22.23__cp39-cp39-win_amd64.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.

Potentially problematic release.


This version of pyxcp might be problematic. Click here for more details.

Files changed (173) hide show
  1. pyxcp/__init__.py +12 -20
  2. pyxcp/aml/EtasCANMonitoring.a2l +82 -83
  3. pyxcp/aml/XCP_Common.aml +0 -1
  4. pyxcp/aml/XCPonUSB.aml +1 -1
  5. pyxcp/aml/ifdata_CAN.a2l +0 -1
  6. pyxcp/aml/ifdata_Eth.a2l +0 -1
  7. pyxcp/aml/ifdata_Flx.a2l +0 -1
  8. pyxcp/aml/ifdata_SxI.a2l +0 -1
  9. pyxcp/aml/ifdata_USB.a2l +0 -1
  10. pyxcp/asam/types.py +4 -4
  11. pyxcp/asamkeydll.c +0 -1
  12. pyxcp/checksum.py +0 -1
  13. pyxcp/cmdline.py +32 -50
  14. pyxcp/config/__init__.py +1100 -0
  15. pyxcp/config/legacy.py +120 -0
  16. pyxcp/constants.py +12 -13
  17. pyxcp/cpp_ext/__init__.py +0 -0
  18. pyxcp/cpp_ext/bin.hpp +104 -0
  19. pyxcp/cpp_ext/blockmem.hpp +58 -0
  20. pyxcp/cpp_ext/cpp_ext.cp38-win_amd64.pyd +0 -0
  21. pyxcp/cpp_ext/cpp_ext.cp39-win_amd64.pyd +0 -0
  22. pyxcp/cpp_ext/daqlist.hpp +200 -0
  23. pyxcp/cpp_ext/event.hpp +67 -0
  24. pyxcp/cpp_ext/extension_wrapper.cpp +96 -0
  25. pyxcp/cpp_ext/helper.hpp +280 -0
  26. pyxcp/cpp_ext/mcobject.hpp +246 -0
  27. pyxcp/cpp_ext/tsqueue.hpp +46 -0
  28. pyxcp/daq_stim/__init__.py +228 -0
  29. pyxcp/daq_stim/optimize/__init__.py +67 -0
  30. pyxcp/daq_stim/optimize/binpacking.py +41 -0
  31. pyxcp/daq_stim/scheduler.cpp +28 -0
  32. pyxcp/daq_stim/scheduler.hpp +75 -0
  33. pyxcp/daq_stim/stim.cp38-win_amd64.pyd +0 -0
  34. pyxcp/daq_stim/stim.cp39-win_amd64.pyd +0 -0
  35. pyxcp/daq_stim/stim.cpp +13 -0
  36. pyxcp/daq_stim/stim.hpp +604 -0
  37. pyxcp/daq_stim/stim_wrapper.cpp +48 -0
  38. pyxcp/dllif.py +21 -18
  39. pyxcp/errormatrix.py +5 -3
  40. pyxcp/examples/conf_can.toml +4 -2
  41. pyxcp/examples/conf_can_vector.json +9 -9
  42. pyxcp/examples/conf_can_vector.toml +4 -2
  43. pyxcp/examples/conf_eth.toml +5 -2
  44. pyxcp/examples/conf_nixnet.json +18 -18
  45. pyxcp/examples/conf_sxi.json +7 -7
  46. pyxcp/examples/ex_arrow.py +109 -0
  47. pyxcp/examples/ex_csv.py +85 -0
  48. pyxcp/examples/ex_excel.py +95 -0
  49. pyxcp/examples/ex_mdf.py +124 -0
  50. pyxcp/examples/ex_sqlite.py +128 -0
  51. pyxcp/examples/run_daq.py +148 -0
  52. pyxcp/examples/xcp_policy.py +6 -7
  53. pyxcp/examples/xcp_read_benchmark.py +8 -6
  54. pyxcp/examples/xcp_skel.py +0 -2
  55. pyxcp/examples/xcp_unlock.py +1 -1
  56. pyxcp/examples/xcp_user_supplied_driver.py +1 -2
  57. pyxcp/examples/xcphello.py +6 -3
  58. pyxcp/examples/xcphello_recorder.py +4 -4
  59. pyxcp/master/__init__.py +1 -2
  60. pyxcp/master/errorhandler.py +107 -74
  61. pyxcp/master/master.py +201 -119
  62. pyxcp/py.typed +0 -0
  63. pyxcp/recorder/__init__.py +27 -6
  64. pyxcp/recorder/converter/__init__.py +37 -0
  65. pyxcp/recorder/lz4.c +129 -51
  66. pyxcp/recorder/lz4.h +45 -28
  67. pyxcp/recorder/lz4hc.c +560 -156
  68. pyxcp/recorder/lz4hc.h +1 -1
  69. pyxcp/recorder/mio.hpp +721 -767
  70. pyxcp/recorder/reader.hpp +139 -0
  71. pyxcp/recorder/reco.py +5 -8
  72. pyxcp/recorder/rekorder.cp38-win_amd64.pyd +0 -0
  73. pyxcp/recorder/rekorder.cp39-win_amd64.pyd +0 -0
  74. pyxcp/recorder/rekorder.cpp +18 -22
  75. pyxcp/recorder/rekorder.hpp +200 -587
  76. pyxcp/recorder/setup.py +11 -10
  77. pyxcp/recorder/test_reko.py +2 -3
  78. pyxcp/recorder/unfolder.hpp +1332 -0
  79. pyxcp/recorder/wrap.cpp +171 -9
  80. pyxcp/recorder/writer.hpp +302 -0
  81. pyxcp/scripts/pyxcp_probe_can_drivers.py +0 -2
  82. pyxcp/scripts/xcp_examples.py +64 -0
  83. pyxcp/scripts/xcp_fetch_a2l.py +15 -10
  84. pyxcp/scripts/xcp_id_scanner.py +2 -6
  85. pyxcp/scripts/xcp_info.py +101 -63
  86. pyxcp/scripts/xcp_profile.py +27 -0
  87. pyxcp/stim/__init__.py +0 -0
  88. pyxcp/tests/test_asam_types.py +2 -2
  89. pyxcp/tests/test_binpacking.py +186 -0
  90. pyxcp/tests/test_can.py +1132 -38
  91. pyxcp/tests/test_checksum.py +2 -1
  92. pyxcp/tests/test_daq.py +193 -0
  93. pyxcp/tests/test_frame_padding.py +6 -3
  94. pyxcp/tests/test_master.py +42 -31
  95. pyxcp/tests/test_transport.py +12 -12
  96. pyxcp/tests/test_utils.py +2 -5
  97. pyxcp/timing.py +0 -2
  98. pyxcp/transport/__init__.py +9 -9
  99. pyxcp/transport/base.py +149 -127
  100. pyxcp/transport/base_transport.hpp +0 -0
  101. pyxcp/transport/can.py +194 -167
  102. pyxcp/transport/eth.py +80 -82
  103. pyxcp/transport/sxi.py +106 -60
  104. pyxcp/transport/transport_wrapper.cpp +0 -0
  105. pyxcp/transport/usb_transport.py +65 -83
  106. pyxcp/types.py +69 -20
  107. pyxcp/utils.py +47 -16
  108. pyxcp/vector/map.py +1 -3
  109. {pyxcp-0.21.10.dist-info → pyxcp-0.22.23.dist-info}/METADATA +28 -23
  110. pyxcp-0.22.23.dist-info/RECORD +131 -0
  111. {pyxcp-0.21.10.dist-info → pyxcp-0.22.23.dist-info}/WHEEL +1 -1
  112. {pyxcp-0.21.10.dist-info → pyxcp-0.22.23.dist-info}/entry_points.txt +2 -0
  113. pyxcp/config.py +0 -57
  114. pyxcp/cxx/asynchiofactory.hpp +0 -24
  115. pyxcp/cxx/blocking_client.cpp +0 -44
  116. pyxcp/cxx/blocking_socket.cpp +0 -43
  117. pyxcp/cxx/blocking_socket.hpp +0 -558
  118. pyxcp/cxx/concurrent_queue.hpp +0 -60
  119. pyxcp/cxx/eth.hpp +0 -57
  120. pyxcp/cxx/exceptions.hpp +0 -30
  121. pyxcp/cxx/iasyncioservice.hpp +0 -31
  122. pyxcp/cxx/iresource.hpp +0 -17
  123. pyxcp/cxx/isocket.hpp +0 -22
  124. pyxcp/cxx/linux/epoll.cpp +0 -51
  125. pyxcp/cxx/linux/epoll.hpp +0 -87
  126. pyxcp/cxx/linux/lit_tester.cpp +0 -19
  127. pyxcp/cxx/linux/socket.hpp +0 -234
  128. pyxcp/cxx/linux/timeout.hpp +0 -81
  129. pyxcp/cxx/memoryblock.hpp +0 -42
  130. pyxcp/cxx/pool.hpp +0 -81
  131. pyxcp/cxx/poolmgr.cpp +0 -6
  132. pyxcp/cxx/poolmgr.hpp +0 -31
  133. pyxcp/cxx/test_queue.cpp +0 -69
  134. pyxcp/cxx/timestamp.hpp +0 -84
  135. pyxcp/cxx/utils.cpp +0 -38
  136. pyxcp/cxx/utils.hpp +0 -29
  137. pyxcp/cxx/win/iocp.cpp +0 -242
  138. pyxcp/cxx/win/iocp.hpp +0 -42
  139. pyxcp/cxx/win/perhandledata.hpp +0 -24
  140. pyxcp/cxx/win/periodata.hpp +0 -97
  141. pyxcp/cxx/win/socket.hpp +0 -185
  142. pyxcp/cxx/win/timeout.hpp +0 -83
  143. pyxcp/examples/conf_can.json +0 -20
  144. pyxcp/examples/conf_eth.json +0 -8
  145. pyxcp/logger.py +0 -67
  146. pyxcp/tests/test_config.py +0 -62
  147. pyxcp/transport/candriver/__init__.py +0 -2
  148. pyxcp/transport/candriver/pc_canalystii.py +0 -27
  149. pyxcp/transport/candriver/pc_etas.py +0 -25
  150. pyxcp/transport/candriver/pc_gsusb.py +0 -23
  151. pyxcp/transport/candriver/pc_iscan.py +0 -23
  152. pyxcp/transport/candriver/pc_ixxat.py +0 -27
  153. pyxcp/transport/candriver/pc_kvaser.py +0 -39
  154. pyxcp/transport/candriver/pc_neovi.py +0 -31
  155. pyxcp/transport/candriver/pc_nican.py +0 -23
  156. pyxcp/transport/candriver/pc_nixnet.py +0 -23
  157. pyxcp/transport/candriver/pc_pcan.py +0 -25
  158. pyxcp/transport/candriver/pc_seeed.py +0 -28
  159. pyxcp/transport/candriver/pc_serial.py +0 -27
  160. pyxcp/transport/candriver/pc_slcan.py +0 -29
  161. pyxcp/transport/candriver/pc_socketcan.py +0 -23
  162. pyxcp/transport/candriver/pc_systec.py +0 -29
  163. pyxcp/transport/candriver/pc_usb2can.py +0 -30
  164. pyxcp/transport/candriver/pc_vector.py +0 -34
  165. pyxcp/transport/candriver/python_can.py +0 -101
  166. pyxcp/transport/cxx_ext/CMakeLists.txt +0 -51
  167. pyxcp/transport/cxx_ext/setup.py +0 -49
  168. pyxcp/transport/cxx_ext/tests/test_basic_socket.cpp +0 -39
  169. pyxcp/transport/cxx_ext/tests/test_pool.cpp +0 -39
  170. pyxcp/transport/cxx_ext/tests/test_timestamp.cpp +0 -27
  171. pyxcp-0.21.10.dist-info/RECORD +0 -147
  172. rekorder.cp39-win_amd64.pyd +0 -0
  173. {pyxcp-0.21.10.dist-info/licenses → pyxcp-0.22.23.dist-info}/LICENSE +0 -0
pyxcp/transport/can.py CHANGED
@@ -1,18 +1,24 @@
1
1
  #!/usr/bin/env python
2
- # -*- coding: utf-8 -*-
3
2
  """
4
3
  """
5
- import abc
4
+
6
5
  import functools
7
6
  import operator
8
7
  from bisect import bisect_left
9
- from collections import OrderedDict
10
- from time import perf_counter
11
- from time import time
8
+ from typing import Any, Dict, Optional, Type
9
+
10
+ from can import CanError, CanInitializationError, Message, detect_available_configs
11
+ from can.bus import BusABC
12
+ from can.interface import _get_class_for_interface
13
+ from rich.console import Console
12
14
 
13
- from pyxcp.config import Configuration
15
+ from pyxcp.config import CAN_INTERFACE_MAP
14
16
  from pyxcp.transport.base import BaseTransport
15
17
 
18
+ from ..utils import seconds_to_nanoseconds
19
+
20
+
21
+ console = Console()
16
22
 
17
23
  CAN_EXTENDED_ID = 0x80000000
18
24
  MAX_11_BIT_IDENTIFIER = (1 << 11) - 1
@@ -27,7 +33,7 @@ class IdentifierOutOfRangeError(Exception):
27
33
  pass
28
34
 
29
35
 
30
- def isExtendedIdentifier(identifier: int) -> bool:
36
+ def is_extended_identifier(identifier: int) -> bool:
31
37
  """Check for extendend CAN identifier.
32
38
 
33
39
  Parameters
@@ -75,8 +81,8 @@ def samplePointToTsegs(tqs: int, samplePoint: float) -> tuple:
75
81
  return (tseg1, tseg2)
76
82
 
77
83
 
78
- def padFrame(frame: bytes, padding_value: int, padding_len: int = 0) -> bytes:
79
- """Pad frame to next discrete DLC value.
84
+ def pad_frame(frame: bytes, pad_frame: bool, padding_value: int) -> bytes:
85
+ """Pad frame to next discrete DLC value (CAN-FD) or on request (CAN-Classic).
80
86
 
81
87
  References:
82
88
  -----------
@@ -85,9 +91,9 @@ def padFrame(frame: bytes, padding_value: int, padding_len: int = 0) -> bytes:
85
91
  AUTOSAR CP Release 4.3.0, Specification of CAN Driver; [SWS_CAN_00502], [ECUC_Can_00485]
86
92
  AUTOSAR CP Release 4.3.0, Requirements on CAN; [SRS_Can_01073], [SRS_Can_01086], [SRS_Can_01160]
87
93
  """
88
- frame_len = max(len(frame), padding_len)
94
+ frame_len = len(frame)
89
95
  if frame_len <= MAX_DLC_CLASSIC:
90
- actual_len = MAX_DLC_CLASSIC
96
+ actual_len = MAX_DLC_CLASSIC if pad_frame else frame_len
91
97
  else:
92
98
  actual_len = CAN_FD_DLCS[bisect_left(CAN_FD_DLCS, frame_len)]
93
99
  # append fill bytes up to MAX_DLC resp. next discrete FD DLC.
@@ -112,13 +118,13 @@ class Identifier:
112
118
  def __init__(self, raw_id: int):
113
119
  self._raw_id = raw_id
114
120
  self._id = stripIdentifier(raw_id)
115
- self._is_extended = isExtendedIdentifier(raw_id)
121
+ self._is_extended = is_extended_identifier(raw_id)
116
122
  if self._is_extended:
117
123
  if self._id > MAX_29_BIT_IDENTIFIER:
118
- raise IdentifierOutOfRangeError("29-bit identifier '{}' is out of range".format(self._id))
124
+ raise IdentifierOutOfRangeError(f"29-bit identifier {self._id!r} is out of range")
119
125
  else:
120
126
  if self._id > MAX_11_BIT_IDENTIFIER:
121
- raise IdentifierOutOfRangeError("11-bit identifier '{}' is out of range".format(self._id))
127
+ raise IdentifierOutOfRangeError(f"11-bit identifier {self._id!r} is out of range")
122
128
 
123
129
  @property
124
130
  def id(self) -> int:
@@ -151,8 +157,20 @@ class Identifier:
151
157
  """
152
158
  return self._is_extended
153
159
 
160
+ @property
161
+ def type_str(self) -> str:
162
+ """
163
+
164
+ Returns
165
+ -------
166
+ str
167
+ - "S" - 11-bit identifier.
168
+ - "E" - 29-bit identifier.
169
+ """
170
+ return "E" if self.is_extended else "S"
171
+
154
172
  @staticmethod
155
- def make_identifier(identifier: int, extended: bool) -> int:
173
+ def make_identifier(identifier: int, extended: bool) -> "Identifier":
156
174
  """Factory method.
157
175
 
158
176
  Parameters
@@ -174,81 +192,107 @@ class Identifier:
174
192
  """
175
193
  return Identifier(identifier if not extended else (identifier | CAN_EXTENDED_ID))
176
194
 
177
- def __eq__(self, other):
195
+ def create_filter_from_id(self) -> Dict:
196
+ """Create a single CAN filter entry.
197
+ s. https://python-can.readthedocs.io/en/stable/bus.html#filtering
198
+ """
199
+ return {
200
+ "can_id": self.id,
201
+ "can_mask": MAX_29_BIT_IDENTIFIER if self.is_extended else MAX_11_BIT_IDENTIFIER,
202
+ "extended": self.is_extended,
203
+ }
204
+
205
+ def __eq__(self, other) -> bool:
178
206
  return (self.id == other.id) and (self.is_extended == other.is_extended)
179
207
 
180
- def __str__(self):
181
- return "Identifier(id = 0x{:08x}, is_extended = {})".format(self.id, self.is_extended)
208
+ def __str__(self) -> str:
209
+ return f"Identifier(id = 0x{self.id:08x}, is_extended = {self.is_extended})"
182
210
 
183
- def __repr__(self):
184
- return "Identifier(0x{:08x})".format(self.raw_id)
211
+ def __repr__(self) -> str:
212
+ return f"Identifier(0x{self.raw_id:08x})"
185
213
 
186
214
 
187
215
  class Frame:
188
216
  """"""
189
217
 
190
- def __init__(self, id_: Identifier, dlc: int, data: bytes, timestamp: int):
191
- self.id = id_
192
- self.dlc = dlc
193
- self.data = data
194
- self.timestamp = timestamp
218
+ def __init__(self, id_: Identifier, dlc: int, data: bytes, timestamp: int) -> None:
219
+ self.id: Identifier = id_
220
+ self.dlc: int = dlc
221
+ self.data: bytes = data
222
+ self.timestamp: int = timestamp
195
223
 
196
- def __repr__(self):
197
- return "Frame(id = 0x{:08x}, dlc = {}, data = {}, timestamp = {})".format(self.id, self.dlc, self.data, self.timestamp)
224
+ def __repr__(self) -> str:
225
+ return f"Frame(id = 0x{self.id:08x}, dlc = {self.dlc}, data = {self.data}, timestamp = {self.timestamp})"
198
226
 
199
227
  __str__ = __repr__
200
228
 
201
229
 
202
- class CanInterfaceBase(metaclass=abc.ABCMeta):
203
- """
204
- Abstract CAN interface handler that can be implemented for any actual CAN device driver
205
- """
206
-
207
- PARAMETER_MAP = {}
208
-
209
- @abc.abstractmethod
210
- def init(self, parent, receive_callback):
211
- """
212
- Must implement any required action for initing the can interface
213
-
214
- Parameters
215
- ----------
216
- parent: :class:`Can`
217
- Refers to owner.
218
- receive_callback: callable
219
- Receive callback function to register with the following argument: payload: bytes
220
- """
221
-
222
- @abc.abstractmethod
223
- def transmit(self, payload: bytes):
224
- """
225
- Must transmit the given payload on the master can id.
226
-
227
- Parameters
228
- ----------
229
- payload: bytes
230
- payload to transmit
231
- """
230
+ class PythonCanWrapper:
231
+ """Wrapper around python-can - github.com/hardbyte/python-can"""
232
232
 
233
- @abc.abstractmethod
234
- def close(self):
235
- """Must implement any required action for disconnecting from the can interface"""
233
+ def __init__(self, parent, interface_name: str, timeout: int, **parameters) -> None:
234
+ self.parent = parent
235
+ self.interface_name: str = interface_name
236
+ self.timeout: int = timeout
237
+ self.parameters = parameters
238
+ self.can_interface_class: Type[BusABC] = _get_class_for_interface(self.interface_name)
239
+ self.can_interface: BusABC
240
+ self.connected: bool = False
236
241
 
237
- @abc.abstractmethod
238
242
  def connect(self):
239
- """Open connection to can interface"""
243
+ if self.connected:
244
+ return
245
+ can_filters = []
246
+ can_filters.append(self.parent.can_id_slave.create_filter_from_id()) # Primary CAN filter.
247
+ if self.parent.has_user_supplied_interface:
248
+ self.can_interface = self.parent.transport_layer_interface
249
+ else:
250
+ self.can_interface = self.can_interface_class(interface=self.interface_name, **self.parameters)
251
+ if self.parent.daq_identifier:
252
+ # Add filters for DAQ identifiers.
253
+ for daq_id in self.parent.daq_identifier:
254
+ can_filters.append(daq_id.create_filter_from_id())
255
+ self.can_interface.set_filters(can_filters)
256
+ self.parent.logger.info(f"XCPonCAN - Using Interface: '{self.can_interface!s}'")
257
+ self.parent.logger.info(f"XCPonCAN - Filters used: {self.can_interface.filters}")
258
+ self.parent.logger.info(f"XCPonCAN - State: {self.can_interface.state!s}")
259
+ self.connected = True
240
260
 
241
- @abc.abstractmethod
242
- def read(self):
243
- """Read incoming data"""
261
+ def close(self):
262
+ if self.connected and not self.parent.has_user_supplied_interface:
263
+ self.can_interface.shutdown()
264
+ self.connected = False
265
+
266
+ def transmit(self, payload: bytes) -> None:
267
+ frame = Message(
268
+ arbitration_id=self.parent.can_id_master.id,
269
+ is_extended_id=True if self.parent.can_id_master.is_extended else False,
270
+ is_fd=self.parent.fd,
271
+ data=payload,
272
+ )
273
+ self.can_interface.send(frame)
244
274
 
245
- @abc.abstractmethod
246
- def getTimestampResolution(self):
247
- """Get timestamp resolution in nano seconds."""
275
+ def read(self) -> Optional[Frame]:
276
+ if not self.connected:
277
+ return None
278
+ try:
279
+ frame = self.can_interface.recv(self.timeout)
280
+ except CanError:
281
+ return None
282
+ else:
283
+ if frame is None or not len(frame.data):
284
+ return None # Timeout condition.
285
+ extended = frame.is_extended_id
286
+ identifier = Identifier.make_identifier(frame.arbitration_id, extended)
287
+ return Frame(
288
+ id_=identifier,
289
+ dlc=frame.dlc,
290
+ data=frame.data,
291
+ timestamp=seconds_to_nanoseconds(frame.timestamp),
292
+ )
248
293
 
249
- def loadConfig(self, config):
250
- """Load configuration data."""
251
- self.config = Configuration(self.PARAMETER_MAP or {}, config or {})
294
+ def get_timestamp_resolution(self) -> int:
295
+ return 10 * 1000
252
296
 
253
297
 
254
298
  class EmptyHeader:
@@ -258,106 +302,114 @@ class EmptyHeader:
258
302
  return b""
259
303
 
260
304
 
261
- # can.detect_available_configs()
262
-
263
-
264
305
  class Can(BaseTransport):
265
306
  """"""
266
307
 
267
- PARAMETER_MAP = {
268
- # Type Req'd Default
269
- "CAN_DRIVER": (str, True, None),
270
- "CHANNEL": (str, False, ""),
271
- "MAX_DLC_REQUIRED": (bool, False, False),
272
- "MAX_CAN_FD_DLC": (int, False, 64),
273
- "PADDING_VALUE": (int, False, 0),
274
- "CAN_USE_DEFAULT_LISTENER": (bool, False, True),
275
- # defaults to True, in this case the default listener thread is used.
276
- # If the canInterface implements a listener service, this parameter
277
- # can be set to False, and the default listener thread won't be started.
278
- "CAN_ID_MASTER": (int, True, None),
279
- "CAN_ID_SLAVE": (int, True, None),
280
- "CAN_ID_BROADCAST": (int, False, None),
281
- "BITRATE": (int, False, 250000),
282
- "RECEIVE_OWN_MESSAGES": (bool, False, False),
283
- }
284
-
285
- PARAMETER_TO_KW_ARG_MAP = {
286
- "RECEIVE_OWN_MESSAGES": "receive_own_messages",
287
- "CHANNEL": "channel",
288
- "BITRATE": "bitrate",
289
- }
290
-
291
308
  MAX_DATAGRAM_SIZE = 7
292
309
  HEADER = EmptyHeader()
293
310
  HEADER_SIZE = 0
294
311
 
295
- def __init__(self, config=None, policy=None):
296
- """init for CAN transport
297
- :param config: configuration
298
- """
299
- super().__init__(config, policy)
300
- self.loadConfig(config)
301
- drivers = registered_drivers()
302
- interfaceName = self.config.get("CAN_DRIVER")
303
- if interfaceName not in drivers:
304
- raise ValueError("{} is an invalid driver name -- choose from {}".format(interfaceName, [x for x in drivers.keys()]))
305
- canInterfaceClass = drivers[interfaceName]
306
- self.canInterface = canInterfaceClass()
307
- self.useDefaultListener = self.config.get("CAN_USE_DEFAULT_LISTENER")
308
- self.can_id_master = Identifier(self.config.get("CAN_ID_MASTER"))
309
- self.can_id_slave = Identifier(self.config.get("CAN_ID_SLAVE"))
310
- self.canInterface.loadConfig(config)
311
- self.canInterface.init(self, self.dataReceived)
312
- #
312
+ def __init__(self, config, policy=None, transport_layer_interface: Optional[BusABC] = None):
313
+ super().__init__(config, policy, transport_layer_interface)
314
+ self.load_config(config)
315
+ self.useDefaultListener = self.config.use_default_listener
316
+ self.can_id_master = Identifier(self.config.can_id_master)
317
+ self.can_id_slave = Identifier(self.config.can_id_slave)
318
+
313
319
  # Regarding CAN-FD s. AUTOSAR CP Release 4.3.0, Requirements on CAN; [SRS_Can_01160] Padding of bytes due to discrete CAN FD DLC]:
314
320
  # "... If a PDU does not exactly match these configurable sizes the unused bytes shall be padded."
315
321
  #
316
- self.max_dlc_required = self.config.get("MAX_DLC_REQUIRED") or self.canInterface.is_fd
317
- self.padding_value = self.config.get("PADDING_VALUE")
318
- self.padding_len = self.config.get("MAX_CAN_FD_DLC") if self.canInterface.is_fd else MAX_DLC_CLASSIC
322
+ self.fd = self.config.fd
323
+ self.daq_identifier = []
324
+ if self.config.daq_identifier:
325
+ for daq_id in self.config.daq_identifier:
326
+ self.daq_identifier.append(Identifier(daq_id))
327
+ self.max_dlc_required = self.config.max_dlc_required
328
+ self.padding_value = self.config.padding_value
329
+ self.interface_name = self.config.interface
330
+ self.interface_configuration = detect_available_configs(interfaces=[self.interface_name])
331
+ parameters = self.get_interface_parameters()
332
+ self.can_interface = PythonCanWrapper(self, self.interface_name, config.timeout, **parameters)
333
+ self.logger.info(f"XCPonCAN - Interface-Type: {self.interface_name!r} Parameters: {list(parameters.items())}")
334
+ self.logger.info(
335
+ f"XCPonCAN - Master-ID (Tx): 0x{self.can_id_master.id:08X}{self.can_id_master.type_str} -- "
336
+ f"Slave-ID (Rx): 0x{self.can_id_slave.id:08X}{self.can_id_slave.type_str}"
337
+ )
319
338
 
320
- def dataReceived(self, payload: bytes, recv_timestamp: float = None):
321
- self.processResponse(
339
+ def get_interface_parameters(self) -> Dict[str, Any]:
340
+ result = dict(channel=self.config.channel)
341
+
342
+ can_interface_config_class = CAN_INTERFACE_MAP[self.interface_name]
343
+
344
+ # Optional base class parameters.
345
+ optional_parameters = [(p, p.removeprefix("has_")) for p in can_interface_config_class.OPTIONAL_BASE_PARAMS]
346
+ for o, n in optional_parameters:
347
+ opt = getattr(can_interface_config_class, o)
348
+ value = getattr(self.config, n)
349
+ if opt:
350
+ if value is not None:
351
+ result[n] = value
352
+ elif value is not None:
353
+ self.logger.warning(f"XCPonCAN - {self.interface_name!r} has no support for parameter {n!r}.")
354
+
355
+ # Parameter names that need to be mapped.
356
+ for base_name, name in can_interface_config_class.CAN_PARAM_MAP.items():
357
+ value = getattr(self.config, base_name)
358
+ if value is not None:
359
+ result[name] = value
360
+
361
+ # Interface specific parameters.
362
+ cxx = getattr(self.config, self.interface_name)
363
+ for name in can_interface_config_class.class_own_traits().keys():
364
+ value = getattr(cxx, name)
365
+ if value is not None:
366
+ result[name] = value
367
+ return result
368
+
369
+ def data_received(self, payload: bytes, recv_timestamp: int):
370
+ self.process_response(
322
371
  payload,
323
372
  len(payload),
324
- counter=(self.counterReceived + 1) & 0xffff,
373
+ counter=(self.counter_received + 1) & 0xFFFF,
325
374
  recv_timestamp=recv_timestamp,
326
375
  )
327
376
 
328
377
  def listen(self):
329
378
  while True:
330
- if self.closeEvent.isSet():
379
+ if self.closeEvent.is_set():
331
380
  return
332
- frame = self.canInterface.read()
381
+ frame = self.can_interface.read()
333
382
  if frame:
334
- self.dataReceived(frame.data, frame.timestamp)
383
+ self.data_received(frame.data, frame.timestamp)
335
384
 
336
385
  def connect(self):
337
386
  if self.useDefaultListener:
338
- self.startListener()
339
- self.canInterface.connect()
387
+ self.start_listener()
388
+ try:
389
+ self.can_interface.connect()
390
+ except CanInitializationError:
391
+ console.print("[red]\nThere may be a problem with the configuration of your CAN-interface.\n")
392
+ console.print(f"[grey]Current configuration of interface {self.interface_name!r}:")
393
+ console.print(self.interface_configuration)
394
+ raise
340
395
  self.status = 1 # connected
341
396
 
342
- def send(self, frame):
343
- # XCP on CAN trailer: if required, FILL bytes must be appended
344
- if self.max_dlc_required:
345
- frame = padFrame(frame, self.padding_value, self.padding_len)
397
+ def send(self, frame: bytes) -> None:
346
398
  # send the request
347
- self.pre_send_timestamp = time()
348
- self.canInterface.transmit(payload=frame)
349
- self.post_send_timestamp = time()
399
+ self.pre_send_timestamp = self.timestamp.value
400
+ self.can_interface.transmit(payload=pad_frame(frame, self.max_dlc_required, self.padding_value))
401
+ self.post_send_timestamp = self.timestamp.value
350
402
 
351
- def closeConnection(self):
352
- if hasattr(self, "canInterface"):
353
- self.canInterface.close()
403
+ def close_connection(self):
404
+ if hasattr(self, "can_interface"):
405
+ self.can_interface.close()
354
406
 
355
407
  def close(self):
356
- self.finishListener()
357
- self.closeConnection()
408
+ self.finish_listener()
409
+ self.close_connection()
358
410
 
359
411
 
360
- def setDLC(length: int):
412
+ def set_DLC(length: int):
361
413
  """Return DLC value according to CAN-FD.
362
414
 
363
415
  :param length: Length value to be mapped to a valid CAN-FD DLC.
@@ -376,41 +428,16 @@ def setDLC(length: int):
376
428
  raise ValueError("DLC could be at most 64.")
377
429
 
378
430
 
379
- def calculateFilter(ids: list):
431
+ def calculate_filter(ids: list):
380
432
  """
381
433
  :param ids: An iterable (usually list or tuple) containing CAN identifiers.
382
434
 
383
435
  :return: Calculated filter and mask.
384
436
  :rtype: tuple (int, int)
385
437
  """
386
- any_extended_ids = any(isExtendedIdentifier(i) for i in ids)
438
+ any_extended_ids = any(is_extended_identifier(i) for i in ids)
387
439
  raw_ids = [stripIdentifier(i) for i in ids]
388
440
  cfilter = functools.reduce(operator.and_, raw_ids)
389
441
  cmask = functools.reduce(operator.or_, raw_ids) ^ cfilter
390
442
  cmask ^= 0x1FFFFFFF if any_extended_ids else 0x7FF
391
443
  return (cfilter, cmask)
392
-
393
-
394
- def try_to_install_system_supplied_drivers():
395
- """Register available pyxcp CAN drivers."""
396
- import importlib
397
- import pkgutil
398
- import pyxcp.transport.candriver as cdr
399
-
400
- for _, modname, _ in pkgutil.walk_packages(cdr.__path__, "{}.".format(cdr.__name__)):
401
- try:
402
- importlib.import_module(modname)
403
- except Exception:
404
- pass
405
-
406
-
407
- def registered_drivers():
408
- """
409
- Returns
410
- -------
411
- dict (name, class)
412
- Dictionary containing CAN driver names and classes of all
413
- available drivers (pyxcp supplied and user-defined).
414
- """
415
- sub_classes = CanInterfaceBase.__subclasses__()
416
- return OrderedDict(zip(([c.__name__ for c in sub_classes]), sub_classes))