pyxcp 0.21.10__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 +201 -119
- 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.10.dist-info → pyxcp-0.22.23.dist-info}/METADATA +28 -23
- pyxcp-0.22.23.dist-info/RECORD +134 -0
- {pyxcp-0.21.10.dist-info → pyxcp-0.22.23.dist-info}/WHEEL +1 -1
- {pyxcp-0.21.10.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.10.dist-info/RECORD +0 -147
- rekorder.cp310-win_amd64.pyd +0 -0
- {pyxcp-0.21.10.dist-info/licenses → pyxcp-0.22.23.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;
|
|
@@ -12,33 +11,23 @@ import logging
|
|
|
12
11
|
import struct
|
|
13
12
|
import traceback
|
|
14
13
|
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
|
|
14
|
+
from typing import Any, Callable, Collection, Dict, List, Optional, Tuple
|
|
15
|
+
|
|
16
|
+
from pyxcp import checksum, types
|
|
17
|
+
from pyxcp.constants import (
|
|
18
|
+
makeBytePacker,
|
|
19
|
+
makeByteUnpacker,
|
|
20
|
+
makeDLongPacker,
|
|
21
|
+
makeDLongUnpacker,
|
|
22
|
+
makeDWordPacker,
|
|
23
|
+
makeDWordUnpacker,
|
|
24
|
+
makeWordPacker,
|
|
25
|
+
makeWordUnpacker,
|
|
26
|
+
)
|
|
27
|
+
from pyxcp.daq_stim.stim import DaqEventInfo, Stim
|
|
28
|
+
from pyxcp.master.errorhandler import SystemExit, disable_error_handling, wrapped
|
|
29
|
+
from pyxcp.transport.base import create_transport
|
|
30
|
+
from pyxcp.utils import decode_bytes, delay, short_sleep
|
|
42
31
|
|
|
43
32
|
|
|
44
33
|
def broadcasted(func: Callable):
|
|
@@ -50,7 +39,7 @@ class SlaveProperties(dict):
|
|
|
50
39
|
"""Container class for fixed parameters, like byte-order, maxCTO, ..."""
|
|
51
40
|
|
|
52
41
|
def __init__(self, *args, **kws):
|
|
53
|
-
super(
|
|
42
|
+
super().__init__(*args, **kws)
|
|
54
43
|
|
|
55
44
|
def __getattr__(self, name):
|
|
56
45
|
return self[name]
|
|
@@ -70,39 +59,34 @@ class Master:
|
|
|
70
59
|
|
|
71
60
|
Parameters
|
|
72
61
|
----------
|
|
73
|
-
|
|
62
|
+
transport_name : str
|
|
74
63
|
XCP transport layer name ['can', 'eth', 'sxi']
|
|
75
64
|
config: dict
|
|
76
65
|
"""
|
|
77
66
|
|
|
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):
|
|
67
|
+
def __init__(self, transport_name: Optional[str], config, policy=None, transport_layer_interface=None):
|
|
68
|
+
if transport_name is None:
|
|
69
|
+
raise ValueError("No transport-layer selected") # Never reached -- to keep type-checkers happy.
|
|
92
70
|
self.ctr = 0
|
|
93
71
|
self.succeeded = True
|
|
94
|
-
self.config =
|
|
95
|
-
self.logger = logging.getLogger("
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
self.transport =
|
|
100
|
-
self.
|
|
72
|
+
self.config = config.general
|
|
73
|
+
self.logger = logging.getLogger("PyXCP")
|
|
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
|
+
self.stim = Stim(self.config.stim_support)
|
|
79
|
+
self.stim.clear()
|
|
80
|
+
self.stim.set_policy_feeder(self.transport.policy.feed)
|
|
81
|
+
self.stim.set_frame_sender(self.transport.block_request)
|
|
101
82
|
|
|
102
83
|
# In some cases the transport-layer needs to communicate with us.
|
|
103
84
|
self.transport.parent = self
|
|
104
85
|
self.service = None
|
|
105
86
|
|
|
87
|
+
# Policies may issue XCP commands on there own.
|
|
88
|
+
self.transport.policy.xcp_master = self
|
|
89
|
+
|
|
106
90
|
# (D)Word (un-)packers are byte-order dependent
|
|
107
91
|
# -- byte-order is returned by CONNECT_Resp (COMM_MODE_BASIC)
|
|
108
92
|
self.BYTE_pack = None
|
|
@@ -119,11 +103,13 @@ class Master:
|
|
|
119
103
|
self.mta = types.MtaType(None, None)
|
|
120
104
|
self.currentDaqPtr = None
|
|
121
105
|
self.currentProtectionStatus = None
|
|
122
|
-
self.
|
|
123
|
-
self.
|
|
124
|
-
self.
|
|
106
|
+
self.seed_n_key_dll = self.config.seed_n_key_dll
|
|
107
|
+
self.seed_n_key_function = self.config.seed_n_key_function
|
|
108
|
+
self.seed_n_key_dll_same_bit_width = self.config.seed_n_key_dll_same_bit_width
|
|
109
|
+
self.disconnect_response_optional = self.config.disconnect_response_optional
|
|
125
110
|
self.slaveProperties = SlaveProperties()
|
|
126
111
|
self.slaveProperties.pgmProcessor = SlaveProperties()
|
|
112
|
+
self.slaveProperties.transport_layer = self.transport_name.upper()
|
|
127
113
|
|
|
128
114
|
def __enter__(self):
|
|
129
115
|
"""Context manager entry part."""
|
|
@@ -460,7 +446,7 @@ class Master:
|
|
|
460
446
|
response += data[1 : rem + 1]
|
|
461
447
|
rem = byte_count - len(response)
|
|
462
448
|
else:
|
|
463
|
-
|
|
449
|
+
short_sleep()
|
|
464
450
|
return response
|
|
465
451
|
|
|
466
452
|
@wrapped
|
|
@@ -580,7 +566,7 @@ class Master:
|
|
|
580
566
|
address is not included because of services implicitly setting address information like :meth:`getID` .
|
|
581
567
|
"""
|
|
582
568
|
if limitPayload and limitPayload < 8:
|
|
583
|
-
raise ValueError("Payload must be at least 8 bytes - given: {}"
|
|
569
|
+
raise ValueError(f"Payload must be at least 8 bytes - given: {limitPayload}")
|
|
584
570
|
|
|
585
571
|
slaveBlockMode = self.slaveProperties.slaveBlockMode
|
|
586
572
|
if slaveBlockMode:
|
|
@@ -602,7 +588,7 @@ class Master:
|
|
|
602
588
|
|
|
603
589
|
pull = fetch # fetch() may be completely replaced by pull() someday.
|
|
604
590
|
|
|
605
|
-
def push(self, address: int, data: bytes, callback=None):
|
|
591
|
+
def push(self, address: int, address_ext: int, data: bytes, callback=None):
|
|
606
592
|
"""Convenience function for data-transfer from master to slave.
|
|
607
593
|
(Not part of the XCP Specification).
|
|
608
594
|
|
|
@@ -618,6 +604,7 @@ class Master:
|
|
|
618
604
|
"""
|
|
619
605
|
self._generalized_downloader(
|
|
620
606
|
address=address,
|
|
607
|
+
address_ext=address_ext,
|
|
621
608
|
data=data,
|
|
622
609
|
maxCto=self.slaveProperties.maxCto,
|
|
623
610
|
maxBs=self.slaveProperties.maxBs,
|
|
@@ -657,6 +644,7 @@ class Master:
|
|
|
657
644
|
def _generalized_downloader(
|
|
658
645
|
self,
|
|
659
646
|
address: int,
|
|
647
|
+
address_ext: int,
|
|
660
648
|
data: bytes,
|
|
661
649
|
maxCto: int,
|
|
662
650
|
maxBs: int,
|
|
@@ -667,7 +655,7 @@ class Master:
|
|
|
667
655
|
callback=None,
|
|
668
656
|
):
|
|
669
657
|
""" """
|
|
670
|
-
self.setMta(address)
|
|
658
|
+
self.setMta(address, address_ext)
|
|
671
659
|
minSt /= 10000.0
|
|
672
660
|
block_downloader = functools.partial(
|
|
673
661
|
self._block_downloader,
|
|
@@ -977,14 +965,15 @@ class Master:
|
|
|
977
965
|
# DAQ
|
|
978
966
|
|
|
979
967
|
@wrapped
|
|
980
|
-
def setDaqPtr(self, daqListNumber, odtNumber, odtEntryNumber):
|
|
968
|
+
def setDaqPtr(self, daqListNumber: int, odtNumber: int, odtEntryNumber: int):
|
|
981
969
|
self.currentDaqPtr = types.DaqPtr(daqListNumber, odtNumber, odtEntryNumber) # Needed for errorhandling.
|
|
982
970
|
daqList = self.WORD_pack(daqListNumber)
|
|
983
971
|
response = self.transport.request(types.Command.SET_DAQ_PTR, 0, *daqList, odtNumber, odtEntryNumber)
|
|
972
|
+
self.stim.setDaqPtr(daqListNumber, odtNumber, odtEntryNumber)
|
|
984
973
|
return response
|
|
985
974
|
|
|
986
975
|
@wrapped
|
|
987
|
-
def clearDaqList(self, daqListNumber):
|
|
976
|
+
def clearDaqList(self, daqListNumber: int):
|
|
988
977
|
"""Clear DAQ list configuration.
|
|
989
978
|
|
|
990
979
|
Parameters
|
|
@@ -992,10 +981,12 @@ class Master:
|
|
|
992
981
|
daqListNumber : int
|
|
993
982
|
"""
|
|
994
983
|
daqList = self.WORD_pack(daqListNumber)
|
|
995
|
-
|
|
984
|
+
result = self.transport.request(types.Command.CLEAR_DAQ_LIST, 0, *daqList)
|
|
985
|
+
self.stim.clearDaqList(daqListNumber)
|
|
986
|
+
return result
|
|
996
987
|
|
|
997
988
|
@wrapped
|
|
998
|
-
def writeDaq(self, bitOffset, entrySize, addressExt, address):
|
|
989
|
+
def writeDaq(self, bitOffset: int, entrySize: int, addressExt: int, address: int):
|
|
999
990
|
"""Write element in ODT entry.
|
|
1000
991
|
|
|
1001
992
|
Parameters
|
|
@@ -1008,12 +999,15 @@ class Master:
|
|
|
1008
999
|
address : int
|
|
1009
1000
|
"""
|
|
1010
1001
|
addr = self.DWORD_pack(address)
|
|
1011
|
-
|
|
1002
|
+
result = self.transport.request(types.Command.WRITE_DAQ, bitOffset, entrySize, addressExt, *addr)
|
|
1003
|
+
self.stim.writeDaq(bitOffset, entrySize, addressExt, address)
|
|
1004
|
+
return result
|
|
1012
1005
|
|
|
1013
1006
|
@wrapped
|
|
1014
1007
|
def setDaqListMode(self, mode, daqListNumber, eventChannelNumber, prescaler, priority):
|
|
1015
1008
|
dln = self.WORD_pack(daqListNumber)
|
|
1016
1009
|
ecn = self.WORD_pack(eventChannelNumber)
|
|
1010
|
+
self.stim.setDaqListMode(mode, daqListNumber, eventChannelNumber, prescaler, priority)
|
|
1017
1011
|
return self.transport.request(types.Command.SET_DAQ_LIST_MODE, mode, *dln, *ecn, prescaler, priority)
|
|
1018
1012
|
|
|
1019
1013
|
@wrapped
|
|
@@ -1033,7 +1027,7 @@ class Master:
|
|
|
1033
1027
|
return types.GetDaqListModeResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1034
1028
|
|
|
1035
1029
|
@wrapped
|
|
1036
|
-
def startStopDaqList(self, mode, daqListNumber):
|
|
1030
|
+
def startStopDaqList(self, mode: int, daqListNumber: int):
|
|
1037
1031
|
"""Start /stop/select DAQ list.
|
|
1038
1032
|
|
|
1039
1033
|
Parameters
|
|
@@ -1046,7 +1040,10 @@ class Master:
|
|
|
1046
1040
|
"""
|
|
1047
1041
|
dln = self.WORD_pack(daqListNumber)
|
|
1048
1042
|
response = self.transport.request(types.Command.START_STOP_DAQ_LIST, mode, *dln)
|
|
1049
|
-
|
|
1043
|
+
self.stim.startStopDaqList(mode, daqListNumber)
|
|
1044
|
+
firstPid = types.StartStopDaqListResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1045
|
+
self.stim.set_first_pid(daqListNumber, firstPid.firstPid)
|
|
1046
|
+
return firstPid
|
|
1050
1047
|
|
|
1051
1048
|
@wrapped
|
|
1052
1049
|
def startStopSynch(self, mode):
|
|
@@ -1059,7 +1056,9 @@ class Master:
|
|
|
1059
1056
|
1 = start selected
|
|
1060
1057
|
2 = stop selected
|
|
1061
1058
|
"""
|
|
1062
|
-
|
|
1059
|
+
res = self.transport.request(types.Command.START_STOP_SYNCH, mode)
|
|
1060
|
+
self.stim.startStopSynch(mode)
|
|
1061
|
+
return res
|
|
1063
1062
|
|
|
1064
1063
|
@wrapped
|
|
1065
1064
|
def writeDaqMultiple(self, daqElements):
|
|
@@ -1070,7 +1069,7 @@ class Master:
|
|
|
1070
1069
|
daqElements : list of `dict` containing the following keys: *bitOffset*, *size*, *address*, *addressExt*.
|
|
1071
1070
|
"""
|
|
1072
1071
|
if len(daqElements) > self.slaveProperties.maxWriteDaqMultipleElements:
|
|
1073
|
-
raise ValueError("At most {} daqElements are permitted."
|
|
1072
|
+
raise ValueError(f"At most {self.slaveProperties.maxWriteDaqMultipleElements} daqElements are permitted.")
|
|
1074
1073
|
data = bytearray()
|
|
1075
1074
|
data.append(len(daqElements))
|
|
1076
1075
|
|
|
@@ -1217,10 +1216,12 @@ class Master:
|
|
|
1217
1216
|
@wrapped
|
|
1218
1217
|
def freeDaq(self):
|
|
1219
1218
|
"""Clear dynamic DAQ configuration."""
|
|
1220
|
-
|
|
1219
|
+
result = self.transport.request(types.Command.FREE_DAQ)
|
|
1220
|
+
self.stim.freeDaq()
|
|
1221
|
+
return result
|
|
1221
1222
|
|
|
1222
1223
|
@wrapped
|
|
1223
|
-
def allocDaq(self, daqCount):
|
|
1224
|
+
def allocDaq(self, daqCount: int):
|
|
1224
1225
|
"""Allocate DAQ lists.
|
|
1225
1226
|
|
|
1226
1227
|
Parameters
|
|
@@ -1229,17 +1230,23 @@ class Master:
|
|
|
1229
1230
|
number of DAQ lists to be allocated
|
|
1230
1231
|
"""
|
|
1231
1232
|
dq = self.WORD_pack(daqCount)
|
|
1232
|
-
|
|
1233
|
+
result = self.transport.request(types.Command.ALLOC_DAQ, 0, *dq)
|
|
1234
|
+
self.stim.allocDaq(daqCount)
|
|
1235
|
+
return result
|
|
1233
1236
|
|
|
1234
1237
|
@wrapped
|
|
1235
|
-
def allocOdt(self, daqListNumber, odtCount):
|
|
1238
|
+
def allocOdt(self, daqListNumber: int, odtCount: int):
|
|
1236
1239
|
dln = self.WORD_pack(daqListNumber)
|
|
1237
|
-
|
|
1240
|
+
result = self.transport.request(types.Command.ALLOC_ODT, 0, *dln, odtCount)
|
|
1241
|
+
self.stim.allocOdt(daqListNumber, odtCount)
|
|
1242
|
+
return result
|
|
1238
1243
|
|
|
1239
1244
|
@wrapped
|
|
1240
|
-
def allocOdtEntry(self, daqListNumber, odtNumber, odtEntriesCount):
|
|
1245
|
+
def allocOdtEntry(self, daqListNumber: int, odtNumber: int, odtEntriesCount: int):
|
|
1241
1246
|
dln = self.WORD_pack(daqListNumber)
|
|
1242
|
-
|
|
1247
|
+
result = self.transport.request(types.Command.ALLOC_ODT_ENTRY, 0, *dln, odtNumber, odtEntriesCount)
|
|
1248
|
+
self.stim.allocOdtEntry(daqListNumber, odtNumber, odtEntriesCount)
|
|
1249
|
+
return result
|
|
1243
1250
|
|
|
1244
1251
|
# PGM
|
|
1245
1252
|
@wrapped
|
|
@@ -1621,12 +1628,13 @@ class Master:
|
|
|
1621
1628
|
@broadcasted
|
|
1622
1629
|
@wrapped
|
|
1623
1630
|
def getSlaveID(self, mode: int):
|
|
1624
|
-
self.transportLayerCmd(types.TransportLayerCommands.GET_SLAVE_ID, "X", "C", "P", mode)
|
|
1631
|
+
response = self.transportLayerCmd(types.TransportLayerCommands.GET_SLAVE_ID, ord("X"), ord("C"), ord("P"), mode)
|
|
1632
|
+
return types.GetSlaveIdResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1625
1633
|
|
|
1626
1634
|
def getDaqId(self, daqListNumber: int):
|
|
1627
1635
|
response = self.transportLayerCmd(types.TransportLayerCommands.GET_DAQ_ID, *self.WORD_pack(daqListNumber))
|
|
1628
|
-
if response:
|
|
1629
|
-
|
|
1636
|
+
# if response:
|
|
1637
|
+
return types.GetDaqIdResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
|
|
1630
1638
|
|
|
1631
1639
|
def setDaqId(self, daqListNumber: int, identifier: int):
|
|
1632
1640
|
response = self.transportLayerCmd(
|
|
@@ -1650,11 +1658,11 @@ class Master:
|
|
|
1650
1658
|
"""
|
|
1651
1659
|
self.setMta(addr)
|
|
1652
1660
|
cs = self.buildChecksum(length)
|
|
1653
|
-
self.logger.debug("BuildChecksum return'd: 0x{:08X} [{}]"
|
|
1661
|
+
self.logger.debug(f"BuildChecksum return'd: 0x{cs.checksum:08X} [{cs.checksumType}]")
|
|
1654
1662
|
self.setMta(addr)
|
|
1655
1663
|
data = self.fetch(length)
|
|
1656
1664
|
cc = checksum.check(data, cs.checksumType)
|
|
1657
|
-
self.logger.debug("Our checksum : 0x{:08X}"
|
|
1665
|
+
self.logger.debug(f"Our checksum : 0x{cc:08X}")
|
|
1658
1666
|
return cs.checksum == cc
|
|
1659
1667
|
|
|
1660
1668
|
def getDaqInfo(self):
|
|
@@ -1696,10 +1704,18 @@ class Master:
|
|
|
1696
1704
|
},
|
|
1697
1705
|
}
|
|
1698
1706
|
result["resolution"] = resolutionInfo
|
|
1699
|
-
|
|
1700
1707
|
channels = []
|
|
1708
|
+
daq_events = []
|
|
1701
1709
|
for ecn in range(dpi.maxEventChannel):
|
|
1702
1710
|
eci = self.getDaqEventInfo(ecn)
|
|
1711
|
+
cycle = eci["eventChannelTimeCycle"]
|
|
1712
|
+
maxDaqList = eci["maxDaqList"]
|
|
1713
|
+
priority = eci["eventChannelPriority"]
|
|
1714
|
+
time_unit = eci["eventChannelTimeUnit"]
|
|
1715
|
+
consistency = eci["daqEventProperties"]["consistency"]
|
|
1716
|
+
daq_supported = eci["daqEventProperties"]["daq"]
|
|
1717
|
+
stim_supported = eci["daqEventProperties"]["stim"]
|
|
1718
|
+
packed_supported = eci["daqEventProperties"]["packed"]
|
|
1703
1719
|
name = self.fetch(eci.eventChannelNameLength)
|
|
1704
1720
|
if name:
|
|
1705
1721
|
name = decode_bytes(name)
|
|
@@ -1710,14 +1726,27 @@ class Master:
|
|
|
1710
1726
|
"cycle": eci["eventChannelTimeCycle"],
|
|
1711
1727
|
"maxDaqList": eci["maxDaqList"],
|
|
1712
1728
|
"properties": {
|
|
1713
|
-
"consistency":
|
|
1714
|
-
"daq":
|
|
1715
|
-
"stim":
|
|
1716
|
-
"packed":
|
|
1729
|
+
"consistency": consistency,
|
|
1730
|
+
"daq": daq_supported,
|
|
1731
|
+
"stim": stim_supported,
|
|
1732
|
+
"packed": packed_supported,
|
|
1717
1733
|
},
|
|
1718
1734
|
}
|
|
1735
|
+
daq_event_info = DaqEventInfo(
|
|
1736
|
+
name,
|
|
1737
|
+
types.EVENT_CHANNEL_TIME_UNIT_TO_EXP[time_unit],
|
|
1738
|
+
cycle,
|
|
1739
|
+
maxDaqList,
|
|
1740
|
+
priority,
|
|
1741
|
+
consistency,
|
|
1742
|
+
daq_supported,
|
|
1743
|
+
stim_supported,
|
|
1744
|
+
packed_supported,
|
|
1745
|
+
)
|
|
1746
|
+
daq_events.append(daq_event_info)
|
|
1719
1747
|
channels.append(channel)
|
|
1720
1748
|
result["channels"] = channels
|
|
1749
|
+
self.stim.setDaqEventInfo(daq_events)
|
|
1721
1750
|
return result
|
|
1722
1751
|
|
|
1723
1752
|
def getCurrentProtectionStatus(self):
|
|
@@ -1760,12 +1789,14 @@ class Master:
|
|
|
1760
1789
|
In case of DLL related issues.
|
|
1761
1790
|
"""
|
|
1762
1791
|
import re
|
|
1763
|
-
|
|
1792
|
+
|
|
1793
|
+
from pyxcp.dllif import SeedNKeyError, SeedNKeyResult, getKey
|
|
1764
1794
|
|
|
1765
1795
|
MAX_PAYLOAD = self.slaveProperties["maxCto"] - 2
|
|
1766
1796
|
|
|
1767
|
-
|
|
1768
|
-
|
|
1797
|
+
protection_status = self.getCurrentProtectionStatus()
|
|
1798
|
+
if any(protection_status.values()) and (not (self.seed_n_key_dll or self.seed_n_key_function)):
|
|
1799
|
+
raise RuntimeError("Neither seed-and-key DLL nor function specified, cannot proceed.") # TODO: ConfigurationError
|
|
1769
1800
|
if resources is None:
|
|
1770
1801
|
result = []
|
|
1771
1802
|
if self.slaveProperties["supportsCalpag"]:
|
|
@@ -1777,11 +1808,10 @@ class Master:
|
|
|
1777
1808
|
if self.slaveProperties["supportsPgm"]:
|
|
1778
1809
|
result.append("pgm")
|
|
1779
1810
|
resources = ",".join(result)
|
|
1780
|
-
protection_status = self.getCurrentProtectionStatus()
|
|
1781
1811
|
resource_names = [r.lower() for r in re.split(r"[ ,]", resources) if r]
|
|
1782
1812
|
for name in resource_names:
|
|
1783
1813
|
if name not in types.RESOURCE_VALUES:
|
|
1784
|
-
raise ValueError("Invalid resource name
|
|
1814
|
+
raise ValueError(f"Invalid resource name {name!r}.")
|
|
1785
1815
|
if not protection_status[name]:
|
|
1786
1816
|
continue
|
|
1787
1817
|
resource_value = types.RESOURCE_VALUES[name]
|
|
@@ -1790,30 +1820,38 @@ class Master:
|
|
|
1790
1820
|
length = result.length
|
|
1791
1821
|
if length == 0:
|
|
1792
1822
|
continue
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
self.
|
|
1802
|
-
self.
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
self.
|
|
1806
|
-
|
|
1823
|
+
if length > MAX_PAYLOAD:
|
|
1824
|
+
remaining = length - len(seed)
|
|
1825
|
+
while remaining > 0:
|
|
1826
|
+
result = self.getSeed(types.XcpGetSeedMode.REMAINING, resource_value)
|
|
1827
|
+
seed.extend(list(result.seed))
|
|
1828
|
+
remaining -= result.length
|
|
1829
|
+
self.logger.debug(f"Got seed {seed!r} for resource {resource_value!r}.")
|
|
1830
|
+
if self.seed_n_key_function:
|
|
1831
|
+
key = self.seed_n_key_function(resource_value, bytes(seed))
|
|
1832
|
+
self.logger.debug(f"Using seed and key function {self.seed_n_key_function.__name__!r}().")
|
|
1833
|
+
result = SeedNKeyResult.ACK
|
|
1834
|
+
elif self.seed_n_key_dll:
|
|
1835
|
+
self.logger.debug(f"Using seed and key DLL {self.seed_n_key_dll!r}.")
|
|
1836
|
+
result, key = getKey(
|
|
1837
|
+
self.logger,
|
|
1838
|
+
self.seed_n_key_dll,
|
|
1839
|
+
resource_value,
|
|
1840
|
+
bytes(seed),
|
|
1841
|
+
self.seed_n_key_dll_same_bit_width,
|
|
1842
|
+
)
|
|
1807
1843
|
if result == SeedNKeyResult.ACK:
|
|
1808
1844
|
key = list(key)
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
while
|
|
1812
|
-
data = key[
|
|
1813
|
-
|
|
1814
|
-
|
|
1845
|
+
self.logger.debug(f"Unlocking resource {resource_value!r} with key {key!r}.")
|
|
1846
|
+
remaining = len(key)
|
|
1847
|
+
while key:
|
|
1848
|
+
data = key[:MAX_PAYLOAD]
|
|
1849
|
+
key_len = len(data)
|
|
1850
|
+
self.unlock(remaining, data)
|
|
1851
|
+
key = key[MAX_PAYLOAD:]
|
|
1852
|
+
remaining -= key_len
|
|
1815
1853
|
else:
|
|
1816
|
-
raise SeedNKeyError("SeedAndKey DLL returned: {
|
|
1854
|
+
raise SeedNKeyError(f"SeedAndKey DLL returned: {SeedNKeyResult(result).name!r}")
|
|
1817
1855
|
|
|
1818
1856
|
def identifier(self, id_value: int) -> str:
|
|
1819
1857
|
"""Return the identifier for the given value.
|
|
@@ -1891,18 +1929,61 @@ class Master:
|
|
|
1891
1929
|
|
|
1892
1930
|
gen = make_generator(scan_ranges)
|
|
1893
1931
|
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:
|
|
1932
|
+
status, response = self.try_command(self.identifier, id_value)
|
|
1933
|
+
if status == types.TryCommandResult.OK and response:
|
|
1903
1934
|
result[name] = response
|
|
1935
|
+
elif status == types.TryCommandResult.XCP_ERROR and response.error_code == types.XcpError.ERR_CMD_UNKNOWN:
|
|
1936
|
+
break # Nothing to do here.
|
|
1937
|
+
elif status == types.TryCommandResult.OTHER_ERROR:
|
|
1938
|
+
raise RuntimeError(f"Error while scanning for ID {id_value}: {response!r}")
|
|
1904
1939
|
return result
|
|
1905
1940
|
|
|
1941
|
+
@property
|
|
1942
|
+
def start_datetime(self) -> int:
|
|
1943
|
+
""""""
|
|
1944
|
+
return self.transport.start_datetime
|
|
1945
|
+
|
|
1946
|
+
def try_command(self, cmd: Callable, *args, **kws) -> Tuple[types.TryCommandResult, Any]:
|
|
1947
|
+
"""Call master functions and handle XCP errors more gracefuly.
|
|
1948
|
+
|
|
1949
|
+
Parameter
|
|
1950
|
+
---------
|
|
1951
|
+
cmd: Callable
|
|
1952
|
+
args: list
|
|
1953
|
+
variable length arguments to `cmd`.
|
|
1954
|
+
kws: dict
|
|
1955
|
+
keyword arguments to `cmd`.
|
|
1956
|
+
|
|
1957
|
+
`extra_msg`: str
|
|
1958
|
+
Additional info to log message (not passed to `cmd`).
|
|
1959
|
+
|
|
1960
|
+
Returns
|
|
1961
|
+
-------
|
|
1962
|
+
|
|
1963
|
+
Note
|
|
1964
|
+
----
|
|
1965
|
+
Mainly used for plug-and-play applications, e.g. `id_scanner` may confronted with `ERR_OUT_OF_RANGE` errors, which
|
|
1966
|
+
is normal for this kind of applications -- or to test for optional commands.
|
|
1967
|
+
Use carefuly not to hide serious error causes.
|
|
1968
|
+
"""
|
|
1969
|
+
try:
|
|
1970
|
+
extra_msg: Optional[str] = kws.get("extra_msg")
|
|
1971
|
+
if extra_msg:
|
|
1972
|
+
kws.pop("extra_msg")
|
|
1973
|
+
res = cmd(*args, **kws)
|
|
1974
|
+
except SystemExit as e:
|
|
1975
|
+
if e.error_code == types.XcpError.ERR_CMD_UNKNOWN:
|
|
1976
|
+
# This is a rather common use-case, so let the user know that there is some functionality missing.
|
|
1977
|
+
if extra_msg:
|
|
1978
|
+
self.logger.warning(f"Optional command {cmd.__name__!r} not implemented -- {extra_msg!r}")
|
|
1979
|
+
else:
|
|
1980
|
+
self.logger.warning(f"Optional command {cmd.__name__!r} not implemented.")
|
|
1981
|
+
return (types.TryCommandResult.XCP_ERROR, e)
|
|
1982
|
+
except Exception as e:
|
|
1983
|
+
return (types.TryCommandResult.OTHER_ERROR, e)
|
|
1984
|
+
else:
|
|
1985
|
+
return (types.TryCommandResult.OK, res)
|
|
1986
|
+
|
|
1906
1987
|
|
|
1907
1988
|
def ticks_to_seconds(ticks, resolution):
|
|
1908
1989
|
"""Convert DAQ timestamp/tick value to seconds.
|
|
@@ -1916,6 +1997,7 @@ def ticks_to_seconds(ticks, resolution):
|
|
|
1916
1997
|
warnings.warn(
|
|
1917
1998
|
"ticks_to_seconds() deprecated, use factory :func:`make_tick_converter` instead.",
|
|
1918
1999
|
Warning,
|
|
2000
|
+
stacklevel=1,
|
|
1919
2001
|
)
|
|
1920
2002
|
return (10 ** types.DAQ_TIMESTAMP_UNIT_TO_EXP[resolution.timestampMode.unit]) * resolution.timestampTicks * ticks
|
|
1921
2003
|
|
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):
|