pyxcp 0.21.10__cp311-cp311-win_amd64.whl → 0.22.1__cp311-cp311-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 (168) hide show
  1. CMakeLists.txt +117 -0
  2. pyxcp/__init__.py +12 -20
  3. pyxcp/aml/EtasCANMonitoring.a2l +82 -83
  4. pyxcp/aml/XCP_Common.aml +0 -1
  5. pyxcp/aml/XCPonUSB.aml +1 -1
  6. pyxcp/aml/ifdata_CAN.a2l +0 -1
  7. pyxcp/aml/ifdata_Eth.a2l +0 -1
  8. pyxcp/aml/ifdata_Flx.a2l +0 -1
  9. pyxcp/aml/ifdata_SxI.a2l +0 -1
  10. pyxcp/aml/ifdata_USB.a2l +0 -1
  11. pyxcp/asam/types.py +4 -4
  12. pyxcp/asamkeydll.c +0 -1
  13. pyxcp/checksum.py +0 -1
  14. pyxcp/cmdline.py +32 -50
  15. pyxcp/config/__init__.py +1089 -0
  16. pyxcp/config/legacy.py +120 -0
  17. pyxcp/constants.py +12 -13
  18. pyxcp/cpp_ext/__init__.py +8 -0
  19. pyxcp/cpp_ext/bin.hpp +104 -0
  20. pyxcp/cpp_ext/blockmem.hpp +58 -0
  21. pyxcp/cpp_ext/cpp_ext.cp312-win_amd64.pyd +0 -0
  22. pyxcp/cpp_ext/daqlist.hpp +197 -0
  23. pyxcp/cpp_ext/event.hpp +67 -0
  24. pyxcp/cpp_ext/extension_wrapper.cpp +94 -0
  25. pyxcp/cpp_ext/helper.hpp +264 -0
  26. pyxcp/cpp_ext/mcobject.hpp +241 -0
  27. pyxcp/cpp_ext/tsqueue.hpp +46 -0
  28. pyxcp/daq_stim/__init__.py +226 -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.cp312-win_amd64.pyd +0 -0
  34. pyxcp/daq_stim/stim.cpp +13 -0
  35. pyxcp/daq_stim/stim.hpp +604 -0
  36. pyxcp/daq_stim/stim_wrapper.cpp +48 -0
  37. pyxcp/dllif.py +21 -18
  38. pyxcp/errormatrix.py +5 -3
  39. pyxcp/examples/conf_can.toml +4 -2
  40. pyxcp/examples/conf_can_vector.json +9 -9
  41. pyxcp/examples/conf_can_vector.toml +4 -2
  42. pyxcp/examples/conf_eth.toml +5 -2
  43. pyxcp/examples/conf_nixnet.json +18 -18
  44. pyxcp/examples/conf_sxi.json +7 -7
  45. pyxcp/examples/ex_arrow.py +109 -0
  46. pyxcp/examples/ex_mdf.py +124 -0
  47. pyxcp/examples/ex_sqlite.py +128 -0
  48. pyxcp/examples/run_daq.py +146 -0
  49. pyxcp/examples/xcp_policy.py +6 -7
  50. pyxcp/examples/xcp_read_benchmark.py +8 -6
  51. pyxcp/examples/xcp_skel.py +0 -2
  52. pyxcp/examples/xcp_unlock.py +1 -1
  53. pyxcp/examples/xcp_user_supplied_driver.py +1 -2
  54. pyxcp/examples/xcphello.py +6 -3
  55. pyxcp/examples/xcphello_recorder.py +4 -4
  56. pyxcp/master/__init__.py +1 -2
  57. pyxcp/master/errorhandler.py +107 -74
  58. pyxcp/master/master.py +198 -117
  59. pyxcp/py.typed +0 -0
  60. pyxcp/recorder/__init__.py +27 -6
  61. pyxcp/recorder/converter/__init__.py +37 -0
  62. pyxcp/recorder/lz4.c +129 -51
  63. pyxcp/recorder/lz4.h +45 -28
  64. pyxcp/recorder/lz4hc.c +560 -156
  65. pyxcp/recorder/lz4hc.h +1 -1
  66. pyxcp/recorder/mio.hpp +721 -767
  67. pyxcp/recorder/reader.hpp +139 -0
  68. pyxcp/recorder/reco.py +5 -8
  69. pyxcp/recorder/rekorder.cp312-win_amd64.pyd +0 -0
  70. pyxcp/recorder/rekorder.cpp +18 -22
  71. pyxcp/recorder/rekorder.hpp +200 -587
  72. pyxcp/recorder/setup.py +11 -10
  73. pyxcp/recorder/test_reko.py +2 -3
  74. pyxcp/recorder/unfolder.hpp +1249 -0
  75. pyxcp/recorder/wrap.cpp +171 -9
  76. pyxcp/recorder/writer.hpp +302 -0
  77. pyxcp/scripts/pyxcp_probe_can_drivers.py +0 -2
  78. pyxcp/scripts/xcp_fetch_a2l.py +15 -10
  79. pyxcp/scripts/xcp_id_scanner.py +2 -6
  80. pyxcp/scripts/xcp_info.py +98 -63
  81. pyxcp/scripts/xcp_profile.py +27 -0
  82. pyxcp/stim/__init__.py +0 -0
  83. pyxcp/tests/test_asam_types.py +2 -2
  84. pyxcp/tests/test_binpacking.py +184 -0
  85. pyxcp/tests/test_can.py +1132 -38
  86. pyxcp/tests/test_checksum.py +2 -1
  87. pyxcp/tests/test_daq.py +188 -0
  88. pyxcp/tests/test_frame_padding.py +3 -3
  89. pyxcp/tests/test_master.py +42 -31
  90. pyxcp/tests/test_transport.py +12 -12
  91. pyxcp/tests/test_utils.py +2 -5
  92. pyxcp/timing.py +0 -2
  93. pyxcp/transport/__init__.py +9 -9
  94. pyxcp/transport/base.py +142 -124
  95. pyxcp/transport/base_transport.hpp +0 -0
  96. pyxcp/transport/can.py +194 -167
  97. pyxcp/transport/eth.py +80 -82
  98. pyxcp/transport/sxi.py +106 -60
  99. pyxcp/transport/transport_wrapper.cpp +0 -0
  100. pyxcp/transport/usb_transport.py +65 -83
  101. pyxcp/types.py +69 -20
  102. pyxcp/utils.py +47 -16
  103. pyxcp/vector/map.py +1 -3
  104. {pyxcp-0.21.10.dist-info → pyxcp-0.22.1.dist-info}/METADATA +27 -22
  105. pyxcp-0.22.1.dist-info/RECORD +126 -0
  106. {pyxcp-0.21.10.dist-info → pyxcp-0.22.1.dist-info}/WHEEL +1 -1
  107. {pyxcp-0.21.10.dist-info → pyxcp-0.22.1.dist-info}/entry_points.txt +1 -0
  108. pyxcp/config.py +0 -57
  109. pyxcp/cxx/asynchiofactory.hpp +0 -24
  110. pyxcp/cxx/blocking_client.cpp +0 -44
  111. pyxcp/cxx/blocking_socket.cpp +0 -43
  112. pyxcp/cxx/blocking_socket.hpp +0 -558
  113. pyxcp/cxx/concurrent_queue.hpp +0 -60
  114. pyxcp/cxx/eth.hpp +0 -57
  115. pyxcp/cxx/exceptions.hpp +0 -30
  116. pyxcp/cxx/iasyncioservice.hpp +0 -31
  117. pyxcp/cxx/iresource.hpp +0 -17
  118. pyxcp/cxx/isocket.hpp +0 -22
  119. pyxcp/cxx/linux/epoll.cpp +0 -51
  120. pyxcp/cxx/linux/epoll.hpp +0 -87
  121. pyxcp/cxx/linux/lit_tester.cpp +0 -19
  122. pyxcp/cxx/linux/socket.hpp +0 -234
  123. pyxcp/cxx/linux/timeout.hpp +0 -81
  124. pyxcp/cxx/memoryblock.hpp +0 -42
  125. pyxcp/cxx/pool.hpp +0 -81
  126. pyxcp/cxx/poolmgr.cpp +0 -6
  127. pyxcp/cxx/poolmgr.hpp +0 -31
  128. pyxcp/cxx/test_queue.cpp +0 -69
  129. pyxcp/cxx/timestamp.hpp +0 -84
  130. pyxcp/cxx/utils.cpp +0 -38
  131. pyxcp/cxx/utils.hpp +0 -29
  132. pyxcp/cxx/win/iocp.cpp +0 -242
  133. pyxcp/cxx/win/iocp.hpp +0 -42
  134. pyxcp/cxx/win/perhandledata.hpp +0 -24
  135. pyxcp/cxx/win/periodata.hpp +0 -97
  136. pyxcp/cxx/win/socket.hpp +0 -185
  137. pyxcp/cxx/win/timeout.hpp +0 -83
  138. pyxcp/examples/conf_can.json +0 -20
  139. pyxcp/examples/conf_eth.json +0 -8
  140. pyxcp/logger.py +0 -67
  141. pyxcp/tests/test_config.py +0 -62
  142. pyxcp/transport/candriver/__init__.py +0 -2
  143. pyxcp/transport/candriver/pc_canalystii.py +0 -27
  144. pyxcp/transport/candriver/pc_etas.py +0 -25
  145. pyxcp/transport/candriver/pc_gsusb.py +0 -23
  146. pyxcp/transport/candriver/pc_iscan.py +0 -23
  147. pyxcp/transport/candriver/pc_ixxat.py +0 -27
  148. pyxcp/transport/candriver/pc_kvaser.py +0 -39
  149. pyxcp/transport/candriver/pc_neovi.py +0 -31
  150. pyxcp/transport/candriver/pc_nican.py +0 -23
  151. pyxcp/transport/candriver/pc_nixnet.py +0 -23
  152. pyxcp/transport/candriver/pc_pcan.py +0 -25
  153. pyxcp/transport/candriver/pc_seeed.py +0 -28
  154. pyxcp/transport/candriver/pc_serial.py +0 -27
  155. pyxcp/transport/candriver/pc_slcan.py +0 -29
  156. pyxcp/transport/candriver/pc_socketcan.py +0 -23
  157. pyxcp/transport/candriver/pc_systec.py +0 -29
  158. pyxcp/transport/candriver/pc_usb2can.py +0 -30
  159. pyxcp/transport/candriver/pc_vector.py +0 -34
  160. pyxcp/transport/candriver/python_can.py +0 -101
  161. pyxcp/transport/cxx_ext/CMakeLists.txt +0 -51
  162. pyxcp/transport/cxx_ext/setup.py +0 -49
  163. pyxcp/transport/cxx_ext/tests/test_basic_socket.cpp +0 -39
  164. pyxcp/transport/cxx_ext/tests/test_pool.cpp +0 -39
  165. pyxcp/transport/cxx_ext/tests/test_timestamp.cpp +0 -27
  166. pyxcp-0.21.10.dist-info/RECORD +0 -147
  167. rekorder.cp311-win_amd64.pyd +0 -0
  168. {pyxcp-0.21.10.dist-info/licenses → pyxcp-0.22.1.dist-info}/LICENSE +0 -0
