pyxcp 0.21.11__cp311-cp311-win32.whl → 0.22.1__cp311-cp311-win32.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.
- CMakeLists.txt +117 -0
- 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.cp312-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.cp312-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.cp312-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.1.dist-info}/METADATA +27 -22
- pyxcp-0.22.1.dist-info/RECORD +126 -0
- {pyxcp-0.21.11.dist-info → pyxcp-0.22.1.dist-info}/WHEEL +1 -1
- {pyxcp-0.21.11.dist-info → pyxcp-0.22.1.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.cp311-win32.pyd +0 -0
- {pyxcp-0.21.11.dist-info/licenses → pyxcp-0.22.1.dist-info}/LICENSE +0 -0
pyxcp/master/master.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
"""Lowlevel API reflecting available XCP services.
|
|
4
3
|
|
|
5
4
|
.. note:: For technical reasons the API is split into two parts;
|
|
@@ -11,35 +10,23 @@ import functools
|
|
|
11
10
|
import struct
|
|
12
11
|
import traceback
|
|
13
12
|
import warnings
|
|
14
|
-
from
|
|
15
|
-
|
|
16
|
-
from
|
|
17
|
-
from
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
from pyxcp.
|
|
28
|
-
from pyxcp.
|
|
29
|
-
from pyxcp.
|
|
30
|
-
from pyxcp.
|
|
31
|
-
from pyxcp.constants import makeDWordUnpacker
|
|
32
|
-
from pyxcp.constants import makeWordPacker
|
|
33
|
-
from pyxcp.constants import makeWordUnpacker
|
|
34
|
-
from pyxcp.constants import PackerType
|
|
35
|
-
from pyxcp.constants import UnpackerType
|
|
36
|
-
from pyxcp.logger import Logger
|
|
37
|
-
from pyxcp.master.errorhandler import disable_error_handling
|
|
38
|
-
from pyxcp.master.errorhandler import wrapped
|
|
39
|
-
from pyxcp.transport.base import createTransport
|
|
40
|
-
from pyxcp.utils import decode_bytes
|
|
41
|
-
from pyxcp.utils import delay
|
|
42
|
-
from pyxcp.utils import SHORT_SLEEP
|
|
13
|
+
from typing import Any, Callable, Collection, Dict, List, Optional, Tuple
|
|
14
|
+
|
|
15
|
+
from pyxcp import checksum, types
|
|
16
|
+
from pyxcp.constants import (
|
|
17
|
+
makeBytePacker,
|
|
18
|
+
makeByteUnpacker,
|
|
19
|
+
makeDLongPacker,
|
|
20
|
+
makeDLongUnpacker,
|
|
21
|
+
makeDWordPacker,
|
|
22
|
+
makeDWordUnpacker,
|
|
23
|
+
makeWordPacker,
|
|
24
|
+
makeWordUnpacker,
|
|
25
|
+
)
|
|
26
|
+
from pyxcp.daq_stim.stim import DaqEventInfo, Stim
|
|
27
|
+
from pyxcp.master.errorhandler import SystemExit, disable_error_handling, wrapped
|
|
28
|
+
from pyxcp.transport.base import create_transport
|
|
29
|
+
from pyxcp.utils import decode_bytes, delay, short_sleep
|
|
43
30
|
|
|
44
31
|
|
|
45
32
|
def broadcasted(func: Callable):
|
|
@@ -51,7 +38,7 @@ class SlaveProperties(dict):
|
|
|
51
38
|
"""Container class for fixed parameters, like byte-order, maxCTO, ..."""
|
|
52
39
|
|
|
53
40
|
def __init__(self, *args, **kws):
|
|
54
|
-
super(
|
|
41
|
+
super().__init__(*args, **kws)
|
|
55
42
|
|
|
56
43
|
def __getattr__(self, name):
|
|
57
44
|
return self[name]
|
|
@@ -71,38 +58,36 @@ class Master:
|
|
|
71
58
|
|
|
72
59
|
Parameters
|
|
73
60
|
----------
|
|
74
|
-
|
|
61
|
+
transport_name : str
|
|
75
62
|
XCP transport layer name ['can', 'eth', 'sxi']
|
|
76
63
|
config: dict
|
|
77
64
|
"""
|
|
78
65
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"DISABLE_ERROR_HANDLING": (
|
|
83
|
-
bool,
|
|
84
|
-
False,
|
|
85
|
-
False,
|
|
86
|
-
), # Bypass error-handling for performance reasons.
|
|
87
|
-
"SEED_N_KEY_DLL": (str, False, ""),
|
|
88
|
-
"SEED_N_KEY_DLL_SAME_BIT_WIDTH": (bool, False, False),
|
|
89
|
-
"DISCONNECT_RESPONSE_OPTIONAL": (bool, False, False),
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
def __init__(self, transportName, config=None, policy=None):
|
|
66
|
+
def __init__(self, transport_name: Optional[str], config, policy=None, transport_layer_interface=None):
|
|
67
|
+
if transport_name is None:
|
|
68
|
+
raise ValueError("No transport-layer selected") # Never reached -- to keep type-checkers happy.
|
|
93
69
|
self.ctr = 0
|
|
94
70
|
self.succeeded = True
|
|
95
|
-
self.config =
|
|
96
|
-
self.logger =
|
|
97
|
-
disable_error_handling(self.config.get("DISABLE_ERROR_HANDLING"))
|
|
71
|
+
self.config = config.general
|
|
72
|
+
self.logger = config.log
|
|
98
73
|
|
|
99
|
-
self.
|
|
100
|
-
self.transport_name =
|
|
74
|
+
disable_error_handling(self.config.disable_error_handling)
|
|
75
|
+
self.transport_name = transport_name.lower()
|
|
76
|
+
transport_config = config.transport
|
|
77
|
+
self.transport = create_transport(transport_name, transport_config, policy, transport_layer_interface)
|
|
78
|
+
|
|
79
|
+
self.stim = Stim(self.config.stim_support)
|
|
80
|
+
self.stim.clear()
|
|
81
|
+
self.stim.set_policy_feeder(self.transport.policy.feed)
|
|
82
|
+
self.stim.set_frame_sender(self.transport.block_request)
|
|
101
83
|
|
|
102
84
|
# In some cases the transport-layer needs to communicate with us.
|
|
103
85
|
self.transport.parent = self
|
|
104
86
|
self.service = None
|
|
105
87
|
|
|
88
|
+
# Policies may issue XCP commands on there own.
|
|
89
|
+
self.transport.policy.xcp_master = self
|
|
90
|
+
|
|
106
91
|
# (D)Word (un-)packers are byte-order dependent
|
|
107
92
|
# -- byte-order is returned by CONNECT_Resp (COMM_MODE_BASIC)
|
|
108
93
|
self.BYTE_pack = None
|
|
@@ -119,9 +104,10 @@ class Master:
|
|
|
119
104
|
self.mta = types.MtaType(None, None)
|
|
120
105
|
self.currentDaqPtr = None
|
|
121
106
|
self.currentProtectionStatus = None
|
|
122
|
-
self.
|
|
123
|
-
self.
|
|
124
|
-
self.
|
|
107
|
+
self.seed_n_key_dll = self.config.seed_n_key_dll
|
|
108
|
+
self.seed_n_key_function = self.config.seed_n_key_function
|
|
109
|
+
self.seed_n_key_dll_same_bit_width = self.config.seed_n_key_dll_same_bit_width
|
|
110
|
+
self.disconnect_response_optional = self.config.disconnect_response_optional
|
|
125
111
|
self.slaveProperties = SlaveProperties()
|
|
126
112
|
self.slaveProperties.pgmProcessor = SlaveProperties()
|
|
127
113
|
self.slaveProperties.transport_layer = self.transport_name.upper()
|
|
@@ -461,7 +447,7 @@ class Master:
|
|
|
461
447
|
response += data[1 : rem + 1]
|
|
462
448
|
rem = byte_count - len(response)
|
|
463
449
|
else:
|
|
464
|
-
|
|
450
|
+
short_sleep()
|
|
465
451
|
return response
|
|
466
452
|
|
|
467
453
|
@wrapped
|
|
@@ -581,7 +567,7 @@ class Master:
|
|
|
581
567
|
address is not included because of services implicitly setting address information like :meth:`getID` .
|
|
582
568
|
"""
|
|
583
569
|
if limitPayload and limitPayload < 8:
|
|
584
|
-
raise ValueError("Payload must be at least 8 bytes - given: {}"
|
|
570
|
+
raise ValueError(f"Payload must be at least 8 bytes - given: {limitPayload}")
|
|
585
571
|
|
|
586
572
|
slaveBlockMode = self.slaveProperties.slaveBlockMode
|
|
587
573
|
if slaveBlockMode:
|
|
@@ -978,14 +964,15 @@ class Master:
|
|
|
978
964
|
# DAQ
|
|
979
965
|
|
|
980
966
|
@wrapped
|
|
981
|
-
def setDaqPtr(self, daqListNumber, odtNumber, odtEntryNumber):
|
|
967
|
+
def setDaqPtr(self, daqListNumber: int, odtNumber: int, odtEntryNumber: int):
|
|
982
968
|
self.currentDaqPtr = types.DaqPtr(daqListNumber, odtNumber, odtEntryNumber) # Needed for errorhandling.
|
|
983
969
|
daqList = self.WORD_pack(daqListNumber)
|
|
984
970
|
response = self.transport.request(types.Command.SET_DAQ_PTR, 0, *daqList, odtNumber, odtEntryNumber)
|
|
971
|
+
self.stim.setDaqPtr(daqListNumber, odtNumber, odtEntryNumber)
|
|
985
972
|
return response
|
|
986
973
|
|
|
987
974
|
@wrapped
|
|
988
|
-
def clearDaqList(self, daqListNumber):
|
|
975
|
+
def clearDaqList(self, daqListNumber: int):
|
|
989
976
|
"""Clear DAQ list configuration.
|
|
990
977
|
|
|
991
978
|
Parameters
|
|
@@ -993,10 +980,12 @@ class Master:
|
|
|
993
980
|
daqListNumber : int
|
|
994
981
|
"""
|
|
995
982
|
daqList = self.WORD_pack(daqListNumber)
|
|
996
|
-
|
|
983
|
+
result = self.transport.request(types.Command.CLEAR_DAQ_LIST, 0, *daqList)
|
|
984
|
+
self.stim.clearDaqList(daqListNumber)
|
|
985
|
+
return result
|
|
997
986
|
|
|
998
987
|
@wrapped
|
|
999
|
-
def writeDaq(self, bitOffset, entrySize, addressExt, address):
|
|
988
|
+
def writeDaq(self, bitOffset: int, entrySize: int, addressExt: int, address: int):
|
|
1000
989
|
"""Write element in ODT entry.
|
|
1001
990
|
|
|
1002
991
|
Parameters
|
|
@@ -1009,12 +998,15 @@ class Master:
|
|
|
1009
998
|
address : int
|
|
1010
999
|
"""
|
|
1011
1000
|
addr = self.DWORD_pack(address)
|
|
1012
|
-
|
|
1001
|
+
result = self.transport.request(types.Command.WRITE_DAQ, bitOffset, entrySize, addressExt, *addr)
|
|
1002
|
+
self.stim.writeDaq(bitOffset, entrySize, addressExt, address)
|
|
1003
|
+
return result
|
|
1013
1004
|
|
|
1014
1005
|
@wrapped
|
|
1015
1006
|
def setDaqListMode(self, mode, daqListNumber, eventChannelNumber, prescaler, priority):
|
|
1016
1007
|
dln = self.WORD_pack(daqListNumber)
|
|
1017
1008
|
ecn = self.WORD_pack(eventChannelNumber)
|
|
1009
|
+
self.stim.setDaqListMode(mode, daqListNumber, eventChannelNumber, prescaler, priority)
|
|
1018
1010
|
return self.transport.request(types.Command.SET_DAQ_LIST_MODE, mode, *dln, *ecn, prescaler, priority)
|
|
1019
1011
|
|
|
1020
1012
|
@wrapped
|
|
@@ -1034,7 +1026,7 @@ class Master:
|
|
|
1034
1026
|
return types.GetDaqListModeResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1035
1027
|
|
|
1036
1028
|
@wrapped
|
|
1037
|
-
def startStopDaqList(self, mode, daqListNumber):
|
|
1029
|
+
def startStopDaqList(self, mode: int, daqListNumber: int):
|
|
1038
1030
|
"""Start /stop/select DAQ list.
|
|
1039
1031
|
|
|
1040
1032
|
Parameters
|
|
@@ -1047,7 +1039,10 @@ class Master:
|
|
|
1047
1039
|
"""
|
|
1048
1040
|
dln = self.WORD_pack(daqListNumber)
|
|
1049
1041
|
response = self.transport.request(types.Command.START_STOP_DAQ_LIST, mode, *dln)
|
|
1050
|
-
|
|
1042
|
+
self.stim.startStopDaqList(mode, daqListNumber)
|
|
1043
|
+
firstPid = types.StartStopDaqListResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1044
|
+
self.stim.set_first_pid(daqListNumber, firstPid.firstPid)
|
|
1045
|
+
return firstPid
|
|
1051
1046
|
|
|
1052
1047
|
@wrapped
|
|
1053
1048
|
def startStopSynch(self, mode):
|
|
@@ -1060,7 +1055,9 @@ class Master:
|
|
|
1060
1055
|
1 = start selected
|
|
1061
1056
|
2 = stop selected
|
|
1062
1057
|
"""
|
|
1063
|
-
|
|
1058
|
+
res = self.transport.request(types.Command.START_STOP_SYNCH, mode)
|
|
1059
|
+
self.stim.startStopSynch(mode)
|
|
1060
|
+
return res
|
|
1064
1061
|
|
|
1065
1062
|
@wrapped
|
|
1066
1063
|
def writeDaqMultiple(self, daqElements):
|
|
@@ -1071,7 +1068,7 @@ class Master:
|
|
|
1071
1068
|
daqElements : list of `dict` containing the following keys: *bitOffset*, *size*, *address*, *addressExt*.
|
|
1072
1069
|
"""
|
|
1073
1070
|
if len(daqElements) > self.slaveProperties.maxWriteDaqMultipleElements:
|
|
1074
|
-
raise ValueError("At most {} daqElements are permitted."
|
|
1071
|
+
raise ValueError(f"At most {self.slaveProperties.maxWriteDaqMultipleElements} daqElements are permitted.")
|
|
1075
1072
|
data = bytearray()
|
|
1076
1073
|
data.append(len(daqElements))
|
|
1077
1074
|
|
|
@@ -1218,10 +1215,12 @@ class Master:
|
|
|
1218
1215
|
@wrapped
|
|
1219
1216
|
def freeDaq(self):
|
|
1220
1217
|
"""Clear dynamic DAQ configuration."""
|
|
1221
|
-
|
|
1218
|
+
result = self.transport.request(types.Command.FREE_DAQ)
|
|
1219
|
+
self.stim.freeDaq()
|
|
1220
|
+
return result
|
|
1222
1221
|
|
|
1223
1222
|
@wrapped
|
|
1224
|
-
def allocDaq(self, daqCount):
|
|
1223
|
+
def allocDaq(self, daqCount: int):
|
|
1225
1224
|
"""Allocate DAQ lists.
|
|
1226
1225
|
|
|
1227
1226
|
Parameters
|
|
@@ -1230,17 +1229,23 @@ class Master:
|
|
|
1230
1229
|
number of DAQ lists to be allocated
|
|
1231
1230
|
"""
|
|
1232
1231
|
dq = self.WORD_pack(daqCount)
|
|
1233
|
-
|
|
1232
|
+
result = self.transport.request(types.Command.ALLOC_DAQ, 0, *dq)
|
|
1233
|
+
self.stim.allocDaq(daqCount)
|
|
1234
|
+
return result
|
|
1234
1235
|
|
|
1235
1236
|
@wrapped
|
|
1236
|
-
def allocOdt(self, daqListNumber, odtCount):
|
|
1237
|
+
def allocOdt(self, daqListNumber: int, odtCount: int):
|
|
1237
1238
|
dln = self.WORD_pack(daqListNumber)
|
|
1238
|
-
|
|
1239
|
+
result = self.transport.request(types.Command.ALLOC_ODT, 0, *dln, odtCount)
|
|
1240
|
+
self.stim.allocOdt(daqListNumber, odtCount)
|
|
1241
|
+
return result
|
|
1239
1242
|
|
|
1240
1243
|
@wrapped
|
|
1241
|
-
def allocOdtEntry(self, daqListNumber, odtNumber, odtEntriesCount):
|
|
1244
|
+
def allocOdtEntry(self, daqListNumber: int, odtNumber: int, odtEntriesCount: int):
|
|
1242
1245
|
dln = self.WORD_pack(daqListNumber)
|
|
1243
|
-
|
|
1246
|
+
result = self.transport.request(types.Command.ALLOC_ODT_ENTRY, 0, *dln, odtNumber, odtEntriesCount)
|
|
1247
|
+
self.stim.allocOdtEntry(daqListNumber, odtNumber, odtEntriesCount)
|
|
1248
|
+
return result
|
|
1244
1249
|
|
|
1245
1250
|
# PGM
|
|
1246
1251
|
@wrapped
|
|
@@ -1622,12 +1627,13 @@ class Master:
|
|
|
1622
1627
|
@broadcasted
|
|
1623
1628
|
@wrapped
|
|
1624
1629
|
def getSlaveID(self, mode: int):
|
|
1625
|
-
self.transportLayerCmd(types.TransportLayerCommands.GET_SLAVE_ID, "X", "C", "P", mode)
|
|
1630
|
+
response = self.transportLayerCmd(types.TransportLayerCommands.GET_SLAVE_ID, ord("X"), ord("C"), ord("P"), mode)
|
|
1631
|
+
return types.GetSlaveIdResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1626
1632
|
|
|
1627
1633
|
def getDaqId(self, daqListNumber: int):
|
|
1628
1634
|
response = self.transportLayerCmd(types.TransportLayerCommands.GET_DAQ_ID, *self.WORD_pack(daqListNumber))
|
|
1629
|
-
if response:
|
|
1630
|
-
|
|
1635
|
+
# if response:
|
|
1636
|
+
return types.GetDaqIdResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1631
1637
|
|
|
1632
1638
|
def setDaqId(self, daqListNumber: int, identifier: int):
|
|
1633
1639
|
response = self.transportLayerCmd(
|
|
@@ -1651,11 +1657,11 @@ class Master:
|
|
|
1651
1657
|
"""
|
|
1652
1658
|
self.setMta(addr)
|
|
1653
1659
|
cs = self.buildChecksum(length)
|
|
1654
|
-
self.logger.debug("BuildChecksum return'd: 0x{:08X} [{}]"
|
|
1660
|
+
self.logger.debug(f"BuildChecksum return'd: 0x{cs.checksum:08X} [{cs.checksumType}]")
|
|
1655
1661
|
self.setMta(addr)
|
|
1656
1662
|
data = self.fetch(length)
|
|
1657
1663
|
cc = checksum.check(data, cs.checksumType)
|
|
1658
|
-
self.logger.debug("Our checksum : 0x{:08X}"
|
|
1664
|
+
self.logger.debug(f"Our checksum : 0x{cc:08X}")
|
|
1659
1665
|
return cs.checksum == cc
|
|
1660
1666
|
|
|
1661
1667
|
def getDaqInfo(self):
|
|
@@ -1697,10 +1703,18 @@ class Master:
|
|
|
1697
1703
|
},
|
|
1698
1704
|
}
|
|
1699
1705
|
result["resolution"] = resolutionInfo
|
|
1700
|
-
|
|
1701
1706
|
channels = []
|
|
1707
|
+
daq_events = []
|
|
1702
1708
|
for ecn in range(dpi.maxEventChannel):
|
|
1703
1709
|
eci = self.getDaqEventInfo(ecn)
|
|
1710
|
+
cycle = eci["eventChannelTimeCycle"]
|
|
1711
|
+
maxDaqList = eci["maxDaqList"]
|
|
1712
|
+
priority = eci["eventChannelPriority"]
|
|
1713
|
+
time_unit = eci["eventChannelTimeUnit"]
|
|
1714
|
+
consistency = eci["daqEventProperties"]["consistency"]
|
|
1715
|
+
daq_supported = eci["daqEventProperties"]["daq"]
|
|
1716
|
+
stim_supported = eci["daqEventProperties"]["stim"]
|
|
1717
|
+
packed_supported = eci["daqEventProperties"]["packed"]
|
|
1704
1718
|
name = self.fetch(eci.eventChannelNameLength)
|
|
1705
1719
|
if name:
|
|
1706
1720
|
name = decode_bytes(name)
|
|
@@ -1711,14 +1725,27 @@ class Master:
|
|
|
1711
1725
|
"cycle": eci["eventChannelTimeCycle"],
|
|
1712
1726
|
"maxDaqList": eci["maxDaqList"],
|
|
1713
1727
|
"properties": {
|
|
1714
|
-
"consistency":
|
|
1715
|
-
"daq":
|
|
1716
|
-
"stim":
|
|
1717
|
-
"packed":
|
|
1728
|
+
"consistency": consistency,
|
|
1729
|
+
"daq": daq_supported,
|
|
1730
|
+
"stim": stim_supported,
|
|
1731
|
+
"packed": packed_supported,
|
|
1718
1732
|
},
|
|
1719
1733
|
}
|
|
1734
|
+
daq_event_info = DaqEventInfo(
|
|
1735
|
+
name,
|
|
1736
|
+
types.EVENT_CHANNEL_TIME_UNIT_TO_EXP[time_unit],
|
|
1737
|
+
cycle,
|
|
1738
|
+
maxDaqList,
|
|
1739
|
+
priority,
|
|
1740
|
+
consistency,
|
|
1741
|
+
daq_supported,
|
|
1742
|
+
stim_supported,
|
|
1743
|
+
packed_supported,
|
|
1744
|
+
)
|
|
1745
|
+
daq_events.append(daq_event_info)
|
|
1720
1746
|
channels.append(channel)
|
|
1721
1747
|
result["channels"] = channels
|
|
1748
|
+
self.stim.setDaqEventInfo(daq_events)
|
|
1722
1749
|
return result
|
|
1723
1750
|
|
|
1724
1751
|
def getCurrentProtectionStatus(self):
|
|
@@ -1761,12 +1788,14 @@ class Master:
|
|
|
1761
1788
|
In case of DLL related issues.
|
|
1762
1789
|
"""
|
|
1763
1790
|
import re
|
|
1764
|
-
|
|
1791
|
+
|
|
1792
|
+
from pyxcp.dllif import SeedNKeyError, SeedNKeyResult, getKey
|
|
1765
1793
|
|
|
1766
1794
|
MAX_PAYLOAD = self.slaveProperties["maxCto"] - 2
|
|
1767
1795
|
|
|
1768
|
-
|
|
1769
|
-
|
|
1796
|
+
protection_status = self.getCurrentProtectionStatus()
|
|
1797
|
+
if any(protection_status.values()) and (not (self.seed_n_key_dll or self.seed_n_key_function)):
|
|
1798
|
+
raise RuntimeError("Neither seed-and-key DLL nor function specified, cannot proceed.") # TODO: ConfigurationError
|
|
1770
1799
|
if resources is None:
|
|
1771
1800
|
result = []
|
|
1772
1801
|
if self.slaveProperties["supportsCalpag"]:
|
|
@@ -1778,11 +1807,10 @@ class Master:
|
|
|
1778
1807
|
if self.slaveProperties["supportsPgm"]:
|
|
1779
1808
|
result.append("pgm")
|
|
1780
1809
|
resources = ",".join(result)
|
|
1781
|
-
protection_status = self.getCurrentProtectionStatus()
|
|
1782
1810
|
resource_names = [r.lower() for r in re.split(r"[ ,]", resources) if r]
|
|
1783
1811
|
for name in resource_names:
|
|
1784
1812
|
if name not in types.RESOURCE_VALUES:
|
|
1785
|
-
raise ValueError("Invalid resource name
|
|
1813
|
+
raise ValueError(f"Invalid resource name {name!r}.")
|
|
1786
1814
|
if not protection_status[name]:
|
|
1787
1815
|
continue
|
|
1788
1816
|
resource_value = types.RESOURCE_VALUES[name]
|
|
@@ -1791,30 +1819,38 @@ class Master:
|
|
|
1791
1819
|
length = result.length
|
|
1792
1820
|
if length == 0:
|
|
1793
1821
|
continue
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
self.
|
|
1803
|
-
self.
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
self.
|
|
1807
|
-
|
|
1822
|
+
if length > MAX_PAYLOAD:
|
|
1823
|
+
remaining = length - len(seed)
|
|
1824
|
+
while remaining > 0:
|
|
1825
|
+
result = self.getSeed(types.XcpGetSeedMode.REMAINING, resource_value)
|
|
1826
|
+
seed.extend(list(result.seed))
|
|
1827
|
+
remaining -= result.length
|
|
1828
|
+
self.logger.debug(f"Got seed {seed!r} for resource {resource_value!r}.")
|
|
1829
|
+
if self.seed_n_key_function:
|
|
1830
|
+
key = self.seed_n_key_function(resource_value, bytes(seed))
|
|
1831
|
+
self.logger.debug(f"Using seed and key function {self.seed_n_key_function.__name__!r}().")
|
|
1832
|
+
result = SeedNKeyResult.ACK
|
|
1833
|
+
elif self.seed_n_key_dll:
|
|
1834
|
+
self.logger.debug(f"Using seed and key DLL {self.seed_n_key_dll!r}.")
|
|
1835
|
+
result, key = getKey(
|
|
1836
|
+
self.logger,
|
|
1837
|
+
self.seed_n_key_dll,
|
|
1838
|
+
resource_value,
|
|
1839
|
+
bytes(seed),
|
|
1840
|
+
self.seed_n_key_dll_same_bit_width,
|
|
1841
|
+
)
|
|
1808
1842
|
if result == SeedNKeyResult.ACK:
|
|
1809
1843
|
key = list(key)
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
while
|
|
1813
|
-
data = key[
|
|
1814
|
-
|
|
1815
|
-
|
|
1844
|
+
self.logger.debug(f"Unlocking resource {resource_value!r} with key {key!r}.")
|
|
1845
|
+
remaining = len(key)
|
|
1846
|
+
while key:
|
|
1847
|
+
data = key[:MAX_PAYLOAD]
|
|
1848
|
+
key_len = len(data)
|
|
1849
|
+
self.unlock(remaining, data)
|
|
1850
|
+
key = key[MAX_PAYLOAD:]
|
|
1851
|
+
remaining -= key_len
|
|
1816
1852
|
else:
|
|
1817
|
-
raise SeedNKeyError("SeedAndKey DLL returned: {
|
|
1853
|
+
raise SeedNKeyError(f"SeedAndKey DLL returned: {SeedNKeyResult(result).name!r}")
|
|
1818
1854
|
|
|
1819
1855
|
def identifier(self, id_value: int) -> str:
|
|
1820
1856
|
"""Return the identifier for the given value.
|
|
@@ -1892,18 +1928,20 @@ class Master:
|
|
|
1892
1928
|
|
|
1893
1929
|
gen = make_generator(scan_ranges)
|
|
1894
1930
|
for id_value, name in gen:
|
|
1895
|
-
response =
|
|
1896
|
-
|
|
1897
|
-
response = self.identifier(id_value)
|
|
1898
|
-
except types.XcpResponseError:
|
|
1899
|
-
# don't depend on confirming implementation, i.e.: ID not implemented ==> empty response.
|
|
1900
|
-
pass
|
|
1901
|
-
except Exception:
|
|
1902
|
-
raise
|
|
1903
|
-
if response:
|
|
1931
|
+
status, response = self.try_command(self.identifier, id_value)
|
|
1932
|
+
if status == types.TryCommandResult.OK and response:
|
|
1904
1933
|
result[name] = response
|
|
1934
|
+
elif status == types.TryCommandResult.XCP_ERROR and response.error_code == types.XcpError.ERR_CMD_UNKNOWN:
|
|
1935
|
+
break # Nothing to do here.
|
|
1936
|
+
elif status == types.TryCommandResult.OTHER_ERROR:
|
|
1937
|
+
raise RuntimeError(f"Error while scanning for ID {id_value}: {response!r}")
|
|
1905
1938
|
return result
|
|
1906
1939
|
|
|
1940
|
+
@property
|
|
1941
|
+
def start_datetime(self) -> int:
|
|
1942
|
+
""""""
|
|
1943
|
+
return self.transport.start_datetime
|
|
1944
|
+
|
|
1907
1945
|
def try_command(self, cmd: Callable, *args, **kws) -> Tuple[types.TryCommandResult, Any]:
|
|
1908
1946
|
"""Call master functions and handle XCP errors more gracefuly.
|
|
1909
1947
|
|
|
@@ -1958,6 +1996,7 @@ def ticks_to_seconds(ticks, resolution):
|
|
|
1958
1996
|
warnings.warn(
|
|
1959
1997
|
"ticks_to_seconds() deprecated, use factory :func:`make_tick_converter` instead.",
|
|
1960
1998
|
Warning,
|
|
1999
|
+
stacklevel=1,
|
|
1961
2000
|
)
|
|
1962
2001
|
return (10 ** types.DAQ_TIMESTAMP_UNIT_TO_EXP[resolution.timestampMode.unit]) * resolution.timestampTicks * ticks
|
|
1963
2002
|
|
pyxcp/py.typed
ADDED
|
File without changes
|
pyxcp/recorder/__init__.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
"""XCP Frame Recording Facility.
|
|
4
3
|
"""
|
|
5
4
|
from dataclasses import dataclass
|
|
6
|
-
from enum import IntEnum
|
|
7
5
|
from typing import Union
|
|
8
6
|
|
|
9
7
|
from pyxcp.types import FrameCategory
|
|
10
8
|
|
|
9
|
+
|
|
11
10
|
try:
|
|
12
11
|
import pandas as pd
|
|
13
12
|
except ImportError:
|
|
@@ -15,13 +14,28 @@ except ImportError:
|
|
|
15
14
|
else:
|
|
16
15
|
HAS_PANDAS = True
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
from pyxcp.recorder.rekorder import DaqOnlinePolicy # noqa: F401
|
|
18
|
+
from pyxcp.recorder.rekorder import (
|
|
19
|
+
DaqRecorderPolicy,
|
|
20
|
+
Deserializer,
|
|
21
|
+
MeasurementParameters,
|
|
22
|
+
ValueHolder,
|
|
23
|
+
XcpLogFileDecoder,
|
|
24
|
+
_PyXcpLogFileReader,
|
|
25
|
+
_PyXcpLogFileWriter,
|
|
26
|
+
data_types,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
DATA_TYPES = data_types()
|
|
19
31
|
|
|
20
32
|
|
|
21
33
|
@dataclass
|
|
22
34
|
class XcpLogFileHeader:
|
|
23
35
|
""" """
|
|
24
36
|
|
|
37
|
+
version: int
|
|
38
|
+
options: int
|
|
25
39
|
num_containers: int
|
|
26
40
|
record_count: int
|
|
27
41
|
size_uncompressed: int
|
|
@@ -36,11 +50,18 @@ class XcpLogFileReader:
|
|
|
36
50
|
""" """
|
|
37
51
|
|
|
38
52
|
def __init__(self, file_name):
|
|
39
|
-
self._reader =
|
|
53
|
+
self._reader = _PyXcpLogFileReader(file_name)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def header(self):
|
|
57
|
+
return self._reader.get_header()
|
|
40
58
|
|
|
41
59
|
def get_header(self):
|
|
42
60
|
return XcpLogFileHeader(*self._reader.get_header_as_tuple())
|
|
43
61
|
|
|
62
|
+
def get_metadata(self):
|
|
63
|
+
return self._reader.get_metadata()
|
|
64
|
+
|
|
44
65
|
def __iter__(self):
|
|
45
66
|
while True:
|
|
46
67
|
frames = self._reader.next_block()
|
|
@@ -66,8 +87,8 @@ class XcpLogFileReader:
|
|
|
66
87
|
class XcpLogFileWriter:
|
|
67
88
|
""" """
|
|
68
89
|
|
|
69
|
-
def __init__(self, file_name: str, prealloc=
|
|
70
|
-
self._writer =
|
|
90
|
+
def __init__(self, file_name: str, prealloc=500, chunk_size=1):
|
|
91
|
+
self._writer = _PyXcpLogFileWriter(file_name, prealloc, chunk_size)
|
|
71
92
|
self._finalized = False
|
|
72
93
|
|
|
73
94
|
def __del__(self):
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from array import array
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
MAP_TO_ARRAY = {
|
|
8
|
+
"U8": "B",
|
|
9
|
+
"I8": "b",
|
|
10
|
+
"U16": "H",
|
|
11
|
+
"I16": "h",
|
|
12
|
+
"U32": "L",
|
|
13
|
+
"I32": "l",
|
|
14
|
+
"U64": "Q",
|
|
15
|
+
"I64": "q",
|
|
16
|
+
"F32": "f",
|
|
17
|
+
"F64": "d",
|
|
18
|
+
"F16": "f",
|
|
19
|
+
"BF16": "f",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger("PyXCP")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class Storage:
|
|
27
|
+
name: str
|
|
28
|
+
arrow_type: Any
|
|
29
|
+
arr: array
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class StorageContainer:
|
|
34
|
+
name: str
|
|
35
|
+
arr: List[Storage] = field(default_factory=[])
|
|
36
|
+
ts0: List[int] = field(default_factory=lambda: array("Q"))
|
|
37
|
+
ts1: List[int] = field(default_factory=lambda: array("Q"))
|