nsqdriver 0.12.17__cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.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.
Files changed (41) hide show
  1. nsqdriver/NS_CST.py +260 -0
  2. nsqdriver/NS_DDS_v3.py +591 -0
  3. nsqdriver/NS_DDS_v4.py +778 -0
  4. nsqdriver/NS_MCI.py +597 -0
  5. nsqdriver/NS_QSYNC.py +812 -0
  6. nsqdriver/__init__.py +10 -0
  7. nsqdriver/common.py +20 -0
  8. nsqdriver/compiler/__init__.py +0 -0
  9. nsqdriver/compiler/assembler.cpython-313-x86_64-linux-gnu.so +0 -0
  10. nsqdriver/compiler/ns_wave.cpython-313-x86_64-linux-gnu.so +0 -0
  11. nsqdriver/compiler/ns_wave.pyi +151 -0
  12. nsqdriver/compiler/py_wave_asm.cpython-313-x86_64-linux-gnu.so +0 -0
  13. nsqdriver/compiler/py_wave_asm.pyi +29 -0
  14. nsqdriver/nswave/__init__.py +9 -0
  15. nsqdriver/nswave/_asm.pyi +97 -0
  16. nsqdriver/nswave/_checkers.cpython-313-x86_64-linux-gnu.so +0 -0
  17. nsqdriver/nswave/_checkers.pyi +47 -0
  18. nsqdriver/nswave/_errors.cpython-313-x86_64-linux-gnu.so +0 -0
  19. nsqdriver/nswave/_errors.pyi +24 -0
  20. nsqdriver/nswave/_functions.cpython-313-x86_64-linux-gnu.so +0 -0
  21. nsqdriver/nswave/_functions.pyi +34 -0
  22. nsqdriver/nswave/_ir.cpython-313-x86_64-linux-gnu.so +0 -0
  23. nsqdriver/nswave/_ir.pyi +283 -0
  24. nsqdriver/nswave/_ir_pass.cpython-313-x86_64-linux-gnu.so +0 -0
  25. nsqdriver/nswave/_ir_pass.pyi +7 -0
  26. nsqdriver/nswave/_optimizations.cpython-313-x86_64-linux-gnu.so +0 -0
  27. nsqdriver/nswave/_optimizations.pyi +16 -0
  28. nsqdriver/nswave/_rules.cpython-313-x86_64-linux-gnu.so +0 -0
  29. nsqdriver/nswave/_rules.pyi +56 -0
  30. nsqdriver/nswave/_simulator.cpython-313-x86_64-linux-gnu.so +0 -0
  31. nsqdriver/nswave/_translate.cpython-313-x86_64-linux-gnu.so +0 -0
  32. nsqdriver/nswave/_translate.pyi +12 -0
  33. nsqdriver/nswave/kernel.cpython-313-x86_64-linux-gnu.so +0 -0
  34. nsqdriver/nswave/kernel.pyi +57 -0
  35. nsqdriver/wrapper/AWG_ADC.py +534 -0
  36. nsqdriver/wrapper/ND_NSMCI.py +245 -0
  37. nsqdriver/wrapper/__init__.py +0 -0
  38. nsqdriver-0.12.17.dist-info/METADATA +117 -0
  39. nsqdriver-0.12.17.dist-info/RECORD +41 -0
  40. nsqdriver-0.12.17.dist-info/WHEEL +6 -0
  41. nsqdriver-0.12.17.dist-info/top_level.txt +1 -0