pyxcp/transport/base.py CHANGED
@@ -1,23 +1,21 @@
1
1
  #!/usr/bin/env python
2
- # -*- coding: utf-8 -*-
3
2
  import abc
4
3
  import threading
5
- import typing
6
4
  from collections import deque
7
- from datetime import datetime
8
- from enum import IntEnum
9
- from time import perf_counter
10
- from time import sleep
11
- from time import time
5
+ from typing import Any, Dict, Optional, Set, Type
12
6
 
13
7
  import pyxcp.types as types
14
- from ..logger import Logger
8
+
9
+ from ..cpp_ext import Timestamp, TimestampType
15
10
  from ..recorder import XcpLogFileWriter
16
11
  from ..timing import Timing
17
- from ..utils import flatten
18
- from ..utils import hexDump
19
- from ..utils import SHORT_SLEEP
20
- from pyxcp.config import Configuration
12
+ from ..utils import (
13
+ CurrentDatetime,
14
+ flatten,
15
+ hexDump,
16
+ seconds_to_nanoseconds,
17
+ short_sleep,
18
+ )
21
19
 
22
20
 
23
21
  class FrameAcquisitionPolicy:
@@ -34,20 +32,20 @@ class FrameAcquisitionPolicy:
34
32
  ==> care only about DAQ frames.
