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
nbs_bl/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Why do I have anything at all in __init__?
|
|
3
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
4
|
+
from os.path import join, exists
|
|
5
|
+
from .configuration import loadDeviceConfig
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
__version__ = version("sst_funcs")
|
|
9
|
+
except PackageNotFoundError:
|
|
10
|
+
# package is not installed
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
# ip.user_ns['RE'] = RE
|
nbs_bl/beamline.py
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
from bluesky.preprocessors import SupplementalData
|
|
2
|
+
from .queueserver import GLOBAL_USER_STATUS
|
|
3
|
+
from .status import StatusDict
|
|
4
|
+
from .hw import HardwareGroup, DetectorGroup, loadDevices
|
|
5
|
+
from nbs_core.autoload import instantiateOphyd, _find_deferred_devices
|
|
6
|
+
|
|
7
|
+
import IPython
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BeamlineModel:
|
|
11
|
+
default_groups = [
|
|
12
|
+
"shutters",
|
|
13
|
+
"gatevalves",
|
|
14
|
+
"apertures",
|
|
15
|
+
"pinholes",
|
|
16
|
+
"gauges",
|
|
17
|
+
"motors",
|
|
18
|
+
"detectors",
|
|
19
|
+
"manipulators",
|
|
20
|
+
"mirrors",
|
|
21
|
+
"controllers",
|
|
22
|
+
"vacuum",
|
|
23
|
+
"misc",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
default_roles = [
|
|
27
|
+
"beam_current",
|
|
28
|
+
"beam_status",
|
|
29
|
+
"default_shutter",
|
|
30
|
+
"energy",
|
|
31
|
+
"intensity_detector",
|
|
32
|
+
"primary_sampleholder",
|
|
33
|
+
"reference_sampleholder",
|
|
34
|
+
"slits",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
reserved = [
|
|
38
|
+
"current_sample",
|
|
39
|
+
"samples",
|
|
40
|
+
"config",
|
|
41
|
+
"groups",
|
|
42
|
+
"roles",
|
|
43
|
+
"supplemental_data",
|
|
44
|
+
"devices",
|
|
45
|
+
"redis",
|
|
46
|
+
"RE",
|
|
47
|
+
"md",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
def __init__(self, *args, **kwargs):
|
|
51
|
+
"""
|
|
52
|
+
Creates an empty BeamlineModel.
|
|
53
|
+
|
|
54
|
+
The model is a pure data structure. Use BeamlineInitializer
|
|
55
|
+
to initialize it from configuration files.
|
|
56
|
+
"""
|
|
57
|
+
print("Initializing beamline model")
|
|
58
|
+
self.reset()
|
|
59
|
+
|
|
60
|
+
def add_device(self, device_name, device_info):
|
|
61
|
+
"""
|
|
62
|
+
Add a device to the beamline model.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
device_name : str
|
|
67
|
+
Name of the device
|
|
68
|
+
device_info : dict
|
|
69
|
+
Dictionary containing the following keys:
|
|
70
|
+
- device: The device object
|
|
71
|
+
- loaded: Whether the device is loaded
|
|
72
|
+
- groups: List of groups the device is in
|
|
73
|
+
- roles: List of roles the device is in
|
|
74
|
+
"""
|
|
75
|
+
# print(f"Adding device {device_name} to beamline model: {device_info}")
|
|
76
|
+
self._add_device_to_modes(device_name, device_info)
|
|
77
|
+
if not device_info["loaded"]:
|
|
78
|
+
self._deferred_devices.add(device_name)
|
|
79
|
+
self._deferred_config[device_name] = device_info["config"]
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
self.devices[device_name] = device_info["device"]
|
|
83
|
+
self._add_device_to_groups(device_name, device_info)
|
|
84
|
+
self._set_device_roles(device_name, device_info)
|
|
85
|
+
|
|
86
|
+
def _add_device_to_modes(self, device_name, device_info):
|
|
87
|
+
modes = device_info["config"].get("_modes", [])
|
|
88
|
+
for mode in modes:
|
|
89
|
+
if mode in self.modes:
|
|
90
|
+
self.modes[mode].append(device_name)
|
|
91
|
+
else:
|
|
92
|
+
self.modes[mode] = [device_name]
|
|
93
|
+
|
|
94
|
+
def _set_device_roles(self, device_name, device_info):
|
|
95
|
+
"""
|
|
96
|
+
Handle role assignment and special device setup.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
role : str
|
|
101
|
+
Role name to configure
|
|
102
|
+
key : str
|
|
103
|
+
Device key to assign to the role
|
|
104
|
+
"""
|
|
105
|
+
roles = device_info["roles"]
|
|
106
|
+
for role in roles:
|
|
107
|
+
if role in self.reserved:
|
|
108
|
+
raise KeyError(f"Key {role} is reserved, use a different role name")
|
|
109
|
+
if role != "":
|
|
110
|
+
if role not in self.roles:
|
|
111
|
+
self.roles.append(role)
|
|
112
|
+
print(f"Setting {role} to {device_name}")
|
|
113
|
+
setattr(self, role, self.devices[device_name])
|
|
114
|
+
|
|
115
|
+
def handle_special_devices(self):
|
|
116
|
+
"""
|
|
117
|
+
Handle special device setup, particularly sampleholders.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
roles : dict
|
|
122
|
+
Dictionary mapping role names to device names
|
|
123
|
+
"""
|
|
124
|
+
self._setup_sampleholder(
|
|
125
|
+
self.primary_sampleholder,
|
|
126
|
+
"GLOBAL_SAMPLES",
|
|
127
|
+
"GLOBAL_SELECTED",
|
|
128
|
+
is_primary=True,
|
|
129
|
+
)
|
|
130
|
+
if "reference_sampleholder" in self.roles:
|
|
131
|
+
self._setup_sampleholder(
|
|
132
|
+
self.reference_sampleholder,
|
|
133
|
+
"REFERENCE_SAMPLES",
|
|
134
|
+
"REFERENCE_SELECTED",
|
|
135
|
+
is_primary=False,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def _setup_sampleholder(self, holder, samples_key, current_key, is_primary=False):
|
|
139
|
+
"""
|
|
140
|
+
Set up a sampleholder with Redis data.
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
holder : object
|
|
145
|
+
The sampleholder device to set up
|
|
146
|
+
samples_key : str
|
|
147
|
+
Redis key for samples data
|
|
148
|
+
current_key : str
|
|
149
|
+
Redis key for current sample data
|
|
150
|
+
is_primary : bool, optional
|
|
151
|
+
Whether this is the primary sampleholder
|
|
152
|
+
"""
|
|
153
|
+
if holder is None:
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
tmp_samples = GLOBAL_USER_STATUS.request_status_dict(
|
|
157
|
+
samples_key, use_redis=True
|
|
158
|
+
)
|
|
159
|
+
tmp_samples.update(holder.samples)
|
|
160
|
+
holder.samples = tmp_samples
|
|
161
|
+
|
|
162
|
+
tmp_current = GLOBAL_USER_STATUS.request_status_dict(
|
|
163
|
+
current_key, use_redis=True
|
|
164
|
+
)
|
|
165
|
+
tmp_current.update(holder.current_sample)
|
|
166
|
+
holder.current_sample = tmp_current
|
|
167
|
+
|
|
168
|
+
if is_primary:
|
|
169
|
+
self.samples = holder.samples
|
|
170
|
+
self.current_sample = holder.current_sample
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
holder.reload_sample_frames()
|
|
174
|
+
except Exception as e:
|
|
175
|
+
print(f"Error reloading sample frames for primary sampleholder: {e}")
|
|
176
|
+
|
|
177
|
+
def activate_mode(self, modes):
|
|
178
|
+
if not isinstance(modes, (list, tuple)):
|
|
179
|
+
modes = [modes]
|
|
180
|
+
all_devices = set(self.devices.keys())
|
|
181
|
+
devices_to_defer = set()
|
|
182
|
+
devices_to_load = set()
|
|
183
|
+
for mode, mode_devices in self.modes.items():
|
|
184
|
+
if mode in modes:
|
|
185
|
+
devices_to_load.update(mode_devices)
|
|
186
|
+
else:
|
|
187
|
+
devices_to_defer.update(mode_devices)
|
|
188
|
+
devices_to_defer.difference_update(devices_to_load)
|
|
189
|
+
devices_to_defer &= all_devices
|
|
190
|
+
devices_to_load.difference_update(all_devices)
|
|
191
|
+
|
|
192
|
+
for device_name in devices_to_defer:
|
|
193
|
+
self.defer_device(device_name)
|
|
194
|
+
for device_name in devices_to_load:
|
|
195
|
+
self.load_deferred_device(device_name)
|
|
196
|
+
|
|
197
|
+
def deactivate_mode(self, modes):
|
|
198
|
+
if not isinstance(modes, (list, tuple)):
|
|
199
|
+
modes = [modes]
|
|
200
|
+
devices_to_defer = set()
|
|
201
|
+
for mode in modes:
|
|
202
|
+
if mode in self.modes:
|
|
203
|
+
for device_name in self.modes[mode]:
|
|
204
|
+
devices_to_defer.add(device_name)
|
|
205
|
+
for device_name in devices_to_defer:
|
|
206
|
+
self.defer_device(device_name)
|
|
207
|
+
|
|
208
|
+
def load_deferred_device(self, device_name, ns=None):
|
|
209
|
+
"""
|
|
210
|
+
Load a specific deferred device and its dependencies.
|
|
211
|
+
If an alias is requested, loads its root device.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
device_name : str
|
|
216
|
+
Name of the device to load
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
object
|
|
221
|
+
The loaded device
|
|
222
|
+
|
|
223
|
+
Raises
|
|
224
|
+
------
|
|
225
|
+
KeyError
|
|
226
|
+
If the device is not in the deferred devices list
|
|
227
|
+
RuntimeError
|
|
228
|
+
If loading the device fails
|
|
229
|
+
"""
|
|
230
|
+
if device_name not in self._deferred_devices:
|
|
231
|
+
raise KeyError(f"Device {device_name} is not in deferred devices")
|
|
232
|
+
|
|
233
|
+
# If it's an alias, get and load the root device
|
|
234
|
+
config = self._deferred_config.get(device_name, {})
|
|
235
|
+
if isinstance(config, dict) and "_alias" in config:
|
|
236
|
+
root_device = config["_alias"].split(".")[0]
|
|
237
|
+
if root_device != device_name: # Prevent recursion
|
|
238
|
+
return self.load_deferred_device(root_device, ns)
|
|
239
|
+
|
|
240
|
+
# Create config with just this device and its dependencies
|
|
241
|
+
self._deferred_config[device_name]["_defer_loading"] = False
|
|
242
|
+
|
|
243
|
+
try:
|
|
244
|
+
devices = loadDevices(
|
|
245
|
+
self._deferred_config,
|
|
246
|
+
namespace=ns,
|
|
247
|
+
mode=None,
|
|
248
|
+
)
|
|
249
|
+
for device_name, device_info in devices.items():
|
|
250
|
+
self.add_device(device_name, device_info)
|
|
251
|
+
|
|
252
|
+
for device_name in devices:
|
|
253
|
+
self._deferred_config.pop(device_name, None)
|
|
254
|
+
self._deferred_devices.discard(device_name)
|
|
255
|
+
|
|
256
|
+
return self.devices.get(device_name)
|
|
257
|
+
except Exception as e:
|
|
258
|
+
raise RuntimeError(f"Failed to load device {device_name}: {e}") from e
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def get_device(self, device_name, get_subdevice=True):
|
|
262
|
+
"""
|
|
263
|
+
If get_subdevice, follow dotted device names and return the deepest device.
|
|
264
|
+
If False, follow the parents and return the overall parent device
|
|
265
|
+
"""
|
|
266
|
+
device_parts = device_name.split(".")
|
|
267
|
+
device = self.devices[device_parts[0]]
|
|
268
|
+
if get_subdevice:
|
|
269
|
+
for subdev in device_parts[1:]:
|
|
270
|
+
device = getattr(device, subdev)
|
|
271
|
+
else:
|
|
272
|
+
while device.parent is not None:
|
|
273
|
+
device = device.parent
|
|
274
|
+
return device
|
|
275
|
+
|
|
276
|
+
def add_to_baseline(self, device_or_name, only_subdevice=False):
|
|
277
|
+
if isinstance(device_or_name, str):
|
|
278
|
+
device = self.get_device(device_or_name, only_subdevice)
|
|
279
|
+
else:
|
|
280
|
+
device = device_or_name
|
|
281
|
+
if device not in self.supplemental_data.baseline:
|
|
282
|
+
self.supplemental_data.baseline.append(device)
|
|
283
|
+
|
|
284
|
+
def _add_device_to_groups(self, device_key, device_info):
|
|
285
|
+
groups = device_info["groups"]
|
|
286
|
+
# print(f"Adding device {device_key} to groups: {groups}")
|
|
287
|
+
for groupname in groups:
|
|
288
|
+
if groupname in self.reserved:
|
|
289
|
+
raise KeyError(f"Key {groupname} is reserved, use a different group name")
|
|
290
|
+
if groupname not in self.groups:
|
|
291
|
+
self.groups.append(groupname)
|
|
292
|
+
setattr(self, groupname, HardwareGroup(groupname))
|
|
293
|
+
group = getattr(self, groupname)
|
|
294
|
+
group.add(device_key, device_info["device"], **device_info["config"])
|
|
295
|
+
# print(f"Setting {groupname}[{device_key}]")
|
|
296
|
+
|
|
297
|
+
def get_deferred_devices(self):
|
|
298
|
+
"""Return set of currently deferred devices."""
|
|
299
|
+
return self._deferred_devices.copy()
|
|
300
|
+
|
|
301
|
+
def is_device_deferred(self, device_name):
|
|
302
|
+
"""Check if a device is currently deferred."""
|
|
303
|
+
return device_name in self._deferred_devices
|
|
304
|
+
|
|
305
|
+
def defer_device(self, device_name):
|
|
306
|
+
"""
|
|
307
|
+
Move a loaded device to deferred state.
|
|
308
|
+
|
|
309
|
+
Parameters
|
|
310
|
+
----------
|
|
311
|
+
device_name : str
|
|
312
|
+
Name of the device to defer
|
|
313
|
+
|
|
314
|
+
Raises
|
|
315
|
+
------
|
|
316
|
+
KeyError
|
|
317
|
+
If the device is not loaded or already deferred
|
|
318
|
+
RuntimeError
|
|
319
|
+
If the device cannot be deferred
|
|
320
|
+
"""
|
|
321
|
+
ip = IPython.get_ipython()
|
|
322
|
+
|
|
323
|
+
if device_name in self._deferred_devices:
|
|
324
|
+
print(
|
|
325
|
+
f"Device {device_name} is already deferred, continuing to check aliased devices"
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Get device's configuration
|
|
329
|
+
device_config = self.config["devices"].get(device_name)
|
|
330
|
+
if not device_config:
|
|
331
|
+
raise RuntimeError(f"No configuration found for device {device_name}")
|
|
332
|
+
|
|
333
|
+
# Update configuration to defer loading
|
|
334
|
+
device_config["_defer_loading"] = True
|
|
335
|
+
|
|
336
|
+
deferred_devices, _, deferred_config = _find_deferred_devices(
|
|
337
|
+
self.config["devices"]
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
self._deferred_config.update(deferred_config)
|
|
341
|
+
self._deferred_devices.update(deferred_devices)
|
|
342
|
+
|
|
343
|
+
# Remove from groups
|
|
344
|
+
for newly_deferred in deferred_devices:
|
|
345
|
+
if newly_deferred not in self.devices:
|
|
346
|
+
continue
|
|
347
|
+
for group in self.groups:
|
|
348
|
+
group_obj = getattr(self, group)
|
|
349
|
+
if newly_deferred in group_obj.devices:
|
|
350
|
+
group_obj.remove(newly_deferred)
|
|
351
|
+
|
|
352
|
+
# Remove from roles
|
|
353
|
+
for role in self.roles:
|
|
354
|
+
if hasattr(self, role) and getattr(self, role) == self.devices.get(
|
|
355
|
+
newly_deferred, None
|
|
356
|
+
):
|
|
357
|
+
setattr(self, role, None)
|
|
358
|
+
|
|
359
|
+
# Remove from baseline if present
|
|
360
|
+
device = self.devices.get(newly_deferred, None)
|
|
361
|
+
if device != None and device in self.supplemental_data.baseline:
|
|
362
|
+
self.supplemental_data.baseline.remove(device)
|
|
363
|
+
|
|
364
|
+
# Remove from devices registry
|
|
365
|
+
if device != None:
|
|
366
|
+
self.devices.pop(newly_deferred)
|
|
367
|
+
del device
|
|
368
|
+
ip.user_global_ns.pop(newly_deferred, None)
|
|
369
|
+
return device_name
|
|
370
|
+
|
|
371
|
+
def __getitem__(self, key):
|
|
372
|
+
"""Allow dictionary-like access to devices."""
|
|
373
|
+
return self.devices[key]
|
|
374
|
+
|
|
375
|
+
def __setitem__(self, key, value):
|
|
376
|
+
"""Allow dictionary-like setting of devices."""
|
|
377
|
+
self.devices[key] = value
|
|
378
|
+
|
|
379
|
+
def reset(self):
|
|
380
|
+
"""
|
|
381
|
+
Reset the beamline model to an uninitialized state.
|
|
382
|
+
|
|
383
|
+
Clears all devices, configuration, settings, and other initialized
|
|
384
|
+
data while preserving the structure. Useful for debugging and testing.
|
|
385
|
+
"""
|
|
386
|
+
|
|
387
|
+
self.initialized = False
|
|
388
|
+
self.supplemental_data = SupplementalData()
|
|
389
|
+
self.md = {}
|
|
390
|
+
self.RE = None
|
|
391
|
+
self.settings = StatusDict()
|
|
392
|
+
self.devices = StatusDict()
|
|
393
|
+
self.config = {}
|
|
394
|
+
self.plan_status = {}
|
|
395
|
+
self._deferred_config = {}
|
|
396
|
+
self._deferred_devices = set()
|
|
397
|
+
|
|
398
|
+
self.groups = list(self.default_groups)
|
|
399
|
+
self.roles = list(self.default_roles)
|
|
400
|
+
|
|
401
|
+
self.modes = {}
|
|
402
|
+
if hasattr(self, "samples"):
|
|
403
|
+
delattr(self, "samples")
|
|
404
|
+
if hasattr(self, "current_sample"):
|
|
405
|
+
delattr(self, "current_sample")
|
|
406
|
+
if hasattr(self, "redis_settings"):
|
|
407
|
+
delattr(self, "redis_settings")
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _initialize_groups(self):
|
|
411
|
+
print("Initializing groups")
|
|
412
|
+
for group in self.default_groups:
|
|
413
|
+
setattr(self, group, HardwareGroup(group))
|
|
414
|
+
|
|
415
|
+
for role in self.default_roles:
|
|
416
|
+
setattr(self, role, None)
|
|
417
|
+
|
|
418
|
+
self.detectors = DetectorGroup("detectors")
|
|
419
|
+
self.motors = HardwareGroup("motors")
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def reset_old(self):
|
|
424
|
+
self.supplemental_data = SupplementalData()
|
|
425
|
+
self.md = {}
|
|
426
|
+
self.RE = None
|
|
427
|
+
self.settings = StatusDict()
|
|
428
|
+
self.devices = StatusDict()
|
|
429
|
+
self.energy = None
|
|
430
|
+
self.primary_sampleholder = None
|
|
431
|
+
self.default_shutter = None
|
|
432
|
+
self.config = {}
|
|
433
|
+
self.groups = list(self.default_groups)
|
|
434
|
+
self.roles = list(self.default_roles)
|
|
435
|
+
self.detectors = DetectorGroup("detectors")
|
|
436
|
+
self.motors = HardwareGroup("motors")
|
|
437
|
+
self.plan_status = {}
|
|
438
|
+
|
|
439
|
+
self._deferred_config = {}
|
|
440
|
+
self._deferred_devices = set()
|
|
441
|
+
|
|
442
|
+
for group in self.default_groups:
|
|
443
|
+
if not hasattr(self, group):
|
|
444
|
+
setattr(self, group, HardwareGroup(group))
|
|
445
|
+
|
|
446
|
+
for role in self.default_roles:
|
|
447
|
+
if not hasattr(self, role):
|
|
448
|
+
setattr(self, role, None)
|
|
449
|
+
|
|
450
|
+
GLOBAL_BEAMLINE = BeamlineModel()
|