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