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/base.py CHANGED
@@ -1,23 +1,21 @@
1
1
  #!/usr/bin/env python
2
- # -*- coding: utf-8 -*-
3
2
  import abc
3
+ import logging
4
4
  import threading
5
- import typing
6
5
  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
6
+ from typing import Any, Dict, Optional, Set, Type
12
7
 
13
8
  import pyxcp.types as types
14
- from ..logger import Logger
15
- from ..recorder import XcpLogFileWriter
16
- 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
9
+ from pyxcp.cpp_ext.cpp_ext import Timestamp, TimestampType
10
+ from pyxcp.recorder import XcpLogFileWriter
11
+ from pyxcp.timing import Timing
12
+ from pyxcp.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,11 @@ 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:
87
- # print(f"{frame_type.name:8} {counter:6} {timestamp:7.7f} {hexDump(payload)}")
84
+ def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None:
88
85
  if frame_type not in self.filtered_out:
89
- self.QUEUE_MAP.get(frame_type).append((counter, timestamp, payload))
86
+ queue = self.QUEUE_MAP.get(frame_type)
87
+ if queue is not None:
88
+ queue.append((counter, timestamp, payload))
90
89
 
91
90
 
92
91
  class FrameRecorderPolicy(FrameAcquisitionPolicy):
@@ -95,14 +94,14 @@ class FrameRecorderPolicy(FrameAcquisitionPolicy):
95
94
  def __init__(
96
95
  self,
97
96
  file_name: str,
98
- filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None,
97
+ filter_out: Optional[Set[types.FrameCategory]] = None,
99
98
  prealloc: int = 10,
100
99
  chunk_size: int = 1,
101
- ):
100
+ ) -> None:
102
101
  super().__init__(filter_out)
103
102
  self.recorder = XcpLogFileWriter(file_name, prealloc=prealloc, chunk_size=chunk_size)
104
103
 
105
- def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None:
104
+ def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None:
106
105
  if frame_type not in self.filtered_out:
107
106
  self.recorder.add_frame(frame_type, counter, timestamp, payload)
108
107
 
@@ -113,32 +112,18 @@ class FrameRecorderPolicy(FrameAcquisitionPolicy):
113
112
  class StdoutPolicy(FrameAcquisitionPolicy):
114
113
  """Frame acquisition policy that prints frames to stdout."""
115
114
 
116
- def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None):
115
+ def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None) -> None:
117
116
  super().__init__(filter_out)
118
117
 
119
- def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None:
118
+ def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None:
120
119
  if frame_type not in self.filtered_out:
121
- print(f"{frame_type.name:8} {counter:6} {timestamp:7.7f} {hexDump(payload)}")
120
+ print(f"{frame_type.name:8} {counter:6} {timestamp:8d} {hexDump(payload)}")
122
121
 
123
122
 
124
123
  class EmptyFrameError(Exception):
125
124
  """Raised when an empty frame is received."""
126
125
 
127
126
 
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
127
  class BaseTransport(metaclass=abc.ABCMeta):
143
128
  """Base class for transport-layers (Can, Eth, Sxi).
144
129
 
@@ -151,79 +136,96 @@ class BaseTransport(metaclass=abc.ABCMeta):
151
136
 
152
137
  """
