nbs-bl 0.2.0__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.
- nbs_bl/__init__.py +15 -0
- nbs_bl/beamline.py +450 -0
- nbs_bl/configuration.py +838 -0
- nbs_bl/detectors.py +89 -0
- nbs_bl/devices/__init__.py +12 -0
- nbs_bl/devices/detectors.py +154 -0
- nbs_bl/devices/motors.py +242 -0
- nbs_bl/devices/sampleholders.py +360 -0
- nbs_bl/devices/shutters.py +120 -0
- nbs_bl/devices/slits.py +51 -0
- nbs_bl/gGrEqns.py +171 -0
- nbs_bl/geometry/__init__.py +0 -0
- nbs_bl/geometry/affine.py +197 -0
- nbs_bl/geometry/bars.py +189 -0
- nbs_bl/geometry/frames.py +534 -0
- nbs_bl/geometry/linalg.py +138 -0
- nbs_bl/geometry/polygons.py +56 -0
- nbs_bl/help.py +126 -0
- nbs_bl/hw.py +270 -0
- nbs_bl/load.py +113 -0
- nbs_bl/motors.py +19 -0
- nbs_bl/planStatus.py +5 -0
- nbs_bl/plans/__init__.py +8 -0
- nbs_bl/plans/batches.py +174 -0
- nbs_bl/plans/conditions.py +77 -0
- nbs_bl/plans/flyscan_base.py +180 -0
- nbs_bl/plans/groups.py +55 -0
- nbs_bl/plans/maximizers.py +423 -0
- nbs_bl/plans/metaplans.py +179 -0
- nbs_bl/plans/plan_stubs.py +246 -0
- nbs_bl/plans/preprocessors.py +160 -0
- nbs_bl/plans/scan_base.py +58 -0
- nbs_bl/plans/scan_decorators.py +524 -0
- nbs_bl/plans/scans.py +145 -0
- nbs_bl/plans/suspenders.py +87 -0
- nbs_bl/plans/time_estimation.py +168 -0
- nbs_bl/plans/xas.py +123 -0
- nbs_bl/printing.py +221 -0
- nbs_bl/qt/models/beamline.py +11 -0
- nbs_bl/qt/models/energy.py +53 -0
- nbs_bl/qt/widgets/energy.py +225 -0
- nbs_bl/queueserver.py +249 -0
- nbs_bl/redisDevice.py +96 -0
- nbs_bl/run_engine.py +63 -0
- nbs_bl/samples.py +130 -0
- nbs_bl/settings.py +68 -0
- nbs_bl/shutters.py +39 -0
- nbs_bl/sim/__init__.py +2 -0
- nbs_bl/sim/config/polphase.nc +0 -0
- nbs_bl/sim/energy.py +403 -0
- nbs_bl/sim/manipulator.py +14 -0
- nbs_bl/sim/utils.py +36 -0
- nbs_bl/startup.py +27 -0
- nbs_bl/status.py +114 -0
- nbs_bl/tests/__init__.py +0 -0
- nbs_bl/tests/modify_regions.py +160 -0
- nbs_bl/tests/test_frames.py +99 -0
- nbs_bl/tests/test_panels.py +69 -0
- nbs_bl/utils.py +235 -0
- nbs_bl-0.2.0.dist-info/METADATA +71 -0
- nbs_bl-0.2.0.dist-info/RECORD +64 -0
- nbs_bl-0.2.0.dist-info/WHEEL +4 -0
- nbs_bl-0.2.0.dist-info/entry_points.txt +2 -0
- nbs_bl-0.2.0.dist-info/licenses/LICENSE +13 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
from bluesky_live.bluesky_run import BlueskyRun, DocumentCache
|
|
2
|
+
import bluesky.preprocessors as bpp
|
|
3
|
+
from .preprocessors import run_return_decorator
|
|
4
|
+
from bluesky.plan_stubs import mv, mvr, trigger_and_read
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from nbs_bl.plans.flyscan_base import fly_scan
|
|
8
|
+
from .scan_decorators import wrap_plan_name
|
|
9
|
+
import pandas as pd
|
|
10
|
+
from typing import List
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def process_fly_scan(run, signal_names=None, time_offsets=None):
|
|
14
|
+
if time_offsets is None:
|
|
15
|
+
time_offsets = {}
|
|
16
|
+
|
|
17
|
+
df = pd.DataFrame()
|
|
18
|
+
for stream_name in run:
|
|
19
|
+
if "monitor" not in stream_name:
|
|
20
|
+
print(stream_name)
|
|
21
|
+
continue
|
|
22
|
+
column_name = stream_name.replace("_monitor", "")
|
|
23
|
+
newdf = pd.DataFrame(
|
|
24
|
+
{
|
|
25
|
+
"time": run[stream_name].read()["time"].to_numpy(),
|
|
26
|
+
column_name: run[stream_name].read()[column_name].to_numpy(),
|
|
27
|
+
}
|
|
28
|
+
).set_index("time")
|
|
29
|
+
newdf.index += time_offsets.get(stream_name, 0.0)
|
|
30
|
+
df = pd.concat((df, newdf))
|
|
31
|
+
non_monitor_signals = [
|
|
32
|
+
signal for signal in signal_names if signal not in df.columns
|
|
33
|
+
]
|
|
34
|
+
print("Non-monitor signals: ", non_monitor_signals)
|
|
35
|
+
primary = run.primary.read()
|
|
36
|
+
for signal_name in non_monitor_signals:
|
|
37
|
+
if signal_name in primary:
|
|
38
|
+
newdf = pd.DataFrame(
|
|
39
|
+
{
|
|
40
|
+
"time": primary["time"].to_numpy(),
|
|
41
|
+
signal_name: primary[signal_name].to_numpy(),
|
|
42
|
+
}
|
|
43
|
+
).set_index("time")
|
|
44
|
+
newdf.index += time_offsets.get(signal_name, 0.0)
|
|
45
|
+
df = pd.concat((df, newdf))
|
|
46
|
+
# df = df[~df.index.duplicated(keep='first')].sort_index().interpolate(method='index').ffill().bfill()
|
|
47
|
+
|
|
48
|
+
df = (
|
|
49
|
+
df.groupby("time")
|
|
50
|
+
.mean()
|
|
51
|
+
.sort_index()
|
|
52
|
+
.interpolate(method="index")
|
|
53
|
+
.ffill()
|
|
54
|
+
.bfill()
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return df
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def find_optimum_motor_pos(
|
|
61
|
+
run,
|
|
62
|
+
motor_name: str,
|
|
63
|
+
signal_names: List[str],
|
|
64
|
+
time_offsets=None,
|
|
65
|
+
invert=False,
|
|
66
|
+
):
|
|
67
|
+
"""
|
|
68
|
+
df = process_fly_scan(run, [motor_name] + signal_names, time_offsets)
|
|
69
|
+
max_signal_dict = {}
|
|
70
|
+
for signal_name in signal_names:
|
|
71
|
+
idx = df[signal_name].idxmax() if not invert else df[signal_name].idxmin()
|
|
72
|
+
max_signal_dict[signal_name] = {}
|
|
73
|
+
max_signal_dict[signal_name]["time"] = idx
|
|
74
|
+
max_signal_dict[signal_name][motor_name] = df[motor_name][idx]
|
|
75
|
+
max_signal_dict[signal_name]["value"] = df[signal_name][idx]
|
|
76
|
+
return max_signal_dict
|
|
77
|
+
"""
|
|
78
|
+
table = run.primary.read()
|
|
79
|
+
max_info = {}
|
|
80
|
+
for detname in signal_names:
|
|
81
|
+
if invert:
|
|
82
|
+
idx = int(table[detname].argmin())
|
|
83
|
+
print(f"Minimum found at step {idx} for detector {detname}")
|
|
84
|
+
else:
|
|
85
|
+
idx = int(table[detname].argmax())
|
|
86
|
+
print(f"Maximum found at step {idx} for detector {detname}")
|
|
87
|
+
max_info[detname] = {"idx": idx, "value": table[detname][idx]}
|
|
88
|
+
max_val = float(table[motor_name][idx])
|
|
89
|
+
max_info[detname][motor_name] = max_val
|
|
90
|
+
return max_info
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@wrap_plan_name
|
|
94
|
+
def fly_max(
|
|
95
|
+
detectors,
|
|
96
|
+
motor,
|
|
97
|
+
*args,
|
|
98
|
+
max_channel=None,
|
|
99
|
+
invert=False,
|
|
100
|
+
end_on_max=True,
|
|
101
|
+
md=None,
|
|
102
|
+
period: float = 1,
|
|
103
|
+
time_offsets: dict = None,
|
|
104
|
+
**kwargs,
|
|
105
|
+
):
|
|
106
|
+
r"""
|
|
107
|
+
plan: tune a motor to the maximum of signal(motor)
|
|
108
|
+
|
|
109
|
+
Initially, traverse the range from start to stop with
|
|
110
|
+
the number of points specified. Repeat with progressively
|
|
111
|
+
smaller step size until the minimum step size is reached.
|
|
112
|
+
Rescans will be centered on the signal maximum
|
|
113
|
+
with original scan range reduced by ``step_factor``.
|
|
114
|
+
|
|
115
|
+
Set ``snake=True`` if your positions are reproducible
|
|
116
|
+
moving from either direction. This will not
|
|
117
|
+
decrease the number of traversals required to reach convergence.
|
|
118
|
+
Snake motion reduces the total time spent on motion
|
|
119
|
+
to reset the positioner. For some positioners, such as
|
|
120
|
+
those with hysteresis, snake scanning may not be appropriate.
|
|
121
|
+
For such positioners, always approach the positions from the
|
|
122
|
+
same direction.
|
|
123
|
+
|
|
124
|
+
Note: if there are multiple maxima, this function may find a smaller one
|
|
125
|
+
unless the initial number of steps is large enough.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
detectors : Signal
|
|
130
|
+
list of 'readable' objects
|
|
131
|
+
motor : object
|
|
132
|
+
any 'settable' object (motor, temp controller, etc.)
|
|
133
|
+
start : float
|
|
134
|
+
start of range
|
|
135
|
+
stop : float
|
|
136
|
+
end of range, note: start < stop
|
|
137
|
+
velocities : list of floats
|
|
138
|
+
list of speeds to set motor to during run.
|
|
139
|
+
max_channel : list of strings
|
|
140
|
+
detector fields whose output is to maximize. If not given, the first detector is used.
|
|
141
|
+
(the first will be maximized, but secondardy maxes will be recorded during the scans for the first -
|
|
142
|
+
if the maxima are not in the same range this will not be useful)
|
|
143
|
+
md : dict, optional
|
|
144
|
+
metadata
|
|
145
|
+
period: float, optional
|
|
146
|
+
Detector integration time in seconds.
|
|
147
|
+
**kwargs : dict, optional
|
|
148
|
+
additional arguments to pass to fly_scan
|
|
149
|
+
|
|
150
|
+
"""
|
|
151
|
+
if max_channel is None:
|
|
152
|
+
max_channel = [detectors[0].name]
|
|
153
|
+
_md = {
|
|
154
|
+
"maximizer_args": {
|
|
155
|
+
"plan": "fly_scan",
|
|
156
|
+
"max_channel": max_channel,
|
|
157
|
+
"end_on_max": end_on_max,
|
|
158
|
+
"invert": invert,
|
|
159
|
+
},
|
|
160
|
+
"hints": {},
|
|
161
|
+
}
|
|
162
|
+
_md.update(md or {})
|
|
163
|
+
|
|
164
|
+
dc = DocumentCache()
|
|
165
|
+
|
|
166
|
+
@bpp.subs_decorator(dc)
|
|
167
|
+
def inner_maximizer():
|
|
168
|
+
yield from fly_scan(detectors, motor, *args, md=_md, period=period, **kwargs)
|
|
169
|
+
run = BlueskyRun(dc)
|
|
170
|
+
motor_name = motor.name
|
|
171
|
+
max_info = {}
|
|
172
|
+
max_info = find_optimum_motor_pos(run, motor_name, max_channel, invert=invert)
|
|
173
|
+
if end_on_max:
|
|
174
|
+
motor_pos = max_info[max_channel[0]][motor_name]
|
|
175
|
+
print(f"going to position {motor_pos} for {motor_name}")
|
|
176
|
+
yield from mv(motor, motor_pos)
|
|
177
|
+
return max_info
|
|
178
|
+
|
|
179
|
+
return (yield from inner_maximizer())
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@wrap_plan_name
|
|
183
|
+
def find_max(
|
|
184
|
+
plan,
|
|
185
|
+
dets,
|
|
186
|
+
*args,
|
|
187
|
+
max_channel=None,
|
|
188
|
+
invert=False,
|
|
189
|
+
end_on_max=True,
|
|
190
|
+
md=None,
|
|
191
|
+
**kwargs,
|
|
192
|
+
):
|
|
193
|
+
"""
|
|
194
|
+
invert turns find_max into find_min
|
|
195
|
+
"""
|
|
196
|
+
dc = DocumentCache()
|
|
197
|
+
|
|
198
|
+
md = md or {}
|
|
199
|
+
|
|
200
|
+
_md = {
|
|
201
|
+
"maximizer_args": {
|
|
202
|
+
"plan": plan.__name__,
|
|
203
|
+
"max_channel": max_channel,
|
|
204
|
+
"invert": invert,
|
|
205
|
+
"end_on_max": end_on_max,
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
_md.update(md)
|
|
209
|
+
|
|
210
|
+
@bpp.subs_decorator(dc)
|
|
211
|
+
def inner_maximizer():
|
|
212
|
+
yield from plan(dets, *args, md=_md, **kwargs)
|
|
213
|
+
run = BlueskyRun(dc)
|
|
214
|
+
table = run.primary.read()
|
|
215
|
+
motor_names = run.metadata["start"]["motors"]
|
|
216
|
+
motors = [m for m in args if getattr(m, "name", None) in motor_names]
|
|
217
|
+
if max_channel is None:
|
|
218
|
+
detname = dets[0].name
|
|
219
|
+
else:
|
|
220
|
+
detname = max_channel
|
|
221
|
+
if invert:
|
|
222
|
+
max_idx = int(table[detname].argmin())
|
|
223
|
+
print(f"Minimum found at step {max_idx} for detector {detname}")
|
|
224
|
+
else:
|
|
225
|
+
max_idx = int(table[detname].argmax())
|
|
226
|
+
print(f"Maximum found at step {max_idx} for detector {detname}")
|
|
227
|
+
ret = []
|
|
228
|
+
for m in motors:
|
|
229
|
+
max_val = float(table[m.name][max_idx])
|
|
230
|
+
print(f"setting {m.name} to {max_val}")
|
|
231
|
+
ret.append([m, max_val])
|
|
232
|
+
if end_on_max:
|
|
233
|
+
print("going to found motor positions")
|
|
234
|
+
flat_list = [item for sublist in ret for item in sublist]
|
|
235
|
+
yield from mv(*flat_list)
|
|
236
|
+
return ret
|
|
237
|
+
|
|
238
|
+
return (yield from inner_maximizer())
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def find_min(plan, dets, *args):
|
|
242
|
+
return (yield from find_max(plan, dets, *args, invert=True))
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def find_max_deriv(plan, dets, *args, max_channel=None):
|
|
246
|
+
dc = DocumentCache()
|
|
247
|
+
|
|
248
|
+
@bpp.subs_decorator(dc)
|
|
249
|
+
def inner_maximizer():
|
|
250
|
+
yield from plan(dets, *args)
|
|
251
|
+
run = BlueskyRun(dc)
|
|
252
|
+
table = run.primary.read()
|
|
253
|
+
motor_names = run.metadata["start"]["motors"]
|
|
254
|
+
motors = [m for m in args if getattr(m, "name", None) in motor_names]
|
|
255
|
+
if len(motors) > 1:
|
|
256
|
+
print(
|
|
257
|
+
"Derivative with multiple motors unsupported, \
|
|
258
|
+
taking first motor"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if max_channel is None:
|
|
262
|
+
detname = dets[0].name
|
|
263
|
+
else:
|
|
264
|
+
detname = max_channel
|
|
265
|
+
motname = motors[0].name
|
|
266
|
+
max_idx = np.argmax(np.abs(np.gradient(table[detname], table[motname])))
|
|
267
|
+
print(f"Maximum derivative found at step {max_idx} for detector {detname}")
|
|
268
|
+
ret = []
|
|
269
|
+
for m in motors:
|
|
270
|
+
max_val = float(table[m.name][max_idx])
|
|
271
|
+
print(f"setting {m.name} to {max_val}")
|
|
272
|
+
ret.append([m, max_val])
|
|
273
|
+
yield from mv(m, max_val)
|
|
274
|
+
return ret
|
|
275
|
+
|
|
276
|
+
return (yield from inner_maximizer())
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def find_halfmax(plan, dets, *args, max_channel=None, **kwargs):
|
|
280
|
+
"""
|
|
281
|
+
For a plan and detector that goes from low to high, find
|
|
282
|
+
the motor value where the detector is half of the maximum
|
|
283
|
+
value
|
|
284
|
+
"""
|
|
285
|
+
dc = DocumentCache()
|
|
286
|
+
|
|
287
|
+
@bpp.subs_decorator(dc)
|
|
288
|
+
def inner_maximizer():
|
|
289
|
+
yield from plan(dets, *args, **kwargs)
|
|
290
|
+
run = BlueskyRun(dc)
|
|
291
|
+
table = run.primary.read()
|
|
292
|
+
motor_names = run.metadata["start"]["motors"]
|
|
293
|
+
motors = [m for m in args if getattr(m, "name", None) in motor_names]
|
|
294
|
+
if max_channel is None:
|
|
295
|
+
detname = dets[0].name
|
|
296
|
+
else:
|
|
297
|
+
detname = max_channel
|
|
298
|
+
max_val = float(table[detname].max())
|
|
299
|
+
halftable = table[detname] - max_val / 2.0
|
|
300
|
+
half_idx = 0
|
|
301
|
+
for n, v in enumerate(halftable.data):
|
|
302
|
+
if v > 0:
|
|
303
|
+
half_idx = n
|
|
304
|
+
break
|
|
305
|
+
ret = []
|
|
306
|
+
for m in motors:
|
|
307
|
+
mot_val = float(table[m.name][half_idx])
|
|
308
|
+
print(f"setting {m.name} to {mot_val}")
|
|
309
|
+
ret.append([m, mot_val])
|
|
310
|
+
yield from mv(m, mot_val)
|
|
311
|
+
return ret
|
|
312
|
+
|
|
313
|
+
return (yield from inner_maximizer())
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def halfmax_adaptive(
|
|
317
|
+
dets, motor, step: float = 5, precision: float = 1, maxct=None, max_channel=None
|
|
318
|
+
):
|
|
319
|
+
if max_channel is None:
|
|
320
|
+
detname = dets[0].name
|
|
321
|
+
else:
|
|
322
|
+
detname = max_channel
|
|
323
|
+
|
|
324
|
+
def ct():
|
|
325
|
+
ret = yield from trigger_and_read(dets)
|
|
326
|
+
return ret[detname]["value"]
|
|
327
|
+
|
|
328
|
+
@bpp.stage_decorator(dets)
|
|
329
|
+
@run_return_decorator()
|
|
330
|
+
def halfmax_inner(step, maxct=None):
|
|
331
|
+
if maxct is None:
|
|
332
|
+
maxct = yield from ct()
|
|
333
|
+
current = maxct
|
|
334
|
+
while np.abs(step) > precision / 2.0:
|
|
335
|
+
yield from mvr(motor, -1 * step)
|
|
336
|
+
current = yield from ct()
|
|
337
|
+
while current > maxct / 2.0:
|
|
338
|
+
yield from mvr(motor, step)
|
|
339
|
+
current = yield from ct()
|
|
340
|
+
step = step / 2.0
|
|
341
|
+
if np.abs(step) > precision / 2.0:
|
|
342
|
+
print(f"{detname} halfmax at {motor.name}:{motor.position}")
|
|
343
|
+
print(f"Value: {current}, Max: {maxct}, reducing step to {step}")
|
|
344
|
+
|
|
345
|
+
print(f"{detname} halfmax at {motor.name}:{motor.position}")
|
|
346
|
+
print(f"Value: {current}, Max: {maxct}, " f"precision: {precision} reached")
|
|
347
|
+
return motor.position
|
|
348
|
+
|
|
349
|
+
return (yield from halfmax_inner(step, maxct))
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def threshold_adaptive(
|
|
353
|
+
dets, motor, threshold, step: float = 2, limit: int = 15, max_channel=None
|
|
354
|
+
):
|
|
355
|
+
"""
|
|
356
|
+
Attempt to get a detector over a threshold by moving a motor
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
if max_channel is None:
|
|
360
|
+
detname = dets[0].name
|
|
361
|
+
else:
|
|
362
|
+
detname = max_channel
|
|
363
|
+
|
|
364
|
+
def ct():
|
|
365
|
+
ret = yield from trigger_and_read(dets)
|
|
366
|
+
return ret[detname]["value"]
|
|
367
|
+
|
|
368
|
+
@bpp.stage_decorator(dets)
|
|
369
|
+
@run_return_decorator()
|
|
370
|
+
def inner_threshold():
|
|
371
|
+
pos = motor.position
|
|
372
|
+
maxpos = pos
|
|
373
|
+
|
|
374
|
+
n = 0
|
|
375
|
+
current = yield from ct()
|
|
376
|
+
|
|
377
|
+
if current > threshold:
|
|
378
|
+
mincurrent = current
|
|
379
|
+
minpos = motor.position
|
|
380
|
+
print(
|
|
381
|
+
f"Starting above threshold of {detname}, try to get below {threshold} by moving {motor.name} with starting position {pos} and step -{step}"
|
|
382
|
+
)
|
|
383
|
+
while current > threshold and n < limit:
|
|
384
|
+
yield from mvr(motor, -1 * step)
|
|
385
|
+
current = yield from ct()
|
|
386
|
+
if current < mincurrent:
|
|
387
|
+
mincurrent = current
|
|
388
|
+
minpos = motor.position
|
|
389
|
+
n += 1
|
|
390
|
+
if current < threshold:
|
|
391
|
+
pass
|
|
392
|
+
else:
|
|
393
|
+
raise ValueError(
|
|
394
|
+
f"Detector {detname} did not fall below {threshold} after"
|
|
395
|
+
f" {limit} moves of {motor.name} with -{step} step "
|
|
396
|
+
f"size. Minimum value was {mincurrent} at {minpos}."
|
|
397
|
+
f"Check if {motor.name} is going the right direction,"
|
|
398
|
+
f" and if {detname} is on."
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
print(
|
|
402
|
+
f"Searching for threshold value {threshold} of {detname} for {motor.name} with starting position {pos} and step {step}"
|
|
403
|
+
)
|
|
404
|
+
maxcurrent = current
|
|
405
|
+
while current < threshold and n < limit:
|
|
406
|
+
yield from mvr(motor, step)
|
|
407
|
+
current = yield from ct()
|
|
408
|
+
if current > maxcurrent:
|
|
409
|
+
maxcurrent = current
|
|
410
|
+
maxpos = motor.position
|
|
411
|
+
n += 1
|
|
412
|
+
if current > threshold:
|
|
413
|
+
return motor.position
|
|
414
|
+
else:
|
|
415
|
+
raise ValueError(
|
|
416
|
+
f"Detector {detname} did not exceed {threshold} after"
|
|
417
|
+
f" {limit} moves of {motor.name} with {step} step "
|
|
418
|
+
f"size. Maximum value was {maxcurrent} at {maxpos}."
|
|
419
|
+
f"Check if {motor.name} is going the right direction,"
|
|
420
|
+
f" and if {detname} is on."
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
return (yield from inner_threshold())
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import typing
|
|
3
|
+
from ..help import add_to_plan_list
|
|
4
|
+
|
|
5
|
+
from bluesky_queueserver import parameter_annotation_decorator
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@add_to_plan_list
|
|
9
|
+
@parameter_annotation_decorator(
|
|
10
|
+
{
|
|
11
|
+
"parameters": {
|
|
12
|
+
"plans": {
|
|
13
|
+
"annotation": "typing.List[__PLAN__]",
|
|
14
|
+
"description": "List of plan names or callables to run in sequence.",
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
def repeat_plan_sequence_for_duration(
|
|
20
|
+
plans,
|
|
21
|
+
plan_args_list: typing.List[typing.List],
|
|
22
|
+
plan_kwargs_list: typing.List[typing.Dict],
|
|
23
|
+
duration: float,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Repeat a sequence of plans for a specified duration.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
plans : list of str or callables
|
|
31
|
+
List of plan names (or callables) to run in sequence.
|
|
32
|
+
plan_args_list : list of list
|
|
33
|
+
List of argument lists, one for each plan.
|
|
34
|
+
plan_kwargs_list : list of dict
|
|
35
|
+
List of kwargs dicts, one for each plan.
|
|
36
|
+
duration : float
|
|
37
|
+
Total duration to repeat the sequence (seconds).
|
|
38
|
+
|
|
39
|
+
Yields
|
|
40
|
+
------
|
|
41
|
+
Msg
|
|
42
|
+
Bluesky messages from the repeated plans.
|
|
43
|
+
"""
|
|
44
|
+
start_time = time.time()
|
|
45
|
+
n_plans = len(plans)
|
|
46
|
+
idx = 0
|
|
47
|
+
while (time.time() - start_time) < duration:
|
|
48
|
+
plan = plans[idx % n_plans]
|
|
49
|
+
args = plan_args_list[idx % n_plans]
|
|
50
|
+
kwargs = plan_kwargs_list[idx % n_plans]
|
|
51
|
+
yield from plan(*args, **kwargs)
|
|
52
|
+
idx += 1
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@add_to_plan_list
|
|
56
|
+
@parameter_annotation_decorator(
|
|
57
|
+
{
|
|
58
|
+
"parameters": {
|
|
59
|
+
"plans": {
|
|
60
|
+
"annotation": "typing.List[__PLAN__]",
|
|
61
|
+
"description": "List of plan names or callables to run in sequence.",
|
|
62
|
+
},
|
|
63
|
+
"condition": {
|
|
64
|
+
"annotation": "__PLAN__",
|
|
65
|
+
"description": "A plan that returns a boolean; controls loop continuation.",
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
def repeat_plan_sequence_while_condition(
|
|
71
|
+
plans,
|
|
72
|
+
plan_args_list: typing.List[typing.List],
|
|
73
|
+
plan_kwargs_list: typing.List[typing.Dict],
|
|
74
|
+
condition,
|
|
75
|
+
condition_args: typing.List = None,
|
|
76
|
+
condition_kwargs: typing.Dict = None,
|
|
77
|
+
):
|
|
78
|
+
"""
|
|
79
|
+
Repeat a sequence of plans while a condition plan returns True.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
plans : list of str or callables
|
|
84
|
+
List of plan names (or callables) to run in sequence.
|
|
85
|
+
plan_args_list : list of list
|
|
86
|
+
List of argument lists, one for each plan.
|
|
87
|
+
plan_kwargs_list : list of dict
|
|
88
|
+
List of kwargs dicts, one for each plan.
|
|
89
|
+
condition : str or callable
|
|
90
|
+
A plan that returns a boolean; controls loop continuation.
|
|
91
|
+
condition_args : list, optional
|
|
92
|
+
Arguments for the condition plan.
|
|
93
|
+
condition_kwargs : dict, optional
|
|
94
|
+
Keyword arguments for the condition plan.
|
|
95
|
+
|
|
96
|
+
Yields
|
|
97
|
+
------
|
|
98
|
+
Msg
|
|
99
|
+
Bluesky messages from the repeated plans.
|
|
100
|
+
"""
|
|
101
|
+
n_plans = len(plans)
|
|
102
|
+
idx = 0
|
|
103
|
+
if condition_args is None:
|
|
104
|
+
condition_args = []
|
|
105
|
+
if condition_kwargs is None:
|
|
106
|
+
condition_kwargs = {}
|
|
107
|
+
while True:
|
|
108
|
+
val = yield from condition(*condition_args, **condition_kwargs)
|
|
109
|
+
if not val:
|
|
110
|
+
break
|
|
111
|
+
plan = plans[idx % n_plans]
|
|
112
|
+
args = plan_args_list[idx % n_plans]
|
|
113
|
+
kwargs = plan_kwargs_list[idx % n_plans]
|
|
114
|
+
yield from plan(*args, **kwargs)
|
|
115
|
+
idx += 1
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@add_to_plan_list
|
|
119
|
+
@parameter_annotation_decorator(
|
|
120
|
+
{
|
|
121
|
+
"parameters": {
|
|
122
|
+
"plans": {
|
|
123
|
+
"annotation": "typing.List[__PLAN__]",
|
|
124
|
+
"description": "List of plan names or callables to run in sequence.",
|
|
125
|
+
},
|
|
126
|
+
"condition": {
|
|
127
|
+
"annotation": "__PLAN__",
|
|
128
|
+
"description": "A plan that returns a boolean; controls loop continuation.",
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
def repeat_plan_sequence_until_condition(
|
|
134
|
+
plans,
|
|
135
|
+
plan_args_list: typing.List[typing.List],
|
|
136
|
+
plan_kwargs_list: typing.List[typing.Dict],
|
|
137
|
+
condition,
|
|
138
|
+
condition_args: typing.List = None,
|
|
139
|
+
condition_kwargs: typing.Dict = None,
|
|
140
|
+
):
|
|
141
|
+
"""
|
|
142
|
+
Repeat a sequence of plans until a condition plan returns True.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
plans : list of str or callables
|
|
147
|
+
List of plan names (or callables) to run in sequence.
|
|
148
|
+
plan_args_list : list of list
|
|
149
|
+
List of argument lists, one for each plan.
|
|
150
|
+
plan_kwargs_list : list of dict
|
|
151
|
+
List of kwargs dicts, one for each plan.
|
|
152
|
+
condition : str or callable
|
|
153
|
+
A plan that returns a boolean; controls loop continuation.
|
|
154
|
+
condition_args : list, optional
|
|
155
|
+
Arguments for the condition plan.
|
|
156
|
+
condition_kwargs : dict, optional
|
|
157
|
+
Keyword arguments for the condition plan.
|
|
158
|
+
|
|
159
|
+
Yields
|
|
160
|
+
------
|
|
161
|
+
Msg
|
|
162
|
+
Bluesky messages from the repeated plans.
|
|
163
|
+
"""
|
|
164
|
+
n_plans = len(plans)
|
|
165
|
+
idx = 0
|
|
166
|
+
if condition_args is None:
|
|
167
|
+
condition_args = []
|
|
168
|
+
if condition_kwargs is None:
|
|
169
|
+
condition_kwargs = {}
|
|
170
|
+
while True:
|
|
171
|
+
val = yield from condition(*condition_args, **condition_kwargs)
|
|
172
|
+
if val:
|
|
173
|
+
break
|
|
174
|
+
plan = plans[idx % n_plans]
|
|
175
|
+
args = plan_args_list[idx % n_plans]
|
|
176
|
+
kwargs = plan_kwargs_list[idx % n_plans]
|
|
177
|
+
yield from plan(*args, **kwargs)
|
|
178
|
+
idx += 1
|
|
179
|
+
|