genie-python 15.1.0rc1__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.
- genie_python/.pylintrc +539 -0
- genie_python/__init__.py +1 -0
- genie_python/block_names.py +123 -0
- genie_python/channel_access_exceptions.py +45 -0
- genie_python/genie.py +2462 -0
- genie_python/genie_advanced.py +418 -0
- genie_python/genie_alerts.py +195 -0
- genie_python/genie_api_setup.py +451 -0
- genie_python/genie_blockserver.py +64 -0
- genie_python/genie_cachannel_wrapper.py +545 -0
- genie_python/genie_change_cache.py +151 -0
- genie_python/genie_dae.py +2218 -0
- genie_python/genie_epics_api.py +906 -0
- genie_python/genie_experimental_data.py +186 -0
- genie_python/genie_logging.py +200 -0
- genie_python/genie_p4p_wrapper.py +203 -0
- genie_python/genie_plot.py +77 -0
- genie_python/genie_pre_post_cmd_manager.py +21 -0
- genie_python/genie_pv_connection_protocol.py +36 -0
- genie_python/genie_script_checker.py +507 -0
- genie_python/genie_script_generator.py +212 -0
- genie_python/genie_simulate.py +69 -0
- genie_python/genie_simulate_impl.py +1265 -0
- genie_python/genie_startup.py +29 -0
- genie_python/genie_toggle_settings.py +58 -0
- genie_python/genie_wait_for_move.py +154 -0
- genie_python/genie_waitfor.py +576 -0
- genie_python/matplotlib_backend/__init__.py +0 -0
- genie_python/matplotlib_backend/ibex_websocket_backend.py +366 -0
- genie_python/mysql_abstraction_layer.py +272 -0
- genie_python/run_tests.py +56 -0
- genie_python/scanning_instrument_pylint_plugin.py +31 -0
- genie_python/typings/CaChannel/CaChannel.pyi +893 -0
- genie_python/typings/CaChannel/__init__.pyi +9 -0
- genie_python/typings/CaChannel/_version.pyi +6 -0
- genie_python/typings/CaChannel/ca.pyi +31 -0
- genie_python/utilities.py +406 -0
- genie_python/version.py +1 -0
- genie_python-15.1.0rc1.dist-info/LICENSE +28 -0
- genie_python-15.1.0rc1.dist-info/METADATA +95 -0
- genie_python-15.1.0rc1.dist-info/RECORD +43 -0
- genie_python-15.1.0rc1.dist-info/WHEEL +5 -0
- genie_python-15.1.0rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1265 @@
|
|
|
1
|
+
from __future__ import absolute_import, print_function
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
import os
|
|
5
|
+
import xml.etree.ElementTree as ET
|
|
6
|
+
from builtins import object, str
|
|
7
|
+
from collections import OrderedDict
|
|
8
|
+
from typing import TYPE_CHECKING, Callable
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.typing as npt
|
|
12
|
+
|
|
13
|
+
from genie_python.genie_logging import GenieLogger
|
|
14
|
+
from genie_python.genie_pre_post_cmd_manager import PrePostCmdManager
|
|
15
|
+
from genie_python.utilities import require_runstate
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from genie_python.genie import PVValue, _GetspectrumReturn
|
|
19
|
+
from genie_python.genie_waitfor import WAITFOR_VALUE
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Waitfor(object):
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def start_waiting(
|
|
27
|
+
self,
|
|
28
|
+
block: str | None = None,
|
|
29
|
+
value: "WAITFOR_VALUE | None" = None,
|
|
30
|
+
lowlimit: float | None = None,
|
|
31
|
+
highlimit: float | None = None,
|
|
32
|
+
maxwait: float | None = None,
|
|
33
|
+
wait_all: bool = False,
|
|
34
|
+
seconds: float | None = None,
|
|
35
|
+
minutes: float | None = None,
|
|
36
|
+
hours: float | None = None,
|
|
37
|
+
time: str | None = None,
|
|
38
|
+
frames: int | None = None,
|
|
39
|
+
raw_frames: int | None = None,
|
|
40
|
+
uamps: float | None = None,
|
|
41
|
+
mevents: float | None = None,
|
|
42
|
+
early_exit: Callable[[], bool] = lambda: False,
|
|
43
|
+
quiet: bool = False,
|
|
44
|
+
) -> None:
|
|
45
|
+
# from https://stackoverflow.com/questions/582056/getting-list-of-parameter-names-inside-python-function
|
|
46
|
+
frame = inspect.currentframe()
|
|
47
|
+
args, _, _, values = inspect.getargvalues(frame)
|
|
48
|
+
wait_str = [
|
|
49
|
+
"{}={}".format(arg, values[arg])
|
|
50
|
+
for arg in args
|
|
51
|
+
if values[arg] is not None and arg != "self"
|
|
52
|
+
]
|
|
53
|
+
print("Waiting for {}".format(", ".join(wait_str)))
|
|
54
|
+
|
|
55
|
+
def wait_for_runstate(
|
|
56
|
+
self, state: str, maxwaitsecs: int = 3600, onexit: bool = False, quiet: bool = False
|
|
57
|
+
) -> None:
|
|
58
|
+
if onexit:
|
|
59
|
+
print("Waiting to exit state {}".format(state))
|
|
60
|
+
else:
|
|
61
|
+
print("Waiting for state {}".format(state))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class WaitForMoveController(object):
|
|
65
|
+
def __init__(self) -> None:
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def wait(self, start_timeout: float | None = None, move_timeout: float | None = None) -> None:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
def wait_specific(
|
|
72
|
+
self,
|
|
73
|
+
blocks: list[str],
|
|
74
|
+
start_timeout: float | None = None,
|
|
75
|
+
move_timeout: float | None = None,
|
|
76
|
+
) -> None:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
def wait_for_start(self, timeout: float | None, check_for_move: bool) -> None:
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ChangeCache(object):
|
|
84
|
+
def __init__(self) -> None:
|
|
85
|
+
self.wiring = None
|
|
86
|
+
self.detector = None
|
|
87
|
+
self.spectra = None
|
|
88
|
+
self.mon_spect = None
|
|
89
|
+
self.mon_from = None
|
|
90
|
+
self.mon_to = None
|
|
91
|
+
self.dae_sync = None
|
|
92
|
+
self.tcb_file = None
|
|
93
|
+
self.tcb_tables = []
|
|
94
|
+
self.smp_veto = None
|
|
95
|
+
self.ts2_veto = None
|
|
96
|
+
self.hz50_veto = None
|
|
97
|
+
self.ext0_veto = None
|
|
98
|
+
self.ext1_veto = None
|
|
99
|
+
self.ext2_veto = None
|
|
100
|
+
self.ext3_veto = None
|
|
101
|
+
self.fermi_veto = None
|
|
102
|
+
self.fermi_delay = None
|
|
103
|
+
self.fermi_width = None
|
|
104
|
+
self.periods_soft_num = None
|
|
105
|
+
self.periods_type = None
|
|
106
|
+
self.periods_src = None
|
|
107
|
+
self.periods_file = None
|
|
108
|
+
self.periods_seq = None
|
|
109
|
+
self.periods_delay = None
|
|
110
|
+
self.periods_settings = []
|
|
111
|
+
|
|
112
|
+
def set_monitor(self, spec: int, low: float, high: float) -> None:
|
|
113
|
+
self.mon_spect = spec
|
|
114
|
+
self.mon_from = low
|
|
115
|
+
self.mon_to = high
|
|
116
|
+
|
|
117
|
+
def clear_vetos(self) -> None:
|
|
118
|
+
self.smp_veto = 0
|
|
119
|
+
self.ts2_veto = 0
|
|
120
|
+
self.hz50_veto = 0
|
|
121
|
+
self.ext0_veto = 0
|
|
122
|
+
self.ext1_veto = 0
|
|
123
|
+
self.ext2_veto = 0
|
|
124
|
+
self.ext3_veto = 0
|
|
125
|
+
|
|
126
|
+
def set_fermi(self, enable: bool, delay: float = 1.0, width: float = 1.0) -> None:
|
|
127
|
+
self.fermi_veto = 1
|
|
128
|
+
self.fermi_delay = delay
|
|
129
|
+
self.fermi_width = width
|
|
130
|
+
|
|
131
|
+
def change_dae_settings(self, root: ET.Element) -> bool:
|
|
132
|
+
changed = False
|
|
133
|
+
if self.wiring is not None:
|
|
134
|
+
self._change_xml(root, "String", "Wiring Table", self.wiring)
|
|
135
|
+
changed = True
|
|
136
|
+
if self.detector is not None:
|
|
137
|
+
self._change_xml(root, "String", "Detector Table", self.detector)
|
|
138
|
+
changed = True
|
|
139
|
+
if self.spectra is not None:
|
|
140
|
+
self._change_xml(root, "String", "Spectra Table", self.spectra)
|
|
141
|
+
changed = True
|
|
142
|
+
if self.mon_spect is not None:
|
|
143
|
+
self._change_xml(root, "I32", "Monitor Spectrum", self.mon_spect)
|
|
144
|
+
changed = True
|
|
145
|
+
if self.mon_from is not None:
|
|
146
|
+
self._change_xml(root, "DBL", "from", self.mon_from)
|
|
147
|
+
changed = True
|
|
148
|
+
if self.mon_to is not None:
|
|
149
|
+
self._change_xml(root, "DBL", "to", self.mon_to)
|
|
150
|
+
changed = True
|
|
151
|
+
if self.dae_sync is not None:
|
|
152
|
+
self._change_xml(root, "EW", "DAETimingSource", self.dae_sync)
|
|
153
|
+
changed = True
|
|
154
|
+
if self.fermi_veto is not None:
|
|
155
|
+
self._change_xml(root, "EW", " Fermi Chopper Veto", self.fermi_veto)
|
|
156
|
+
self._change_xml(root, "DBL", "FC Delay", self.fermi_delay)
|
|
157
|
+
self._change_xml(root, "DBL", "FC Width", self.fermi_width)
|
|
158
|
+
changed = True
|
|
159
|
+
|
|
160
|
+
changed = self._change_vetos(root, changed)
|
|
161
|
+
return changed
|
|
162
|
+
|
|
163
|
+
def _change_vetos(self, root: ET.Element, changed: bool) -> bool:
|
|
164
|
+
if self.smp_veto is not None:
|
|
165
|
+
self._change_xml(root, "EW", "SMP (Chopper) Veto", self.smp_veto)
|
|
166
|
+
changed = True
|
|
167
|
+
if self.ts2_veto is not None:
|
|
168
|
+
self._change_xml(root, "EW", " TS2 Pulse Veto", self.ts2_veto)
|
|
169
|
+
changed = True
|
|
170
|
+
if self.hz50_veto is not None:
|
|
171
|
+
self._change_xml(root, "EW", " ISIS 50Hz Veto", self.hz50_veto)
|
|
172
|
+
changed = True
|
|
173
|
+
if self.ext0_veto is not None:
|
|
174
|
+
self._change_xml(root, "EW", "Veto 0", self.ext0_veto)
|
|
175
|
+
changed = True
|
|
176
|
+
if self.ext1_veto is not None:
|
|
177
|
+
self._change_xml(root, "EW", "Veto 1", self.ext1_veto)
|
|
178
|
+
changed = True
|
|
179
|
+
if self.ext2_veto is not None:
|
|
180
|
+
self._change_xml(root, "EW", "Veto 2", self.ext2_veto)
|
|
181
|
+
changed = True
|
|
182
|
+
if self.ext3_veto is not None:
|
|
183
|
+
self._change_xml(root, "EW", "Veto 3", self.ext3_veto)
|
|
184
|
+
changed = True
|
|
185
|
+
return changed
|
|
186
|
+
|
|
187
|
+
def change_tcb_settings(self, root: ET.Element) -> bool:
|
|
188
|
+
changed = False
|
|
189
|
+
if self.tcb_file is not None:
|
|
190
|
+
self._change_xml(root, "String", "Time Channel File", self.tcb_file)
|
|
191
|
+
changed = True
|
|
192
|
+
changed = self._change_tcb_table(root, changed)
|
|
193
|
+
return changed
|
|
194
|
+
|
|
195
|
+
def _change_tcb_table(self, root: ET.Element, changed: bool) -> bool:
|
|
196
|
+
for row in self.tcb_tables:
|
|
197
|
+
regime = str(row[0])
|
|
198
|
+
trange = str(row[1])
|
|
199
|
+
self._change_xml(root, "DBL", "TR%s From %s" % (regime, trange), row[2])
|
|
200
|
+
self._change_xml(root, "DBL", "TR%s To %s" % (regime, trange), row[3])
|
|
201
|
+
self._change_xml(root, "DBL", "TR%s Steps %s" % (regime, trange), row[4])
|
|
202
|
+
self._change_xml(root, "U16", "TR%s In Mode %s" % (regime, trange), row[5])
|
|
203
|
+
changed = True
|
|
204
|
+
return changed
|
|
205
|
+
|
|
206
|
+
def change_period_settings(self, root: ET.Element) -> bool:
|
|
207
|
+
changed = False
|
|
208
|
+
if self.periods_type is not None:
|
|
209
|
+
self._change_xml(root, "EW", "Period Type", self.periods_type)
|
|
210
|
+
changed = True
|
|
211
|
+
if self.periods_soft_num is not None:
|
|
212
|
+
self._change_xml(root, "I32", "Number Of Software Periods", self.periods_soft_num)
|
|
213
|
+
changed = True
|
|
214
|
+
if self.periods_src is not None:
|
|
215
|
+
self._change_xml(root, "EW", "Period Setup Source", self.periods_src)
|
|
216
|
+
changed = True
|
|
217
|
+
if self.periods_seq is not None:
|
|
218
|
+
self._change_xml(root, "DBL", "Hardware Period Sequences", self.periods_seq)
|
|
219
|
+
changed = True
|
|
220
|
+
if self.periods_delay is not None:
|
|
221
|
+
self._change_xml(root, "DBL", "Output Delay (us)", self.periods_delay)
|
|
222
|
+
changed = True
|
|
223
|
+
if self.periods_file is not None:
|
|
224
|
+
self._change_xml(root, "String", "Period File", self.periods_file)
|
|
225
|
+
changed = True
|
|
226
|
+
if self.periods_settings is not None:
|
|
227
|
+
self._change_period_table(root, changed)
|
|
228
|
+
changed = True
|
|
229
|
+
return changed
|
|
230
|
+
|
|
231
|
+
def _change_period_table(self, root: ET.Element, changed: bool) -> bool:
|
|
232
|
+
for row in self.periods_settings:
|
|
233
|
+
period = row[0]
|
|
234
|
+
ptype = row[1]
|
|
235
|
+
frames = row[2]
|
|
236
|
+
output = row[3]
|
|
237
|
+
label = row[4]
|
|
238
|
+
if ptype is not None:
|
|
239
|
+
self._change_xml(root, "EW", "Type %s" % period, ptype)
|
|
240
|
+
changed = True
|
|
241
|
+
if frames is not None:
|
|
242
|
+
self._change_xml(root, "I32", "Frames %s" % period, frames)
|
|
243
|
+
changed = True
|
|
244
|
+
if output is not None:
|
|
245
|
+
self._change_xml(root, "U16", "Output %s" % period, output)
|
|
246
|
+
changed = True
|
|
247
|
+
if label is not None:
|
|
248
|
+
self._change_xml(root, "String", "Label %s" % period, label)
|
|
249
|
+
changed = True
|
|
250
|
+
return changed
|
|
251
|
+
|
|
252
|
+
def _change_xml(
|
|
253
|
+
self, xml: ET.Element, node: str, name: str, value: str | int | float | None
|
|
254
|
+
) -> None:
|
|
255
|
+
for top in xml.iter(node):
|
|
256
|
+
n = top.find("Name")
|
|
257
|
+
if n is not None:
|
|
258
|
+
if n.text == name:
|
|
259
|
+
v = top.find("Val")
|
|
260
|
+
if v is not None:
|
|
261
|
+
v.text = str(value)
|
|
262
|
+
break
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class Dae(object):
|
|
266
|
+
def __init__(self) -> None:
|
|
267
|
+
self.run_state = "SETUP"
|
|
268
|
+
self.run_number = "123456"
|
|
269
|
+
self.period_current = 1
|
|
270
|
+
self.num_periods = 1
|
|
271
|
+
self.uamps_current = 0
|
|
272
|
+
self.total_counts = 0
|
|
273
|
+
self.title_current = "Simulation"
|
|
274
|
+
self.display_title = True
|
|
275
|
+
self.rb_number = "1"
|
|
276
|
+
self.mevents = 1.0
|
|
277
|
+
self.good_frames = 1
|
|
278
|
+
self.users = ""
|
|
279
|
+
self.run_duration = 1
|
|
280
|
+
self.raw_frames = 1
|
|
281
|
+
self.beam_current = 1
|
|
282
|
+
self.total_uamps = 1
|
|
283
|
+
self.num_spectra = 1
|
|
284
|
+
self.num_timechannels = 1
|
|
285
|
+
self.monitor_spectrum = 1
|
|
286
|
+
self.monitor_counts = 1
|
|
287
|
+
self.in_change = False
|
|
288
|
+
self.wiring_tables = [""]
|
|
289
|
+
self.spectra_tables = [""]
|
|
290
|
+
self.detector_tables = [""]
|
|
291
|
+
self.tcb_tables = []
|
|
292
|
+
self.period_files = []
|
|
293
|
+
self.spectrum: "_GetspectrumReturn" = {
|
|
294
|
+
"time": [1.0],
|
|
295
|
+
"signal": [1.0],
|
|
296
|
+
"sum": None,
|
|
297
|
+
"mode": "distribution",
|
|
298
|
+
}
|
|
299
|
+
self.change_cache = ChangeCache()
|
|
300
|
+
|
|
301
|
+
@require_runstate(["SETUP"])
|
|
302
|
+
def begin_run(
|
|
303
|
+
self,
|
|
304
|
+
period: int | None = None,
|
|
305
|
+
meas_id: str | None = None,
|
|
306
|
+
meas_type: str | None = None,
|
|
307
|
+
meas_subid: str | None = None,
|
|
308
|
+
sample_id: str | None = None,
|
|
309
|
+
delayed: bool = False,
|
|
310
|
+
quiet: bool = False,
|
|
311
|
+
paused: bool = False,
|
|
312
|
+
prepost: bool = True,
|
|
313
|
+
) -> None:
|
|
314
|
+
"""Starts a data collection run.
|
|
315
|
+
|
|
316
|
+
Parameters:
|
|
317
|
+
period - the period to begin data collection in [optional]
|
|
318
|
+
meas_id - the measurement id [optional]
|
|
319
|
+
meas_type - the type of measurement [optional]
|
|
320
|
+
meas_subid - the measurement sub-id[optional]
|
|
321
|
+
sample_id - the sample id [optional]
|
|
322
|
+
delayed - puts the period card to into delayed start mode [optional]
|
|
323
|
+
quiet - suppress the output to the screen [optional]
|
|
324
|
+
paused - begin in the paused state [optional]
|
|
325
|
+
prepost - run pre and post commands [optional]
|
|
326
|
+
"""
|
|
327
|
+
print("Run started")
|
|
328
|
+
self.run_state = "RUNNING"
|
|
329
|
+
|
|
330
|
+
def post_begin_check(self, verbose: bool = False) -> None:
|
|
331
|
+
pass
|
|
332
|
+
|
|
333
|
+
def post_end_check(self, verbose: bool = False) -> None:
|
|
334
|
+
pass
|
|
335
|
+
|
|
336
|
+
def post_abort_check(self, verbose: bool = False) -> None:
|
|
337
|
+
pass
|
|
338
|
+
|
|
339
|
+
def post_pause_check(self, verbose: bool = False) -> None:
|
|
340
|
+
pass
|
|
341
|
+
|
|
342
|
+
def post_resume_check(self, verbose: bool = False) -> None:
|
|
343
|
+
pass
|
|
344
|
+
|
|
345
|
+
def post_update_store_check(self, verbose: bool = False) -> None:
|
|
346
|
+
pass
|
|
347
|
+
|
|
348
|
+
def post_update_check(self, verbose: bool = False) -> None:
|
|
349
|
+
pass
|
|
350
|
+
|
|
351
|
+
def post_store_check(self, verbose: bool = False) -> None:
|
|
352
|
+
pass
|
|
353
|
+
|
|
354
|
+
@require_runstate(["RUNNING", "VETOING", "WAITING", "PAUSED"])
|
|
355
|
+
def abort_run(self, prepost: bool = True) -> None:
|
|
356
|
+
"""
|
|
357
|
+
Aborts the run and sets run_state to "SETUP"
|
|
358
|
+
Parameters:
|
|
359
|
+
prepost - run pre and post commands [optional]
|
|
360
|
+
"""
|
|
361
|
+
print("Run aborted")
|
|
362
|
+
self.run_state = "SETUP"
|
|
363
|
+
|
|
364
|
+
def get_run_state(self) -> str:
|
|
365
|
+
"""
|
|
366
|
+
Returns the current run state
|
|
367
|
+
Returns: String
|
|
368
|
+
"""
|
|
369
|
+
return self.run_state
|
|
370
|
+
|
|
371
|
+
def get_run_number(self) -> str:
|
|
372
|
+
"""
|
|
373
|
+
Returns the run number
|
|
374
|
+
Returns: Int
|
|
375
|
+
"""
|
|
376
|
+
return self.run_number
|
|
377
|
+
|
|
378
|
+
@require_runstate(["RUNNING", "VETOING", "WAITING", "PAUSED", "ENDING"])
|
|
379
|
+
def end_run(
|
|
380
|
+
self,
|
|
381
|
+
verbose: bool = False,
|
|
382
|
+
quiet: bool = False,
|
|
383
|
+
immediate: bool = False,
|
|
384
|
+
prepost: bool = True,
|
|
385
|
+
) -> None:
|
|
386
|
+
print("Run ended")
|
|
387
|
+
self.run_state = "SETUP"
|
|
388
|
+
|
|
389
|
+
@require_runstate(["RUNNING", "VETOING", "WAITING", "PAUSING"])
|
|
390
|
+
def pause_run(self, immediate: bool = False, prepost: bool = True) -> None:
|
|
391
|
+
"""
|
|
392
|
+
Parameters:
|
|
393
|
+
prepost - run pre and post commands [optional]
|
|
394
|
+
"""
|
|
395
|
+
print("Run paused")
|
|
396
|
+
self.run_state = "PAUSED"
|
|
397
|
+
|
|
398
|
+
@require_runstate(["PAUSED"])
|
|
399
|
+
def resume_run(self, prepost: bool = True) -> None:
|
|
400
|
+
"""
|
|
401
|
+
Parameters:
|
|
402
|
+
prepost - run pre and post commands [optional]
|
|
403
|
+
"""
|
|
404
|
+
print("Run resumed")
|
|
405
|
+
self.run_state = "RUNNING"
|
|
406
|
+
|
|
407
|
+
def update_store_run(self) -> None:
|
|
408
|
+
"""
|
|
409
|
+
Does nothing but throw error if run_state is not "RUNNING" or "PAUSED"
|
|
410
|
+
"""
|
|
411
|
+
if self.run_state == "RUNNING" or self.run_state == "PAUSED":
|
|
412
|
+
pass
|
|
413
|
+
else:
|
|
414
|
+
raise Exception("Can only be called when RUNNING or PAUSED")
|
|
415
|
+
|
|
416
|
+
def update_run(self) -> None:
|
|
417
|
+
"""
|
|
418
|
+
Does nothing but throw error if run_state is not "RUNNING" or "PAUSED"
|
|
419
|
+
"""
|
|
420
|
+
if self.run_state == "RUNNING" or self.run_state == "PAUSED":
|
|
421
|
+
pass
|
|
422
|
+
else:
|
|
423
|
+
raise Exception("Can only be called when RUNNING or PAUSED")
|
|
424
|
+
|
|
425
|
+
@require_runstate(["RUNNING", "VETOING", "WAITING", "PAUSED"])
|
|
426
|
+
def store_run(self) -> None:
|
|
427
|
+
print("Run stored")
|
|
428
|
+
|
|
429
|
+
def recover_run(self) -> None:
|
|
430
|
+
if self.run_state == "SETUP":
|
|
431
|
+
pass
|
|
432
|
+
else:
|
|
433
|
+
raise Exception("Can only be called when SETUP")
|
|
434
|
+
|
|
435
|
+
def get_period(self) -> int:
|
|
436
|
+
"""
|
|
437
|
+
returns the current period number
|
|
438
|
+
Returns: Int
|
|
439
|
+
|
|
440
|
+
"""
|
|
441
|
+
return self.period_current
|
|
442
|
+
|
|
443
|
+
def get_num_periods(self) -> int:
|
|
444
|
+
"""
|
|
445
|
+
returns the current number of periods
|
|
446
|
+
Returns: Int
|
|
447
|
+
"""
|
|
448
|
+
return self.num_periods
|
|
449
|
+
|
|
450
|
+
def set_period(self, period: int) -> None:
|
|
451
|
+
"""
|
|
452
|
+
sets the current period to the period parameter if it is equal to
|
|
453
|
+
or less than the number of periods
|
|
454
|
+
Args:
|
|
455
|
+
period: Int
|
|
456
|
+
"""
|
|
457
|
+
if period <= self.num_periods:
|
|
458
|
+
self.period_current = period
|
|
459
|
+
else:
|
|
460
|
+
raise Exception("Cannot set period as it is higher than the number of periods")
|
|
461
|
+
|
|
462
|
+
def get_uamps(self, period: bool = False) -> float:
|
|
463
|
+
"""Returns the current number of micro-amp hours.
|
|
464
|
+
|
|
465
|
+
Parameters:
|
|
466
|
+
period - whether to return the micro-amp hours for the current period [optional]
|
|
467
|
+
"""
|
|
468
|
+
if period:
|
|
469
|
+
return self.uamps_current
|
|
470
|
+
else:
|
|
471
|
+
return self.uamps_current
|
|
472
|
+
|
|
473
|
+
def get_total_counts(self) -> int:
|
|
474
|
+
"""Get the total counts for the current run."""
|
|
475
|
+
return self.total_counts
|
|
476
|
+
|
|
477
|
+
def get_title(self) -> str:
|
|
478
|
+
"""Returns the current title
|
|
479
|
+
|
|
480
|
+
Returns: String : the current title
|
|
481
|
+
|
|
482
|
+
"""
|
|
483
|
+
return self.title_current
|
|
484
|
+
|
|
485
|
+
def set_title(self, title: str) -> None:
|
|
486
|
+
"""Sets the current title
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
title: String: the new title
|
|
490
|
+
"""
|
|
491
|
+
print("Setting title to {}".format(title))
|
|
492
|
+
self.title_current = title
|
|
493
|
+
|
|
494
|
+
def get_display_title(self) -> bool:
|
|
495
|
+
"""Returns the current display title status
|
|
496
|
+
|
|
497
|
+
Returns: boolean : the current display title status
|
|
498
|
+
|
|
499
|
+
"""
|
|
500
|
+
return self.display_title
|
|
501
|
+
|
|
502
|
+
def set_display_title(self, display_title: bool) -> None:
|
|
503
|
+
"""Sets whether to display title & users
|
|
504
|
+
|
|
505
|
+
Args:
|
|
506
|
+
display_title: boolean: the new display title status
|
|
507
|
+
"""
|
|
508
|
+
print("Setting title display status to {}".format(display_title))
|
|
509
|
+
self.display_title = display_title
|
|
510
|
+
|
|
511
|
+
def get_rb_number(self) -> str:
|
|
512
|
+
"""Returns the current RB number
|
|
513
|
+
|
|
514
|
+
Returns: String : the RB number
|
|
515
|
+
|
|
516
|
+
"""
|
|
517
|
+
return self.rb_number
|
|
518
|
+
|
|
519
|
+
def set_rb_number(self, rb: str) -> None:
|
|
520
|
+
self.rb_number = rb
|
|
521
|
+
|
|
522
|
+
def get_mevents(self) -> float:
|
|
523
|
+
return self.mevents
|
|
524
|
+
|
|
525
|
+
def get_good_frames(self, period: bool = False) -> int:
|
|
526
|
+
if period:
|
|
527
|
+
return self.good_frames
|
|
528
|
+
else:
|
|
529
|
+
return self.good_frames
|
|
530
|
+
|
|
531
|
+
def get_users(self) -> str:
|
|
532
|
+
return self.users
|
|
533
|
+
|
|
534
|
+
def get_run_duration(self) -> float:
|
|
535
|
+
return self.run_duration
|
|
536
|
+
|
|
537
|
+
def get_raw_frames(self, period: bool = False) -> int:
|
|
538
|
+
return self.raw_frames
|
|
539
|
+
|
|
540
|
+
def get_beam_current(self) -> float:
|
|
541
|
+
return self.beam_current
|
|
542
|
+
|
|
543
|
+
def get_total_uamps(self) -> float:
|
|
544
|
+
return self.total_uamps
|
|
545
|
+
|
|
546
|
+
def get_num_spectra(self) -> int:
|
|
547
|
+
return self.num_spectra
|
|
548
|
+
|
|
549
|
+
def get_num_timechannels(self) -> int:
|
|
550
|
+
return self.num_timechannels
|
|
551
|
+
|
|
552
|
+
def get_monitor_spectrum(self) -> int:
|
|
553
|
+
return self.monitor_spectrum
|
|
554
|
+
|
|
555
|
+
def get_monitor_from(self) -> float:
|
|
556
|
+
return 0.0
|
|
557
|
+
|
|
558
|
+
def get_monitor_to(self) -> float:
|
|
559
|
+
return 1.0
|
|
560
|
+
|
|
561
|
+
def get_monitor_counts(self) -> int:
|
|
562
|
+
return 1
|
|
563
|
+
|
|
564
|
+
def set_users(self, users: str) -> None:
|
|
565
|
+
self.users = users
|
|
566
|
+
|
|
567
|
+
def change_start(self) -> None:
|
|
568
|
+
"""Start a change operation.
|
|
569
|
+
The operaton is finished when change_finish is called.
|
|
570
|
+
Between these two calls a sequence of other change commands can be called.
|
|
571
|
+
For example: change_tables, change_tcb etc.
|
|
572
|
+
"""
|
|
573
|
+
# Check in setup
|
|
574
|
+
if self.get_run_state() != "SETUP":
|
|
575
|
+
raise Exception("Must be in SETUP before starting change!")
|
|
576
|
+
if self.in_change:
|
|
577
|
+
raise Exception("Already in change - previous cached values will be used")
|
|
578
|
+
else:
|
|
579
|
+
self.in_change = True
|
|
580
|
+
self.change_cache = ChangeCache()
|
|
581
|
+
|
|
582
|
+
def change_finish(self) -> None:
|
|
583
|
+
if self.in_change:
|
|
584
|
+
self.in_change = False
|
|
585
|
+
self.change_cache = ChangeCache()
|
|
586
|
+
|
|
587
|
+
def get_spectrum(
|
|
588
|
+
self, spectrum: int, period: int = 1, dist: bool = True, use_numpy: bool | None = None
|
|
589
|
+
) -> "_GetspectrumReturn":
|
|
590
|
+
return self.spectrum
|
|
591
|
+
|
|
592
|
+
def get_spec_data(self) -> npt.NDArray:
|
|
593
|
+
return np.array([1.0, 2.0, 3.0, 4.0]) # spectrum 0 and 1, time bins 0 and 1
|
|
594
|
+
|
|
595
|
+
def get_spec_integrals(self) -> npt.NDArray:
|
|
596
|
+
return np.array([1.0, 2.0]) # spectrum 0 and 1
|
|
597
|
+
|
|
598
|
+
def integrate_spectrum(
|
|
599
|
+
self, spectrum: int, period: int = 1, t_min: float | None = None, t_max: float | None = None
|
|
600
|
+
) -> float:
|
|
601
|
+
"""
|
|
602
|
+
Integrates the spectrum within the time period and returns neutron counts.
|
|
603
|
+
|
|
604
|
+
The underlying algorithm sums the counts from each bin, if a bin is
|
|
605
|
+
split by the time region then a proportional
|
|
606
|
+
fraction of the count for that bin is used.
|
|
607
|
+
|
|
608
|
+
Args:
|
|
609
|
+
spectrum (int): the spectrum number
|
|
610
|
+
period (int, optional): the period
|
|
611
|
+
t_min (float, optional): time of flight to start from
|
|
612
|
+
t_max (float, optional): time of flight to finish at
|
|
613
|
+
|
|
614
|
+
Returns:
|
|
615
|
+
float: integral of the spectrum (neutron counts) which
|
|
616
|
+
is 1 proporiontal to the width requested
|
|
617
|
+
"""
|
|
618
|
+
return 1.0 / (t_max - t_min)
|
|
619
|
+
|
|
620
|
+
def change_monitor(self, spec: int, low: float, high: float) -> None:
|
|
621
|
+
"""Change the monitor to a specified spectrum and range.
|
|
622
|
+
|
|
623
|
+
Parameters:
|
|
624
|
+
spectrum - the spectrum number (integer)
|
|
625
|
+
low - the low end of the integral (float)
|
|
626
|
+
high - the high end of the integral (float)
|
|
627
|
+
"""
|
|
628
|
+
try:
|
|
629
|
+
spec = int(spec)
|
|
630
|
+
except ValueError:
|
|
631
|
+
raise TypeError("Spectrum number must be an integer")
|
|
632
|
+
try:
|
|
633
|
+
low = float(low)
|
|
634
|
+
except ValueError:
|
|
635
|
+
raise TypeError("Low must be a float")
|
|
636
|
+
try:
|
|
637
|
+
high = float(high)
|
|
638
|
+
except ValueError:
|
|
639
|
+
raise TypeError("High must be a float")
|
|
640
|
+
did_change = False
|
|
641
|
+
if not self.in_change:
|
|
642
|
+
self.change_start()
|
|
643
|
+
did_change = True
|
|
644
|
+
pass
|
|
645
|
+
if did_change:
|
|
646
|
+
self.change_finish()
|
|
647
|
+
|
|
648
|
+
def get_wiring_tables(self) -> list[str]:
|
|
649
|
+
return self.wiring_tables
|
|
650
|
+
|
|
651
|
+
def get_spectra_tables(self) -> list[str]:
|
|
652
|
+
return self.spectra_tables
|
|
653
|
+
|
|
654
|
+
def get_detector_tables(self) -> list[str]:
|
|
655
|
+
return self.detector_tables
|
|
656
|
+
|
|
657
|
+
def get_period_files(self) -> list[str]:
|
|
658
|
+
return self.period_files
|
|
659
|
+
|
|
660
|
+
def configure_hard_periods(
|
|
661
|
+
self,
|
|
662
|
+
mode: str,
|
|
663
|
+
period_file: str | None = None,
|
|
664
|
+
sequences: int | None = None,
|
|
665
|
+
output_delay: int | None = None,
|
|
666
|
+
period: int | None = None,
|
|
667
|
+
daq: bool = False,
|
|
668
|
+
dwell: bool = False,
|
|
669
|
+
unused: bool = False,
|
|
670
|
+
frames: int | None = None,
|
|
671
|
+
output: int | None = None,
|
|
672
|
+
label: str | None = None,
|
|
673
|
+
) -> None:
|
|
674
|
+
"""Configures the DAE's hardware periods.
|
|
675
|
+
|
|
676
|
+
Parameters:
|
|
677
|
+
mode - set the mode to internal ('int') or external ('ext')
|
|
678
|
+
|
|
679
|
+
Internal periods parameters [optional]:
|
|
680
|
+
period_file - the file containing the internal period
|
|
681
|
+
settings (ignores any other settings)
|
|
682
|
+
sequences - the number of period sequences
|
|
683
|
+
output_delay - the output delay in microseconds
|
|
684
|
+
period - the number of the period to set the following for:
|
|
685
|
+
daq - it is a aquisition period
|
|
686
|
+
dwell - it is a dwell period
|
|
687
|
+
unused - it is a unused period
|
|
688
|
+
frames - the number of frames to count for
|
|
689
|
+
output - the binary output
|
|
690
|
+
label - the label for the period
|
|
691
|
+
|
|
692
|
+
Note: if the period number is unspecified then the settings
|
|
693
|
+
will be applied to all periods.
|
|
694
|
+
|
|
695
|
+
EXAMPLE: setting external periods
|
|
696
|
+
enable_hardware_periods('ext')
|
|
697
|
+
|
|
698
|
+
EXAMPLE: setting internal periods from a file
|
|
699
|
+
enable_hardware_periods('int', 'myperiods.txt')
|
|
700
|
+
"""
|
|
701
|
+
did_change = False
|
|
702
|
+
if not self.in_change:
|
|
703
|
+
self.change_start()
|
|
704
|
+
did_change = True
|
|
705
|
+
# Set the source to 'Use Parameters Below' by default
|
|
706
|
+
self.change_cache.periods_src = 0
|
|
707
|
+
if mode.strip().lower() == "int":
|
|
708
|
+
self.change_cache.periods_type = 1
|
|
709
|
+
if period_file is not None:
|
|
710
|
+
if not os.path.exists(period_file):
|
|
711
|
+
raise Exception("Period file could not be found")
|
|
712
|
+
self.change_cache.periods_src = 1
|
|
713
|
+
self.change_cache.periods_file = period_file
|
|
714
|
+
else:
|
|
715
|
+
self.configure_internal_periods(
|
|
716
|
+
sequences, output_delay, period, daq, dwell, unused, frames, output, label
|
|
717
|
+
)
|
|
718
|
+
elif mode.strip().lower() == "ext":
|
|
719
|
+
self.change_cache.periods_type = 2
|
|
720
|
+
else:
|
|
721
|
+
raise Exception('Period mode invalid, it should be "int" or "ext"')
|
|
722
|
+
if did_change:
|
|
723
|
+
self.change_finish()
|
|
724
|
+
|
|
725
|
+
def configure_internal_periods(
|
|
726
|
+
self,
|
|
727
|
+
sequences: int | None = None,
|
|
728
|
+
output_delay: int | None = None,
|
|
729
|
+
period: int | None = None,
|
|
730
|
+
daq: bool = False,
|
|
731
|
+
dwell: bool = False,
|
|
732
|
+
unused: bool = False,
|
|
733
|
+
frames: int | None = None,
|
|
734
|
+
output: int | None = None,
|
|
735
|
+
label: str | None = None,
|
|
736
|
+
) -> None:
|
|
737
|
+
"""Configure the internal periods without switching to internal period mode.
|
|
738
|
+
|
|
739
|
+
Parameters:
|
|
740
|
+
file - the file containing the internal period settings
|
|
741
|
+
(ignores any other settings) [optional]
|
|
742
|
+
sequences - the number of period sequences [optional]
|
|
743
|
+
output_delay - the output delay in microseconds [optional]
|
|
744
|
+
period - the number of the period to set values for [optional]
|
|
745
|
+
daq - the specified period is a aquisition period [optional]
|
|
746
|
+
dwell - the specified period is a dwell period [optional]
|
|
747
|
+
unused - the specified period is a unused period [optional]
|
|
748
|
+
frames - the number of frames to count for the specified period [optional]
|
|
749
|
+
output - the binary output the specified period [optional]
|
|
750
|
+
label - the label for the period the specified period [optional]
|
|
751
|
+
|
|
752
|
+
Note: if the period number is unspecified then the settings will be
|
|
753
|
+
applied to all periods.
|
|
754
|
+
"""
|
|
755
|
+
did_change = False
|
|
756
|
+
if not self.in_change:
|
|
757
|
+
self.change_start()
|
|
758
|
+
did_change = True
|
|
759
|
+
if sequences is not None:
|
|
760
|
+
if isinstance(sequences, int):
|
|
761
|
+
self.change_cache.periods_seq = sequences
|
|
762
|
+
else:
|
|
763
|
+
raise Exception("Number of period sequences must be an integer")
|
|
764
|
+
if output_delay is not None:
|
|
765
|
+
if isinstance(output_delay, int):
|
|
766
|
+
self.change_cache.periods_delay = output_delay
|
|
767
|
+
else:
|
|
768
|
+
raise Exception("Output delay of periods must be an integer (microseconds)")
|
|
769
|
+
self.define_hard_period(period, daq, dwell, unused, frames, output, label)
|
|
770
|
+
if did_change:
|
|
771
|
+
self.change_finish()
|
|
772
|
+
|
|
773
|
+
def define_hard_period(
|
|
774
|
+
self,
|
|
775
|
+
period: int | None = None,
|
|
776
|
+
daq: bool = False,
|
|
777
|
+
dwell: bool = False,
|
|
778
|
+
unused: bool = False,
|
|
779
|
+
frames: int | None = None,
|
|
780
|
+
output: int | None = None,
|
|
781
|
+
label: str | None = None,
|
|
782
|
+
) -> None:
|
|
783
|
+
"""Define the hardware periods.
|
|
784
|
+
|
|
785
|
+
Parameters:
|
|
786
|
+
period - the number of the period to set values for [optional]
|
|
787
|
+
daq - the specified period is a aquisition period [optional]
|
|
788
|
+
dwell - the specified period is a dwell period [optional]
|
|
789
|
+
unused - the specified period is a unused period [optional]
|
|
790
|
+
frames - the number of frames to count for the specified period [optional]
|
|
791
|
+
output - the binary output the specified period [optional]
|
|
792
|
+
label - the label for the period the specified period [optional]
|
|
793
|
+
|
|
794
|
+
Note: if the period number is unspecified then the settings will be
|
|
795
|
+
applied to all periods.
|
|
796
|
+
"""
|
|
797
|
+
did_change = False
|
|
798
|
+
if not self.in_change:
|
|
799
|
+
self.change_start()
|
|
800
|
+
did_change = True
|
|
801
|
+
if period is None:
|
|
802
|
+
# Do for all periods (1 to 8)
|
|
803
|
+
for i in range(1, 9):
|
|
804
|
+
self.define_hard_period(i, daq, dwell, unused, frames, output, label)
|
|
805
|
+
else:
|
|
806
|
+
if isinstance(period, int) and period > 0 and period < 9:
|
|
807
|
+
p_type = None # unchanged
|
|
808
|
+
if unused:
|
|
809
|
+
p_type = 0
|
|
810
|
+
elif daq:
|
|
811
|
+
p_type = 1
|
|
812
|
+
elif dwell:
|
|
813
|
+
p_type = 2
|
|
814
|
+
p_frames = None # unchanged
|
|
815
|
+
if frames is not None and isinstance(frames, int):
|
|
816
|
+
p_frames = frames
|
|
817
|
+
p_output = None # unchanged
|
|
818
|
+
if output is not None and isinstance(output, int):
|
|
819
|
+
p_output = output
|
|
820
|
+
p_label = None # unchanged
|
|
821
|
+
if label is not None:
|
|
822
|
+
p_label = label
|
|
823
|
+
self.change_cache.periods_settings.append(
|
|
824
|
+
(period, p_type, p_frames, p_output, p_label)
|
|
825
|
+
)
|
|
826
|
+
else:
|
|
827
|
+
raise Exception("Period number must be an integer from 1 to 8")
|
|
828
|
+
if did_change:
|
|
829
|
+
self.change_finish()
|
|
830
|
+
|
|
831
|
+
def change_tables(
|
|
832
|
+
self, wiring: str | None = None, detector: str | None = None, spectra: str | None = None
|
|
833
|
+
) -> None:
|
|
834
|
+
"""Load the wiring, detector and/or spectra tables.
|
|
835
|
+
|
|
836
|
+
Parameters:
|
|
837
|
+
wiring - the filename of the wiring table file [optional]
|
|
838
|
+
detector - the filename of the detector table file [optional]
|
|
839
|
+
spectra - the filename of the spectra table file [optional]
|
|
840
|
+
"""
|
|
841
|
+
did_change = False
|
|
842
|
+
if not self.in_change:
|
|
843
|
+
self.change_start()
|
|
844
|
+
did_change = True
|
|
845
|
+
if wiring is not None:
|
|
846
|
+
self.change_cache.wiring = wiring
|
|
847
|
+
if detector is not None:
|
|
848
|
+
self.change_cache.detector = detector
|
|
849
|
+
if spectra is not None:
|
|
850
|
+
self.change_cache.spectra = spectra
|
|
851
|
+
if did_change:
|
|
852
|
+
self.change_finish()
|
|
853
|
+
|
|
854
|
+
def change_sync(self, source: str) -> None:
|
|
855
|
+
"""Change the source the DAE using for synchronisation.
|
|
856
|
+
|
|
857
|
+
Parameters:
|
|
858
|
+
source - the source to use ('isis', 'internal', 'smp',
|
|
859
|
+
'muon cerenkov', 'muon ms', 'isis (first ts1)')
|
|
860
|
+
"""
|
|
861
|
+
did_change = False
|
|
862
|
+
if not self.in_change:
|
|
863
|
+
self.change_start()
|
|
864
|
+
did_change = True
|
|
865
|
+
source = source.strip().lower()
|
|
866
|
+
if source == "isis":
|
|
867
|
+
value = 0
|
|
868
|
+
elif source == "internal":
|
|
869
|
+
value = 1
|
|
870
|
+
elif source == "smp":
|
|
871
|
+
value = 2
|
|
872
|
+
elif source == "muon cerenkov":
|
|
873
|
+
value = 3
|
|
874
|
+
elif source == "muon ms":
|
|
875
|
+
value = 4
|
|
876
|
+
elif source == "isis (first ts1)":
|
|
877
|
+
value = 5
|
|
878
|
+
else:
|
|
879
|
+
raise Exception("Invalid timing source entered, try help(change_sync)!")
|
|
880
|
+
self.change_cache.dae_sync = value
|
|
881
|
+
if did_change:
|
|
882
|
+
self.change_finish()
|
|
883
|
+
|
|
884
|
+
def change_tcb_file(self, tcb_file: str | None = None, default: bool = False) -> None:
|
|
885
|
+
"""Change the time channel boundaries.
|
|
886
|
+
|
|
887
|
+
Parameters:
|
|
888
|
+
tcb_file - the file to load [optional]
|
|
889
|
+
default - load the default file "c:\\labview modules\\dae\\tcb.dat" [optional]
|
|
890
|
+
"""
|
|
891
|
+
did_change = False
|
|
892
|
+
if not self.in_change:
|
|
893
|
+
self.change_start()
|
|
894
|
+
did_change = True
|
|
895
|
+
if tcb_file is not None:
|
|
896
|
+
print("Reading TCB boundaries from", tcb_file)
|
|
897
|
+
elif default:
|
|
898
|
+
tcb_file = "c:\\labview modules\\dae\\tcb.dat"
|
|
899
|
+
else:
|
|
900
|
+
raise Exception("No tcb file specified")
|
|
901
|
+
if not os.path.exists(tcb_file):
|
|
902
|
+
raise Exception("Tcb file could not be found")
|
|
903
|
+
self.change_cache.tcb_file = tcb_file
|
|
904
|
+
if did_change:
|
|
905
|
+
self.change_finish()
|
|
906
|
+
|
|
907
|
+
def change_tcb(
|
|
908
|
+
self, low: float, high: float, step: float, trange: int, log: bool = False, regime: int = 1
|
|
909
|
+
) -> None:
|
|
910
|
+
"""Change the time channel boundaries.
|
|
911
|
+
|
|
912
|
+
Parameters:
|
|
913
|
+
low - the lower limit
|
|
914
|
+
high - the upper limit
|
|
915
|
+
step - the step size
|
|
916
|
+
trange - the time range (1 to 5)
|
|
917
|
+
log - whether to use LOG binning [optional]
|
|
918
|
+
regime - the time regime to set (1 to 6)[optional]
|
|
919
|
+
"""
|
|
920
|
+
did_change = False
|
|
921
|
+
if not self.in_change:
|
|
922
|
+
self.change_start()
|
|
923
|
+
did_change = True
|
|
924
|
+
if log:
|
|
925
|
+
print("Setting TCB range", low, "to", high, "step", step, "(LOG binning)")
|
|
926
|
+
self.change_cache.tcb_tables.append((regime, trange, low, high, step, 2))
|
|
927
|
+
else:
|
|
928
|
+
print("Setting TCB range", low, "to", high, "step", step, "(LINEAR binning)")
|
|
929
|
+
self.change_cache.tcb_tables.append((regime, trange, low, high, step, 1))
|
|
930
|
+
if did_change:
|
|
931
|
+
self.change_finish()
|
|
932
|
+
|
|
933
|
+
def change_vetos(self, **params: bool) -> None:
|
|
934
|
+
"""Change the DAE veto settings.
|
|
935
|
+
|
|
936
|
+
Parameters:
|
|
937
|
+
clearall - remove all vetos [optional]
|
|
938
|
+
smp - set SMP veto [optional]
|
|
939
|
+
ts2 - set TS2 veto [optional]
|
|
940
|
+
hz50 - set 50 hz veto [optional]
|
|
941
|
+
ext0 - set external veto 0 [optional]
|
|
942
|
+
ext1 - set external veto 1 [optional]
|
|
943
|
+
ext2 - set external veto 2 [optional]
|
|
944
|
+
ext3 - set external veto 3 [optional]
|
|
945
|
+
|
|
946
|
+
If clearall is specified then all vetos are turned off, but it is possible
|
|
947
|
+
to turn other vetoes back on at the same time, for example:
|
|
948
|
+
|
|
949
|
+
change_vetos(clearall=True, smp=True) #Turns all vetoes off then
|
|
950
|
+
#turns the SMP veto back on
|
|
951
|
+
"""
|
|
952
|
+
did_change = False
|
|
953
|
+
if not self.in_change:
|
|
954
|
+
self.change_start()
|
|
955
|
+
did_change = True
|
|
956
|
+
if "clearall" in params:
|
|
957
|
+
if isinstance(params["clearall"], bool):
|
|
958
|
+
self.change_cache.clear_vetos()
|
|
959
|
+
if "smp" in params:
|
|
960
|
+
if isinstance(params["smp"], bool):
|
|
961
|
+
self.change_cache.smp_veto = int(params["smp"])
|
|
962
|
+
if "ts2" in params:
|
|
963
|
+
if isinstance(params["ts2"], bool):
|
|
964
|
+
self.change_cache.ts2_veto = int(params["ts2"])
|
|
965
|
+
if "hz50" in params:
|
|
966
|
+
if isinstance(params["hz50"], bool):
|
|
967
|
+
self.change_cache.hz50_veto = int(params["hz50"])
|
|
968
|
+
if "ext0" in params:
|
|
969
|
+
if isinstance(params["ext0"], bool):
|
|
970
|
+
self.change_cache.ext0_veto = int(params["ext0"])
|
|
971
|
+
if "ext1" in params:
|
|
972
|
+
if isinstance(params["ext1"], bool):
|
|
973
|
+
self.change_cache.ext1_veto = int(params["ext1"])
|
|
974
|
+
if "ext2" in params:
|
|
975
|
+
if isinstance(params["ext2"], bool):
|
|
976
|
+
self.change_cache.ext2_veto = int(params["ext2"])
|
|
977
|
+
if "ext3" in params:
|
|
978
|
+
if isinstance(params["ext3"], bool):
|
|
979
|
+
self.change_cache.ext3_veto = int(params["ext3"])
|
|
980
|
+
if did_change:
|
|
981
|
+
self.change_finish()
|
|
982
|
+
|
|
983
|
+
def set_fermi_veto(self, enable: bool = None, delay: float = 1.0, width: float = 1.0) -> None:
|
|
984
|
+
"""Configure the fermi chopper veto.
|
|
985
|
+
|
|
986
|
+
Parameters:
|
|
987
|
+
enable - enable the fermi veto
|
|
988
|
+
delay - the veto delay
|
|
989
|
+
width - the veto width
|
|
990
|
+
"""
|
|
991
|
+
if not isinstance(enable, bool):
|
|
992
|
+
raise Exception("Fermi veto: enable must be a boolean value")
|
|
993
|
+
if not isinstance(delay, float) and not isinstance(delay, int):
|
|
994
|
+
raise Exception("Fermi veto: delay must be a numeric value")
|
|
995
|
+
if not isinstance(width, float) and not isinstance(width, int):
|
|
996
|
+
raise Exception("Fermi veto: width must be a numeric value")
|
|
997
|
+
did_change = False
|
|
998
|
+
if not self.in_change:
|
|
999
|
+
self.change_start()
|
|
1000
|
+
did_change = True
|
|
1001
|
+
if enable:
|
|
1002
|
+
self.change_cache.set_fermi(1, delay, width)
|
|
1003
|
+
print("SET_FERMI_VETO: requested status is ON, delay:", delay, "width:", width)
|
|
1004
|
+
else:
|
|
1005
|
+
self.change_cache.set_fermi(0)
|
|
1006
|
+
print("SET_FERMI_VETO: requested status is OFF")
|
|
1007
|
+
if did_change:
|
|
1008
|
+
self.change_finish()
|
|
1009
|
+
|
|
1010
|
+
def set_num_soft_periods(self, number: int) -> None:
|
|
1011
|
+
"""Sets the number of software periods for the DAE.
|
|
1012
|
+
|
|
1013
|
+
Parameters:
|
|
1014
|
+
number - the number of periods to create
|
|
1015
|
+
"""
|
|
1016
|
+
if not isinstance(number, float) and not isinstance(number, int):
|
|
1017
|
+
raise Exception("Number of soft periods must be a numeric value")
|
|
1018
|
+
did_change = False
|
|
1019
|
+
if not self.in_change:
|
|
1020
|
+
self.change_start()
|
|
1021
|
+
did_change = True
|
|
1022
|
+
if number >= 0:
|
|
1023
|
+
self.num_periods = number
|
|
1024
|
+
if did_change:
|
|
1025
|
+
self.change_finish()
|
|
1026
|
+
|
|
1027
|
+
def set_period_mode(self, mode: str) -> None:
|
|
1028
|
+
"""Sets the period mode for the DAE
|
|
1029
|
+
|
|
1030
|
+
Parameters:
|
|
1031
|
+
mode - the mode to switch to ('soft', 'int', 'ext')
|
|
1032
|
+
"""
|
|
1033
|
+
did_change = False
|
|
1034
|
+
if not self.in_change:
|
|
1035
|
+
self.change_start()
|
|
1036
|
+
did_change = True
|
|
1037
|
+
if mode.strip().lower() == "soft":
|
|
1038
|
+
self.change_cache.periods_type = 0
|
|
1039
|
+
else:
|
|
1040
|
+
self.configure_hard_periods(mode)
|
|
1041
|
+
if did_change:
|
|
1042
|
+
self.change_finish()
|
|
1043
|
+
|
|
1044
|
+
def snapshot_crpt(self, name: str) -> None:
|
|
1045
|
+
pass
|
|
1046
|
+
|
|
1047
|
+
def post_snapshot_check(self, verbose: bool = False) -> None:
|
|
1048
|
+
pass
|
|
1049
|
+
|
|
1050
|
+
def set_verbose(self, verbose: bool) -> None:
|
|
1051
|
+
pass
|
|
1052
|
+
|
|
1053
|
+
def get_table_path(self, table_type: str) -> str:
|
|
1054
|
+
if table_type == "Wiring":
|
|
1055
|
+
return self.change_cache.wiring
|
|
1056
|
+
if table_type == "Detector":
|
|
1057
|
+
return self.change_cache.detector
|
|
1058
|
+
if table_type == "Spectra":
|
|
1059
|
+
return self.change_cache.spectra
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
class API(object):
|
|
1063
|
+
def __init__(
|
|
1064
|
+
self, pv_prefix: str | None = None, globs: dict | None = None, strict_block: bool = True
|
|
1065
|
+
) -> None:
|
|
1066
|
+
"""
|
|
1067
|
+
Constructor for the simulated API.
|
|
1068
|
+
|
|
1069
|
+
Args:
|
|
1070
|
+
pv_prefix: used for prefixing the PV and block names
|
|
1071
|
+
globs: globals
|
|
1072
|
+
strict_block: if True will throw an exception if setting a block that
|
|
1073
|
+
doesn't exist, otherwise will create the block
|
|
1074
|
+
"""
|
|
1075
|
+
self.inst_prefix = None
|
|
1076
|
+
self.pre_post_cmd_manager = PrePostCmdManager()
|
|
1077
|
+
self.block_dict = dict()
|
|
1078
|
+
self.num_periods = 1
|
|
1079
|
+
self.run_number = "123456"
|
|
1080
|
+
self.waitfor = Waitfor()
|
|
1081
|
+
self.wait_for_move = WaitForMoveController()
|
|
1082
|
+
self.dae = Dae()
|
|
1083
|
+
self.beamline_pars = {}
|
|
1084
|
+
self.sample_pars = {}
|
|
1085
|
+
self.strict_block = strict_block
|
|
1086
|
+
self.logger = GenieLogger(sim_mode=True)
|
|
1087
|
+
|
|
1088
|
+
def set_instrument(
|
|
1089
|
+
self, pv_prefix: str, globs: dict | None, import_instrument_init: bool
|
|
1090
|
+
) -> None:
|
|
1091
|
+
self.inst_prefix = pv_prefix
|
|
1092
|
+
|
|
1093
|
+
def prefix_pv_name(self, name: str) -> str:
|
|
1094
|
+
"""Adds the instrument prefix to the specified PV"""
|
|
1095
|
+
if self.inst_prefix is not None and not name.startswith(self.inst_prefix):
|
|
1096
|
+
if not self.inst_prefix.endswith(":"):
|
|
1097
|
+
name = ":" + name
|
|
1098
|
+
return self.inst_prefix + name
|
|
1099
|
+
return name
|
|
1100
|
+
|
|
1101
|
+
def set_pv_value(
|
|
1102
|
+
self, name: str, value: str, wait: bool = False, is_local: bool = False
|
|
1103
|
+
) -> None:
|
|
1104
|
+
if is_local:
|
|
1105
|
+
name = self.prefix_pv_name(name)
|
|
1106
|
+
print(
|
|
1107
|
+
"set_pv_value called (name=%s value=%s wait=%s is_local=%s)"
|
|
1108
|
+
% (name, value, wait, is_local)
|
|
1109
|
+
)
|
|
1110
|
+
|
|
1111
|
+
def get_pv_value(
|
|
1112
|
+
self, name: str, to_string: bool = False, attempts: int = 3, is_local: bool = False
|
|
1113
|
+
) -> None:
|
|
1114
|
+
if is_local:
|
|
1115
|
+
name = self.prefix_pv_name(name)
|
|
1116
|
+
print(
|
|
1117
|
+
"get_pv_value called (name=%s value=%s attempts=%s is_local=%s)"
|
|
1118
|
+
% (name, to_string, attempts, is_local)
|
|
1119
|
+
)
|
|
1120
|
+
|
|
1121
|
+
def pv_exists(self, name: str) -> bool:
|
|
1122
|
+
return True
|
|
1123
|
+
|
|
1124
|
+
def reload_current_config(self) -> None:
|
|
1125
|
+
pass
|
|
1126
|
+
|
|
1127
|
+
def correct_blockname(self, name: str, add_prefix: bool = True) -> str:
|
|
1128
|
+
return name
|
|
1129
|
+
|
|
1130
|
+
def get_block_names(self) -> list():
|
|
1131
|
+
return list(self.block_dict.keys())
|
|
1132
|
+
|
|
1133
|
+
def block_exists(self, name: str) -> bool:
|
|
1134
|
+
return name in self.block_dict.keys() if self.strict_block else True
|
|
1135
|
+
|
|
1136
|
+
def set_block_value(
|
|
1137
|
+
self,
|
|
1138
|
+
name: str,
|
|
1139
|
+
value: "PVValue" = None,
|
|
1140
|
+
runcontrol: bool | None = None,
|
|
1141
|
+
lowlimit: float | None = None,
|
|
1142
|
+
highlimit: float | None = None,
|
|
1143
|
+
wait: bool = False,
|
|
1144
|
+
) -> None:
|
|
1145
|
+
"""Sets a block's values.
|
|
1146
|
+
If the block already exists, update the block. Only update values
|
|
1147
|
+
specified in the arguments.
|
|
1148
|
+
|
|
1149
|
+
Args:
|
|
1150
|
+
name (string): the name of the block
|
|
1151
|
+
value (int): the value of the block
|
|
1152
|
+
runcontrol (boolean): whether to set runcontrol for this block
|
|
1153
|
+
lowlimit (float): the lower limit for runcontrol or waiting
|
|
1154
|
+
highlimit (float): the upper limit for runcontrol or waiting
|
|
1155
|
+
wait (boolean): pause execution until setpoint is reached (one block only)
|
|
1156
|
+
|
|
1157
|
+
"""
|
|
1158
|
+
print("Setting {} to value {}".format(name, value))
|
|
1159
|
+
if name not in self.block_dict:
|
|
1160
|
+
self.block_dict[name] = {
|
|
1161
|
+
"value": value,
|
|
1162
|
+
"runcontrol": runcontrol,
|
|
1163
|
+
"lowlimit": lowlimit,
|
|
1164
|
+
"highlimit": highlimit,
|
|
1165
|
+
}
|
|
1166
|
+
else:
|
|
1167
|
+
if wait:
|
|
1168
|
+
self.block_dict[name]["value"] = value
|
|
1169
|
+
else:
|
|
1170
|
+
# from https://stackoverflow.com/questions/582056/
|
|
1171
|
+
# getting-list-of-parameter-names-inside-python-function
|
|
1172
|
+
frame = inspect.currentframe()
|
|
1173
|
+
args, _, _, values = inspect.getargvalues(frame)
|
|
1174
|
+
for arg in args:
|
|
1175
|
+
if values[arg] is not None and arg != "self":
|
|
1176
|
+
self.block_dict[name][arg] = values[arg]
|
|
1177
|
+
if wait:
|
|
1178
|
+
self.waitfor.start_waiting(block=name, value=value)
|
|
1179
|
+
|
|
1180
|
+
def get_block_data(self, block: str, fail_fast: bool = False) -> dict():
|
|
1181
|
+
ans = OrderedDict()
|
|
1182
|
+
ans["connected"] = True
|
|
1183
|
+
|
|
1184
|
+
ans["name"] = block
|
|
1185
|
+
ans["value"] = self.block_dict[block].get("value", None)
|
|
1186
|
+
ans["runcontrol"], ans["lowlimit"], ans["highlimit"] = self.get_runcontrol_settings(block)
|
|
1187
|
+
|
|
1188
|
+
ans["alarm"] = self.get_alarm_from_block(block)
|
|
1189
|
+
return ans
|
|
1190
|
+
|
|
1191
|
+
def set_multiple_blocks(self, names: list, values: list) -> None:
|
|
1192
|
+
temp = list(zip(names, values))
|
|
1193
|
+
for name, value in temp:
|
|
1194
|
+
if name in self.block_dict:
|
|
1195
|
+
self.block_dict[name]["value"] = value
|
|
1196
|
+
else:
|
|
1197
|
+
self.block_dict[name] = {
|
|
1198
|
+
"value": value,
|
|
1199
|
+
"runcontrol": None,
|
|
1200
|
+
"lowlimit": None,
|
|
1201
|
+
"highlimit": None,
|
|
1202
|
+
"wait": False,
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
def run_pre_post_cmd(self, command: str, **pars: str) -> None:
|
|
1206
|
+
pass
|
|
1207
|
+
|
|
1208
|
+
def get_sample_pars(self) -> dict:
|
|
1209
|
+
return self.sample_pars
|
|
1210
|
+
|
|
1211
|
+
def set_sample_par(self, name: str, value: str) -> None:
|
|
1212
|
+
self.sample_pars[name] = value
|
|
1213
|
+
|
|
1214
|
+
def get_beamline_pars(self) -> dict:
|
|
1215
|
+
return self.beamline_pars
|
|
1216
|
+
|
|
1217
|
+
def set_beamline_par(self, name: str, value: str) -> None:
|
|
1218
|
+
self.beamline_pars[name] = value
|
|
1219
|
+
|
|
1220
|
+
def get_runcontrol_settings(self, name: str) -> tuple():
|
|
1221
|
+
return (
|
|
1222
|
+
self.block_dict[name]["runcontrol"],
|
|
1223
|
+
self.block_dict[name]["lowlimit"],
|
|
1224
|
+
self.block_dict[name]["highlimit"],
|
|
1225
|
+
)
|
|
1226
|
+
|
|
1227
|
+
def check_alarms(*blocks: str) -> tuple[list[str], list[str], list[str]]:
|
|
1228
|
+
minor = list()
|
|
1229
|
+
major = list()
|
|
1230
|
+
invalid = list()
|
|
1231
|
+
return (minor, major, invalid)
|
|
1232
|
+
|
|
1233
|
+
def check_limit_violations(self, blocks: str) -> list:
|
|
1234
|
+
return list()
|
|
1235
|
+
|
|
1236
|
+
def get_current_block_values(self) -> dict:
|
|
1237
|
+
"""
|
|
1238
|
+
Values are returned for each IBEX block.
|
|
1239
|
+
Returns:
|
|
1240
|
+
dictionary of blocks each with a list of values in
|
|
1241
|
+
|
|
1242
|
+
"""
|
|
1243
|
+
order_of_keys = ["value", "runcontrol", "lowlimit", "highlimit"]
|
|
1244
|
+
block_values = {}
|
|
1245
|
+
for key, values in self.block_dict.items():
|
|
1246
|
+
return_values = []
|
|
1247
|
+
for order_key in order_of_keys:
|
|
1248
|
+
return_values.append(values.get(order_key, None))
|
|
1249
|
+
block_values[key] = return_values
|
|
1250
|
+
return block_values
|
|
1251
|
+
|
|
1252
|
+
def send_sms(self, phone_num: str, message: str) -> None:
|
|
1253
|
+
print(('SMS "{}" sent to {}'.format(message, phone_num)))
|
|
1254
|
+
|
|
1255
|
+
def send_email(self, address: str, message: str) -> None:
|
|
1256
|
+
print(('Email "{}" sent to {}'.format(message, address)))
|
|
1257
|
+
|
|
1258
|
+
def send_alert(self, message: str, inst: str) -> str:
|
|
1259
|
+
print(('Slack message "{}" sent to {}'.format(message, inst)))
|
|
1260
|
+
|
|
1261
|
+
def get_alarm_from_block(self, block: str) -> str:
|
|
1262
|
+
return "NO_ALARM"
|
|
1263
|
+
|
|
1264
|
+
def get_block_units(self, block: str) -> str:
|
|
1265
|
+
return "mm"
|