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.
Files changed (64) hide show
  1. nbs_bl/__init__.py +15 -0
  2. nbs_bl/beamline.py +450 -0
  3. nbs_bl/configuration.py +838 -0
  4. nbs_bl/detectors.py +89 -0
  5. nbs_bl/devices/__init__.py +12 -0
  6. nbs_bl/devices/detectors.py +154 -0
  7. nbs_bl/devices/motors.py +242 -0
  8. nbs_bl/devices/sampleholders.py +360 -0
  9. nbs_bl/devices/shutters.py +120 -0
  10. nbs_bl/devices/slits.py +51 -0
  11. nbs_bl/gGrEqns.py +171 -0
  12. nbs_bl/geometry/__init__.py +0 -0
  13. nbs_bl/geometry/affine.py +197 -0
  14. nbs_bl/geometry/bars.py +189 -0
  15. nbs_bl/geometry/frames.py +534 -0
  16. nbs_bl/geometry/linalg.py +138 -0
  17. nbs_bl/geometry/polygons.py +56 -0
  18. nbs_bl/help.py +126 -0
  19. nbs_bl/hw.py +270 -0
  20. nbs_bl/load.py +113 -0
  21. nbs_bl/motors.py +19 -0
  22. nbs_bl/planStatus.py +5 -0
  23. nbs_bl/plans/__init__.py +8 -0
  24. nbs_bl/plans/batches.py +174 -0
  25. nbs_bl/plans/conditions.py +77 -0
  26. nbs_bl/plans/flyscan_base.py +180 -0
  27. nbs_bl/plans/groups.py +55 -0
  28. nbs_bl/plans/maximizers.py +423 -0
  29. nbs_bl/plans/metaplans.py +179 -0
  30. nbs_bl/plans/plan_stubs.py +246 -0
  31. nbs_bl/plans/preprocessors.py +160 -0
  32. nbs_bl/plans/scan_base.py +58 -0
  33. nbs_bl/plans/scan_decorators.py +524 -0
  34. nbs_bl/plans/scans.py +145 -0
  35. nbs_bl/plans/suspenders.py +87 -0
  36. nbs_bl/plans/time_estimation.py +168 -0
  37. nbs_bl/plans/xas.py +123 -0
  38. nbs_bl/printing.py +221 -0
  39. nbs_bl/qt/models/beamline.py +11 -0
  40. nbs_bl/qt/models/energy.py +53 -0
  41. nbs_bl/qt/widgets/energy.py +225 -0
  42. nbs_bl/queueserver.py +249 -0
  43. nbs_bl/redisDevice.py +96 -0
  44. nbs_bl/run_engine.py +63 -0
  45. nbs_bl/samples.py +130 -0
  46. nbs_bl/settings.py +68 -0
  47. nbs_bl/shutters.py +39 -0
  48. nbs_bl/sim/__init__.py +2 -0
  49. nbs_bl/sim/config/polphase.nc +0 -0
  50. nbs_bl/sim/energy.py +403 -0
  51. nbs_bl/sim/manipulator.py +14 -0
  52. nbs_bl/sim/utils.py +36 -0
  53. nbs_bl/startup.py +27 -0
  54. nbs_bl/status.py +114 -0
  55. nbs_bl/tests/__init__.py +0 -0
  56. nbs_bl/tests/modify_regions.py +160 -0
  57. nbs_bl/tests/test_frames.py +99 -0
  58. nbs_bl/tests/test_panels.py +69 -0
  59. nbs_bl/utils.py +235 -0
  60. nbs_bl-0.2.0.dist-info/METADATA +71 -0
  61. nbs_bl-0.2.0.dist-info/RECORD +64 -0
  62. nbs_bl-0.2.0.dist-info/WHEEL +4 -0
  63. nbs_bl-0.2.0.dist-info/entry_points.txt +2 -0
  64. nbs_bl-0.2.0.dist-info/licenses/LICENSE +13 -0
