nsqdriver 0.6.0__cp39-cp39-macosx_10_9_universal2.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 nsqdriver might be problematic. Click here for more details.

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