pyxcp 0.21.9__cp310-cp310-win_amd64.whl → 0.22.23__cp310-cp310-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 +32 -50
- pyxcp/config/__init__.py +1100 -0
- pyxcp/config/legacy.py +120 -0
- pyxcp/constants.py +12 -13
- pyxcp/cpp_ext/__init__.py +0 -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.cp38-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp39-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/daqlist.hpp +200 -0
- pyxcp/cpp_ext/event.hpp +67 -0
- pyxcp/cpp_ext/extension_wrapper.cpp +96 -0
- pyxcp/cpp_ext/helper.hpp +280 -0
- pyxcp/cpp_ext/mcobject.hpp +246 -0
- pyxcp/cpp_ext/tsqueue.hpp +46 -0
- pyxcp/daq_stim/__init__.py +228 -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.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_csv.py +85 -0
- pyxcp/examples/ex_excel.py +95 -0
- pyxcp/examples/ex_mdf.py +124 -0
- pyxcp/examples/ex_sqlite.py +128 -0
- pyxcp/examples/run_daq.py +148 -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 +196 -114
- 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.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 +1332 -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_examples.py +64 -0
- pyxcp/scripts/xcp_fetch_a2l.py +15 -10
- pyxcp/scripts/xcp_id_scanner.py +2 -6
- pyxcp/scripts/xcp_info.py +101 -63
- 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 +186 -0
- pyxcp/tests/test_can.py +1132 -38
- pyxcp/tests/test_checksum.py +2 -1
- pyxcp/tests/test_daq.py +193 -0
- pyxcp/tests/test_frame_padding.py +6 -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 +149 -127
- 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 +69 -20
- pyxcp/utils.py +47 -16
- pyxcp/vector/map.py +1 -3
- {pyxcp-0.21.9.dist-info → pyxcp-0.22.23.dist-info}/METADATA +28 -23
- pyxcp-0.22.23.dist-info/RECORD +134 -0
- {pyxcp-0.21.9.dist-info → pyxcp-0.22.23.dist-info}/WHEEL +1 -1
- {pyxcp-0.21.9.dist-info → pyxcp-0.22.23.dist-info}/entry_points.txt +2 -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 -67
- 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.9.dist-info/RECORD +0 -147
- rekorder.cp310-win_amd64.pyd +0 -0
- {pyxcp-0.21.9.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
|
|
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
|
|
15
|
-
from
|
|
16
|
-
from
|
|
17
|
-
from
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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:
|
|
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,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:
|
|
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)
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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):
|
|
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.
|
|
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(
|
|
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
|
-
|
|
193
|
-
self.
|
|
176
|
+
def __del__(self) -> None:
|
|
177
|
+
self.finish_listener()
|
|
178
|
+
self.close_connection()
|
|
194
179
|
|
|
195
|
-
|
|
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
|
-
|
|
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.
|
|
187
|
+
self.finish_listener()
|
|
210
188
|
if self.listener.is_alive():
|
|
211
189
|
self.listener.join()
|
|
212
|
-
self.
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
282
|
-
|
|
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
|
-
|
|
293
|
-
packet = bytes(flatten(cmd.to_bytes(
|
|
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.
|
|
296
|
-
self.
|
|
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 =
|
|
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
|
|
343
|
+
if self.timestamp.value - start > self.timeout:
|
|
335
344
|
raise types.XcpTimeoutError("Response timed out [block_receive].") from None
|
|
336
|
-
|
|
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
|
|
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
|
|
362
|
-
if counter == self.
|
|
363
|
-
self.logger.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
390
|
-
|
|
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
|
|
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 =
|
|
423
|
+
transports = available_transports()
|
|
402
424
|
if name in transports:
|
|
403
|
-
|
|
425
|
+
transport_class: Type[BaseTransport] = transports[name]
|
|
404
426
|
else:
|
|
405
|
-
raise ValueError(f"
|
|
406
|
-
return
|
|
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
|
|
431
|
+
def available_transports() -> Dict[str, Type[BaseTransport]]:
|
|
410
432
|
"""List all subclasses of :class:`BaseTransport`.
|
|
411
433
|
|
|
412
434
|
Returns
|
|
File without changes
|