35
33
  """
36
34
 
37
- def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None):
35
+ def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None):
38
36
  self._frame_types_to_filter_out = filter_out or set()
39
37
 
40
38
  @property
41
- def filtered_out(self) -> typing.Set[types.FrameCategory]:
39
+ def filtered_out(self) -> Set[types.FrameCategory]:
42
40
  return self._frame_types_to_filter_out
43
41
 
44
- def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None:
45
- ...
42
+ def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: ... # noqa: E704
46
43
 
47
44
  def finalize(self) -> None:
48
45
  """
49
46
  Finalize the frame acquisition policy (if required).
50
47
  """
48
+ ...
51
49
 
52
50
 
53
51
  class NoOpPolicy(FrameAcquisitionPolicy):
@@ -62,7 +60,7 @@ class LegacyFrameAcquisitionPolicy(FrameAcquisitionPolicy):
62
60
  Deprecated: Use only for compatibility reasons.
63
61
  """
64
62
 
65
- def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None):
63
+ def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None) -> None:
66
64
  super().__init__(filter_out)
67
65
  self.reqQueue = deque()
68
66
  self.resQueue = deque()
@@ -83,10 +81,12 @@ class LegacyFrameAcquisitionPolicy(FrameAcquisitionPolicy):
83
81
  types.FrameCategory.STIM: self.stimQueue,
