QuLab 2.0.0__py3-none-any.whl → 2.10.12__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.
Files changed (69) hide show
  1. qulab/__init__.py +33 -1
  2. qulab/__main__.py +4 -0
  3. qulab/cli/commands.py +31 -0
  4. qulab/cli/config.py +170 -0
  5. qulab/cli/decorators.py +28 -0
  6. qulab/executor/__init__.py +5 -0
  7. qulab/executor/analyze.py +188 -0
  8. qulab/executor/cli.py +447 -0
  9. qulab/executor/load.py +563 -0
  10. qulab/executor/registry.py +185 -0
  11. qulab/executor/schedule.py +543 -0
  12. qulab/executor/storage.py +615 -0
  13. qulab/executor/template.py +259 -0
  14. qulab/executor/utils.py +194 -0
  15. qulab/monitor/__init__.py +1 -93
  16. qulab/monitor/__main__.py +8 -0
  17. qulab/monitor/monitor.py +115 -0
  18. qulab/scan/__init__.py +2 -4
  19. qulab/scan/curd.py +221 -0
  20. qulab/scan/models.py +554 -0
  21. qulab/scan/optimize.py +76 -0
  22. qulab/scan/query.py +387 -0
  23. qulab/scan/record.py +603 -0
  24. qulab/scan/scan.py +1166 -0
  25. qulab/scan/server.py +450 -0
  26. qulab/scan/space.py +213 -0
  27. qulab/scan/utils.py +230 -34
  28. qulab/sys/__init__.py +2 -0
  29. qulab/sys/device/basedevice.py +50 -16
  30. qulab/sys/device/loader.py +1 -1
  31. qulab/sys/device/utils.py +46 -13
  32. qulab/sys/drivers/FakeInstrument.py +36 -20
  33. qulab/sys/net/nginx.py +16 -14
  34. qulab/sys/rpc/router.py +35 -0
  35. qulab/sys/rpc/zmq_socket.py +227 -0
  36. qulab/tools/__init__.py +0 -0
  37. qulab/tools/connection_helper.py +39 -0
  38. qulab/typing.py +2 -0
  39. qulab/utils.py +95 -0
  40. qulab/version.py +1 -1
  41. qulab/visualization/__init__.py +188 -0
  42. qulab/visualization/__main__.py +71 -0
  43. qulab/visualization/_autoplot.py +464 -0
  44. qulab/visualization/plot_circ.py +319 -0
  45. qulab/visualization/plot_layout.py +408 -0
  46. qulab/visualization/plot_seq.py +242 -0
  47. qulab/visualization/qdat.py +152 -0
  48. qulab/visualization/rot3d.py +23 -0
  49. qulab/visualization/widgets.py +86 -0
  50. {QuLab-2.0.0.dist-info → qulab-2.10.12.dist-info}/METADATA +28 -12
  51. qulab-2.10.12.dist-info/RECORD +104 -0
  52. {QuLab-2.0.0.dist-info → qulab-2.10.12.dist-info}/WHEEL +1 -1
  53. qulab-2.10.12.dist-info/entry_points.txt +2 -0
  54. QuLab-2.0.0.dist-info/RECORD +0 -71
  55. qulab/monitor/multiploter/__init__.py +0 -1
  56. qulab/scan/base.py +0 -544
  57. qulab/scan/expression.py +0 -360
  58. qulab/scan/scanner.py +0 -244
  59. qulab/scan/transforms.py +0 -16
  60. /qulab/{scan/dataset.py → cli/__init__.py} +0 -0
  61. /qulab/monitor/{multiploter/config.py → config.py} +0 -0
  62. /qulab/monitor/{multiploter/dataset.py → dataset.py} +0 -0
  63. /qulab/monitor/{multiploter/event_queue.py → event_queue.py} +0 -0
  64. /qulab/monitor/{multiploter/main.py → mainwindow.py} +0 -0
  65. /qulab/monitor/{multiploter/ploter.py → ploter.py} +0 -0
  66. /qulab/monitor/{multiploter/qt_compat.py → qt_compat.py} +0 -0
  67. /qulab/monitor/{multiploter/toolbar.py → toolbar.py} +0 -0
  68. {QuLab-2.0.0.dist-info → qulab-2.10.12.dist-info/licenses}/LICENSE +0 -0
  69. {QuLab-2.0.0.dist-info → qulab-2.10.12.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
- from concurrent.futures import Future
3
-
4
-
5
- def call_func_with_kwds(func, args, kwds, log=None):
6
- funcname = getattr(func, '__name__', repr(func))
7
- sig = inspect.signature(func)
8
- for p in sig.parameters.values():
9
- if p.kind == p.VAR_KEYWORD:
10
- return func(*args, **kwds)
11
- kw = {
12
- k: v
13
- for k, v in kwds.items()
14
- if k in list(sig.parameters.keys())[len(args):]
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
- args = [
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
- if log:
27
- log.exception(f'Call {funcname} with {args} and {kw}')
28
- raise
29
- finally:
30
- if log:
31
- log.debug(f'Call {funcname} with {args} and {kw}')
32
-
33
-
34
- def try_to_call(x, args, kwds, log=None):
35
- if callable(x):
36
- return call_func_with_kwds(x, args, kwds, log)
37
- return x
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
@@ -0,0 +1,2 @@
1
+ from .device import (BaseDevice, VisaDevice, action, create_device, delete,
2
+ exclude, get, post, set)
@@ -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._status = {}
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
- pass
161
+ self._status = "CONNECTED"
139
162
 
140
163
  def close(self) -> None:
141
- pass
164
+ self._status = "NOT CONNECTED"
165
+ self._state.clear()
142
166
 
143
167
  def reset(self) -> None:
144
- self._status.clear()
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._status[key] = result
174
+ self._state[key] = result
151
175
  return result
152
176
  else:
153
- return self._status.get(key, default)
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._status[key] = value
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._status[key]
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
- errors = []
205
- while True:
206
- error = self.resource.query('SYST:ERR?')
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')
@@ -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"waveforms.sys.drivers.{driver_name}",
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
- from waveforms.sys.device import BaseDevice, exclude, get, set
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
- @set('{channel}.Vpp', channel=exclude(['M1', 'M2']))
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
- return self._status.get(f'{channel}.Vpp', default)
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 set_frequency(
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 get_frequency(self, channel: str, default=0.0) -> float:
33
- return self._status.get(f'{channel}.Offset', default)
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
- return self._status.get(f'{channel}.Waveform', default)
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 get_size(self, channel: str) -> str:
49
- import numpy as np
50
- size = self._status.get(f'{channel}.Size', 1024)
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, local_codeserver_port, page):
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("/ping")
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"https://systemq.baqis.ac.cn:{codeserver_port}?tkn=lZBAQISFF532&folder={codeserver_path}"
166
- notebook_url = f"https://systemq.baqis.ac.cn:{notebook_port}/tree"
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, local_notebook_port, local_codeserver_port, page)
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(r'C:\Users\Administrator\Desktop\nginx\nginx-1.20.2')
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