QuLab 2.0.0__py3-none-any.whl → 2.10.11__py3-none-any.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.
- qulab/__init__.py +33 -1
- qulab/__main__.py +4 -0
- qulab/cli/commands.py +31 -0
- qulab/cli/config.py +170 -0
- qulab/cli/decorators.py +28 -0
- qulab/executor/__init__.py +5 -0
- qulab/executor/analyze.py +188 -0
- qulab/executor/cli.py +447 -0
- qulab/executor/load.py +563 -0
- qulab/executor/registry.py +185 -0
- qulab/executor/schedule.py +543 -0
- qulab/executor/storage.py +615 -0
- qulab/executor/template.py +259 -0
- qulab/executor/utils.py +194 -0
- qulab/monitor/__init__.py +1 -93
- qulab/monitor/__main__.py +8 -0
- qulab/monitor/monitor.py +115 -0
- qulab/scan/__init__.py +2 -4
- qulab/scan/curd.py +221 -0
- qulab/scan/models.py +554 -0
- qulab/scan/optimize.py +76 -0
- qulab/scan/query.py +387 -0
- qulab/scan/record.py +603 -0
- qulab/scan/scan.py +1166 -0
- qulab/scan/server.py +450 -0
- qulab/scan/space.py +213 -0
- qulab/scan/utils.py +230 -34
- qulab/sys/__init__.py +2 -0
- qulab/sys/device/basedevice.py +50 -16
- qulab/sys/device/loader.py +1 -1
- qulab/sys/device/utils.py +46 -13
- qulab/sys/drivers/FakeInstrument.py +36 -20
- qulab/sys/net/nginx.py +16 -14
- qulab/sys/rpc/router.py +35 -0
- qulab/sys/rpc/zmq_socket.py +227 -0
- qulab/tools/__init__.py +0 -0
- qulab/tools/connection_helper.py +39 -0
- qulab/typing.py +2 -0
- qulab/utils.py +95 -0
- qulab/version.py +1 -1
- qulab/visualization/__init__.py +188 -0
- qulab/visualization/__main__.py +71 -0
- qulab/visualization/_autoplot.py +464 -0
- qulab/visualization/plot_circ.py +319 -0
- qulab/visualization/plot_layout.py +408 -0
- qulab/visualization/plot_seq.py +242 -0
- qulab/visualization/qdat.py +152 -0
- qulab/visualization/rot3d.py +23 -0
- qulab/visualization/widgets.py +86 -0
- {QuLab-2.0.0.dist-info → qulab-2.10.11.dist-info}/METADATA +28 -12
- qulab-2.10.11.dist-info/RECORD +104 -0
- {QuLab-2.0.0.dist-info → qulab-2.10.11.dist-info}/WHEEL +1 -1
- qulab-2.10.11.dist-info/entry_points.txt +2 -0
- QuLab-2.0.0.dist-info/RECORD +0 -71
- qulab/monitor/multiploter/__init__.py +0 -1
- qulab/scan/base.py +0 -544
- qulab/scan/expression.py +0 -360
- qulab/scan/scanner.py +0 -244
- qulab/scan/transforms.py +0 -16
- /qulab/{scan/dataset.py → cli/__init__.py} +0 -0
- /qulab/monitor/{multiploter/config.py → config.py} +0 -0
- /qulab/monitor/{multiploter/dataset.py → dataset.py} +0 -0
- /qulab/monitor/{multiploter/event_queue.py → event_queue.py} +0 -0
- /qulab/monitor/{multiploter/main.py → mainwindow.py} +0 -0
- /qulab/monitor/{multiploter/ploter.py → ploter.py} +0 -0
- /qulab/monitor/{multiploter/qt_compat.py → qt_compat.py} +0 -0
- /qulab/monitor/{multiploter/toolbar.py → toolbar.py} +0 -0
- {QuLab-2.0.0.dist-info → qulab-2.10.11.dist-info/licenses}/LICENSE +0 -0
- {QuLab-2.0.0.dist-info → qulab-2.10.11.dist-info}/top_level.txt +0 -0
qulab/scan/utils.py
CHANGED
@@ -1,37 +1,233 @@
|
|
1
|
+
import ast
|
2
|
+
import asyncio
|
1
3
|
import inspect
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
4
|
+
import platform
|
5
|
+
import re
|
6
|
+
import subprocess
|
7
|
+
import sys
|
8
|
+
import uuid
|
9
|
+
import warnings
|
10
|
+
from typing import Any, Callable
|
11
|
+
|
12
|
+
import dill
|
13
|
+
from qlispreg.expression import Env, Expression
|
14
|
+
|
15
|
+
|
16
|
+
class Unpicklable:
|
17
|
+
|
18
|
+
def __init__(self, obj):
|
19
|
+
self.type = str(type(obj))
|
20
|
+
self.id = id(obj)
|
21
|
+
|
22
|
+
def __repr__(self):
|
23
|
+
return f'<Unpicklable: {self.type} at 0x{id(self):x}>'
|
24
|
+
|
25
|
+
|
26
|
+
class TooLarge:
|
27
|
+
|
28
|
+
def __init__(self, obj):
|
29
|
+
self.type = str(type(obj))
|
30
|
+
self.id = id(obj)
|
31
|
+
|
32
|
+
def __repr__(self):
|
33
|
+
return f'<TooLarge: {self.type} at 0x{id(self):x}>'
|
34
|
+
|
35
|
+
|
36
|
+
def dump_dict(d, keys=[]):
|
37
|
+
ret = {}
|
38
|
+
|
39
|
+
for key, value in d.items():
|
40
|
+
if key in keys:
|
41
|
+
ret[key] = value
|
42
|
+
continue
|
43
|
+
if isinstance(value, dict) and isinstance(key, str):
|
44
|
+
ret[key] = dump_dict(value,
|
45
|
+
keys=[
|
46
|
+
k[len(key) + 1:] for k in keys
|
47
|
+
if k.startswith(f'{key}.')
|
48
|
+
])
|
49
|
+
else:
|
50
|
+
try:
|
51
|
+
ret[key] = dill.dumps(value)
|
52
|
+
except:
|
53
|
+
ret[key] = Unpicklable(value)
|
54
|
+
|
55
|
+
return dill.dumps(ret)
|
56
|
+
|
57
|
+
|
58
|
+
def load_dict(buff):
|
59
|
+
if isinstance(buff, dict):
|
60
|
+
return {key: load_dict(value) for key, value in buff.items()}
|
61
|
+
|
62
|
+
if not isinstance(buff, bytes):
|
63
|
+
return buff
|
64
|
+
|
16
65
|
try:
|
17
|
-
|
18
|
-
arg.result() if isinstance(arg, Future) else arg for arg in args
|
19
|
-
]
|
20
|
-
kw = {
|
21
|
-
k: v.result() if isinstance(v, Future) else v
|
22
|
-
for k, v in kw.items()
|
23
|
-
}
|
24
|
-
return func(*args, **kw)
|
66
|
+
ret = dill.loads(buff)
|
25
67
|
except:
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
68
|
+
return buff
|
69
|
+
|
70
|
+
if isinstance(ret, dict):
|
71
|
+
return load_dict(ret)
|
72
|
+
else:
|
73
|
+
return ret
|
74
|
+
|
75
|
+
|
76
|
+
def dump_globals(ns=None, *, size_limit=10 * 1024 * 1024, warn=False):
|
77
|
+
import __main__
|
78
|
+
|
79
|
+
if ns is None:
|
80
|
+
ns = __main__.__dict__
|
81
|
+
|
82
|
+
namespace = {}
|
83
|
+
|
84
|
+
for name, value in ns.items():
|
85
|
+
try:
|
86
|
+
buf = dill.dumps(value)
|
87
|
+
except:
|
88
|
+
namespace[name] = Unpicklable(value)
|
89
|
+
if warn:
|
90
|
+
warnings.warn(f'Unpicklable: {name} {type(value)}')
|
91
|
+
if len(buf) > size_limit:
|
92
|
+
namespace[name] = TooLarge(value)
|
93
|
+
if warn:
|
94
|
+
warnings.warn(f'TooLarge: {name} {type(value)}')
|
95
|
+
else:
|
96
|
+
namespace[name] = buf
|
97
|
+
|
98
|
+
return namespace
|
99
|
+
|
100
|
+
|
101
|
+
def is_valid_identifier(s: str) -> bool:
|
102
|
+
"""
|
103
|
+
Check if a string is a valid identifier.
|
104
|
+
"""
|
105
|
+
try:
|
106
|
+
ast.parse(f"f({s}=0)")
|
107
|
+
return True
|
108
|
+
except SyntaxError:
|
109
|
+
return False
|
110
|
+
|
111
|
+
|
112
|
+
async def async_next(aiter):
|
113
|
+
try:
|
114
|
+
if hasattr(aiter, '__anext__'):
|
115
|
+
return await aiter.__anext__()
|
116
|
+
else:
|
117
|
+
return next(aiter)
|
118
|
+
except StopIteration:
|
119
|
+
raise StopAsyncIteration from None
|
120
|
+
|
121
|
+
|
122
|
+
async def async_zip(*aiters):
|
123
|
+
aiters = [
|
124
|
+
ait.__aiter__() if hasattr(ait, '__aiter__') else iter(ait)
|
125
|
+
for ait in aiters
|
126
|
+
]
|
127
|
+
try:
|
128
|
+
while True:
|
129
|
+
# 使用 asyncio.gather 等待所有异步生成器返回下一个元素
|
130
|
+
result = await asyncio.gather(*(async_next(ait) for ait in aiters))
|
131
|
+
yield tuple(result)
|
132
|
+
except StopAsyncIteration:
|
133
|
+
# 当任一异步生成器耗尽时停止迭代
|
134
|
+
return
|
135
|
+
|
136
|
+
|
137
|
+
async def call_function(func: Callable | Expression, variables: dict[str,
|
138
|
+
Any]):
|
139
|
+
if isinstance(func, Expression):
|
140
|
+
env = Env()
|
141
|
+
for name in func.symbols():
|
142
|
+
if name in variables:
|
143
|
+
if inspect.isawaitable(variables[name]):
|
144
|
+
variables[name] = await variables[name]
|
145
|
+
env.variables[name] = variables[name]
|
146
|
+
else:
|
147
|
+
raise ValueError(f'{name} is not provided.')
|
148
|
+
return func.eval(env)
|
149
|
+
|
150
|
+
try:
|
151
|
+
sig = inspect.signature(func)
|
152
|
+
except:
|
153
|
+
return func()
|
154
|
+
args = []
|
155
|
+
for name, param in sig.parameters.items():
|
156
|
+
if param.kind == param.POSITIONAL_OR_KEYWORD:
|
157
|
+
if name in variables:
|
158
|
+
if inspect.isawaitable(variables[name]):
|
159
|
+
variables[name] = await variables[name]
|
160
|
+
args.append(variables[name])
|
161
|
+
elif param.default is not param.empty:
|
162
|
+
args.append(param.default)
|
163
|
+
else:
|
164
|
+
raise ValueError(f'parameter {name} is not provided.')
|
165
|
+
elif param.kind == param.VAR_POSITIONAL:
|
166
|
+
raise ValueError('not support VAR_POSITIONAL')
|
167
|
+
elif param.kind == param.VAR_KEYWORD:
|
168
|
+
ret = func(**variables)
|
169
|
+
if inspect.isawaitable(ret):
|
170
|
+
ret = await ret
|
171
|
+
return ret
|
172
|
+
ret = func(*args)
|
173
|
+
if inspect.isawaitable(ret):
|
174
|
+
ret = await ret
|
175
|
+
return ret
|
176
|
+
|
177
|
+
|
178
|
+
def yapf_reformat(cell_text):
|
179
|
+
try:
|
180
|
+
import isort
|
181
|
+
import yapf.yapflib.yapf_api
|
182
|
+
|
183
|
+
fname = f"f{uuid.uuid1().hex}"
|
184
|
+
|
185
|
+
def wrap(source):
|
186
|
+
lines = [f"async def {fname}():"]
|
187
|
+
for line in source.split('\n'):
|
188
|
+
lines.append(" " + line)
|
189
|
+
return '\n'.join(lines)
|
190
|
+
|
191
|
+
def unwrap(source):
|
192
|
+
lines = []
|
193
|
+
for line in source.split('\n'):
|
194
|
+
if line.startswith(f"async def {fname}():"):
|
195
|
+
continue
|
196
|
+
lines.append(line[4:])
|
197
|
+
return '\n'.join(lines)
|
198
|
+
|
199
|
+
cell_text = re.sub('^%', '#%#', cell_text, flags=re.M)
|
200
|
+
try:
|
201
|
+
reformated_text = yapf.yapflib.yapf_api.FormatCode(
|
202
|
+
isort.code(cell_text))[0]
|
203
|
+
except:
|
204
|
+
reformated_text = unwrap(
|
205
|
+
yapf.yapflib.yapf_api.FormatCode(wrap(
|
206
|
+
isort.code(cell_text)))[0])
|
207
|
+
return re.sub('^#%#', '%', reformated_text, flags=re.M)
|
208
|
+
except:
|
209
|
+
return cell_text
|
210
|
+
|
211
|
+
|
212
|
+
def get_installed_packages():
|
213
|
+
result = subprocess.run([sys.executable, '-m', 'pip', 'freeze'],
|
214
|
+
stdout=subprocess.PIPE,
|
215
|
+
text=True)
|
216
|
+
|
217
|
+
lines = result.stdout.split('\n')
|
218
|
+
packages = []
|
219
|
+
for line in lines:
|
220
|
+
if line:
|
221
|
+
packages.append(line)
|
222
|
+
return packages
|
223
|
+
|
224
|
+
|
225
|
+
def get_system_info():
|
226
|
+
info = {
|
227
|
+
'OS': platform.uname()._asdict(),
|
228
|
+
'Python': sys.version,
|
229
|
+
'PythonExecutable': sys.executable,
|
230
|
+
'PythonPath': sys.path,
|
231
|
+
'packages': get_installed_packages()
|
232
|
+
}
|
233
|
+
return info
|
qulab/sys/__init__.py
CHANGED
qulab/sys/device/basedevice.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
import copy
|
1
2
|
import itertools
|
2
3
|
import logging
|
3
4
|
import re
|
5
|
+
import string
|
4
6
|
from collections import defaultdict
|
5
7
|
from functools import partial
|
6
8
|
from typing import Any, Callable, Literal, NamedTuple
|
@@ -12,6 +14,14 @@ Decorator = Callable[[Callable], Callable]
|
|
12
14
|
_buildin_set = set
|
13
15
|
|
14
16
|
|
17
|
+
class Source():
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
class Sink():
|
22
|
+
pass
|
23
|
+
|
24
|
+
|
15
25
|
def action(key: str,
|
16
26
|
method: Literal['get', 'set', 'post', 'delete'] = 'get',
|
17
27
|
**kwds) -> Decorator:
|
@@ -79,7 +89,8 @@ def _add_action(attrs: dict, key: str, method: str, func: Callable, doc: dict,
|
|
79
89
|
matrix[arg] = attrs[arg]
|
80
90
|
for values in itertools.product(*[matrix[arg] for arg in arguments]):
|
81
91
|
kwds = dict(zip(arguments, values))
|
82
|
-
mapping[key.format(**kwds)] = partial(func, **kwds)
|
92
|
+
# mapping[key.format(**kwds)] = partial(func, **kwds)
|
93
|
+
mapping[string.Template(key).substitute(kwds)] = partial(func, **kwds)
|
83
94
|
|
84
95
|
|
85
96
|
def _build_docs(mapping: dict, attrs: dict) -> str:
|
@@ -117,7 +128,8 @@ class BaseDevice(metaclass=DeviceMeta):
|
|
117
128
|
__log__ = None
|
118
129
|
|
119
130
|
def __init__(self, address: str = None, **options):
|
120
|
-
self.
|
131
|
+
self._state = {}
|
132
|
+
self._status = "NOT CONNECTED"
|
121
133
|
self.address = address
|
122
134
|
self.options = options
|
123
135
|
|
@@ -134,28 +146,40 @@ class BaseDevice(metaclass=DeviceMeta):
|
|
134
146
|
f"{self.__class__.__module__}.{self.__class__.__name__}")
|
135
147
|
return self.__log__
|
136
148
|
|
149
|
+
@property
|
150
|
+
def state(self):
|
151
|
+
return copy.deepcopy(self._state)
|
152
|
+
|
153
|
+
def get_state(self, key: str, default=None) -> Any:
|
154
|
+
return self._state.get(key, default)
|
155
|
+
|
156
|
+
@property
|
157
|
+
def status(self):
|
158
|
+
return self._status
|
159
|
+
|
137
160
|
def open(self) -> None:
|
138
|
-
|
161
|
+
self._status = "CONNECTED"
|
139
162
|
|
140
163
|
def close(self) -> None:
|
141
|
-
|
164
|
+
self._status = "NOT CONNECTED"
|
165
|
+
self._state.clear()
|
142
166
|
|
143
167
|
def reset(self) -> None:
|
144
|
-
self.
|
168
|
+
self._state.clear()
|
145
169
|
|
146
170
|
def get(self, key: str, default: Any = None) -> Any:
|
147
171
|
self.log.info(f'Get {key!r}')
|
148
172
|
if key in self.__get_actions__:
|
149
173
|
result = self.__get_actions__[key](self)
|
150
|
-
self.
|
174
|
+
self._state[key] = result
|
151
175
|
return result
|
152
176
|
else:
|
153
|
-
return self.
|
177
|
+
return self._state.get(key, default)
|
154
178
|
|
155
179
|
def set(self, key: str, value: Any = None) -> None:
|
156
180
|
self.log.info(f'Set {key!r} = {value!r}')
|
157
181
|
self.__set_actions__[key](self, value)
|
158
|
-
self.
|
182
|
+
self._state[key] = value
|
159
183
|
|
160
184
|
def post(self, key: str, value: Any = None) -> Any:
|
161
185
|
self.log.info(f'Post {key!r} = {value!r}')
|
@@ -164,7 +188,7 @@ class BaseDevice(metaclass=DeviceMeta):
|
|
164
188
|
def delete(self, key: str) -> None:
|
165
189
|
self.log.info(f'Delete {key!r}')
|
166
190
|
self.__delete_actions__[key](self)
|
167
|
-
del self.
|
191
|
+
del self._state[key]
|
168
192
|
|
169
193
|
def __repr__(self) -> str:
|
170
194
|
return f'{self.__class__.__name__}({self.address!r})'
|
@@ -172,6 +196,8 @@ class BaseDevice(metaclass=DeviceMeta):
|
|
172
196
|
|
173
197
|
class VisaDevice(BaseDevice):
|
174
198
|
|
199
|
+
error_query = 'SYST:ERR?'
|
200
|
+
|
175
201
|
def open(self) -> None:
|
176
202
|
import pyvisa
|
177
203
|
kwds = self.options.copy()
|
@@ -180,14 +206,26 @@ class VisaDevice(BaseDevice):
|
|
180
206
|
else:
|
181
207
|
rm = pyvisa.ResourceManager()
|
182
208
|
self.resource = rm.open_resource(self.address, **kwds)
|
209
|
+
self.errors = []
|
210
|
+
super().open()
|
183
211
|
|
184
212
|
def close(self) -> None:
|
213
|
+
super().close()
|
185
214
|
self.resource.close()
|
186
215
|
|
187
216
|
def reset(self) -> None:
|
188
217
|
super().reset()
|
189
218
|
self.resource.write('*RST')
|
190
219
|
|
220
|
+
def check_error(self):
|
221
|
+
if self.error_query:
|
222
|
+
while True:
|
223
|
+
error = self.resource.query(self.error_query)
|
224
|
+
error_code = int(error.split(',')[0])
|
225
|
+
if error_code == 0:
|
226
|
+
break
|
227
|
+
self.errors.append(error)
|
228
|
+
|
191
229
|
@get('idn')
|
192
230
|
def get_idn(self) -> str:
|
193
231
|
"""Get instrument identification."""
|
@@ -201,13 +239,9 @@ class VisaDevice(BaseDevice):
|
|
201
239
|
@get('errors')
|
202
240
|
def get_errors(self) -> list[str]:
|
203
241
|
"""Get error queue."""
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
error_code = int(error.split(',')[0])
|
208
|
-
if error_code == 0:
|
209
|
-
break
|
210
|
-
errors.append(error)
|
242
|
+
self.check_error()
|
243
|
+
errors = self.errors
|
244
|
+
self.errors = []
|
211
245
|
return errors
|
212
246
|
|
213
247
|
@set('timeout')
|
qulab/sys/device/loader.py
CHANGED
@@ -79,7 +79,7 @@ def create_device(driver_name: str, *args, **kwds) -> BaseDevice:
|
|
79
79
|
except:
|
80
80
|
pass
|
81
81
|
|
82
|
-
dev = create_device_from_module(f"
|
82
|
+
dev = create_device_from_module(f"qulab.sys.drivers.{driver_name}",
|
83
83
|
args, kwds)
|
84
84
|
return dev
|
85
85
|
except:
|
qulab/sys/device/utils.py
CHANGED
@@ -2,6 +2,20 @@ import struct
|
|
2
2
|
|
3
3
|
import numpy as np
|
4
4
|
|
5
|
+
types = {
|
6
|
+
"b" : ( int, 'b'), "B" : ( int, 'B'),
|
7
|
+
"h" : ( int, 'h'), "H" : ( int, 'H'),
|
8
|
+
"i" : ( int, 'i'), "I" : ( int, 'I'),
|
9
|
+
"q" : ( int, 'q'), "Q" : ( int, 'Q'),
|
10
|
+
"f" : (float, 'f'), "d" : (float, 'd'),
|
11
|
+
"int8" : ( int, 'b'), "uint8" : ( int, 'B'),
|
12
|
+
"int16" : ( int, 'h'), "uint16" : ( int, 'H'),
|
13
|
+
"int32" : ( int, 'i'), "uint32" : ( int, 'I'),
|
14
|
+
"int64" : ( int, 'q'), "uint64" : ( int, 'Q'),
|
15
|
+
"float" : (float, 'f'), "double" : (float, 'd'),
|
16
|
+
"float32": (float, 'f'), "float64": (float, 'd')
|
17
|
+
} # yapf: disable
|
18
|
+
|
5
19
|
|
6
20
|
def IEEE_488_2_BinBlock(datalist, dtype="int16", is_big_endian=True):
|
7
21
|
"""
|
@@ -16,19 +30,6 @@ def IEEE_488_2_BinBlock(datalist, dtype="int16", is_big_endian=True):
|
|
16
30
|
binblock, header
|
17
31
|
二进制块, 以及其 'header'
|
18
32
|
"""
|
19
|
-
types = {"b" : ( int, 'b'), "B" : ( int, 'B'),
|
20
|
-
"h" : ( int, 'h'), "H" : ( int, 'H'),
|
21
|
-
"i" : ( int, 'i'), "I" : ( int, 'I'),
|
22
|
-
"q" : ( int, 'q'), "Q" : ( int, 'Q'),
|
23
|
-
"f" : (float, 'f'), "d" : (float, 'd'),
|
24
|
-
"int8" : ( int, 'b'), "uint8" : ( int, 'B'),
|
25
|
-
"int16" : ( int, 'h'), "uint16" : ( int, 'H'),
|
26
|
-
"int32" : ( int, 'i'), "uint32" : ( int, 'I'),
|
27
|
-
"int64" : ( int, 'q'), "uint64" : ( int, 'Q'),
|
28
|
-
"float" : (float, 'f'), "double" : (float, 'd'),
|
29
|
-
"float32": (float, 'f'), "float64": (float, 'd')
|
30
|
-
} # yapf: disable
|
31
|
-
|
32
33
|
if isinstance(datalist, bytes):
|
33
34
|
datablock = datalist
|
34
35
|
else:
|
@@ -44,3 +45,35 @@ def IEEE_488_2_BinBlock(datalist, dtype="int16", is_big_endian=True):
|
|
44
45
|
header = '#%d%s' % (len(size), size)
|
45
46
|
|
46
47
|
return header.encode() + datablock
|
48
|
+
|
49
|
+
|
50
|
+
def decode_IEEE_488_2_BinBlock(data, dtype="int16", is_big_endian=True):
|
51
|
+
"""
|
52
|
+
解析 IEEE 488.2 标准二进制块
|
53
|
+
|
54
|
+
Args:
|
55
|
+
data : 二进制块
|
56
|
+
dtype : 数据类型
|
57
|
+
endian : 字节序
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
datalist
|
61
|
+
"""
|
62
|
+
if isinstance(data, bytes):
|
63
|
+
header = data[:2]
|
64
|
+
size = int(data[2:2 + int(header[1:2])])
|
65
|
+
assert size == len(data) - 2 - int(header[1:2]), "data size error"
|
66
|
+
datablock = data[2 + int(header[1:2]):size + 2 + int(header[1:2])]
|
67
|
+
else:
|
68
|
+
raise ValueError("data must be bytes")
|
69
|
+
|
70
|
+
if is_big_endian:
|
71
|
+
endianc = '>'
|
72
|
+
else:
|
73
|
+
endianc = '<'
|
74
|
+
datalist = list(
|
75
|
+
struct.unpack(
|
76
|
+
'%s%d%s' % (endianc, size // struct.calcsize(types[dtype][1]),
|
77
|
+
types[dtype][1]), datablock))
|
78
|
+
|
79
|
+
return datalist
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
from qulab.sys import BaseDevice, exclude, get, set
|
2
4
|
|
3
5
|
|
4
6
|
class Device(BaseDevice):
|
@@ -12,41 +14,55 @@ class Device(BaseDevice):
|
|
12
14
|
def get_idn(self) -> str:
|
13
15
|
return 'Fake Instrument'
|
14
16
|
|
15
|
-
@
|
17
|
+
@get('${channel_type}${channel_num}.SampleRate',
|
18
|
+
channel_type=['X', 'Y'],
|
19
|
+
channel_num=[1, 2, 3, 4])
|
20
|
+
def get_sample_rate_DDS(self, channel_type: str,
|
21
|
+
channel_num: int) -> float:
|
22
|
+
return 6e9
|
23
|
+
|
24
|
+
@get('${channel}.SampleRate', channel=['Z1', 'Z2', 'Z3', 'Z4'])
|
25
|
+
def get_sample_rate_Z(self, channel: str) -> float:
|
26
|
+
return 2e9
|
27
|
+
|
28
|
+
@get('${channel}.SampleRate', channel=['M1', 'M2'])
|
29
|
+
def get_sample_rate_AD(self, channel: str) -> float:
|
30
|
+
return 1e9
|
31
|
+
|
32
|
+
@set('${channel}.Vpp', channel=exclude(['M1', 'M2']))
|
16
33
|
def set_voltage(self, value: float, channel: str) -> None:
|
17
34
|
self.log.info(f'Set {channel} Vpp to {value}')
|
18
35
|
|
19
|
-
@get('{channel}.Vpp', channel=exclude(['M1', 'M2']))
|
20
|
-
def get_voltage(self, channel: str, default=0.0) -> float:
|
21
|
-
|
36
|
+
# @get('${channel}.Vpp', channel=exclude(['M1', 'M2']))
|
37
|
+
# def get_voltage(self, channel: str, default=0.0) -> float:
|
38
|
+
# return self._state.get(f'{channel}.Vpp', default)
|
22
39
|
|
23
|
-
@set('{channel}.Offset', channel=exclude(['M1', 'M2']))
|
24
|
-
def
|
40
|
+
@set('${channel}.Offset', channel=exclude(['M1', 'M2']))
|
41
|
+
def set_offset(
|
25
42
|
self,
|
26
43
|
value: float,
|
27
44
|
channel: str,
|
28
45
|
) -> None:
|
29
46
|
self.log.info(f'Set {channel} offset to {value}')
|
30
47
|
|
31
|
-
@get('{channel}.Offset', channel=exclude(['M1', 'M2']))
|
32
|
-
def
|
33
|
-
|
48
|
+
# @get('${channel}.Offset', channel=exclude(['M1', 'M2']))
|
49
|
+
# def get_offset(self, channel: str, default=0.0) -> float:
|
50
|
+
# return self._state.get(f'{channel}.Offset', default)
|
34
51
|
|
35
|
-
@set('{channel}.Waveform', channel=exclude(['M1', 'M2']))
|
52
|
+
@set('${channel}.Waveform', channel=exclude(['M1', 'M2']))
|
36
53
|
def set_waveform(self, value, channel: str) -> None:
|
37
54
|
self.log.info(f'Set {channel} waveform to {value!r}')
|
38
55
|
|
39
|
-
@get('{channel}.Waveform', channel=exclude(['M1', 'M2']))
|
40
|
-
def get_waveform(self, channel: str, default=None) -> str:
|
41
|
-
|
56
|
+
# @get('${channel}.Waveform', channel=exclude(['M1', 'M2']))
|
57
|
+
# def get_waveform(self, channel: str, default=None) -> str:
|
58
|
+
# return self._state.get(f'{channel}.Waveform', default)
|
42
59
|
|
43
|
-
@set('{channel}.Size', channel=['M1', 'M2'])
|
60
|
+
@set('${channel}.Size', channel=['M1', 'M2'])
|
44
61
|
def set_size(self, value, channel: str) -> None:
|
45
62
|
self.log.info(f'Set {channel} size to {value!r}')
|
46
63
|
|
47
|
-
@get('{channel}.Trace', channel=['M1', 'M2'])
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
shots = self._status.get(f'{channel}.Shots', 128)
|
64
|
+
@get('${channel}.Trace', channel=['M1', 'M2'])
|
65
|
+
def get_random_data(self, channel: str) -> np.ndarray:
|
66
|
+
size = self._state.get(f'{channel}.Size', 1024)
|
67
|
+
shots = self._state.get(f'{channel}.Shots', 128)
|
52
68
|
return np.random.randn(shots, size)
|
qulab/sys/net/nginx.py
CHANGED
@@ -6,6 +6,9 @@ from fastapi import FastAPI, Request
|
|
6
6
|
from fastapi.responses import Response
|
7
7
|
from pydantic import BaseModel
|
8
8
|
|
9
|
+
NGINX_DIR = 'C:/Users/Administrator/Desktop/nginx/nginx-1.20.2'
|
10
|
+
HOST_URL = 'https://systemq.baqis.ac.cn'
|
11
|
+
|
9
12
|
db = {
|
10
13
|
"n01": {
|
11
14
|
"notebook_port": 1000,
|
@@ -28,7 +31,8 @@ db = {
|
|
28
31
|
}
|
29
32
|
|
30
33
|
|
31
|
-
def fmt_config(ip, notebook_port, codeserver_port, local_notebook_port,
|
34
|
+
def fmt_config(ip, notebook_port, codeserver_port, local_notebook_port,
|
35
|
+
local_codeserver_port, page):
|
32
36
|
return """
|
33
37
|
server {""" + f"""
|
34
38
|
listen {notebook_port} ssl;
|
@@ -108,6 +112,7 @@ server {""" + f"""
|
|
108
112
|
}
|
109
113
|
"""
|
110
114
|
|
115
|
+
|
111
116
|
def fmt_page(notebook_url, codeserver_url):
|
112
117
|
return """
|
113
118
|
<!DOCTYPE html>
|
@@ -142,7 +147,7 @@ class Node(BaseModel):
|
|
142
147
|
app = FastAPI()
|
143
148
|
|
144
149
|
|
145
|
-
@app.post("/
|
150
|
+
@app.post("/auth")
|
146
151
|
async def auth(request: Request, node: Node):
|
147
152
|
try:
|
148
153
|
assert node.name in db
|
@@ -156,34 +161,31 @@ async def auth(request: Request, node: Node):
|
|
156
161
|
else:
|
157
162
|
print(" CHANGE IP", db[node.name]['ip'], "===>", ip)
|
158
163
|
db[node.name]['ip'] = ip
|
159
|
-
|
164
|
+
|
160
165
|
#ip = request.headers.get('x-real-ip')
|
161
166
|
local_notebook_port = node.notebook_port
|
162
167
|
local_codeserver_port = node.codeserver_port
|
163
168
|
codeserver_path = quote_plus(node.codeserver_path)
|
164
169
|
|
165
|
-
codeserver_url = f"
|
166
|
-
notebook_url = f"
|
170
|
+
codeserver_url = f"{HOST_URL}:{codeserver_port}?tkn=lZBAQISFF532&folder={codeserver_path}"
|
171
|
+
notebook_url = f"{HOST_URL}:{notebook_port}/tree"
|
167
172
|
|
168
173
|
page = fmt_page(notebook_url, codeserver_url)
|
169
174
|
page = ''.join(page.splitlines())
|
170
175
|
|
171
|
-
config = fmt_config(ip, notebook_port, codeserver_port,
|
176
|
+
config = fmt_config(ip, notebook_port, codeserver_port,
|
177
|
+
local_notebook_port, local_codeserver_port, page)
|
172
178
|
|
173
|
-
with open(
|
174
|
-
f"C:/Users/Administrator/Desktop/nginx/nginx-1.20.2/conf/servers/{node.name}.conf",
|
175
|
-
"w") as f:
|
179
|
+
with open(f"{NGINX_DIR}/conf/servers/{node.name}.conf", "w") as f:
|
176
180
|
f.write(config)
|
177
181
|
|
178
182
|
await asyncio.sleep(0.1)
|
179
|
-
|
183
|
+
|
180
184
|
cwd = os.getcwd()
|
181
185
|
|
182
|
-
os.chdir(
|
186
|
+
os.chdir(NGINX_DIR)
|
183
187
|
|
184
|
-
os.system(
|
185
|
-
r'C:\Users\Administrator\Desktop\nginx\nginx-1.20.2\nginx.exe -s reload'
|
186
|
-
)
|
188
|
+
os.system(f'{NGINX_DIR}/nginx.exe -s reload')
|
187
189
|
|
188
190
|
os.chdir(cwd)
|
189
191
|
|