pyxcp 0.21.11__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.

Files changed (168) hide show
  1. CMakeLists.txt +117 -0
  2. pyxcp/__init__.py +12 -20
  3. pyxcp/aml/EtasCANMonitoring.a2l +82 -83
  4. pyxcp/aml/XCP_Common.aml +0 -1
  5. pyxcp/aml/XCPonUSB.aml +1 -1
  6. pyxcp/aml/ifdata_CAN.a2l +0 -1
  7. pyxcp/aml/ifdata_Eth.a2l +0 -1
  8. pyxcp/aml/ifdata_Flx.a2l +0 -1
  9. pyxcp/aml/ifdata_SxI.a2l +0 -1
  10. pyxcp/aml/ifdata_USB.a2l +0 -1
  11. pyxcp/asam/types.py +4 -4
  12. pyxcp/asamkeydll.c +0 -1
  13. pyxcp/checksum.py +0 -1
  14. pyxcp/cmdline.py +30 -53
  15. pyxcp/config/__init__.py +1089 -0
  16. pyxcp/config/legacy.py +120 -0
  17. pyxcp/constants.py +12 -13
  18. pyxcp/cpp_ext/__init__.py +8 -0
  19. pyxcp/cpp_ext/bin.hpp +104 -0
  20. pyxcp/cpp_ext/blockmem.hpp +58 -0
  21. pyxcp/cpp_ext/cpp_ext.cp312-win_amd64.pyd +0 -0
  22. pyxcp/cpp_ext/daqlist.hpp +197 -0
  23. pyxcp/cpp_ext/event.hpp +67 -0
  24. pyxcp/cpp_ext/extension_wrapper.cpp +94 -0
  25. pyxcp/cpp_ext/helper.hpp +264 -0
  26. pyxcp/cpp_ext/mcobject.hpp +241 -0
  27. pyxcp/cpp_ext/tsqueue.hpp +46 -0
  28. pyxcp/daq_stim/__init__.py +226 -0
  29. pyxcp/daq_stim/optimize/__init__.py +67 -0
  30. pyxcp/daq_stim/optimize/binpacking.py +41 -0
  31. pyxcp/daq_stim/scheduler.cpp +28 -0
  32. pyxcp/daq_stim/scheduler.hpp +75 -0
  33. pyxcp/daq_stim/stim.cp312-win_amd64.pyd +0 -0
  34. pyxcp/daq_stim/stim.cpp +13 -0
  35. pyxcp/daq_stim/stim.hpp +604 -0
  36. pyxcp/daq_stim/stim_wrapper.cpp +48 -0
  37. pyxcp/dllif.py +21 -18
  38. pyxcp/errormatrix.py +5 -3
  39. pyxcp/examples/conf_can.toml +4 -2
  40. pyxcp/examples/conf_can_vector.json +9 -9
  41. pyxcp/examples/conf_can_vector.toml +4 -2
  42. pyxcp/examples/conf_eth.toml +5 -2
  43. pyxcp/examples/conf_nixnet.json +18 -18
  44. pyxcp/examples/conf_sxi.json +7 -7
  45. pyxcp/examples/ex_arrow.py +109 -0
  46. pyxcp/examples/ex_mdf.py +124 -0
  47. pyxcp/examples/ex_sqlite.py +128 -0
  48. pyxcp/examples/run_daq.py +146 -0
  49. pyxcp/examples/xcp_policy.py +6 -7
  50. pyxcp/examples/xcp_read_benchmark.py +8 -6
  51. pyxcp/examples/xcp_skel.py +0 -2
  52. pyxcp/examples/xcp_unlock.py +1 -1
  53. pyxcp/examples/xcp_user_supplied_driver.py +1 -2
  54. pyxcp/examples/xcphello.py +6 -3
  55. pyxcp/examples/xcphello_recorder.py +4 -4
  56. pyxcp/master/__init__.py +1 -2
  57. pyxcp/master/errorhandler.py +107 -74
  58. pyxcp/master/master.py +156 -117
  59. pyxcp/py.typed +0 -0
  60. pyxcp/recorder/__init__.py +27 -6
  61. pyxcp/recorder/converter/__init__.py +37 -0
  62. pyxcp/recorder/lz4.c +129 -51
  63. pyxcp/recorder/lz4.h +45 -28
  64. pyxcp/recorder/lz4hc.c +560 -156
  65. pyxcp/recorder/lz4hc.h +1 -1
  66. pyxcp/recorder/mio.hpp +721 -767
  67. pyxcp/recorder/reader.hpp +139 -0
  68. pyxcp/recorder/reco.py +5 -8
  69. pyxcp/recorder/rekorder.cp312-win_amd64.pyd +0 -0
  70. pyxcp/recorder/rekorder.cpp +18 -22
  71. pyxcp/recorder/rekorder.hpp +200 -587
  72. pyxcp/recorder/setup.py +11 -10
  73. pyxcp/recorder/test_reko.py +2 -3
  74. pyxcp/recorder/unfolder.hpp +1249 -0
  75. pyxcp/recorder/wrap.cpp +171 -9
  76. pyxcp/recorder/writer.hpp +302 -0
  77. pyxcp/scripts/pyxcp_probe_can_drivers.py +0 -2
  78. pyxcp/scripts/xcp_fetch_a2l.py +15 -10
  79. pyxcp/scripts/xcp_id_scanner.py +2 -6
  80. pyxcp/scripts/xcp_info.py +91 -71
  81. pyxcp/scripts/xcp_profile.py +27 -0
  82. pyxcp/stim/__init__.py +0 -0
  83. pyxcp/tests/test_asam_types.py +2 -2
  84. pyxcp/tests/test_binpacking.py +184 -0
  85. pyxcp/tests/test_can.py +1132 -38
  86. pyxcp/tests/test_checksum.py +2 -1
  87. pyxcp/tests/test_daq.py +188 -0
  88. pyxcp/tests/test_frame_padding.py +3 -3
  89. pyxcp/tests/test_master.py +42 -31
  90. pyxcp/tests/test_transport.py +12 -12
  91. pyxcp/tests/test_utils.py +2 -5
  92. pyxcp/timing.py +0 -2
  93. pyxcp/transport/__init__.py +9 -9
  94. pyxcp/transport/base.py +142 -124
  95. pyxcp/transport/base_transport.hpp +0 -0
  96. pyxcp/transport/can.py +194 -167
  97. pyxcp/transport/eth.py +80 -82
  98. pyxcp/transport/sxi.py +106 -60
  99. pyxcp/transport/transport_wrapper.cpp +0 -0
  100. pyxcp/transport/usb_transport.py +65 -83
  101. pyxcp/types.py +61 -20
  102. pyxcp/utils.py +47 -16
  103. pyxcp/vector/map.py +1 -3
  104. {pyxcp-0.21.11.dist-info → pyxcp-0.22.1.dist-info}/METADATA +27 -22
  105. pyxcp-0.22.1.dist-info/RECORD +126 -0
  106. {pyxcp-0.21.11.dist-info → pyxcp-0.22.1.dist-info}/WHEEL +1 -1
  107. {pyxcp-0.21.11.dist-info → pyxcp-0.22.1.dist-info}/entry_points.txt +1 -0
  108. pyxcp/config.py +0 -57
  109. pyxcp/cxx/asynchiofactory.hpp +0 -24
  110. pyxcp/cxx/blocking_client.cpp +0 -44
  111. pyxcp/cxx/blocking_socket.cpp +0 -43
  112. pyxcp/cxx/blocking_socket.hpp +0 -558
  113. pyxcp/cxx/concurrent_queue.hpp +0 -60
  114. pyxcp/cxx/eth.hpp +0 -57
  115. pyxcp/cxx/exceptions.hpp +0 -30
  116. pyxcp/cxx/iasyncioservice.hpp +0 -31
  117. pyxcp/cxx/iresource.hpp +0 -17
  118. pyxcp/cxx/isocket.hpp +0 -22
  119. pyxcp/cxx/linux/epoll.cpp +0 -51
  120. pyxcp/cxx/linux/epoll.hpp +0 -87
  121. pyxcp/cxx/linux/lit_tester.cpp +0 -19
  122. pyxcp/cxx/linux/socket.hpp +0 -234
  123. pyxcp/cxx/linux/timeout.hpp +0 -81
  124. pyxcp/cxx/memoryblock.hpp +0 -42
  125. pyxcp/cxx/pool.hpp +0 -81
  126. pyxcp/cxx/poolmgr.cpp +0 -6
  127. pyxcp/cxx/poolmgr.hpp +0 -31
  128. pyxcp/cxx/test_queue.cpp +0 -69
  129. pyxcp/cxx/timestamp.hpp +0 -84
  130. pyxcp/cxx/utils.cpp +0 -38
  131. pyxcp/cxx/utils.hpp +0 -29
  132. pyxcp/cxx/win/iocp.cpp +0 -242
  133. pyxcp/cxx/win/iocp.hpp +0 -42
  134. pyxcp/cxx/win/perhandledata.hpp +0 -24
  135. pyxcp/cxx/win/periodata.hpp +0 -97
  136. pyxcp/cxx/win/socket.hpp +0 -185
  137. pyxcp/cxx/win/timeout.hpp +0 -83
  138. pyxcp/examples/conf_can.json +0 -20
  139. pyxcp/examples/conf_eth.json +0 -8
  140. pyxcp/logger.py +0 -64
  141. pyxcp/tests/test_config.py +0 -62
  142. pyxcp/transport/candriver/__init__.py +0 -2
  143. pyxcp/transport/candriver/pc_canalystii.py +0 -27
  144. pyxcp/transport/candriver/pc_etas.py +0 -25
  145. pyxcp/transport/candriver/pc_gsusb.py +0 -23
  146. pyxcp/transport/candriver/pc_iscan.py +0 -23
  147. pyxcp/transport/candriver/pc_ixxat.py +0 -27
  148. pyxcp/transport/candriver/pc_kvaser.py +0 -39
  149. pyxcp/transport/candriver/pc_neovi.py +0 -31
  150. pyxcp/transport/candriver/pc_nican.py +0 -23
  151. pyxcp/transport/candriver/pc_nixnet.py +0 -23
  152. pyxcp/transport/candriver/pc_pcan.py +0 -25
  153. pyxcp/transport/candriver/pc_seeed.py +0 -28
  154. pyxcp/transport/candriver/pc_serial.py +0 -27
  155. pyxcp/transport/candriver/pc_slcan.py +0 -29
  156. pyxcp/transport/candriver/pc_socketcan.py +0 -23
  157. pyxcp/transport/candriver/pc_systec.py +0 -29
  158. pyxcp/transport/candriver/pc_usb2can.py +0 -30
  159. pyxcp/transport/candriver/pc_vector.py +0 -34
  160. pyxcp/transport/candriver/python_can.py +0 -101
  161. pyxcp/transport/cxx_ext/CMakeLists.txt +0 -51
  162. pyxcp/transport/cxx_ext/setup.py +0 -49
  163. pyxcp/transport/cxx_ext/tests/test_basic_socket.cpp +0 -39
  164. pyxcp/transport/cxx_ext/tests/test_pool.cpp +0 -39
  165. pyxcp/transport/cxx_ext/tests/test_timestamp.cpp +0 -27
  166. pyxcp-0.21.11.dist-info/RECORD +0 -147
  167. rekorder.cp312-win_amd64.pyd +0 -0
  168. {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 time import sleep
15
- from typing import Any
16
- from typing import Callable
17
- from typing import Collection
18
- from typing import Dict
19
- from typing import List
20
- from typing import Optional
21
- from typing import Tuple
22
-
23
- from pyxcp import checksum
24
- from pyxcp import types
25
- from pyxcp.config import Configuration
26
- from pyxcp.constants import makeBytePacker
27
- from pyxcp.constants import makeByteUnpacker
28
- from pyxcp.constants import makeDLongPacker
29
- from pyxcp.constants import makeDLongUnpacker
30
- from pyxcp.constants import makeDWordPacker
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(SlaveProperties, self).__init__(*args, **kws)
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
- transportName : str
61
+ transport_name : str
75
62
  XCP transport layer name ['can', 'eth', 'sxi']
76
63
  config: dict
77
64
  """
78
65
 
79
- PARAMETER_MAP = {
80
- # Type Req'd Default
81
- "LOGLEVEL": (str, False, "WARN"),
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 = Configuration(self.PARAMETER_MAP or {}, config or {})
96
- self.logger = Logger("master.Master", level=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.transport = createTransport(transportName, config, policy)
100
- self.transport_name = transportName
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.seedNKeyDLL = self.config.get("SEED_N_KEY_DLL")
123
- self.seedNKeyDLL_same_bit_width = self.config.get("SEED_N_KEY_DLL_SAME_BIT_WIDTH")
124
- self.disconnect_response_optional = self.config.get("DISCONNECT_RESPONSE_OPTIONAL")
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
- sleep(SHORT_SLEEP)
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: {}".format(limitPayload))
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
- return self.transport.request(types.Command.CLEAR_DAQ_LIST, 0, *daqList)
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
- return self.transport.request(types.Command.WRITE_DAQ, bitOffset, entrySize, addressExt, *addr)
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
- return types.StartStopDaqListResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
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
- return self.transport.request(types.Command.START_STOP_SYNCH, mode)
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.".format(self.slaveProperties.maxWriteDaqMultipleElements))
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
- return self.transport.request(types.Command.FREE_DAQ)
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
- return self.transport.request(types.Command.ALLOC_DAQ, 0, *dq)
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
- return self.transport.request(types.Command.ALLOC_ODT, 0, *dln, odtCount)
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
- return self.transport.request(types.Command.ALLOC_ODT_ENTRY, 0, *dln, odtNumber, odtEntriesCount)
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
- return types.GetDaqIdResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
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} [{}]".format(cs.checksum, cs.checksumType))
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}".format(cc))
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": eci["daqEventProperties"]["consistency"],
1715
- "daq": eci["daqEventProperties"]["daq"],
1716
- "stim": eci["daqEventProperties"]["stim"],
1717
- "packed": eci["daqEventProperties"]["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
- from pyxcp.dllif import getKey, SeedNKeyResult, SeedNKeyError
1791
+
1792
+ from pyxcp.dllif import SeedNKeyError, SeedNKeyResult, getKey
1765
1793
 
1766
1794
  MAX_PAYLOAD = self.slaveProperties["maxCto"] - 2
1767
1795
 
1768
- if not self.seedNKeyDLL:
1769
- raise RuntimeError("No seed and key DLL specified, cannot proceed.")
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 '{}'.".format(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
- while length - len(seed) > 0:
1796
- result = self.getSeed(types.XcpGetSeedMode.REMAINING, resource_value)
1797
- seed.extend(list(result.seed))
1798
-
1799
- seed = seed[:length] # maybe there are some padding bytes
1800
-
1801
- result, key = getKey(
1802
- self.logger,
1803
- self.seedNKeyDLL,
1804
- resource_value,
1805
- bytes(seed),
1806
- self.seedNKeyDLL_same_bit_width,
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
- total_length = len(key)
1811
- offset = 0
1812
- while offset < total_length:
1813
- data = key[offset : offset + MAX_PAYLOAD]
1814
- self.unlock(total_length - offset, data)
1815
- offset += len(data)
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: {}".format(SeedNKeyResult(result).name))
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 = b""
1896
- try:
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
@@ -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
- import rekorder as rec
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 = rec._PyXcpLogFileReader(file_name)
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=10, chunk_size=1):
70
- self._writer = rec._PyXcpLogFileWriter(file_name, prealloc, chunk_size)
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"))