84
82
  }
85
83
 
86
- def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None:
84
+ def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None:
87
85
  # print(f"{frame_type.name:8} {counter:6} {timestamp:7.7f} {hexDump(payload)}")
88
86
  if frame_type not in self.filtered_out:
89
- self.QUEUE_MAP.get(frame_type).append((counter, timestamp, payload))
87
+ queue = self.QUEUE_MAP.get(frame_type)
88
+ if queue:
89
+ queue.append((counter, timestamp, payload))
90
90
 
91
91
 
92
92
  class FrameRecorderPolicy(FrameAcquisitionPolicy):
@@ -95,14 +95,14 @@ class FrameRecorderPolicy(FrameAcquisitionPolicy):
95
95
  def __init__(
96
96
  self,
97
97
  file_name: str,
98
- filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None,
98
+ filter_out: Optional[Set[types.FrameCategory]] = None,
99
99
  prealloc: int = 10,
100
100
  chunk_size: int = 1,
101
- ):
101
+ ) -> None:
102
102
  super().__init__(filter_out)
103
103
  self.recorder = XcpLogFileWriter(file_name, prealloc=prealloc, chunk_size=chunk_size)
104
104
 
105
- def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None:
105
+ def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None:
106
106
  if frame_type not in self.filtered_out:
107
107
  self.recorder.add_frame(frame_type, counter, timestamp, payload)
108
108
 
@@ -113,32 +113,18 @@ class FrameRecorderPolicy(FrameAcquisitionPolicy):
113
113
  class StdoutPolicy(FrameAcquisitionPolicy):
114
114
  """Frame acquisition policy that prints frames to stdout."""
115
115
 
116
- def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None):
116
+ def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None) -> None:
117
117
  super().__init__(filter_out)
118
118
 
119
- def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None:
119
+ def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None:
120
120
  if frame_type not in self.filtered_out:
121
- print(f"{frame_type.name:8} {counter:6} {timestamp:7.7f} {hexDump(payload)}")
121
+ print(f"{frame_type.name:8} {counter:6} {timestamp:8u} {hexDump(payload)}")
122
122
 
123
123
 
124
124
  class EmptyFrameError(Exception):
125
125
  """Raised when an empty frame is received."""
126
126
 
127
127
 