@@ -0,0 +1,225 @@
1
+ from qtpy.QtWidgets import (
2
+ QGroupBox,
3
+ QVBoxLayout,
4
+ QHBoxLayout,
5
+ QComboBox,
6
+ QPushButton,
7
+ QMessageBox,
8
+ QDialog,
9
+ )
10
+ from nbs_gui.views.views import AutoControl, AutoMonitor
11
+ from nbs_gui.views.motor import MotorMonitor
12
+ from bluesky_queueserver_api import BPlan
13
+ from nbs_gui.settings import get_top_level_model
14
+
15
+
16
+ class EnergyMonitor(QGroupBox):
17
+ """Display widget for monitoring energy-related components.
18
+
19
+ Parameters
20
+ ----------
21
+ energy : object
22
+ Energy model containing energy, gap, and phase attributes
23
+ parent_model : object
24
+ Parent model containing beamline configuration
25
+ orientation : str, optional
26
+ Layout orientation
27
+ *args, **kwargs
28
+ Additional arguments passed to QGroupBox
29
+ """
30
+
31
+ def __init__(self, energy, parent_model, *args, orientation=None, **kwargs):
32
+ print("Initializing EnergyMonitor")
33
+ super().__init__("Energy Monitor", *args, **kwargs)
34
+ self.model = energy
35
+ self.top_level_model = get_top_level_model()
36
+ self.beamline_model = self.top_level_model.beamline
37
+ vbox1 = QVBoxLayout()
38
+
39
+ print("Adding energy monitor")
40
+ vbox1.addWidget(AutoMonitor(self.model.energy, parent_model))
41
+
42
+ has_slits = (
43
+ hasattr(self.beamline_model, "slits")
44
+ and self.beamline_model.slits is not None
45
+ )
46
+ if has_slits:
47
+ print("Adding slits monitor")
48
+ vbox1.addWidget(AutoMonitor(self.beamline_model.slits, parent_model))
49
+
50
+ print("Adding CFF monitor")
51
+ vbox1.addWidget(AutoMonitor(self.model.cff, parent_model))
52
+
53
+ print("Adding grating motor monitor")
54
+ if hasattr(self.model, "grating_motor"):
55
+ vbox1.addWidget(MotorMonitor(self.model.grating_motor, parent_model))
56
+
57
+ self.setLayout(vbox1)
58
+ print("EnergyMonitor initialization complete")
59
+
60
+
61
+ class EnergyControl(QGroupBox):
62
+ """Widget for controlling energy-related components.
63
+
64
+ Parameters
65
+ ----------
66
+ energy : object
67
+ Energy model containing energy, gap, and phase attributes
68
+ parent_model : object
69
+ Parent model containing beamline configuration
70
+ orientation : str, optional
71
+ Layout orientation
72
+ *args, **kwargs
73
+ Additional arguments passed to QGroupBox
74
+ """
75
+
76
+ def __init__(self, energy, parent_model, *args, orientation=None, **kwargs):
77
+ print("Initializing EnergyControl")
78
+ super().__init__("Energy Control", *args, **kwargs)
79
+
80
+ self.model = energy
81
+ self.parent_model = parent_model
82
+ top_model = get_top_level_model()
83
+ self.REClientModel = top_model.run_engine
84
+
85
+ print("Creating Energy Control layout")
86
+ hbox = QHBoxLayout()
87
+ ebox = QVBoxLayout()
88
+ hbox2 = QHBoxLayout()
89
+
90
+ print("Adding energy control")
91
+ if hasattr(energy, "energy"):
92
+ hbox.addWidget(AutoControl(energy.energy, parent_model))
93
+
94
+ has_slits = (
95
+ hasattr(top_model.beamline, "slits")
96
+ and top_model.beamline.slits is not None
97
+ )
98
+ if has_slits:
99
+ print("Adding exit slit control")
100
+ ebox.addWidget(AutoControl(top_model.beamline.slits, parent_model))
101
+
102
+ print("Adding CFF and grating monitors")
103
+ if hasattr(energy, "cff"):
104
+ hbox2.addWidget(AutoMonitor(energy.cff, parent_model))
105
+ if hasattr(energy, "grating_motor"):
106
+ hbox2.addWidget(AutoMonitor(energy.grating_motor, parent_model))
107
+
108
+ self.advancedControlButton = QPushButton("Advanced Controls")
109
+ self.advancedControlButton.clicked.connect(self.showAdvancedControls)
110
+ hbox2.addWidget(self.advancedControlButton)
111
+ ebox.addLayout(hbox2)
112
+ hbox.addLayout(ebox)
113
+ self.setLayout(hbox)
114
+ print("EnergyControl initialization complete")
115
+
116
+ def showAdvancedControls(self):
117
+ """Show the Advanced Energy Control dialog."""
118
+ print("Opening advanced controls dialog")
119
+ self.advancedDialog = QDialog(self)
120
+ self.advancedDialog.setWindowTitle("Advanced Energy Control")
121
+ layout = QVBoxLayout()
122
+ advancedControl = AdvancedEnergyControl(
123
+ self.model, self.parent_model, self.advancedDialog
124
+ )
125
+ layout.addWidget(advancedControl)
126
+ self.advancedDialog.setLayout(layout)
127
+ self.advancedDialog.exec_()
128
+
129
+
130
+ class AdvancedEnergyControl(QGroupBox):
131
+ """Advanced controls for energy-related components.
132
+
133
+ Parameters
134
+ ----------
135
+ model : object
136
+ Energy model containing energy, gap, and phase attributes
137
+ parent_model : object
138
+ Parent model containing beamline configuration
139
+ parent : QWidget, optional
140
+ Parent widget
141
+ """
142
+
143
+ def __init__(self, model, parent_model, parent=None):
144
+ print("Initializing AdvancedEnergyControl")
145
+ super().__init__("Advanced Energy Control", parent)
146
+ self.model = model
147
+ self.parent_model = parent_model
148
+ top_model = get_top_level_model()
149
+ self.REClientModel = top_model.run_engine
150
+
151
+ layout = QVBoxLayout()
152
+
153
+ print("Adding CFF control")
154
+ if hasattr(model, "cff"):
155
+ layout.addWidget(AutoControl(model.cff, parent_model))
156
+
157
+ print("Creating grating controls")
158
+ hbox = QHBoxLayout()
159
+ if hasattr(model, "grating_motor"):
160
+ hbox.addWidget(MotorMonitor(model.grating_motor, parent_model))
161
+
162
+ print("Setting up grating selection")
163
+ cb = QComboBox()
164
+ self.cb = cb
165
+ if hasattr(model.grating_motor.obj.setpoint, "enum_strs"):
166
+ for n, s in enumerate(model.grating_motor.obj.setpoint.enum_strs):
167
+ if s != "":
168
+ cb.addItem(s, n)
169
+
170
+ self.button = QPushButton("Change Grating")
171
+ self.button.clicked.connect(self.change_grating)
172
+ hbox.addWidget(cb)
173
+ hbox.addWidget(self.button)
174
+
175
+ self.tuneButton = QPushButton("Tune grating offsets")
176
+ self.tuneButton.clicked.connect(self.tune_grating)
177
+ hbox.addWidget(self.tuneButton)
178
+
179
+ layout.addLayout(hbox)
180
+ self.setLayout(layout)
181
+ print("AdvancedEnergyControl initialization complete")
182
+
183
+ def tune_grating(self):
184
+ """Execute grating tuning plan."""
185
+ print("Initiating grating tune")
186
+ msg = "Ensure beamline is opened to multimesh reference ladder for tune"
187
+ if self.confirm_dialog(msg):
188
+ plan = BPlan("tune_grating")
189
+ self.REClientModel._client.item_execute(plan)
190
+
191
+ def change_grating(self):
192
+ """Execute grating change plan."""
193
+ print("Initiating grating change")
194
+ enum = self.cb.currentData()
195
+ print(f"Selected grating enum: {enum}")
196
+ msg = (
197
+ "Are you sure you want to change gratings?\n"
198
+ "Ensure beam is open to the multimesh.\n"
199
+ "Grating change and tune will run as queue item"
200
+ )
201
+ if self.confirm_dialog(msg):
202
+ plan = BPlan("change_grating", enum)
203
+ self.REClientModel._client.item_execute(plan)
204
+
205
+ def confirm_dialog(self, confirm_message):
206
+ """Show a confirmation dialog.
207
+
208
+ Parameters
209
+ ----------
210
+ confirm_message : str
211
+ Message to display in the dialog
212
+
213
+ Returns
214
+ -------
215
+ bool
216
+ True if user confirmed, False otherwise
217
+ """
218
+ msg = QMessageBox()
219
+ msg.setIcon(QMessageBox.Question)
220
+ msg.setText(confirm_message)
221
+ msg.setStyleSheet("button-layout: 1")
222
+ msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
223
+ msg.setDefaultButton(QMessageBox.No)
224
+ ret = msg.exec_()
225
+ return ret == QMessageBox.Yes
nbs_bl/queueserver.py ADDED
@@ -0,0 +1,249 @@
1
+ from .status import StatusDict, StatusContainerBase, RedisStatusDict, StatusList
2
+ from collections import abc
3
+ from ophyd import OphydObject
4
+ import redis
5
+
6
+
7
+ class GlobalStatusManager:
8
+ """
9
+ Manager class for handling global status dictionaries and lists with Redis connections
10
+
11
+ Parameters
12
+ ----------
13
+ redis_host : str, optional
14
+ Redis server hostname, by default 'localhost'
15
+ redis_port : int, optional
16
+ Redis server port, by default 6379
17
+ """
18
+
19
+ def __init__(self, redis_host="localhost", redis_port=6379):
20
+ self._status_dict = StatusDict()
21
+ self._redis_client = None
22
+ self._redis_host = redis_host
23
+ self._redis_port = redis_port
24
+ self._global_prefix = None
25
+
26
+ def init_redis(self, host=None, port=None, db=0, global_prefix="status:"):
27
+ """
28
+ Initialize Redis connection with optional new host/port and global prefix
29
+
30
+ Parameters
31
+ ----------
32
+ host : str, optional
33
+ Redis server hostname
34
+ port : int, optional
35
+ Redis server port
36
+ global_prefix : str, optional
37
+ Global prefix for all Redis keys, by default "status:"
38
+ """
39
+ if host is not None:
40
+ self._redis_host = host
41
+ if port is not None:
42
+ self._redis_port = port
43
+
44
+ self._global_prefix = global_prefix
45
+ print(f"Initializing redis Client with {host}, {port}, {db}")
46
+ self._redis_client = redis.Redis(
47
+ host=self._redis_host, port=self._redis_port, db=db
48
+ )
49
+ return self._redis_client
50
+
51
+ def add_status(self, key, container: StatusContainerBase):
52
+ """Add a status container to the manager"""
53
+ self._status_dict[key] = container
54
+
55
+ def remove_status(self, key):
56
+ """Remove a status container from the manager"""
57
+ del self._status_dict[key]
58
+
59
+ def get_status(self):
60
+ """Get dictionary of all status UIDs"""
61
+ return {k: str(v.get_uid()) for k, v in self._status_dict.items()}
62
+
63
+ def request_status_dict(self, key, use_redis=False, prefix=None):
64
+ """
65
+ Create and return a new status dictionary
66
+
67
+ Parameters
68
+ ----------
69
+ key : str
70
+ Key for the status dictionary
71
+ use_redis : bool, optional
72
+ If True, returns RedisStatusDict, otherwise StatusDict
73
+ prefix : str, optional
74
+ Additional prefix for Redis keys if using RedisStatusDict.
75
+ If None, uses the key as prefix.
76
+
77
+ Returns
78
+ -------
79
+ StatusDict or RedisStatusDict
80
+ The requested status dictionary
81
+
82
+ Raises
83
+ ------
84
+ RuntimeError
85
+ If Redis is requested but not initialized
86
+ """
87
+ if use_redis:
88
+ if self._redis_client is None:
89
+ import warnings
90
+
91
+ warnings.warn(f"Redis not initialized. Using plain StatusDict for {key} instead.")
92
+ status_dict = StatusDict()
93
+ else:
94
+ # Construct the full prefix
95
+ if prefix is None:
96
+ prefix = key
97
+ full_prefix = f"{self._global_prefix}{prefix}"
98
+ status_dict = RedisStatusDict(self._redis_client, prefix=full_prefix)
99
+ else:
100
+ status_dict = StatusDict()
101
+
102
+ self.add_status(key, status_dict)
103
+ return status_dict
104
+
105
+ def request_status_list(self, key, use_redis=False):
106
+ """
107
+ Request a new status list, optionally backed by Redis
108
+
109
+ Parameters
110
+ ----------
111
+ key : str
112
+ Key for the status list
113
+ use_redis : bool, optional
114
+ If True, creates list in RedisStatusDict, otherwise returns plain StatusList
115
+
116
+ Returns
117
+ -------
118
+ StatusList
119
+ The requested status list
120
+
121
+ Notes
122
+ -----
123
+ When using Redis, the list is stored in a RedisStatusDict using the global prefix,
124
+ with the provided key used as the dictionary key for the list.
125
+ """
126
+ if use_redis:
127
+ if self._redis_client is None:
128
+ import warnings
129
+
130
+ warnings.warn(f"Redis not initialized. Using plain StatusList for {key} instead.")
131
+ status_list = StatusList()
132
+ else:
133
+ # Create or get the Redis dict with only global prefix
134
+ redis_dict = self._get_or_create_redis_dict()
135
+ # Create new status list
136
+ status_list = StatusList()
137
+ # Store in Redis dict under the given key
138
+ redis_dict[key] = status_list
139
+ else:
140
+ status_list = StatusList()
141
+
142
+ # Add to status manager
143
+ self.add_status(key, status_list)
144
+ return status_list
145
+
146
+ def _get_or_create_redis_dict(self):
147
+ """
148
+ Get or create the global RedisStatusDict for storing lists
149
+
150
+ Returns
151
+ -------
152
+ RedisStatusDict
153
+ The global Redis dictionary for storing lists
154
+ """
155
+ # Use a special key for the global Redis dict
156
+ global_dict_key = "_global_redis_dict"
157
+
158
+ if global_dict_key not in self._status_dict:
159
+ # Create new Redis dict with only global prefix
160
+ redis_dict = RedisStatusDict(self._redis_client, prefix=self._global_prefix)
161
+ self.add_status(global_dict_key, redis_dict)
162
+
163
+ return self._status_dict[global_dict_key]
164
+
165
+ def keys(self):
166
+ return self._status_dict.keys()
167
+
168
+ def __getitem__(self, key):
169
+ return self._status_dict[key]
170
+
171
+ def __setitem__(self, key, value):
172
+ self.add_status(key, value)
173
+
174
+ def __delitem__(self, key):
175
+ self.remove_status(key)
176
+
177
+ def __contains__(self, key):
178
+ return key in self._status_dict
179
+
180
+ def request_update(self, key):
181
+ """Request an update for a specific status key"""
182
+ if key in self._status_dict:
183
+ sbuffer = self._status_dict[key]
184
+ if isinstance(sbuffer, abc.Sequence):
185
+ return represent_sequence(sbuffer)
186
+ if isinstance(sbuffer, abc.Mapping):
187
+ return represent_mapping(sbuffer)
188
+ if isinstance(sbuffer, abc.Set):
189
+ return represent_set(sbuffer)
190
+
191
+
192
+ # Create global instance
193
+ GLOBAL_USER_STATUS = GlobalStatusManager()
194
+
195
+
196
+ def request_update(key):
197
+ return GLOBAL_USER_STATUS.request_update(key)
198
+
199
+
200
+ def get_status():
201
+ return GLOBAL_USER_STATUS.get_status()
202
+
203
+
204
+ # Keep existing helper functions
205
+ def represent_item(item):
206
+ if isinstance(item, OphydObject):
207
+ return item.name
208
+ elif isinstance(item, abc.Sequence):
209
+ return represent_sequence(item)
210
+ elif isinstance(item, abc.Mapping):
211
+ return represent_mapping(item)
212
+ elif isinstance(item, abc.Set):
213
+ return represent_set(item)
214
+ else:
215
+ return item
216
+
217
+
218
+ def represent_mapping(m):
219
+ rep = {}
220
+ for k, v in m.items():
221
+ rep[k] = represent_item(v)
222
+ return rep
223
+
224
+
225
+ def represent_sequence(s):
226
+
227
+ if isinstance(s, str):
228
+ return s
229
+ else:
230
+ rep = []
231
+ for v in s:
232
+ rep.append(represent_item(v))
233
+ return rep
234
+
235
+
236
+ def represent_set(s):
237
+ return represent_sequence(s)
238
+
239
+
240
+ def print_mapping(m):
241
+ return str(represent_mapping(m))
242
+
243
+
244
+ def print_sequence(s):
245
+ return str(represent_sequence(s))
246
+
247
+
248
+ def print_set(s):
249
+ return str(represent_set(s))
nbs_bl/redisDevice.py ADDED
@@ -0,0 +1,96 @@
1
+ import json
2
+ from ophyd.signal import Signal
3
+ from ophyd.device import Device, Component as Cpt
4
+
5
+
6
+
7
+ class _RedisSignal(Signal):
8
+ """
9
+ Minimal signal-like wrapper around a Redis-backed USER_STATUS entry.
10
+
11
+ Parameters
12
+ ----------
13
+ dict_name : str
14
+ Name of the USER_STATUS dictionary to access.
15
+ key : str
16
+ Key within the dictionary.
17
+ default : any
18
+ Default value to use if key is missing.
19
+ """
20
+ default_status_provider = None
21
+
22
+ @classmethod
23
+ def set_default_status_provider(cls, provider):
24
+ cls.default_status_provider = provider
25
+
26
+ def __init__(self, *, dict_name=None, default=None, **kwargs):
27
+ super().__init__(**kwargs)
28
+ parent_prefix = getattr(self.parent, "prefix", None)
29
+ resolved_dict = dict_name or parent_prefix
30
+ if not resolved_dict:
31
+ raise ValueError("RedisSignal requires a dict_name or parent prefix")
32
+ self._dict_name = resolved_dict
33
+ self._key = self.name
34
+ self._default = default
35
+ provider = self._status_provider()
36
+ if self._dict_name not in provider:
37
+ provider.request_status_dict(self._dict_name, use_redis=True)
38
+ self._ensure_key_exists()
39
+
40
+ def _ensure_key_exists(self):
41
+ dct = self._status_provider()[self._dict_name]
42
+ if self._key not in dct:
43
+ dct[self._key] = self._default
44
+
45
+ def get(self, **kwargs):
46
+ try:
47
+ dct = self._status_provider()[self._dict_name]
48
+ return dct.get(self._key, self._default)
49
+ except Exception as e:
50
+ print(f"RedisSignal get error for {self._dict_name}:{self._key}: {e}")
51
+ return self._default
52
+
53
+ def put(self, value, **kwargs):
54
+ try:
55
+ dct = self._status_provider()[self._dict_name]
56
+ dct[self._key] = value
57
+ # Notify subscribers
58
+ self._run_subs(sub_type=self.SUB_VALUE, value=value, **kwargs)
59
+ return value
60
+ except Exception as e:
61
+ print(f"RedisSignal put error for {self._dict_name}:{self._key}: {e}")
62
+ return self._default
63
+
64
+ def _status_provider(self):
65
+ provider = getattr(self, "_provider", None) or _RedisSignal.default_status_provider
66
+ if provider is None:
67
+ raise RuntimeError("No status provider configured for RedisDevice")
68
+ return provider
69
+
70
+ def RedisDevice(prefix, name="", keys=None, status_provider=None, **kwargs):
71
+ """
72
+ Factory function to create a Redis-backed Device with one Component per key.
73
+ """
74
+ keys = keys or {}
75
+
76
+ attrs = {
77
+ "_keys_config": keys,
78
+ "_dict_name": None,
79
+ "_status_provider": status_provider or _RedisSignal.default_status_provider,
80
+ }
81
+ for key, default in keys.items():
82
+ attrs[key] = Cpt(_RedisSignal, name=key, default=default)
83
+
84
+ cls = type(f"RedisDevice_{name}", (Device,), attrs)
85
+ obj = cls(prefix=prefix, name=name, **kwargs)
86
+
87
+
88
+ return obj
89
+
90
+ class RedisModeDevice(Device):
91
+
92
+ mode = Cpt(_RedisSignal, name="mode", default="default")
93
+
94
+ def __init__(self, prefix, name="", status_provider=None, **kwargs):
95
+ super().__init__(prefix=prefix, name=name, **kwargs)
96
+ self._status_provider = status_provider
nbs_bl/run_engine.py ADDED
@@ -0,0 +1,63 @@
1
+ import asyncio
2
+ from bluesky import RunEngine
3
+ from .beamline import GLOBAL_BEAMLINE
4
+ from bluesky_queueserver import is_re_worker_active
5
+
6
+
7
+ async def generic_cmd(msg):
8
+ """
9
+ Generic command handler for all commands
10
+ """
11
+ command, obj, args, kwargs, _ = msg
12
+ ret = getattr(obj, command)(*args, **kwargs)
13
+ return ret
14
+
15
+
16
+ async def call_obj(msg):
17
+ """
18
+ Call an object's method
19
+ """
20
+ obj = msg.obj
21
+ kwargs = msg.kwargs
22
+ args = msg.args
23
+ command = kwargs.pop("method")
24
+ ret = getattr(obj, command)(*args, **kwargs)
25
+ return ret
26
+
27
+
28
+ async def _update_plan_status(msg):
29
+ """
30
+ Update the plan status
31
+ """
32
+ command, obj, args, kwargs, _ = msg
33
+ GLOBAL_BEAMLINE.plan_status["status"] = args[0]
34
+
35
+
36
+ async def _clear_plan_status(msg):
37
+ """
38
+ Clear the plan status
39
+ """
40
+ command, obj, args, kwargs, _ = msg
41
+ GLOBAL_BEAMLINE.plan_status["status"] = "idle"
42
+
43
+
44
+ def load_RE_commands(engine):
45
+ engine.register_command("call_obj", call_obj)
46
+ engine.register_command("update_plan_status", _update_plan_status)
47
+ engine.register_command("clear_plan_status", _clear_plan_status)
48
+
49
+
50
+ def setup_run_engine(RE):
51
+ load_RE_commands(RE)
52
+ RE.preprocessors.append(GLOBAL_BEAMLINE.supplemental_data)
53
+ return RE
54
+
55
+
56
+ def create_run_engine(setup=True):
57
+ if is_re_worker_active():
58
+ RE = RunEngine(call_returns_result=False)
59
+ else:
60
+ RE = RunEngine(call_returns_result=True)
61
+ if setup:
62
+ setup_run_engine(RE)
63
+ return RE