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.
- nsqdriver/NS_CST.py +260 -0
- nsqdriver/NS_DDS_v3.py +591 -0
- nsqdriver/NS_DDS_v4.py +778 -0
- nsqdriver/NS_MCI.py +597 -0
- nsqdriver/NS_QSYNC.py +812 -0
- nsqdriver/__init__.py +10 -0
- nsqdriver/common.py +20 -0
- nsqdriver/compiler/__init__.py +0 -0
- nsqdriver/compiler/assembler.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/compiler/ns_wave.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/compiler/ns_wave.pyi +151 -0
- nsqdriver/compiler/py_wave_asm.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/compiler/py_wave_asm.pyi +29 -0
- nsqdriver/nswave/__init__.py +9 -0
- nsqdriver/nswave/_asm.pyi +97 -0
- nsqdriver/nswave/_checkers.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/_checkers.pyi +47 -0
- nsqdriver/nswave/_errors.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/_errors.pyi +24 -0
- nsqdriver/nswave/_functions.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/_functions.pyi +34 -0
- nsqdriver/nswave/_ir.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/_ir.pyi +283 -0
- nsqdriver/nswave/_ir_pass.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/_ir_pass.pyi +7 -0
- nsqdriver/nswave/_optimizations.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/_optimizations.pyi +16 -0
- nsqdriver/nswave/_rules.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/_rules.pyi +56 -0
- nsqdriver/nswave/_simulator.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/_translate.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/_translate.pyi +12 -0
- nsqdriver/nswave/kernel.cpython-313-x86_64-linux-gnu.so +0 -0
- nsqdriver/nswave/kernel.pyi +57 -0
- nsqdriver/wrapper/AWG_ADC.py +534 -0
- nsqdriver/wrapper/ND_NSMCI.py +245 -0
- nsqdriver/wrapper/__init__.py +0 -0
- nsqdriver-0.12.17.dist-info/METADATA +117 -0
- nsqdriver-0.12.17.dist-info/RECORD +41 -0
- nsqdriver-0.12.17.dist-info/WHEEL +6 -0
- 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)
|