128
- def get(q, timeout, restart_event):
129
- """Get an item from a deque considering a timeout condition."""
130
- start = time()
131
- while not q:
132
- if restart_event.is_set():
133
- start = time()
134
- restart_event.clear()
135
- if time() - start > timeout:
136
- raise EmptyFrameError
137
- sleep(SHORT_SLEEP)
138
- item = q.popleft()
139
- return item
140
-
141
-
142
128
  class BaseTransport(metaclass=abc.ABCMeta):
143
129
  """Base class for transport-layers (Can, Eth, Sxi).
144
130
 
@@ -151,79 +137,91 @@ class BaseTransport(metaclass=abc.ABCMeta):
151
137
 
152
138
  """
153
139
 
154
- PARAMETER_MAP = {
155
- # Type Req'd Default
156
- "CREATE_DAQ_TIMESTAMPS": (bool, False, False),
157
- "LOGLEVEL": (str, False, "WARN"),
158
- "TIMEOUT": (float, False, 2.0),
159
- "ALIGNMENT": (int, False, 1),
160
- }
161
-
162
- def __init__(self, config=None, policy: FrameAcquisitionPolicy = None):
140
+ def __init__(self, config, policy: Optional[FrameAcquisitionPolicy] = None, transport_layer_interface: Optional[Any] = None):
141
+ self.has_user_supplied_interface: bool = transport_layer_interface is not None
142
+ self.transport_layer_interface: Optional[Any] = transport_layer_interface
163
143
  self.parent = None