153
138
 
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):
139
+ def __init__(self, config, policy: Optional[FrameAcquisitionPolicy] = None, transport_layer_interface: Optional[Any] = None):
140
+ self.has_user_supplied_interface: bool = transport_layer_interface is not None
141
+ self.transport_layer_interface: Optional[Any] = transport_layer_interface
163
142
  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(
143
+ self.policy: FrameAcquisitionPolicy = policy or LegacyFrameAcquisitionPolicy()
144
+ self.closeEvent: threading.Event = threading.Event()
145
+
146
+ self.command_lock: threading.Lock = threading.Lock()
147
+ self.policy_lock: threading.Lock = threading.Lock()
148
+
149
+ self.logger = logging.getLogger("PyXCP")
150
+ self._debug: bool = self.logger.level == 10
151
+ if transport_layer_interface:
152
+ self.logger.info(f"Transport - User Supplied Transport-Layer Interface: '{transport_layer_interface!s}'")
153
+ self.counter_send: int = 0
154
+ self.counter_received: int = -1
155
+ self.create_daq_timestamps: bool = config.create_daq_timestamps
156
+ self.timestamp = Timestamp(TimestampType.ABSOLUTE_TS)
157
+ self._start_datetime: CurrentDatetime = CurrentDatetime(self.timestamp.initial_value)
158
+ self.alignment: int = config.alignment
159
+ self.timeout: int = seconds_to_nanoseconds(config.timeout)
160
+ self.timer_restart_event: threading.Event = threading.Event()
161
+ self.timing: Timing = Timing()
162
+ self.resQueue: deque = deque()
163
+ self.listener: threading.Thread = threading.Thread(
185
164
  target=self.listen,
186
165
  args=(),
187
166
  kwargs={},
188
167
  )
189
168
 
190
- self.first_daq_timestamp = None
169
+ self.first_daq_timestamp: Optional[int] = None
170
+ # self.timestamp_origin = self.timestamp.value
171
+ # self.datetime_origin = datetime.fromtimestamp(self.timestamp_origin)
172
+ self.pre_send_timestamp: int = self.timestamp.value
173
+ self.post_send_timestamp: int = self.timestamp.value
174
+ self.recv_timestamp: int = self.timestamp.value
191
175
 
192
- self.timestamp_origin = time()
193
- self.datetime_origin = datetime.fromtimestamp(self.timestamp_origin)
176
+ def __del__(self) -> None:
177
+ self.finish_listener()
178
+ self.close_connection()
194
179
 
195
- self.pre_send_timestamp = time()
196
- self.post_send_timestamp = time()
197
- self.recv_timestamp = time()
198
-
199
- def __del__(self):
200
- self.finishListener()
201
- self.closeConnection()
202
-
203
- def loadConfig(self, config):
180
+ def load_config(self, config) -> None:
204
181
  """Load configuration data."""
205
- self.config = Configuration(self.PARAMETER_MAP or {}, config or {})
182
+ class_name: str = self.__class__.__name__.lower()
183
+ self.config: Any = getattr(config, class_name)
206
184
 
207
- def close(self):
185
+ def close(self) -> None:
208
186
  """Close the transport-layer connection and event-loop."""
209
- self.finishListener()
187
+ self.finish_listener()
210
188
  if self.listener.is_alive():
211
189
  self.listener.join()
212
- self.closeConnection()
190
+ self.close_connection()
213
191
 
214
192
  @abc.abstractmethod
215
- def connect(self):
193
+ def connect(self) -> None:
216
194
  pass
217
195
 
218
- def startListener(self):
196
+ def get(self):
197
+ """Get an item from a deque considering a timeout condition."""
198
+ start: int = self.timestamp.value
199
+ while not self.resQueue:
200
+ if self.timer_restart_event.is_set():
201
+ start: int = self.timestamp.value
202
+ self.timer_restart_event.clear()
203
+ if self.timestamp.value - start > self.timeout:
204
+ raise EmptyFrameError
205
+ short_sleep()
206
+ item = self.resQueue.popleft()
207
+ # print("Q", item)
208
+ return item
209
+
210
+ @property
211
+ def start_datetime(self) -> int:
212
+ """datetime of program start.
213
+
214
+ Returns
215
+ -------
216
+ int
217
+ """
218
+ return self._start_datetime
219
+
220
+ def start_listener(self):
219
221
  if self.listener.is_alive():
220
- self.finishListener()
222
+ self.finish_listener()
221
223
  self.listener.join()
222
224
 
223
225
  self.listener = threading.Thread(target=self.listen)
224
226
  self.listener.start()
225
227
 
226
- def finishListener(self):
228
+ def finish_listener(self):
227
229
  if hasattr(self, "closeEvent"):
228
230
  self.closeEvent.set()
229
231
 
@@ -231,18 +233,16 @@ class BaseTransport(metaclass=abc.ABCMeta):
231
233
  with self.command_lock:
232
234
  frame = self._prepare_request(cmd, *data)
233
235
  self.timing.start()
234
- self.policy.feed(types.FrameCategory.CMD, self.counterSend, perf_counter(), frame)
236
+ with self.policy_lock:
237
+ self.policy.feed(types.FrameCategory.CMD, self.counter_send, self.timestamp.value, frame)
235
238
  self.send(frame)
236
239
  try:
237
- xcpPDU = get(
238
- self.resQueue,
239
- timeout=self.timeout,
240
- restart_event=self.timer_restart_event,
241
- )
240
+ xcpPDU = self.get()
242
241
  except EmptyFrameError:
243
242
  if not ignore_timeout:
244
243
  MSG = f"Response timed out (timeout={self.timeout}s)"
245
- self.policy.feed(types.FrameCategory.METADATA, self.counterSend, perf_counter(), bytes(MSG, "ascii"))
244
+ with self.policy_lock:
245
+ self.policy.feed(types.FrameCategory.METADATA, self.counter_send, self.timestamp.value, bytes(MSG, "ascii"))
246
246
  raise types.XcpTimeoutError(MSG) from None
247
247
  else:
248
248
  self.timing.stop()
@@ -250,10 +250,10 @@ class BaseTransport(metaclass=abc.ABCMeta):
250
250
  self.timing.stop()
251
251
  pid = types.Response.parse(xcpPDU).type
252
252
  if pid == "ERR" and cmd.name != "SYNCH":
253
- self.policy.feed(types.FrameCategory.ERROR, self.counterReceived, perf_counter(), xcpPDU[1:])
253
+ with self.policy_lock:
254
+ self.policy.feed(types.FrameCategory.ERROR, self.counter_received, self.timestamp.value, xcpPDU[1:])
254
255
  err = types.XcpError.parse(xcpPDU[1:])
255
256
  raise types.XcpResponseError(err)
256
-
257
257
  return xcpPDU[1:]
258
258
 
259
259
  def request(self, cmd, *data):
@@ -277,9 +277,18 @@ class BaseTransport(metaclass=abc.ABCMeta):
277
277
  if pid == "ERR" and cmd.name != "SYNCH":
278
278
  err = types.XcpError.parse(xcpPDU[1:])
279
279
  raise types.XcpResponseError(err)
280
-
281
- frame = self._prepare_request(cmd, *data)
282
- self.send(frame)
280
+ with self.command_lock:
281
+ if isinstance(*data, list):
282
+ data = data[0] # C++ interfacing.
283
+ frame = self._prepare_request(cmd, *data)
284
+ with self.policy_lock:
285
+ self.policy.feed(
286
+ types.FrameCategory.CMD if int(cmd) >= 0xC0 else types.FrameCategory.STIM,
287
+ self.counter_send,
288
+ self.timestamp.value,
289
+ frame,
290
+ )
291
+ self.send(frame)
283
292
 
284
293
  def _prepare_request(self, cmd, *data):
285
294
  """
@@ -289,11 +298,11 @@ class BaseTransport(metaclass=abc.ABCMeta):
289
298
  self.logger.debug(cmd.name)
290
299
  self.parent._setService(cmd)
291
300
 
292
- cmdlen = cmd.bit_length() // 8 # calculate bytes needed for cmd
293
- packet = bytes(flatten(cmd.to_bytes(cmdlen, "big"), data))
301
+ cmd_len = cmd.bit_length() // 8 # calculate bytes needed for cmd
302
+ packet = bytes(flatten(cmd.to_bytes(cmd_len, "big"), data))
294
303
 
295
- header = self.HEADER.pack(len(packet), self.counterSend)
296
- self.counterSend = (self.counterSend + 1) & 0xFFFF
304
+ header = self.HEADER.pack(len(packet), self.counter_send)
305
+ self.counter_send = (self.counter_send + 1) & 0xFFFF
297
306
 
298
307
  frame = header + packet
299
308
 
@@ -325,15 +334,15 @@ class BaseTransport(metaclass=abc.ABCMeta):
325
334
  :class:`pyxcp.types.XcpTimeoutError`
326
335
  """
327
336
  block_response = b""
328
- start = time()
337
+ start = self.timestamp.value
329
338
  while len(block_response) < length_required:
330
339
  if len(self.resQueue):
331
340
  partial_response = self.resQueue.popleft()
332
341
  block_response += partial_response[1:]
333
342
  else:
334
- if time() - start > self.timeout:
343
+ if self.timestamp.value - start > self.timeout:
335
344
  raise types.XcpTimeoutError("Response timed out [block_receive].") from None
336
- sleep(SHORT_SLEEP)
345
+ short_sleep()
337
346
  return block_response
338
347
 
339
348
  @abc.abstractmethod
@@ -341,7 +350,7 @@ class BaseTransport(metaclass=abc.ABCMeta):
341
350
  pass
342
351
 
343
352
  @abc.abstractmethod
344
- def closeConnection(self):
353
+ def close_connection(self):
345
354
  """Does the actual connection shutdown.
346
355
  Needs to be implemented by any sub-class.
347
356
  """
@@ -358,26 +367,29 @@ class BaseTransport(metaclass=abc.ABCMeta):
358
367
  if ev_type == types.Event.EV_CMD_PENDING:
359
368
  self.timer_restart_event.set()
360
369
 
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")
370
+ def process_response(self, response: bytes, length: int, counter: int, recv_timestamp: int) -> None:
371
+ if counter == self.counter_received:
372
+ self.logger.warning(f"Duplicate message counter {counter} received from the XCP slave")
364
373
  if self._debug:
365
374
  self.logger.debug(f"<- L{length} C{counter} {hexDump(response[:512])}")
366
375
  return
367
- self.counterReceived = counter
376
+ self.counter_received = counter
368
377
  pid = response[0]
369
378
  if pid >= 0xFC:
370
379
  if self._debug:
371
380
  self.logger.debug(f"<- L{length} C{counter} {hexDump(response)}")
372
381
  if pid >= 0xFE:
373
382
  self.resQueue.append(response)
374
- self.policy.feed(types.FrameCategory.RESPONSE, self.counterReceived, perf_counter(), response)
383
+ with self.policy_lock:
384
+ self.policy.feed(types.FrameCategory.RESPONSE, self.counter_received, self.timestamp.value, response)
375
385
  self.recv_timestamp = recv_timestamp
376
386
  elif pid == 0xFD:
377
387
  self.process_event_packet(response)
378
- self.policy.feed(types.FrameCategory.EVENT, self.counterReceived, perf_counter(), response)
388
+ with self.policy_lock:
389
+ self.policy.feed(types.FrameCategory.EVENT, self.counter_received, self.timestamp.value, response)
379
390
  elif pid == 0xFC:
380
- self.policy.feed(types.FrameCategory.SERV, self.counterReceived, perf_counter(), response)
391
+ with self.policy_lock:
392
+ self.policy.feed(types.FrameCategory.SERV, self.counter_received, self.timestamp.value, response)
381
393
  else:
382
394
  if self._debug:
383
395
  self.logger.debug(f"<- L{length} C{counter} ODT_Data[0:8] {hexDump(response[:8])}")
@@ -386,11 +398,21 @@ class BaseTransport(metaclass=abc.ABCMeta):
386
398
  if self.create_daq_timestamps:
387
399
  timestamp = recv_timestamp
388
400
  else:
389
- timestamp = 0.0
390
- self.policy.feed(types.FrameCategory.DAQ, self.counterReceived, timestamp, response)
401
+ timestamp = 0
402
+ with self.policy_lock:
403
+ self.policy.feed(types.FrameCategory.DAQ, self.counter_received, timestamp, response)
404
+
405
+ # @abc.abstractproperty
406
+ # @property
407
+ # def transport_layer_interface(self) -> Any:
408
+ # pass
409
+
410
+ # @transport_layer_interface.setter
411
+ # def transport_layer_interface(self, value: Any) -> None:
412
+ # self._transport_layer_interface = value
391
413
 
392
414
 
393
- def createTransport(name, *args, **kws):
415
+ def create_transport(name: str, *args, **kws) -> BaseTransport:
394
416
  """Factory function for transports.
395
417
 
396
418
  Returns
@@ -398,15 +420,15 @@ def createTransport(name, *args, **kws):
398
420
  :class:`BaseTransport` derived instance.
399
421
  """
400
422
  name = name.lower()
401
- transports = availableTransports()
423
+ transports = available_transports()
402
424
  if name in transports:
403
- transportClass = transports[name]
425
+ transport_class: Type[BaseTransport] = transports[name]
404
426
  else:
405
- raise ValueError(f"'{name}' is an invalid transport -- please choose one of [{' | '.join(transports.keys())}].")
406
- return transportClass(*args, **kws)
427
+ raise ValueError(f"{name!r} is an invalid transport -- please choose one of [{' | '.join(transports.keys())}].")
428
+ return transport_class(*args, **kws)
407
429
 
408
430
 
409
- def availableTransports():
431
+ def available_transports() -> Dict[str, Type[BaseTransport]]:
410
432
  """List all subclasses of :class:`BaseTransport`.
411
433
 
412
434
  Returns
File without changes