pyxcp 0.21.11__cp312-cp312-win_amd64.whl → 0.22.2__cp312-cp312-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.
- pyxcp/__init__.py +12 -20
- pyxcp/aml/EtasCANMonitoring.a2l +82 -83
- pyxcp/aml/XCP_Common.aml +0 -1
- pyxcp/aml/XCPonUSB.aml +1 -1
- pyxcp/aml/ifdata_CAN.a2l +0 -1
- pyxcp/aml/ifdata_Eth.a2l +0 -1
- pyxcp/aml/ifdata_Flx.a2l +0 -1
- pyxcp/aml/ifdata_SxI.a2l +0 -1
- pyxcp/aml/ifdata_USB.a2l +0 -1
- pyxcp/asam/types.py +4 -4
- pyxcp/asamkeydll.c +0 -1
- pyxcp/checksum.py +0 -1
- pyxcp/cmdline.py +30 -53
- pyxcp/config/__init__.py +1089 -0
- pyxcp/config/legacy.py +120 -0
- pyxcp/constants.py +12 -13
- pyxcp/cpp_ext/__init__.py +8 -0
- pyxcp/cpp_ext/bin.hpp +104 -0
- pyxcp/cpp_ext/blockmem.hpp +58 -0
- pyxcp/cpp_ext/cpp_ext.cp310-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp311-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp312-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp38-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp39-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/daqlist.hpp +197 -0
- pyxcp/cpp_ext/event.hpp +67 -0
- pyxcp/cpp_ext/extension_wrapper.cpp +94 -0
- pyxcp/cpp_ext/helper.hpp +264 -0
- pyxcp/cpp_ext/mcobject.hpp +241 -0
- pyxcp/cpp_ext/tsqueue.hpp +46 -0
- pyxcp/daq_stim/__init__.py +226 -0
- pyxcp/daq_stim/optimize/__init__.py +67 -0
- pyxcp/daq_stim/optimize/binpacking.py +41 -0
- pyxcp/daq_stim/scheduler.cpp +28 -0
- pyxcp/daq_stim/scheduler.hpp +75 -0
- pyxcp/daq_stim/stim.cp310-win_amd64.pyd +0 -0
- pyxcp/daq_stim/stim.cp311-win_amd64.pyd +0 -0
- pyxcp/daq_stim/stim.cp312-win_amd64.pyd +0 -0
- pyxcp/daq_stim/stim.cp38-win_amd64.pyd +0 -0
- pyxcp/daq_stim/stim.cp39-win_amd64.pyd +0 -0
- pyxcp/daq_stim/stim.cpp +13 -0
- pyxcp/daq_stim/stim.hpp +604 -0
- pyxcp/daq_stim/stim_wrapper.cpp +48 -0
- pyxcp/dllif.py +21 -18
- pyxcp/errormatrix.py +5 -3
- pyxcp/examples/conf_can.toml +4 -2
- pyxcp/examples/conf_can_vector.json +9 -9
- pyxcp/examples/conf_can_vector.toml +4 -2
- pyxcp/examples/conf_eth.toml +5 -2
- pyxcp/examples/conf_nixnet.json +18 -18
- pyxcp/examples/conf_sxi.json +7 -7
- pyxcp/examples/ex_arrow.py +109 -0
- pyxcp/examples/ex_mdf.py +124 -0
- pyxcp/examples/ex_sqlite.py +128 -0
- pyxcp/examples/run_daq.py +146 -0
- pyxcp/examples/xcp_policy.py +6 -7
- pyxcp/examples/xcp_read_benchmark.py +8 -6
- pyxcp/examples/xcp_skel.py +0 -2
- pyxcp/examples/xcp_unlock.py +1 -1
- pyxcp/examples/xcp_user_supplied_driver.py +1 -2
- pyxcp/examples/xcphello.py +6 -3
- pyxcp/examples/xcphello_recorder.py +4 -4
- pyxcp/master/__init__.py +1 -2
- pyxcp/master/errorhandler.py +107 -74
- pyxcp/master/master.py +156 -117
- pyxcp/py.typed +0 -0
- pyxcp/recorder/__init__.py +27 -6
- pyxcp/recorder/converter/__init__.py +37 -0
- pyxcp/recorder/lz4.c +129 -51
- pyxcp/recorder/lz4.h +45 -28
- pyxcp/recorder/lz4hc.c +560 -156
- pyxcp/recorder/lz4hc.h +1 -1
- pyxcp/recorder/mio.hpp +721 -767
- pyxcp/recorder/reader.hpp +139 -0
- pyxcp/recorder/reco.py +5 -8
- pyxcp/recorder/rekorder.cp310-win_amd64.pyd +0 -0
- pyxcp/recorder/rekorder.cp311-win_amd64.pyd +0 -0
- pyxcp/recorder/rekorder.cp312-win_amd64.pyd +0 -0
- pyxcp/recorder/rekorder.cp38-win_amd64.pyd +0 -0
- pyxcp/recorder/rekorder.cp39-win_amd64.pyd +0 -0
- pyxcp/recorder/rekorder.cpp +18 -22
- pyxcp/recorder/rekorder.hpp +200 -587
- pyxcp/recorder/setup.py +11 -10
- pyxcp/recorder/test_reko.py +2 -3
- pyxcp/recorder/unfolder.hpp +1249 -0
- pyxcp/recorder/wrap.cpp +171 -9
- pyxcp/recorder/writer.hpp +302 -0
- pyxcp/scripts/pyxcp_probe_can_drivers.py +0 -2
- pyxcp/scripts/xcp_fetch_a2l.py +15 -10
- pyxcp/scripts/xcp_id_scanner.py +2 -6
- pyxcp/scripts/xcp_info.py +91 -71
- pyxcp/scripts/xcp_profile.py +27 -0
- pyxcp/stim/__init__.py +0 -0
- pyxcp/tests/test_asam_types.py +2 -2
- pyxcp/tests/test_binpacking.py +184 -0
- pyxcp/tests/test_can.py +1132 -38
- pyxcp/tests/test_checksum.py +2 -1
- pyxcp/tests/test_daq.py +188 -0
- pyxcp/tests/test_frame_padding.py +3 -3
- pyxcp/tests/test_master.py +42 -31
- pyxcp/tests/test_transport.py +12 -12
- pyxcp/tests/test_utils.py +2 -5
- pyxcp/timing.py +0 -2
- pyxcp/transport/__init__.py +9 -9
- pyxcp/transport/base.py +142 -124
- pyxcp/transport/base_transport.hpp +0 -0
- pyxcp/transport/can.py +194 -167
- pyxcp/transport/eth.py +80 -82
- pyxcp/transport/sxi.py +106 -60
- pyxcp/transport/transport_wrapper.cpp +0 -0
- pyxcp/transport/usb_transport.py +65 -83
- pyxcp/types.py +61 -20
- pyxcp/utils.py +47 -16
- pyxcp/vector/map.py +1 -3
- {pyxcp-0.21.11.dist-info → pyxcp-0.22.2.dist-info}/METADATA +27 -22
- pyxcp-0.22.2.dist-info/RECORD +137 -0
- {pyxcp-0.21.11.dist-info → pyxcp-0.22.2.dist-info}/WHEEL +1 -1
- {pyxcp-0.21.11.dist-info → pyxcp-0.22.2.dist-info}/entry_points.txt +1 -0
- pyxcp/config.py +0 -57
- pyxcp/cxx/asynchiofactory.hpp +0 -24
- pyxcp/cxx/blocking_client.cpp +0 -44
- pyxcp/cxx/blocking_socket.cpp +0 -43
- pyxcp/cxx/blocking_socket.hpp +0 -558
- pyxcp/cxx/concurrent_queue.hpp +0 -60
- pyxcp/cxx/eth.hpp +0 -57
- pyxcp/cxx/exceptions.hpp +0 -30
- pyxcp/cxx/iasyncioservice.hpp +0 -31
- pyxcp/cxx/iresource.hpp +0 -17
- pyxcp/cxx/isocket.hpp +0 -22
- pyxcp/cxx/linux/epoll.cpp +0 -51
- pyxcp/cxx/linux/epoll.hpp +0 -87
- pyxcp/cxx/linux/lit_tester.cpp +0 -19
- pyxcp/cxx/linux/socket.hpp +0 -234
- pyxcp/cxx/linux/timeout.hpp +0 -81
- pyxcp/cxx/memoryblock.hpp +0 -42
- pyxcp/cxx/pool.hpp +0 -81
- pyxcp/cxx/poolmgr.cpp +0 -6
- pyxcp/cxx/poolmgr.hpp +0 -31
- pyxcp/cxx/test_queue.cpp +0 -69
- pyxcp/cxx/timestamp.hpp +0 -84
- pyxcp/cxx/utils.cpp +0 -38
- pyxcp/cxx/utils.hpp +0 -29
- pyxcp/cxx/win/iocp.cpp +0 -242
- pyxcp/cxx/win/iocp.hpp +0 -42
- pyxcp/cxx/win/perhandledata.hpp +0 -24
- pyxcp/cxx/win/periodata.hpp +0 -97
- pyxcp/cxx/win/socket.hpp +0 -185
- pyxcp/cxx/win/timeout.hpp +0 -83
- pyxcp/examples/conf_can.json +0 -20
- pyxcp/examples/conf_eth.json +0 -8
- pyxcp/logger.py +0 -64
- pyxcp/tests/test_config.py +0 -62
- pyxcp/transport/candriver/__init__.py +0 -2
- pyxcp/transport/candriver/pc_canalystii.py +0 -27
- pyxcp/transport/candriver/pc_etas.py +0 -25
- pyxcp/transport/candriver/pc_gsusb.py +0 -23
- pyxcp/transport/candriver/pc_iscan.py +0 -23
- pyxcp/transport/candriver/pc_ixxat.py +0 -27
- pyxcp/transport/candriver/pc_kvaser.py +0 -39
- pyxcp/transport/candriver/pc_neovi.py +0 -31
- pyxcp/transport/candriver/pc_nican.py +0 -23
- pyxcp/transport/candriver/pc_nixnet.py +0 -23
- pyxcp/transport/candriver/pc_pcan.py +0 -25
- pyxcp/transport/candriver/pc_seeed.py +0 -28
- pyxcp/transport/candriver/pc_serial.py +0 -27
- pyxcp/transport/candriver/pc_slcan.py +0 -29
- pyxcp/transport/candriver/pc_socketcan.py +0 -23
- pyxcp/transport/candriver/pc_systec.py +0 -29
- pyxcp/transport/candriver/pc_usb2can.py +0 -30
- pyxcp/transport/candriver/pc_vector.py +0 -34
- pyxcp/transport/candriver/python_can.py +0 -101
- pyxcp/transport/cxx_ext/CMakeLists.txt +0 -51
- pyxcp/transport/cxx_ext/setup.py +0 -49
- pyxcp/transport/cxx_ext/tests/test_basic_socket.cpp +0 -39
- pyxcp/transport/cxx_ext/tests/test_pool.cpp +0 -39
- pyxcp/transport/cxx_ext/tests/test_timestamp.cpp +0 -27
- pyxcp-0.21.11.dist-info/RECORD +0 -147
- rekorder.cp312-win_amd64.pyd +0 -0
- {pyxcp-0.21.11.dist-info/licenses → pyxcp-0.22.2.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
|
|
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
|
-
|
|
8
|
+
|
|
9
|
+
from ..cpp_ext import Timestamp, TimestampType
|
|
15
10
|
from ..recorder import XcpLogFileWriter
|
|
16
11
|
from ..timing import Timing
|
|
17
|
-
from ..utils import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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:
|
|
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) ->
|
|
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:
|
|
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:
|
|
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:
|
|
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)
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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.
|
|
165
|
-
self.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
self.
|
|
169
|
-
|
|
170
|
-
self.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
self.
|
|
175
|
-
self.
|
|
176
|
-
create_daq_timestamps =
|
|
177
|
-
self.
|
|
178
|
-
|
|
179
|
-
self.alignment =
|
|
180
|
-
self.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.
|
|
193
|
-
self.
|
|
194
|
-
|
|
195
|
-
self.
|
|
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.
|
|
201
|
-
self.
|
|
177
|
+
def __del__(self) -> None:
|
|
178
|
+
self.finish_listener()
|
|
179
|
+
self.close_connection()
|
|
202
180
|
|
|
203
|
-
def
|
|
181
|
+
def load_config(self, config) -> None:
|
|
204
182
|
"""Load configuration data."""
|
|
205
|
-
|
|
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.
|
|
188
|
+
self.finish_listener()
|
|
210
189
|
if self.listener.is_alive():
|
|
211
190
|
self.listener.join()
|
|
212
|
-
self.
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
282
|
-
|
|
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
|
-
|
|
293
|
-
packet = bytes(flatten(cmd.to_bytes(
|
|
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.
|
|
296
|
-
self.
|
|
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 =
|
|
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
|
|
339
|
+
if self.timestamp.value - start > self.timeout:
|
|
335
340
|
raise types.XcpTimeoutError("Response timed out [block_receive].") from None
|
|
336
|
-
|
|
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
|
|
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
|
|
362
|
-
if counter == self.
|
|
363
|
-
self.logger.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
390
|
-
|
|
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
|
|
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 =
|
|
419
|
+
transports = available_transports()
|
|
402
420
|
if name in transports:
|
|
403
|
-
|
|
421
|
+
transport_class: Type[BaseTransport] = transports[name]
|
|
404
422
|
else:
|
|
405
|
-
raise ValueError(f"
|
|
406
|
-
return
|
|
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
|
|
427
|
+
def available_transports() -> Dict[str, Type[BaseTransport]]:
|
|
410
428
|
"""List all subclasses of :class:`BaseTransport`.
|
|
411
429
|
|
|
412
430
|
Returns
|
|
File without changes
|