164
- self.config = Configuration(BaseTransport.PARAMETER_MAP or {}, config or {})
165
- self.policy = policy or LegacyFrameAcquisitionPolicy()
166
- self.closeEvent = threading.Event()
167
-
168
- self.command_lock = threading.Lock()
169
- loglevel = self.config.get("LOGLEVEL")
170
- self._debug = loglevel == "DEBUG"
171
-
172
- self.logger = Logger("transport.Base")
173
- self.logger.setLevel(loglevel)
174
- self.counterSend = 0
175
- self.counterReceived = -1
176
- create_daq_timestamps = self.config.get("CREATE_DAQ_TIMESTAMPS")
177
- self.create_daq_timestamps = False if create_daq_timestamps is None else create_daq_timestamps
178
- timeout = self.config.get("TIMEOUT")
179
- self.alignment = self.config.get("ALIGNMENT")
180
- self.timeout = 2.0 if timeout is None else timeout
181
- self.timer_restart_event = threading.Event()
182
- self.timing = Timing()
183
- self.resQueue = deque()
184
- self.listener = threading.Thread(
144
+ self.policy: FrameAcquisitionPolicy = policy or LegacyFrameAcquisitionPolicy()
145
+ self.closeEvent: threading.Event = threading.Event()
146
+
147
+ self.command_lock: threading.Lock = threading.Lock()
148
+ self.policy_lock: threading.Lock = threading.Lock()
149
+
150
+ self.logger: Any = config.log
151
+ self._debug: bool = self.logger.level == 10
152
+ if transport_layer_interface:
153
+ self.logger.info(f"Transport - User Supplied Transport-Layer Interface: '{transport_layer_interface!s}'")
154
+ self.counter_send: int = 0
155
+ self.counter_received: int = -1
156
+ self.create_daq_timestamps: bool = config.create_daq_timestamps
157
+ self.timestamp = Timestamp(TimestampType.ABSOLUTE_TS)
158
+ self._start_datetime: CurrentDatetime = CurrentDatetime(self.timestamp.initial_value)
159
+ self.alignment: int = config.alignment
160
+ self.timeout: int = seconds_to_nanoseconds(config.timeout)
161
+ self.timer_restart_event: threading.Event = threading.Event()
162
+ self.timing: Timing = Timing()
163
+ self.resQueue: deque = deque()
164
+ self.listener: threading.Thread = threading.Thread(
185
165
  target=self.listen,
186
166
  args=(),
187
167
  kwargs={},
188
168
  )
189
169
 
190
- self.first_daq_timestamp = None
191
-
192
- self.timestamp_origin = time()
193
- self.datetime_origin = datetime.fromtimestamp(self.timestamp_origin)
194
-
195
- self.pre_send_timestamp = time()
196
- self.post_send_timestamp = time()
197
- self.recv_timestamp = time()
170
+ self.first_daq_timestamp: Optional[int] = None
171
+ # self.timestamp_origin = self.timestamp.value
172
+ # self.datetime_origin = datetime.fromtimestamp(self.timestamp_origin)
173
+ self.pre_send_timestamp: int = self.timestamp.value
174
+ self.post_send_timestamp: int = self.timestamp.value
175
+ self.recv_timestamp: int = self.timestamp.value
198
176
 
199
- def __del__(self):
200
- self.finishListener()
201
- self.closeConnection()
177
+ def __del__(self) -> None:
178
+ self.finish_listener()
179
+ self.close_connection()
202
180
 
203
- def loadConfig(self, config):
181
+ def load_config(self, config) -> None:
204
182
  """Load configuration data."""
205
- self.config = Configuration(self.PARAMETER_MAP or {}, config or {})
183
+ class_name: str = self.__class__.__name__.lower()
184
+ self.config: Any = getattr(config, class_name)
206
185
 
207
- def close(self):
186
+ def close(self) -> None:
208
187
  """Close the transport-layer connection and event-loop."""
209
- self.finishListener()
188
+ self.finish_listener()
210
189
  if self.listener.is_alive():
211
190
  self.listener.join()
212
- self.closeConnection()
191
+ self.close_connection()
213
192
 
214
193
  @abc.abstractmethod
215
- def connect(self):
194
+ def connect(self) -> None:
216
195
  pass
217
196
 
218
- def startListener(self):
197
+ def get(self):
198
+ """Get an item from a deque considering a timeout condition."""
199
+ start: int = self.timestamp.value
200
+ while not self.resQueue:
201
+ if self.timer_restart_event.is_set():
202
+ start: int = self.timestamp.value
203
+ self.timer_restart_event.clear()
204
+ if self.timestamp.value - start > self.timeout:
205
+ raise EmptyFrameError
206
+ short_sleep()
207
+ item = self.resQueue.popleft()
208
+ # print("Q", item)
209
+ return item
210
+
211
+ @property
212
+ def start_datetime(self) -> int:
213
+ """"""
214
+ return self._start_datetime
215
+
216
+ def start_listener(self):
219
217
  if self.listener.is_alive():
220
- self.finishListener()
218
+ self.finish_listener()
221
219
  self.listener.join()
222
220
 
223
221
  self.listener = threading.Thread(target=self.listen)
224
222
  self.listener.start()
225
223
 
226
- def finishListener(self):
224
+ def finish_listener(self):
227
225
  if hasattr(self, "closeEvent"):
228
226
  self.closeEvent.set()
229
227
 
@@ -231,18 +229,16 @@ class BaseTransport(metaclass=abc.ABCMeta):
231
229
  with self.command_lock:
232
230
  frame = self._prepare_request(cmd, *data)
233
231
  self.timing.start()
234
- self.policy.feed(types.FrameCategory.CMD, self.counterSend, perf_counter(), frame)
232
+ with self.policy_lock:
233
+ self.policy.feed(types.FrameCategory.CMD, self.counter_send, self.timestamp.value, frame)
235
234
  self.send(frame)
236
235
  try:
237
- xcpPDU = get(
238
- self.resQueue,
239
- timeout=self.timeout,
240
- restart_event=self.timer_restart_event,
241
- )
236
+ xcpPDU = self.get()
242
237
  except EmptyFrameError:
243
238
  if not ignore_timeout:
244
239
  MSG = f"Response timed out (timeout={self.timeout}s)"
245
- self.policy.feed(types.FrameCategory.METADATA, self.counterSend, perf_counter(), bytes(MSG, "ascii"))
240
+ with self.policy_lock:
241
+ self.policy.feed(types.FrameCategory.METADATA, self.counter_send, self.timestamp.value, bytes(MSG, "ascii"))
246
242
  raise types.XcpTimeoutError(MSG) from None
247
243
  else:
248
244
  self.timing.stop()
@@ -250,10 +246,10 @@ class BaseTransport(metaclass=abc.ABCMeta):
250
246
  self.timing.stop()
251
247
  pid = types.Response.parse(xcpPDU).type
252
248
  if pid == "ERR" and cmd.name != "SYNCH":
253
- self.policy.feed(types.FrameCategory.ERROR, self.counterReceived, perf_counter(), xcpPDU[1:])
249
+ with self.policy_lock:
250
+ self.policy.feed(types.FrameCategory.ERROR, self.counter_received, self.timestamp.value, xcpPDU[1:])
254
251
  err = types.XcpError.parse(xcpPDU[1:])
255
252
  raise types.XcpResponseError(err)
256
-
257
253
  return xcpPDU[1:]
258
254
 
259
255
  def request(self, cmd, *data):
@@ -277,9 +273,18 @@ class BaseTransport(metaclass=abc.ABCMeta):
277
273
  if pid == "ERR" and cmd.name != "SYNCH":
278
274
  err = types.XcpError.parse(xcpPDU[1:])
279
275
  raise types.XcpResponseError(err)
280
-
281
- frame = self._prepare_request(cmd, *data)
282
- self.send(frame)
276
+ with self.command_lock:
277
+ if isinstance(*data, list):
278
+ data = data[0] # C++ interfacing.
279
+ frame = self._prepare_request(cmd, *data)
280
+ with self.policy_lock:
281
+ self.policy.feed(
282
+ types.FrameCategory.CMD if int(cmd) >= 0xC0 else types.FrameCategory.STIM,
283
+ self.counter_send,
284
+ self.timestamp.value,
285
+ frame,
286
+ )
287
+ self.send(frame)
283
288
 
284
289
  def _prepare_request(self, cmd, *data):
285
290
  """
@@ -289,11 +294,11 @@ class BaseTransport(metaclass=abc.ABCMeta):
289
294
  self.logger.debug(cmd.name)
290
295
  self.parent._setService(cmd)
291
296
 
292
- cmdlen = cmd.bit_length() // 8 # calculate bytes needed for cmd
293
- packet = bytes(flatten(cmd.to_bytes(cmdlen, "big"), data))
297
+ cmd_len = cmd.bit_length() // 8 # calculate bytes needed for cmd
298
+ packet = bytes(flatten(cmd.to_bytes(cmd_len, "big"), data))
294
299
 
295
- header = self.HEADER.pack(len(packet), self.counterSend)
296
- self.counterSend = (self.counterSend + 1) & 0xFFFF
300
+ header = self.HEADER.pack(len(packet), self.counter_send)
301
+ self.counter_send = (self.counter_send + 1) & 0xFFFF
297
302
 
298
303
  frame = header + packet
299
304
 
@@ -325,15 +330,15 @@ class BaseTransport(metaclass=abc.ABCMeta):
325
330
  :class:`pyxcp.types.XcpTimeoutError`
326
331
  """
327
332
  block_response = b""
328
- start = time()
333
+ start = self.timestamp.value
329
334
  while len(block_response) < length_required:
330
335
  if len(self.resQueue):
331
336
  partial_response = self.resQueue.popleft()
332
337
  block_response += partial_response[1:]
333
338
  else:
334
- if time() - start > self.timeout:
339
+ if self.timestamp.value - start > self.timeout:
335
340
  raise types.XcpTimeoutError("Response timed out [block_receive].") from None
336
- sleep(SHORT_SLEEP)
341
+ short_sleep()
337
342
  return block_response
338
343
 
339
344
  @abc.abstractmethod
@@ -341,7 +346,7 @@ class BaseTransport(metaclass=abc.ABCMeta):
341
346
  pass
342
347
 
343
348
  @abc.abstractmethod
344
- def closeConnection(self):
349
+ def close_connection(self):
345
350
  """Does the actual connection shutdown.
346
351
  Needs to be implemented by any sub-class.
347
352
  """
@@ -358,26 +363,29 @@ class BaseTransport(metaclass=abc.ABCMeta):
358
363
  if ev_type == types.Event.EV_CMD_PENDING:
359
364
  self.timer_restart_event.set()
360
365
 
361
- def processResponse(self, response, length, counter, recv_timestamp=None):
362
- if counter == self.counterReceived:
363
- self.logger.warn(f"Duplicate message counter {counter} received from the XCP slave")
366
+ def process_response(self, response: bytes, length: int, counter: int, recv_timestamp: int) -> None:
367
+ if counter == self.counter_received:
368
+ self.logger.warning(f"Duplicate message counter {counter} received from the XCP slave")
364
369
  if self._debug:
365
370
  self.logger.debug(f"<- L{length} C{counter} {hexDump(response[:512])}")
366
371
  return
367
- self.counterReceived = counter
372
+ self.counter_received = counter
368
373
  pid = response[0]
369
374
  if pid >= 0xFC:
370
375
  if self._debug:
371
376
  self.logger.debug(f"<- L{length} C{counter} {hexDump(response)}")
372
377
  if pid >= 0xFE:
373
378
  self.resQueue.append(response)
374
- self.policy.feed(types.FrameCategory.RESPONSE, self.counterReceived, perf_counter(), response)
379
+ with self.policy_lock:
380
+ self.policy.feed(types.FrameCategory.RESPONSE, self.counter_received, self.timestamp.value, response)
375
381
  self.recv_timestamp = recv_timestamp
376
382
  elif pid == 0xFD:
377
383
  self.process_event_packet(response)
378
- self.policy.feed(types.FrameCategory.EVENT, self.counterReceived, perf_counter(), response)
384
+ with self.policy_lock:
385
+ self.policy.feed(types.FrameCategory.EVENT, self.counter_received, self.timestamp.value, response)
379
386
  elif pid == 0xFC:
380
- self.policy.feed(types.FrameCategory.SERV, self.counterReceived, perf_counter(), response)
387
+ with self.policy_lock:
388
+ self.policy.feed(types.FrameCategory.SERV, self.counter_received, self.timestamp.value, response)
381
389
  else:
382
390
  if self._debug:
383
391
  self.logger.debug(f"<- L{length} C{counter} ODT_Data[0:8] {hexDump(response[:8])}")
@@ -386,11 +394,21 @@ class BaseTransport(metaclass=abc.ABCMeta):
386
394
  if self.create_daq_timestamps:
387
395
  timestamp = recv_timestamp
388
396
  else:
389
- timestamp = 0.0
390
- self.policy.feed(types.FrameCategory.DAQ, self.counterReceived, timestamp, response)
397
+ timestamp = 0
398
+ with self.policy_lock:
399
+ self.policy.feed(types.FrameCategory.DAQ, self.counter_received, timestamp, response)
400
+
401
+ # @abc.abstractproperty
402
+ # @property
403
+ # def transport_layer_interface(self) -> Any:
404
+ # pass
405
+
406
+ # @transport_layer_interface.setter
407
+ # def transport_layer_interface(self, value: Any) -> None:
408
+ # self._transport_layer_interface = value
391
409
 
392
410
 
393
- def createTransport(name, *args, **kws):
411
+ def create_transport(name: str, *args, **kws) -> BaseTransport:
394
412
  """Factory function for transports.
395
413
 
396
414
  Returns
@@ -398,15 +416,15 @@ def createTransport(name, *args, **kws):
398
416
  :class:`BaseTransport` derived instance.
399
417
  """
400
418
  name = name.lower()
401
- transports = availableTransports()
419
+ transports = available_transports()
402
420
  if name in transports:
403
- transportClass = transports[name]
421
+ transport_class: Type[BaseTransport] = transports[name]
404
422
  else:
405
- raise ValueError(f"'{name}' is an invalid transport -- please choose one of [{' | '.join(transports.keys())}].")
406
- return transportClass(*args, **kws)
423
+ raise ValueError(f"{name!r} is an invalid transport -- please choose one of [{' | '.join(transports.keys())}].")
424
+ return transport_class(*args, **kws)
407
425
 
408
426
 
409
- def availableTransports():
427
+ def available_transports() -> Dict[str, Type[BaseTransport]]:
410
428
  """List all subclasses of :class:`BaseTransport`.
411
429
 
412
430
  Returns
File without changes