nsqdriver 0.5.7__cp38-cp38-manylinux_2_17_x86_64.manylinux2014_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.

Potentially problematic release.


This version of nsqdriver might be problematic. Click here for more details.

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