pyxcp 0.21.10__cp312-cp312-win_amd64.whl → 0.22.1__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.
- 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 +32 -50
- 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 +198 -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 +98 -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 +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 +69 -20
- pyxcp/utils.py +47 -16
- pyxcp/vector/map.py +1 -3
- {pyxcp-0.21.10.dist-info → pyxcp-0.22.1.dist-info}/METADATA +27 -22
- pyxcp-0.22.1.dist-info/RECORD +126 -0
- {pyxcp-0.21.10.dist-info → pyxcp-0.22.1.dist-info}/WHEEL +1 -1
- {pyxcp-0.21.10.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 -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.10.dist-info/RECORD +0 -147
- rekorder.cp312-win_amd64.pyd +0 -0
- {pyxcp-0.21.10.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;
|
|
@@ -8,37 +7,26 @@
|
|
|
8
7
|
.. [1] XCP Specification, Part 2 - Protocol Layer Specification
|
|
9
8
|
"""
|
|
10
9
|
import functools
|
|
11
|
-
import logging
|
|
12
10
|
import struct
|
|
13
11
|
import traceback
|
|
14
12
|
import warnings
|
|
15
|
-
from
|
|
16
|
-
|
|
17
|
-
from
|
|
18
|
-
from
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
from pyxcp.
|
|
29
|
-
from pyxcp.
|
|
30
|
-
from pyxcp.
|
|
31
|
-
from pyxcp.
|
|
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.master.errorhandler import disable_error_handling
|
|
37
|
-
from pyxcp.master.errorhandler import wrapped
|
|
38
|
-
from pyxcp.transport.base import createTransport
|
|
39
|
-
from pyxcp.utils import decode_bytes
|
|
40
|
-
from pyxcp.utils import delay
|
|
41
|
-
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
|
|
42
30
|
|
|
43
31
|
|
|
44
32
|
def broadcasted(func: Callable):
|
|
@@ -50,7 +38,7 @@ class SlaveProperties(dict):
|
|
|
50
38
|
"""Container class for fixed parameters, like byte-order, maxCTO, ..."""
|
|
51
39
|
|
|
52
40
|
def __init__(self, *args, **kws):
|
|
53
|
-
super(
|
|
41
|
+
super().__init__(*args, **kws)
|
|
54
42
|
|
|
55
43
|
def __getattr__(self, name):
|
|
56
44
|
return self[name]
|
|
@@ -70,39 +58,36 @@ class Master:
|
|
|
70
58
|
|
|
71
59
|
Parameters
|
|
72
60
|
----------
|
|
73
|
-
|
|
61
|
+
transport_name : str
|
|
74
62
|
XCP transport layer name ['can', 'eth', 'sxi']
|
|
75
63
|
config: dict
|
|
76
64
|
"""
|
|
77
65
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"DISABLE_ERROR_HANDLING": (
|
|
82
|
-
bool,
|
|
83
|
-
False,
|
|
84
|
-
False,
|
|
85
|
-
), # Bypass error-handling for performance reasons.
|
|
86
|
-
"SEED_N_KEY_DLL": (str, False, ""),
|
|
87
|
-
"SEED_N_KEY_DLL_SAME_BIT_WIDTH": (bool, False, False),
|
|
88
|
-
"DISCONNECT_RESPONSE_OPTIONAL": (bool, False, False),
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
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.
|
|
92
69
|
self.ctr = 0
|
|
93
70
|
self.succeeded = True
|
|
94
|
-
self.config =
|
|
95
|
-
self.logger =
|
|
96
|
-
self.logger.setLevel(self.config.get("LOGLEVEL"))
|
|
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,11 +104,13 @@ 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()
|
|
113
|
+
self.slaveProperties.transport_layer = self.transport_name.upper()
|
|
127
114
|
|
|
128
115
|
def __enter__(self):
|
|
129
116
|
"""Context manager entry part."""
|
|
@@ -460,7 +447,7 @@ class Master:
|
|
|
460
447
|
response += data[1 : rem + 1]
|
|
461
448
|
rem = byte_count - len(response)
|
|
462
449
|
else:
|
|
463
|
-
|
|
450
|
+
short_sleep()
|
|
464
451
|
return response
|
|
465
452
|
|
|
466
453
|
@wrapped
|
|
@@ -580,7 +567,7 @@ class Master:
|
|
|
580
567
|
address is not included because of services implicitly setting address information like :meth:`getID` .
|
|
581
568
|
"""
|
|
582
569
|
if limitPayload and limitPayload < 8:
|
|
583
|
-
raise ValueError("Payload must be at least 8 bytes - given: {}"
|
|
570
|
+
raise ValueError(f"Payload must be at least 8 bytes - given: {limitPayload}")
|
|
584
571
|
|
|
585
572
|
slaveBlockMode = self.slaveProperties.slaveBlockMode
|
|
586
573
|
if slaveBlockMode:
|
|
@@ -977,14 +964,15 @@ class Master:
|
|
|
977
964
|
# DAQ
|
|
978
965
|
|
|
979
966
|
@wrapped
|
|
980
|
-
def setDaqPtr(self, daqListNumber, odtNumber, odtEntryNumber):
|
|
967
|
+
def setDaqPtr(self, daqListNumber: int, odtNumber: int, odtEntryNumber: int):
|
|
981
968
|
self.currentDaqPtr = types.DaqPtr(daqListNumber, odtNumber, odtEntryNumber) # Needed for errorhandling.
|
|
982
969
|
daqList = self.WORD_pack(daqListNumber)
|
|
983
970
|
response = self.transport.request(types.Command.SET_DAQ_PTR, 0, *daqList, odtNumber, odtEntryNumber)
|
|
971
|
+
self.stim.setDaqPtr(daqListNumber, odtNumber, odtEntryNumber)
|
|
984
972
|
return response
|
|
985
973
|
|
|
986
974
|
@wrapped
|
|
987
|
-
def clearDaqList(self, daqListNumber):
|
|
975
|
+
def clearDaqList(self, daqListNumber: int):
|
|
988
976
|
"""Clear DAQ list configuration.
|
|
989
977
|
|
|
990
978
|
Parameters
|
|
@@ -992,10 +980,12 @@ class Master:
|
|
|
992
980
|
daqListNumber : int
|
|
993
981
|
"""
|
|
994
982
|
daqList = self.WORD_pack(daqListNumber)
|
|
995
|
-
|
|
983
|
+
result = self.transport.request(types.Command.CLEAR_DAQ_LIST, 0, *daqList)
|
|
984
|
+
self.stim.clearDaqList(daqListNumber)
|
|
985
|
+
return result
|
|
996
986
|
|
|
997
987
|
@wrapped
|
|
998
|
-
def writeDaq(self, bitOffset, entrySize, addressExt, address):
|
|
988
|
+
def writeDaq(self, bitOffset: int, entrySize: int, addressExt: int, address: int):
|
|
999
989
|
"""Write element in ODT entry.
|
|
1000
990
|
|
|
1001
991
|
Parameters
|
|
@@ -1008,12 +998,15 @@ class Master:
|
|
|
1008
998
|
address : int
|
|
1009
999
|
"""
|
|
1010
1000
|
addr = self.DWORD_pack(address)
|
|
1011
|
-
|
|
1001
|
+
result = self.transport.request(types.Command.WRITE_DAQ, bitOffset, entrySize, addressExt, *addr)
|
|
1002
|
+
self.stim.writeDaq(bitOffset, entrySize, addressExt, address)
|
|
1003
|
+
return result
|
|
1012
1004
|
|
|
1013
1005
|
@wrapped
|
|
1014
1006
|
def setDaqListMode(self, mode, daqListNumber, eventChannelNumber, prescaler, priority):
|
|
1015
1007
|
dln = self.WORD_pack(daqListNumber)
|
|
1016
1008
|
ecn = self.WORD_pack(eventChannelNumber)
|
|
1009
|
+
self.stim.setDaqListMode(mode, daqListNumber, eventChannelNumber, prescaler, priority)
|
|
1017
1010
|
return self.transport.request(types.Command.SET_DAQ_LIST_MODE, mode, *dln, *ecn, prescaler, priority)
|
|
1018
1011
|
|
|
1019
1012
|
@wrapped
|
|
@@ -1033,7 +1026,7 @@ class Master:
|
|
|
1033
1026
|
return types.GetDaqListModeResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1034
1027
|
|
|
1035
1028
|
@wrapped
|
|
1036
|
-
def startStopDaqList(self, mode, daqListNumber):
|
|
1029
|
+
def startStopDaqList(self, mode: int, daqListNumber: int):
|
|
1037
1030
|
"""Start /stop/select DAQ list.
|
|
1038
1031
|
|
|
1039
1032
|
Parameters
|
|
@@ -1046,7 +1039,10 @@ class Master:
|
|
|
1046
1039
|
"""
|
|
1047
1040
|
dln = self.WORD_pack(daqListNumber)
|
|
1048
1041
|
response = self.transport.request(types.Command.START_STOP_DAQ_LIST, mode, *dln)
|
|
1049
|
-
|
|
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
|
|
1050
1046
|
|
|
1051
1047
|
@wrapped
|
|
1052
1048
|
def startStopSynch(self, mode):
|
|
@@ -1059,7 +1055,9 @@ class Master:
|
|
|
1059
1055
|
1 = start selected
|
|
1060
1056
|
2 = stop selected
|
|
1061
1057
|
"""
|
|
1062
|
-
|
|
1058
|
+
res = self.transport.request(types.Command.START_STOP_SYNCH, mode)
|
|
1059
|
+
self.stim.startStopSynch(mode)
|
|
1060
|
+
return res
|
|
1063
1061
|
|
|
1064
1062
|
@wrapped
|
|
1065
1063
|
def writeDaqMultiple(self, daqElements):
|
|
@@ -1070,7 +1068,7 @@ class Master:
|
|
|
1070
1068
|
daqElements : list of `dict` containing the following keys: *bitOffset*, *size*, *address*, *addressExt*.
|
|
1071
1069
|
"""
|
|
1072
1070
|
if len(daqElements) > self.slaveProperties.maxWriteDaqMultipleElements:
|
|
1073
|
-
raise ValueError("At most {} daqElements are permitted."
|
|
1071
|
+
raise ValueError(f"At most {self.slaveProperties.maxWriteDaqMultipleElements} daqElements are permitted.")
|
|
1074
1072
|
data = bytearray()
|
|
1075
1073
|
data.append(len(daqElements))
|
|
1076
1074
|
|
|
@@ -1217,10 +1215,12 @@ class Master:
|
|
|
1217
1215
|
@wrapped
|
|
1218
1216
|
def freeDaq(self):
|
|
1219
1217
|
"""Clear dynamic DAQ configuration."""
|
|
1220
|
-
|
|
1218
|
+
result = self.transport.request(types.Command.FREE_DAQ)
|
|
1219
|
+
self.stim.freeDaq()
|
|
1220
|
+
return result
|
|
1221
1221
|
|
|
1222
1222
|
@wrapped
|
|
1223
|
-
def allocDaq(self, daqCount):
|
|
1223
|
+
def allocDaq(self, daqCount: int):
|
|
1224
1224
|
"""Allocate DAQ lists.
|
|
1225
1225
|
|
|
1226
1226
|
Parameters
|
|
@@ -1229,17 +1229,23 @@ class Master:
|
|
|
1229
1229
|
number of DAQ lists to be allocated
|
|
1230
1230
|
"""
|
|
1231
1231
|
dq = self.WORD_pack(daqCount)
|
|
1232
|
-
|
|
1232
|
+
result = self.transport.request(types.Command.ALLOC_DAQ, 0, *dq)
|
|
1233
|
+
self.stim.allocDaq(daqCount)
|
|
1234
|
+
return result
|
|
1233
1235
|
|
|
1234
1236
|
@wrapped
|
|
1235
|
-
def allocOdt(self, daqListNumber, odtCount):
|
|
1237
|
+
def allocOdt(self, daqListNumber: int, odtCount: int):
|
|
1236
1238
|
dln = self.WORD_pack(daqListNumber)
|
|
1237
|
-
|
|
1239
|
+
result = self.transport.request(types.Command.ALLOC_ODT, 0, *dln, odtCount)
|
|
1240
|
+
self.stim.allocOdt(daqListNumber, odtCount)
|
|
1241
|
+
return result
|
|
1238
1242
|
|
|
1239
1243
|
@wrapped
|
|
1240
|
-
def allocOdtEntry(self, daqListNumber, odtNumber, odtEntriesCount):
|
|
1244
|
+
def allocOdtEntry(self, daqListNumber: int, odtNumber: int, odtEntriesCount: int):
|
|
1241
1245
|
dln = self.WORD_pack(daqListNumber)
|
|
1242
|
-
|
|
1246
|
+
result = self.transport.request(types.Command.ALLOC_ODT_ENTRY, 0, *dln, odtNumber, odtEntriesCount)
|
|
1247
|
+
self.stim.allocOdtEntry(daqListNumber, odtNumber, odtEntriesCount)
|
|
1248
|
+
return result
|
|
1243
1249
|
|
|
1244
1250
|
# PGM
|
|
1245
1251
|
@wrapped
|
|
@@ -1621,12 +1627,13 @@ class Master:
|
|
|
1621
1627
|
@broadcasted
|
|
1622
1628
|
@wrapped
|
|
1623
1629
|
def getSlaveID(self, mode: int):
|
|
1624
|
-
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)
|
|
1625
1632
|
|
|
1626
1633
|
def getDaqId(self, daqListNumber: int):
|
|
1627
1634
|
response = self.transportLayerCmd(types.TransportLayerCommands.GET_DAQ_ID, *self.WORD_pack(daqListNumber))
|
|
1628
|
-
if response:
|
|
1629
|
-
|
|
1635
|
+
# if response:
|
|
1636
|
+
return types.GetDaqIdResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1630
1637
|
|
|
1631
1638
|
def setDaqId(self, daqListNumber: int, identifier: int):
|
|
1632
1639
|
response = self.transportLayerCmd(
|
|
@@ -1650,11 +1657,11 @@ class Master:
|
|
|
1650
1657
|
"""
|
|
1651
1658
|
self.setMta(addr)
|
|
1652
1659
|
cs = self.buildChecksum(length)
|
|
1653
|
-
self.logger.debug("BuildChecksum return'd: 0x{:08X} [{}]"
|
|
1660
|
+
self.logger.debug(f"BuildChecksum return'd: 0x{cs.checksum:08X} [{cs.checksumType}]")
|
|
1654
1661
|
self.setMta(addr)
|
|
1655
1662
|
data = self.fetch(length)
|
|
1656
1663
|
cc = checksum.check(data, cs.checksumType)
|
|
1657
|
-
self.logger.debug("Our checksum : 0x{:08X}"
|
|
1664
|
+
self.logger.debug(f"Our checksum : 0x{cc:08X}")
|
|
1658
1665
|
return cs.checksum == cc
|
|
1659
1666
|
|
|
1660
1667
|
def getDaqInfo(self):
|
|
@@ -1696,10 +1703,18 @@ class Master:
|
|
|
1696
1703
|
},
|
|
1697
1704
|
}
|
|
1698
1705
|
result["resolution"] = resolutionInfo
|
|
1699
|
-
|
|
1700
1706
|
channels = []
|
|
1707
|
+
daq_events = []
|
|
1701
1708
|
for ecn in range(dpi.maxEventChannel):
|
|
1702
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"]
|
|
1703
1718
|
name = self.fetch(eci.eventChannelNameLength)
|
|
1704
1719
|
if name:
|
|
1705
1720
|
name = decode_bytes(name)
|
|
@@ -1710,14 +1725,27 @@ class Master:
|
|
|
1710
1725
|
"cycle": eci["eventChannelTimeCycle"],
|
|
1711
1726
|
"maxDaqList": eci["maxDaqList"],
|
|
1712
1727
|
"properties": {
|
|
1713
|
-
"consistency":
|
|
1714
|
-
"daq":
|
|
1715
|
-
"stim":
|
|
1716
|
-
"packed":
|
|
1728
|
+
"consistency": consistency,
|
|
1729
|
+
"daq": daq_supported,
|
|
1730
|
+
"stim": stim_supported,
|
|
1731
|
+
"packed": packed_supported,
|
|
1717
1732
|
},
|
|
1718
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)
|
|
1719
1746
|
channels.append(channel)
|
|
1720
1747
|
result["channels"] = channels
|
|
1748
|
+
self.stim.setDaqEventInfo(daq_events)
|
|
1721
1749
|
return result
|
|
1722
1750
|
|
|
1723
1751
|
def getCurrentProtectionStatus(self):
|
|
@@ -1760,12 +1788,14 @@ class Master:
|
|
|
1760
1788
|
In case of DLL related issues.
|
|
1761
1789
|
"""
|
|
1762
1790
|
import re
|
|
1763
|
-
|
|
1791
|
+
|
|
1792
|
+
from pyxcp.dllif import SeedNKeyError, SeedNKeyResult, getKey
|
|
1764
1793
|
|
|
1765
1794
|
MAX_PAYLOAD = self.slaveProperties["maxCto"] - 2
|
|
1766
1795
|
|
|
1767
|
-
|
|
1768
|
-
|
|
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
|
|
1769
1799
|
if resources is None:
|
|
1770
1800
|
result = []
|
|
1771
1801
|
if self.slaveProperties["supportsCalpag"]:
|
|
@@ -1777,11 +1807,10 @@ class Master:
|
|
|
1777
1807
|
if self.slaveProperties["supportsPgm"]:
|
|
1778
1808
|
result.append("pgm")
|
|
1779
1809
|
resources = ",".join(result)
|
|
1780
|
-
protection_status = self.getCurrentProtectionStatus()
|
|
1781
1810
|
resource_names = [r.lower() for r in re.split(r"[ ,]", resources) if r]
|
|
1782
1811
|
for name in resource_names:
|
|
1783
1812
|
if name not in types.RESOURCE_VALUES:
|
|
1784
|
-
raise ValueError("Invalid resource name
|
|
1813
|
+
raise ValueError(f"Invalid resource name {name!r}.")
|
|
1785
1814
|
if not protection_status[name]:
|
|
1786
1815
|
continue
|
|
1787
1816
|
resource_value = types.RESOURCE_VALUES[name]
|
|
@@ -1790,30 +1819,38 @@ class Master:
|
|
|
1790
1819
|
length = result.length
|
|
1791
1820
|
if length == 0:
|
|
1792
1821
|
continue
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
self.
|
|
1802
|
-
self.
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
self.
|
|
1806
|
-
|
|
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
|
+
)
|
|
1807
1842
|
if result == SeedNKeyResult.ACK:
|
|
1808
1843
|
key = list(key)
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
while
|
|
1812
|
-
data = key[
|
|
1813
|
-
|
|
1814
|
-
|
|
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
|
|
1815
1852
|
else:
|
|
1816
|
-
raise SeedNKeyError("SeedAndKey DLL returned: {
|
|
1853
|
+
raise SeedNKeyError(f"SeedAndKey DLL returned: {SeedNKeyResult(result).name!r}")
|
|
1817
1854
|
|
|
1818
1855
|
def identifier(self, id_value: int) -> str:
|
|
1819
1856
|
"""Return the identifier for the given value.
|
|
@@ -1891,18 +1928,61 @@ class Master:
|
|
|
1891
1928
|
|
|
1892
1929
|
gen = make_generator(scan_ranges)
|
|
1893
1930
|
for id_value, name in gen:
|
|
1894
|
-
response =
|
|
1895
|
-
|
|
1896
|
-
response = self.identifier(id_value)
|
|
1897
|
-
except types.XcpResponseError:
|
|
1898
|
-
# don't depend on confirming implementation, i.e.: ID not implemented ==> empty response.
|
|
1899
|
-
pass
|
|
1900
|
-
except Exception:
|
|
1901
|
-
raise
|
|
1902
|
-
if response:
|
|
1931
|
+
status, response = self.try_command(self.identifier, id_value)
|
|
1932
|
+
if status == types.TryCommandResult.OK and response:
|
|
1903
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}")
|
|
1904
1938
|
return result
|
|
1905
1939
|
|
|
1940
|
+
@property
|
|
1941
|
+
def start_datetime(self) -> int:
|
|
1942
|
+
""""""
|
|
1943
|
+
return self.transport.start_datetime
|
|
1944
|
+
|
|
1945
|
+
def try_command(self, cmd: Callable, *args, **kws) -> Tuple[types.TryCommandResult, Any]:
|
|
1946
|
+
"""Call master functions and handle XCP errors more gracefuly.
|
|
1947
|
+
|
|
1948
|
+
Parameter
|
|
1949
|
+
---------
|
|
1950
|
+
cmd: Callable
|
|
1951
|
+
args: list
|
|
1952
|
+
variable length arguments to `cmd`.
|
|
1953
|
+
kws: dict
|
|
1954
|
+
keyword arguments to `cmd`.
|
|
1955
|
+
|
|
1956
|
+
`extra_msg`: str
|
|
1957
|
+
Additional info to log message (not passed to `cmd`).
|
|
1958
|
+
|
|
1959
|
+
Returns
|
|
1960
|
+
-------
|
|
1961
|
+
|
|
1962
|
+
Note
|
|
1963
|
+
----
|
|
1964
|
+
Mainly used for plug-and-play applications, e.g. `id_scanner` may confronted with `ERR_OUT_OF_RANGE` errors, which
|
|
1965
|
+
is normal for this kind of applications -- or to test for optional commands.
|
|
1966
|
+
Use carefuly not to hide serious error causes.
|
|
1967
|
+
"""
|
|
1968
|
+
try:
|
|
1969
|
+
extra_msg: Optional[str] = kws.get("extra_msg")
|
|
1970
|
+
if extra_msg:
|
|
1971
|
+
kws.pop("extra_msg")
|
|
1972
|
+
res = cmd(*args, **kws)
|
|
1973
|
+
except SystemExit as e:
|
|
1974
|
+
if e.error_code == types.XcpError.ERR_CMD_UNKNOWN:
|
|
1975
|
+
# This is a rather common use-case, so let the user know that there is some functionality missing.
|
|
1976
|
+
if extra_msg:
|
|
1977
|
+
self.logger.warning(f"Optional command {cmd.__name__!r} not implemented -- {extra_msg!r}")
|
|
1978
|
+
else:
|
|
1979
|
+
self.logger.warning(f"Optional command {cmd.__name__!r} not implemented.")
|
|
1980
|
+
return (types.TryCommandResult.XCP_ERROR, e)
|
|
1981
|
+
except Exception as e:
|
|
1982
|
+
return (types.TryCommandResult.OTHER_ERROR, e)
|
|
1983
|
+
else:
|
|
1984
|
+
return (types.TryCommandResult.OK, res)
|
|
1985
|
+
|
|
1906
1986
|
|
|
1907
1987
|
def ticks_to_seconds(ticks, resolution):
|
|
1908
1988
|
"""Convert DAQ timestamp/tick value to seconds.
|
|
@@ -1916,6 +1996,7 @@ def ticks_to_seconds(ticks, resolution):
|
|
|
1916
1996
|
warnings.warn(
|
|
1917
1997
|
"ticks_to_seconds() deprecated, use factory :func:`make_tick_converter` instead.",
|
|
1918
1998
|
Warning,
|
|
1999
|
+
stacklevel=1,
|
|
1919
2000
|
)
|
|
1920
2001
|
return (10 ** types.DAQ_TIMESTAMP_UNIT_TO_EXP[resolution.timestampMode.unit]) * resolution.timestampTicks * ticks
|
|
1921
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"))
|