nsqdriver/NS_QSYNC.py ADDED
@@ -0,0 +1,812 @@
1
+ import atexit
2
+ import socket
3
+ import struct
4
+ import threading
5
+ import time
6
+ import pickle
7
+ import enum
8
+ import copy
9
+ from concurrent.futures import ThreadPoolExecutor
10
+ from concurrent.futures import wait as wait_futures
11
+ from multiprocessing import shared_memory
12
+ from functools import lru_cache, wraps
13
+ from typing import Union, TYPE_CHECKING, Tuple, Iterable
14
+
15
+ try:
16
+ from .common import BaseDriver, Quantity, QInteger
17
+ except ImportError as e:
18
+ class BaseDriver:
19
+ def __init__(self, addr, timeout, **kw):
20
+ self.addr = addr
21
+ self.timeout = timeout
22
+
23
+
24
+ class Quantity(object):
25
+ def __init__(self, name: str, value=None, ch: int = 1, unit: str = ''):
26
+ self.name = name
27
+ self.default = dict(value=value, ch=ch, unit=unit)
28
+
29
+
30
+ class QInteger:
31
+ def __init__(self, name, value=None, unit='', ch=None,
32
+ get_cmd='', set_cmd='', ):
33
+ self.name = name
34
+
35
+ if TYPE_CHECKING:
36
+ from backend.board_parser import MCIBoard
37
+ from concurrent.futures import Future
38
+
39
+ THREAD_POOL = ThreadPoolExecutor(max_workers=10)
40
+ _scanning_lock = threading.Lock()
41
+ _scanning_stop_event = threading.Event()
42
+ DEVICE_SET = set()
43
+
44
+ DEBUG_PRINT = False
45
+
46
+
47
+ def print_debug(*args, **kwargs):
48
+ if DEBUG_PRINT:
49
+ print(*args, **kwargs)
50
+
51
+
52
+ @atexit.register
53
+ def global_system_exit():
54
+ THREAD_POOL.shutdown(wait=False)
55
+ SHARED_DEVICE_MEM.close()
56
+ try:
57
+ SHARED_DEVICE_MEM.unlink()
58
+ except FileNotFoundError as e:
59
+ pass
60
+
61
+
62
+ def retry(times):
63
+ def decorator(func):
64
+ @wraps(func)
65
+ def wrapper(*args, **kwargs):
66
+ _times = times - 1
67
+ while not func(*args, **kwargs) and _times > 0:
68
+ _times -= 1
69
+ return _times != 0
70
+
71
+ return wrapper
72
+
73
+ return decorator
74
+
75
+
76
+ class Driver(BaseDriver):
77
+ class ScannMode(enum.IntEnum):
78
+ """!
79
+ @detail QC/QR等被同步设备的发现方式,'local': 本地共享内存扫描, 'remote': 网络udp扫描
80
+ """
81
+ local = 0
82
+ """local: 本地共享内存扫描
83
+
84
+ - NS_MCI中的Driver每实例化一次,就会在SharedMemory中记录一次自己的ip
85
+ - 系统进行同步时,会对SharedMemory中记录的QC/QR设备ip进行同步
86
+ - 只有程序整体退出的时候才会清空这片缓存
87
+ """
88
+ remote = 1
89
+ """remote: 网络udp扫描
90
+
91
+ - import 本文件后,会建立一个独立线程间断广播UDP包,扫描局域网内的QC/QR设备,记录被扫描到的设备ip
92
+ - 系统进行同步时,会对扫描到的QC/QR设备ip进行同步
93
+ - 注意:这种方式比较粗暴,会影响局域网内正在运行的设备
94
+ """
95
+ alone = 2
96
+ """alone: 单机运行模式
97
+
98
+ - QC/QR设备单独运行时使用本模式,默认使用QC/QR设备板内的QSYNC做设备内的系统同步
99
+ - 系统进行同步时,会判断QSYNC Driver的ip是否为QC/QR设备的ip
100
+ """
101
+
102
+ _scanning_lock = _scanning_lock
103
+ _device_set = DEVICE_SET
104
+ segment = ('ns', '111|112|113|114|115')
105
+
106
+ icd_head_reset = 0x41000002
107
+ icd_head_status = 0x4100000E
108
+ icd_head_cmd_2 = 0x31000015
109
+ icd_head_cmd_3 = 0x410000B1
110
+ icd_head_cmd_4 = 0x31000013
111
+ icd_head_cmd_5 = 0x410000B2
112
+ icd_head_cmd_6 = 0x3100001A
113
+ icd_head_cmd_7 = 0x410000B3
114
+ CHs = list(range(1, 17))
115
+
116
+ quants = [
117
+ QInteger('TRIG'),
118
+ Quantity('SystemSync', value=None, ch=1), # set/get,运行次数
119
+ Quantity('GenerateTrig', value=None, unit='s'), # set/get,触发周期单位s,触发数量=shot
120
+ Quantity('ResetTrig', value=None),
121
+ Quantity('Shot', value=1024, ch=1), # set/get, 运行次数
122
+ Quantity('TrigPeriod', value=200e-6, ch=1), # set/get, 触发周期
123
+ Quantity('TrigWidth', value=800e-9, ch=1), # set/get, 触发周期
124
+ Quantity('TrigDelay', value=0, ch=1), # set/get, 触发周期
125
+ Quantity('TrigDelayList', value=0, ch=1), # set/get, 触发周期
126
+ Quantity('TrigFrom', value=0, ch=1), # Trig来源: 0:内部产生;1:外部输入
127
+ Quantity('RefClock', value='out', ch=1), # 参考时钟选择: ‘out’:外参考时钟;‘in’:内参考时钟
128
+ Quantity('DiscoveryMode', value=ScannMode.local, ch=1), # QC/QR等被同步设备的发现方式,见DiscoveryMode说明
129
+ Quantity('UpdateFirmware', value='', ch=1), # qsync固件更新
130
+ Quantity('GeneratePrtTrig', value=None, unit='s'), # 生成subprt trig
131
+ Quantity('SubDelayCount', value=0, ch=1),
132
+ Quantity('TrigWidth', value=0, ch=1),
133
+ ]
134
+
135
+ SystemParameter = {
136
+ 'RefClock': 'out', # 参考时钟选择: ‘out’:外参考时钟;‘in’:内参考时钟
137
+ 'TrigFrom': 0, # Trig来源: 0:内部产生;1:外部输入
138
+ 'TrigPeriod': 200e-6, # 触发信号重复周期
139
+ 'TrigWidth': 800e-9, # 触发信号高电平宽度 单位s
140
+ 'TrigDelay': 0, # 触发信号相对开启通知的延迟
141
+ 'Shot': 1024,
142
+ 'DiscoveryMode': ScannMode.local, # QC/QR等被同步设备的发现方式,见DiscoveryMode说明
143
+ }
144
+
145
+ def __init__(self, addr: str = '', timeout: float = 10.0, **kw):
146
+ super().__init__(addr, timeout, **kw)
147
+ self.handle = None
148
+ self.model = 'NS_QSYNC' # 默认为设备名字
149
+ self.srate = None
150
+ self.gen_trig_num = 0
151
+ self.addr = addr
152
+
153
+ self.param = {'Shot': 1024, 'TrigPeriod': 200e-6, 'MixMode': 2}
154
+ self.subprtparam = [
155
+ [[0x5F5F5F5F,
156
+ 0x41000001,
157
+ 0x00000000,
158
+ 4 * (17 * 100 + 17 * 4 + 5), # 指令长度 ,uint32个数*4
159
+ 0, ]],
160
+ [[0, 0, 800, 0] for _ in range(17)],
161
+ [[0 for _ in range(100)] for _ in range(17)],
162
+ ]
163
+ print_debug(f'QSYNC: 实例化成功{addr}')
164
+
165
+ @property
166
+ def device_set(self):
167
+ mode = self.param.get('DiscoveryMode', self.ScannMode.local)
168
+ if mode is self.ScannMode.local:
169
+ return set(SHARED_DEVICE_MEM.ip)
170
+ elif mode is self.ScannMode.alone:
171
+ if self.addr in SHARED_DEVICE_MEM.ip:
172
+ return {self.addr}
173
+ else:
174
+ raise RuntimeError('qsync处于alone模式下,只能控制QC/QR内的qsync,请确认对应QC/QR设备是否已经open成功')
175
+ elif mode is self.ScannMode.remote:
176
+ return self._device_set.copy()
177
+ else:
178
+ return set()
179
+
180
+ def open(self, **kw):
181
+ """!
182
+ 输入IP打开设备,配置默认超时时间为5秒
183
+ 打开设备时配置RFSoC采样时钟,采样时钟以参数定义
184
+ @param kw:
185
+ @return:
186
+ """
187
+ # 配置系统初始值
188
+ system_parameter = kw.get('system_parameter', {})
189
+ values = self.SystemParameter.copy()
190
+ values.update(system_parameter)
191
+ for name, value in values.items():
192
+ if value is not None:
193
+ self.set(name, value, 1)
194
+
195
+ # self.sync_system()
196
+ status = self.get('Status')
197
+ print(f'*********QSYNC{self.addr}开启成功*********\n'
198
+ f'core_temp: {status[0]}℃\n'
199
+ f'10M_locked: {status[1] if len(status) > 1 else "nan"}')
200
+ print(f'qsync {self.addr} opened successfully')
201
+
202
+ def close(self, **kw):
203
+ """
204
+ 关闭设备
205
+ """
206
+ # self.handle.release_dma()
207
+ # self.handle.close()
208
+ ...
209
+
210
+ def write(self, name: str, value, **kw):
211
+ channel = kw.get('ch', 1)
212
+ return self.set(name, value, channel)
213
+
214
+ def read(self, name: str, **kw):
215
+ channel = kw.get('ch', 1)
216
+ result = self.get(name, channel)
217
+ return result
218
+
219
+ def set(self, name, value=None, channel=1):
220
+ """!
221
+ 设置设备属性
222
+ @param name:
223
+ @param value:
224
+ @param channel:
225
+ @return:
226
+ """
227
+ print_debug(f'QSYNC: set操作被调用{name}')
228
+ self.subprt(name, value, channel)
229
+ if name in {'SystemSync', 'ReInit'}:
230
+ self.sync_system()
231
+ elif name == 'TRIG':
232
+ value = self.param['TrigPeriod']
233
+ data = self.__fmt_qsync_start(
234
+ self.param['TrigFrom'], value, self.param['Shot'],
235
+ self.param['TrigWidth'], self.param['TrigDelay']
236
+ )
237
+ self._send_command(data, connect_timeout=2)
238
+ self.gen_trig_num += 1
239
+ elif name == 'GenerateTrig':
240
+ value = self.param['TrigPeriod'] if value is None else value
241
+ data = self.__fmt_qsync_start(
242
+ self.param['TrigFrom'], value, self.param['Shot'],
243
+ self.param['TrigWidth'], self.param['TrigDelay']
244
+ )
245
+ self._send_command(data, connect_timeout=2)
246
+ self.gen_trig_num += 1
247
+ elif name == 'ResetTrig':
248
+ self.subprtparam = [
249
+ [[0x5F5F5F5F,
250
+ 0x41000001,
251
+ 0x00000000,
252
+ 4 * (17 * 100 + 17 * 4 + 5), # 指令长度 ,uint32个数*4
253
+ 0, ]],
254
+ [[0, 0, 800, 0] for _ in range(17)],
255
+ [[0 for _ in range(100)] for _ in range(17)],
256
+ ]
257
+ data = self.__fmt_qsync_common(self.icd_head_reset)
258
+ self._send_command(data)
259
+ elif name == 'RefClock':
260
+ self.change_ref(value)
261
+ elif name == 'UpdateFirmware':
262
+ self.update_firmware(value)
263
+ elif name == 'SetMask':
264
+ if not isinstance(value, list):
265
+ print(f'SetMask set value {value} is not a list')
266
+ return
267
+ data = self.__fmt_qsync_mask(*value)
268
+ self._send_command(data)
269
+ elif name == 'GT_pd':
270
+ data = self._fmt_gt_pd(value)
271
+ self._send_command(data)
272
+
273
+ elif name == "GeneratePrtTrig":
274
+ data = self.__fmt_qsync_prt(self.subprtparam)
275
+ self._send_command(data)
276
+ else:
277
+ self.param[name] = value
278
+
279
+ @lru_cache(maxsize=32)
280
+ def _fmt_gt_pd(self, value):
281
+
282
+ head = 0x61000003
283
+ if value == 1:
284
+ cmd_pack = (
285
+ 0x5F5F5F5F,
286
+ head,
287
+ 0x00000000,
288
+ 32,
289
+ 0xCFF3FFFF,
290
+ 0xFFFFFFFF,
291
+ 0xCFF3FFFF,
292
+ 0xFFFFFFFF
293
+ )
294
+ else:
295
+ cmd_pack = (
296
+ 0x5F5F5F5F,
297
+ head,
298
+ 0x00000000,
299
+ 32,
300
+ 0xFFFFFFFF,
301
+ 0xFFFFFFFF,
302
+ 0xFFFFFFFF,
303
+ 0xFFFFFFFF
304
+ )
305
+
306
+ return struct.pack('=' + 'I' * len(cmd_pack), *cmd_pack)
307
+
308
+ def subprt(self, name, value, ch=1):
309
+ """
310
+ subprt参数过滤
311
+ Args:
312
+ name: _description_
313
+ value: _description_
314
+ ch: _description_
315
+ """
316
+ if name == "Shot":
317
+ self.subprtparam[1][ch - 1][1] = round(value) & 0xFFFFFFFF
318
+ elif name == "TrigPeriod":
319
+ self.subprtparam[1][ch - 1][0] = round(value * 1e9) & 0xFFFFFFFF
320
+ elif name == "TrigWidth":
321
+ self.subprtparam[1][ch - 1][2] = round(value * 1e9) & 0xFFFFFFFF
322
+ # elif name == "TrigDelay":
323
+ # self.subprtparam[1][ch-1][3] = round(value * 1e9) & 0xFFFFFFFF
324
+ elif name == "SubDelayCount":
325
+ self.subprtparam[1][ch - 1][3] = round(value) & 0xFFFFFFFF
326
+ elif name == "TrigDelayList":
327
+ self.subprtparam[2][ch - 1] = [round(i * 1e9) & 0xFFFFFFFF for i in value]
328
+ self.subprtparam[2][ch - 1].extend([0] * (100 - len(self.subprtparam[2][ch - 1]))) # 长度为100
329
+
330
+ def get(self, name, channel=1, value=0):
331
+ """!
332
+ 查询设备属性,获取数据
333
+ @param name:
334
+ @param channel:
335
+ @param value:
336
+ @return:
337
+ """
338
+ print_debug(f'QSYNC: get操作被调用{name}')
339
+ if name == 'Status':
340
+ cmd_data = self.__fmt_qsync_common(self.icd_head_status)
341
+ data = DeviceCmdHandle.send_command(cmd_data, addr=self.addr, return_fdk=True)
342
+ data = struct.unpack('I' * (len(data) // 4), data)
343
+ return data
344
+ return self.param.get(name, None)
345
+
346
+ def sync_system(self):
347
+ """
348
+ 全系统同步流程
349
+
350
+ :return:
351
+ """
352
+ if Driver._scanning_lock.acquire(timeout=10):
353
+ Driver._scanning_lock.release()
354
+ if len(self.device_set) == 0:
355
+ return
356
+
357
+ data = self.__fmt_qsync_common(self.icd_head_reset)
358
+ self._send_command(data)
359
+
360
+ result = True
361
+ if self.param.get('DiscoveryMode', self.ScannMode.local) != self.ScannMode.alone:
362
+ data = self.__fmt_qsync_common(self.icd_head_reset)
363
+ result &= self._sendto_fake_qsync(data)
364
+ data = self.__fmt_qsync_ref_from('out')
365
+ result &= self._sendto_fake_qsync(data)
366
+ result &= self._sendto_device(self.icd_head_cmd_2)
367
+ result &= self._sendto_qsync(self.icd_head_cmd_3)
368
+ result &= self._sendto_device(self.icd_head_cmd_4)
369
+ result &= self._sendto_qsync(self.icd_head_cmd_5)
370
+ result &= self._sendto_device(self.icd_head_cmd_6, 30)
371
+ result &= self._sendto_qsync(self.icd_head_cmd_7)
372
+
373
+ print(f'System synchronization {"succeeded" if result else "FAILED"}')
374
+
375
+ def change_ref(self, value='out'):
376
+ self.param['RefClock'] = value
377
+ self.set('ResetTrig')
378
+ data = self.__fmt_qsync_ref_from(value)
379
+ self._send_command(data)
380
+
381
+ def _sendto_device(self, cmd_head, timeout=2):
382
+ """!
383
+ 将指令发送给设备
384
+ @param cmd_head:
385
+ @param timeout:
386
+ @return:
387
+ """
388
+ cmd_data = self.__fmt_qsync_common(cmd_head)
389
+ devices = list(self.device_set)
390
+ futures = [
391
+ THREAD_POOL.submit(DeviceCmdHandle.send_command, cmd_data, 0, addr, 5000, True, False, timeout)
392
+ for addr in devices
393
+ ]
394
+ wait_futures(futures)
395
+ result = all(future.result() for future in futures)
396
+ for idx, future in enumerate(futures):
397
+ if not future.result():
398
+ print(f'device: {devices[idx]}系统同步过程 {hex(cmd_head)} 执行失败')
399
+ return result
400
+
401
+ def _sendto_fake_qsync(self, cmd_data, timeout=2):
402
+ devices = list(self.device_set)
403
+ futures = [
404
+ THREAD_POOL.submit(self._send_command, cmd_data, 0, addr, 5001, True, False, timeout)
405
+ for addr in devices if addr != self.addr
406
+ ]
407
+ wait_futures(futures)
408
+ result = all(future.result() for future in futures)
409
+ for idx, future in enumerate(futures):
410
+ if not future.result():
411
+ print(f'device: {devices[idx]}系统同步过程 {cmd_data[4:12]} 执行失败')
412
+ return result
413
+
414
+ def _sendto_qsync(self, cmd_head: int):
415
+ """!
416
+ 将指令发送给qsync
417
+
418
+ @param cmd_head:
419
+ @return:
420
+ """
421
+ cmd_data = self.__fmt_qsync_common(cmd_head)
422
+ if not self._send_command(cmd_data, connect_timeout=2):
423
+ print(f'qsync: 系统同步过程 {hex(cmd_head)} 执行失败')
424
+ return False
425
+ return True
426
+
427
+ def update_firmware(self, file_path, boards=None):
428
+ """!
429
+ 固件更新
430
+
431
+ @param file_path: 固件路径
432
+ @param boards:
433
+ @return:
434
+ """
435
+ import os
436
+ if not os.path.exists(file_path):
437
+ raise ValueError(f'文件路径: {file_path} 不存在')
438
+ with open(file_path, 'rb') as fp:
439
+ cmd_data = self.__fmt_update_firmware(fp.read())
440
+ if not self._send_command(cmd_data):
441
+ print(f'qsync: 固件更新 执行失败')
442
+
443
+ def _connect(self, addr=None, port=5001, timeout=None):
444
+ """!
445
+ 获取到指定ip的tcp连接
446
+
447
+ @param addr:
448
+ @param port:
449
+ @return:
450
+ """
451
+ timeout = self.timeout if timeout is None else timeout
452
+ addr = self.addr if addr is None else addr
453
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
454
+ sock.settimeout(timeout+1)
455
+ sock.connect((addr, port))
456
+ return sock
457
+
458
+ @retry(3)
459
+ def _send_command(self, data: Union[str, bytes], wait=0, addr=None, port=5001,
460
+ check_feedback=True, return_fdk=False, connect_timeout=10):
461
+ """!
462
+ 发送指定内容到后端
463
+
464
+ @param data: 指令内容
465
+ @param wait: 指令发送完成后,等待一段时间再接收反馈,阻塞式等待
466
+ @param addr: 后端IP
467
+ @param port: 后端端口
468
+ @param check_feedback: 是否解析反馈
469
+ @param connect_timeout:
470
+ @return:
471
+ """
472
+ command_bak = data
473
+ try:
474
+ sock = self._connect(addr=addr, port=port, timeout=connect_timeout)
475
+ except Exception as e:
476
+ print(f'device: {addr}无法连接 {e}')
477
+ return False
478
+
479
+ try:
480
+ sock.sendall(memoryview(data))
481
+
482
+ time.sleep(wait)
483
+ _feedback = sock.recv(20)
484
+ if check_feedback:
485
+ if not _feedback.startswith(b'\xcf\xcf\xcf\xcf'):
486
+ print('返回指令包头错误')
487
+ return False
488
+ if command_bak[4:8] != _feedback[4:8]:
489
+ print('返回指令ID错误')
490
+ return False
491
+ # print(_feedback)
492
+ _feedback = struct.unpack('=IIIII', _feedback)
493
+ if _feedback[4] != 0:
494
+ print('指令成功下发,但执行失败')
495
+ return False
496
+ except Exception as e:
497
+ print(f'device: {addr}指令{command_bak[:4]}发送失败 {e}')
498
+ return False
499
+ finally:
500
+ sock.close()
501
+ return True
502
+
503
+ @lru_cache(maxsize=32)
504
+ def __fmt_qsync_common(self, head):
505
+ cmd_pack = (
506
+ 0x5F5F5F5F,
507
+ head,
508
+ 0x00000000,
509
+ 16,
510
+ )
511
+
512
+ return struct.pack('=' + 'I' * len(cmd_pack), *cmd_pack)
513
+
514
+ @lru_cache(maxsize=16)
515
+ def __fmt_qsync_ref_from(self, _from):
516
+ if _from == 'in':
517
+ _from = 0
518
+ elif _from == 'out':
519
+ _from = 1
520
+ else:
521
+ _from = 0
522
+ cmd_pack = (
523
+ 0x5F5F5F5F,
524
+ 0x4100000F,
525
+ 0x00000000,
526
+ 20,
527
+ _from
528
+ )
529
+
530
+ return struct.pack('=' + 'I' * len(cmd_pack), *cmd_pack)
531
+
532
+ @lru_cache(maxsize=32)
533
+ def __fmt_qsync_mask(self, *mask):
534
+ cmd_pack = (
535
+ 0x5F5F5F5F,
536
+ 0x51000001,
537
+ 0x00000000,
538
+ 80,
539
+ *mask
540
+ )
541
+
542
+ return struct.pack('=' + 'I' * len(cmd_pack), *cmd_pack)
543
+
544
+ @lru_cache(maxsize=32)
545
+ def __fmt_qsync_start(self, src, period, shots, width, delay):
546
+ cmd_pack = (
547
+ 0x5F5F5F5F,
548
+ 0x41000001,
549
+ 0x00000000,
550
+ 36,
551
+ int(src),
552
+ int(period * 1e9) & 0xFFFFFFFF,
553
+ int(shots),
554
+ int(width * 1e9) & 0xFFFFFFFF,
555
+ int(delay * 1e9) & 0xFFFFFFFF
556
+ )
557
+
558
+ return struct.pack('=' + 'I' * len(cmd_pack), *cmd_pack)
559
+
560
+ # @lru_cache(maxsize=8)
561
+ def __fmt_qsync_prt(self, para):
562
+ cmd_pack = []
563
+ for i in para:
564
+ for ii in i:
565
+ for iii in ii:
566
+ cmd_pack.append(iii)
567
+ return struct.pack('=' + 'I' * len(cmd_pack), *cmd_pack)
568
+
569
+ @staticmethod
570
+ def __fmt_update_firmware(file_data):
571
+ cmd_pack = (
572
+ 0x5F5F5F5F,
573
+ 0x31000006,
574
+ 0x00000000,
575
+ 16 + len(file_data),
576
+ )
577
+ return struct.pack('=' + 'I' * len(cmd_pack), *cmd_pack) + file_data
578
+
579
+ # DG645兼容
580
+ def Trigger_singleshot(self):
581
+ self.set('GenerateTrig')
582
+
583
+ def Convention_init(self, rate=5000, **kwds):
584
+ self.set('ResetTrig')
585
+ self.set('Shot', 0xFFFFFFFF)
586
+ self.set('TrigPeriod', 1 / rate)
587
+ self.set('GenerateTrig')
588
+
589
+ def BurstMode_init(self, count=2048, delay=200e-6, period=200e-6, source=0, **kwds):
590
+ self.set('ResetTrig')
591
+ self.set('Shot', count)
592
+ self.set('TrigPeriod', period)
593
+ self.set('TrigDelay', delay)
594
+ self.set('TrigFrom', source)
595
+
596
+ def startGun(self):
597
+ self.Trigger_singleshot()
598
+
599
+
600
+ def do_scanning():
601
+ """
602
+ 扫描板卡
603
+
604
+ :return:
605
+ """
606
+ while True:
607
+ dest = ('<broadcast>', 5003)
608
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
609
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
610
+ addrs = socket.getaddrinfo(socket.gethostname(), None)
611
+ addr = [addr[4][0] for addr in addrs if addr[4][0].startswith('192.168.1.')]
612
+ _bind = False
613
+ _port = 15000
614
+ with _scanning_lock:
615
+ while not _bind:
616
+ try:
617
+ s.bind((addr[0], _port))
618
+ _bind = True
619
+ except Exception as e:
620
+ _port += 1
621
+ if _port >= 30000:
622
+ raise e
623
+ s.sendto(b"____\x20\x00\x002\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00", dest)
624
+ s.settimeout(3)
625
+
626
+ try:
627
+ while True:
628
+ (feedback, _addr) = s.recvfrom(20)
629
+ if feedback:
630
+ DEVICE_SET.add(_addr[0])
631
+ except Exception as e:
632
+ s.close()
633
+ time.sleep(5)
634
+
635
+
636
+ class DeviceCmdHandle:
637
+ """!
638
+ @brief 封装与QC/QR设备间的交互
639
+ """
640
+ error_map = {b'\x1a\x00\x001': {1: '指令Resync执行失败',
641
+ 2: 'REFCLK not Detected',
642
+ 3: 'Clock Maybe unstable, DAC Settings Error',
643
+ 4: 'Clock Maybe unstable, ADC Settings Error, '
644
+ 'Please check if the amplifier is too large',
645
+ 5: 'SYSREF(External TRIG) not Detected, MTS failed / Sample Rate not Support',
646
+ 7: 'ADC Calibration Failed'}
647
+ }
648
+
649
+ @staticmethod
650
+ def packing_result(boards: Iterable["MCIBoard"],
651
+ futures: Iterable["Future"],
652
+ cmd_data: memoryview,
653
+ head: bytes) -> Tuple[bytes, list]:
654
+ res = []
655
+ errors = []
656
+ for board, future in zip(boards, futures):
657
+ if board.has_cmd_link and board.has_stream_link:
658
+ _id = (board.sig_fpga_pci_port & 0xFF) << 24
659
+ _res = struct.unpack('IIIII', future.result())[-1] & 0xFFFFFF
660
+ if _res:
661
+ errors.append(f'指令{cmd_data[4:8].tobytes()}向{board.cs_target}转发失败\n')
662
+ res.append(_id + _res)
663
+ return b''.join((head, cmd_data[4:12], struct.pack('=I' + 'I' * len(res), 16 + len(res) * 4, *res))), errors
664
+
665
+ @staticmethod
666
+ def packing_fake_result(boards: Iterable["MCIBoard"], cmd_data: memoryview, head: bytes) -> bytes:
667
+ res = []
668
+ for board in boards:
669
+ if board.has_cmd_link and board.has_stream_link:
670
+ _id = (board.ds_id & 0xFF) << 24
671
+ res.append(_id + 0)
672
+ return b''.join((head, cmd_data[4:12], struct.pack('=I' + 'I' * len(res), 16 + len(res) * 4, *res)))
673
+
674
+ @classmethod
675
+ def unpacking_result(cls, _feedback, _res, command_bak, addr):
676
+ cmd_id = command_bak[4:8]
677
+ if not _feedback.startswith(b'\xcf\xcf\xcf\xcf'):
678
+ print(f'设备{addr}-指令{cmd_id}-返回指令包头错误')
679
+ return False
680
+ if cmd_id != _feedback[4:8]:
681
+ print(f'设备{addr}-指令{cmd_id}-返回指令ID错误{_feedback[4:8]}')
682
+ return False
683
+ if len(_res) % 4 != 0:
684
+ print(f'设备{addr}-指令{cmd_id}-返回结果{_res}长度错误')
685
+ return False
686
+ command_status = True
687
+ results = struct.unpack('I' * (len(_res) // 4), _res)
688
+ for result in results:
689
+ board_id = (result & 0xFF000000) >> 24
690
+ board_res = result & 0xFFFFFF
691
+ error_map = cls.error_map.get(command_bak[4:8], {})
692
+ if board_res:
693
+ print(f'设备{addr}-板卡{board_id}-指令{cmd_id}-{error_map.get(board_res, "执行失败")}')
694
+ command_status &= False
695
+ return command_status
696
+
697
+ @classmethod
698
+ def send_command(cls, data: Union[str, bytes], wait=0, addr=None, port=5001,
699
+ check_feedback=True, return_fdk=False, connect_timeout=None):
700
+ """!
701
+ 发送指定内容到后端
702
+
703
+ @param data: 指令内容
704
+ @param wait: 指令发送完成后,等待一段时间再接收反馈,阻塞式等待
705
+ @param addr: 后端IP
706
+ @param port: 后端端口
707
+ @param check_feedback:
708
+ @param return_fdk:
709
+ @param connect_timeout:
710
+ @return:
711
+ """
712
+ command_bak = data
713
+ try:
714
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
715
+ sock.settimeout(connect_timeout)
716
+ sock.connect((addr, port))
717
+ except Exception as e:
718
+ print(e)
719
+ return False
720
+
721
+ try:
722
+ sock.sendall(memoryview(data))
723
+
724
+ time.sleep(wait)
725
+ _feedback = sock.recv(16)
726
+ if check_feedback:
727
+ _res = sock.recv(struct.unpack('I', _feedback[12:16])[0] - 16)
728
+ if return_fdk:
729
+ return _res
730
+ else:
731
+ return cls.unpacking_result(_feedback, _res, command_bak, addr)
732
+ except Exception as e:
733
+ print(e)
734
+ return False
735
+ finally:
736
+ sock.close()
737
+ return True
738
+
739
+
740
+ class InfoSharedList:
741
+ memory_name = 'NS_DEVICE_MEMORY'
742
+ memory_size = 1024 ** 2
743
+ head_size = 32 # memory中前head_size的长度为头部预留信息
744
+
745
+ def __init__(self):
746
+ try:
747
+ self._memory = shared_memory.SharedMemory(name=self.memory_name, create=True, size=self.memory_size)
748
+ except FileExistsError:
749
+ self._memory = shared_memory.SharedMemory(name=self.memory_name, create=False, size=self.memory_size)
750
+
751
+ def clear_ip(self):
752
+ _exit_pkl = pickle.dumps(self.ip)
753
+ self._memory.buf[self.head_size:len(_exit_pkl) + self.head_size] = b'\x00' * len(_exit_pkl)
754
+
755
+ @property
756
+ def ip(self):
757
+ try:
758
+ return pickle.loads(self._memory.buf[self.head_size:])
759
+ except pickle.UnpicklingError:
760
+ return []
761
+
762
+ @ip.setter
763
+ def ip(self, value):
764
+ ips = self.ip
765
+ ips.append(value)
766
+ ips = list(set(ips))
767
+ _pkl = pickle.dumps(ips)
768
+ self._memory.buf[self.head_size:len(_pkl) + self.head_size] = _pkl
769
+
770
+ def close(self):
771
+ self._memory.close()
772
+
773
+ def unlink(self):
774
+ try:
775
+ self._memory.unlink()
776
+ except FileNotFoundError as e:
777
+ pass
778
+
779
+
780
+ # threading.Thread(target=do_scanning, daemon=True, name='qsync_scanning_device').start()
781
+ SHARED_DEVICE_MEM = InfoSharedList()
782
+
783
+ if __name__ == '__main__':
784
+ ins = Driver('127.0.0.1')
785
+
786
+ count = 1024
787
+ period = 400e-6
788
+ delay = 0
789
+ source = 0
790
+
791
+ # ins.set('ResetTrig')
792
+
793
+ for ch in [1, 2, 3, 4, 5, 6, 7]:
794
+ ins.set('Shot', count, channel=ch)
795
+ ins.set('TrigPeriod', period, channel=ch)
796
+ ins.set('TrigDelay', delay, channel=ch)
797
+ ins.set('TrigFrom', source, channel=ch)
798
+
799
+ for ch in [8]:
800
+ ins.set('Shot', count, channel=ch)
801
+ ins.set('TrigWidth', 1600e-9, channel=ch)
802
+ ins.set('TrigPeriod', period, channel=ch)
803
+ ins.set('SubTriggerCount', 4, channel=ch)
804
+ ins.set('TrigDelayList', [1600e-9 * i for i in range(4)])
805
+ ins.set('TrigFrom', source, channel=ch)
806
+
807
+ for i in range(1):
808
+ # ins.set('GenerateTrig')
809
+ ins.set('GeneratePrtTrig')
810
+ import time
811
+
812
+ time.sleep(1)