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,418 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Genie Advanced module:
|
|
3
|
+
|
|
4
|
+
This module is used for advanced commands that are for expert users.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import contextlib
|
|
8
|
+
from datetime import datetime, timedelta
|
|
9
|
+
from time import sleep
|
|
10
|
+
from typing import Any, Callable, Iterator, TypedDict
|
|
11
|
+
|
|
12
|
+
import numpy.typing as npt
|
|
13
|
+
|
|
14
|
+
from genie_python.genie_api_setup import (
|
|
15
|
+
__api,
|
|
16
|
+
helparglist,
|
|
17
|
+
log_command_and_handle_exception,
|
|
18
|
+
usercommand,
|
|
19
|
+
)
|
|
20
|
+
from genie_python.genie_waitfor import DELAY_IN_WAIT_FOR_SLEEP_LOOP
|
|
21
|
+
from genie_python.utilities import check_break
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@usercommand
|
|
25
|
+
@helparglist("")
|
|
26
|
+
def get_manager_mode() -> bool:
|
|
27
|
+
"""
|
|
28
|
+
Returns whether you are in manager mode or not.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
manager_mode (Bool): Manager mode on or off.
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
return __api.get_pv_value("CS:MANAGER", True, 3, True) == "Yes"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@usercommand
|
|
39
|
+
@helparglist("")
|
|
40
|
+
def assert_in_manager_mode() -> None:
|
|
41
|
+
"""
|
|
42
|
+
Checks that the user is in manager mode so can use advanced functions.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
if not get_manager_mode():
|
|
51
|
+
raise RuntimeError("You need to be in Manager mode to complete this action.")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@contextlib.contextmanager
|
|
55
|
+
def motor_in_set_mode(pv_name: str) -> Iterator:
|
|
56
|
+
"""
|
|
57
|
+
Uses a context to place motor into set mode and ensure that it leaves
|
|
58
|
+
set mode after context has ended. If it can not set the mode correctly
|
|
59
|
+
will not run the yield.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
pv_name: pv of motor on which to set the mode
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
if not __api.pv_exists(pv_name):
|
|
68
|
+
raise ValueError("Cannot find pv " + pv_name)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
__api.set_pv_value(pv_name + ".SET", 1, True)
|
|
72
|
+
offset_freeze_switch = __api.get_pv_value(pv_name + ".FOFF")
|
|
73
|
+
__api.set_pv_value(pv_name + ".FOFF", "Frozen", True)
|
|
74
|
+
except IOError as ex:
|
|
75
|
+
raise ValueError("Can not set motor set and frozen offset mode: {}".format(ex))
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
yield
|
|
79
|
+
finally:
|
|
80
|
+
try:
|
|
81
|
+
__api.set_pv_value(pv_name + ".SET", 0, True)
|
|
82
|
+
__api.set_pv_value(pv_name + ".FOFF", offset_freeze_switch, True)
|
|
83
|
+
except IOError as ex:
|
|
84
|
+
raise ValueError("Can not reset motor set and frozen offset mode: {}".format(ex))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@usercommand
|
|
88
|
+
@helparglist("name str, value flt")
|
|
89
|
+
def redefine_motor_position(name: str, value: float | int) -> None:
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
Change the motor Move Abs value.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
name: Name of the motor. e.g MTR0101
|
|
96
|
+
value: The new value of Move Abs.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
assert_in_manager_mode()
|
|
103
|
+
|
|
104
|
+
pv_name = f"{__api.inst_prefix}MOT:{name}"
|
|
105
|
+
|
|
106
|
+
with motor_in_set_mode(pv_name):
|
|
107
|
+
if __api.get_pv_value(pv_name + ".MOVN") == 1:
|
|
108
|
+
raise RuntimeError("Cannot change motor " + name + " position while it is moving.")
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
__api.set_pv_value(pv_name + ".VAL", value, True)
|
|
112
|
+
except IOError as ex:
|
|
113
|
+
raise ValueError("Can not set new motor position: {}".format(ex))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@usercommand
|
|
117
|
+
@helparglist("block str")
|
|
118
|
+
@log_command_and_handle_exception
|
|
119
|
+
def get_pv_from_block(block: str) -> str:
|
|
120
|
+
"""
|
|
121
|
+
Get the full PV name for a given block.
|
|
122
|
+
This is an advanced function because of the need to use the pv name correctly.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
block (str): A block object
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
pv_name (Str): The pv name as a string
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
return __api.get_pv_from_block(block)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@usercommand
|
|
135
|
+
@helparglist("pv str")
|
|
136
|
+
@log_command_and_handle_exception
|
|
137
|
+
def pv_exists(pv: str, is_local: bool = False) -> None:
|
|
138
|
+
"""
|
|
139
|
+
Check if PV exists.
|
|
140
|
+
|
|
141
|
+
Params:
|
|
142
|
+
pv (str): The address of the PV
|
|
143
|
+
is_local (bool, optional): is it a local PV i.e. needs prefix adding
|
|
144
|
+
"""
|
|
145
|
+
return __api.pv_exists(pv, is_local=is_local)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@usercommand
|
|
149
|
+
@helparglist("pv str, value[, maxwait]")
|
|
150
|
+
@log_command_and_handle_exception
|
|
151
|
+
def wait_for_pv(
|
|
152
|
+
pv: str, value: bool | int | float | str | None, maxwait: int | None = None
|
|
153
|
+
) -> None:
|
|
154
|
+
"""
|
|
155
|
+
Wait until a PV has reached a given value.
|
|
156
|
+
|
|
157
|
+
Params:
|
|
158
|
+
pv (str): The address of the PV
|
|
159
|
+
value: The value to wait for
|
|
160
|
+
maxwait (int, optional): The maximum time to wait for in seconds
|
|
161
|
+
"""
|
|
162
|
+
start_time = datetime.utcnow()
|
|
163
|
+
while True:
|
|
164
|
+
curr_value = __api.get_pv_value(pv)
|
|
165
|
+
if curr_value == value:
|
|
166
|
+
break
|
|
167
|
+
if maxwait is not None:
|
|
168
|
+
if timedelta(seconds=maxwait) < datetime.utcnow() - start_time:
|
|
169
|
+
break
|
|
170
|
+
sleep(DELAY_IN_WAIT_FOR_SLEEP_LOOP)
|
|
171
|
+
check_break(2)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@usercommand
|
|
175
|
+
@helparglist("")
|
|
176
|
+
def set_begin_precmd(begin_precmd: Callable[[Any], str | None]) -> None:
|
|
177
|
+
"""
|
|
178
|
+
Set the function to call before the begin command.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
begin_precmd (function): The function to call (which should return
|
|
182
|
+
None if it wants the run to start, or a string with the reason why not to start run).
|
|
183
|
+
"""
|
|
184
|
+
__api.pre_post_cmd_manager.begin_precmd = begin_precmd
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@usercommand
|
|
188
|
+
@helparglist("")
|
|
189
|
+
def set_begin_postcmd(begin_postcmd: Callable[[Any], str | None]) -> None:
|
|
190
|
+
"""
|
|
191
|
+
Set the function to call after the begin command.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
begin_postcmd (function): The function to call.
|
|
195
|
+
"""
|
|
196
|
+
__api.pre_post_cmd_manager.begin_postcmd = begin_postcmd
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@usercommand
|
|
200
|
+
@helparglist("")
|
|
201
|
+
def set_abort_precmd(abort_precmd: Callable[[Any], str | None]) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Set the function to call before the abort command.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
abort_precmd (function): The function to call.
|
|
207
|
+
"""
|
|
208
|
+
__api.pre_post_cmd_manager.abort_precmd = abort_precmd
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@usercommand
|
|
212
|
+
@helparglist("")
|
|
213
|
+
def set_abort_postcmd(abort_postcmd: Callable[[Any], str | None]) -> None:
|
|
214
|
+
"""
|
|
215
|
+
Set the function to call after the abort command.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
abort_postcmd (function): The function to call.
|
|
219
|
+
"""
|
|
220
|
+
__api.pre_post_cmd_manager.abort_postcmd = abort_postcmd
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@usercommand
|
|
224
|
+
@helparglist("")
|
|
225
|
+
def set_end_precmd(end_precmd: Callable[[Any], str | None]) -> None:
|
|
226
|
+
"""
|
|
227
|
+
Set the function to call before the end command.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
end_precmd (function): The function to call.
|
|
231
|
+
"""
|
|
232
|
+
__api.pre_post_cmd_manager.end_precmd = end_precmd
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@usercommand
|
|
236
|
+
@helparglist("")
|
|
237
|
+
def set_end_postcmd(end_postcmd: Callable[[Any], str | None]) -> None:
|
|
238
|
+
"""
|
|
239
|
+
Set the function to call after the end command.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
end_postcmd (function): The function to call.
|
|
243
|
+
"""
|
|
244
|
+
__api.pre_post_cmd_manager.end_postcmd = end_postcmd
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@usercommand
|
|
248
|
+
@helparglist("")
|
|
249
|
+
def set_pause_precmd(pause_precmd: Callable[[Any], str | None]) -> None:
|
|
250
|
+
"""
|
|
251
|
+
Set the function to call before the pause command.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
pause_precmd (function): The function to call.
|
|
255
|
+
"""
|
|
256
|
+
__api.pre_post_cmd_manager.pause_precmd = pause_precmd
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@usercommand
|
|
260
|
+
@helparglist("")
|
|
261
|
+
def set_pause_postcmd(pause_postcmd: Callable[[Any], str | None]) -> None:
|
|
262
|
+
"""
|
|
263
|
+
Set the function to call after the pause command.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
pause_postcmd (function): The function to call.
|
|
267
|
+
"""
|
|
268
|
+
__api.pre_post_cmd_manager.pause_postcmd = pause_postcmd
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@usercommand
|
|
272
|
+
@helparglist("")
|
|
273
|
+
def set_resume_precmd(resume_precmd: Callable[[Any], str | None]) -> None:
|
|
274
|
+
"""
|
|
275
|
+
Set the function to call before the resume command.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
resume_precmd (function): The function to call.
|
|
279
|
+
"""
|
|
280
|
+
__api.pre_post_cmd_manager.resume_precmd = resume_precmd
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
@usercommand
|
|
284
|
+
@helparglist("")
|
|
285
|
+
def set_resume_postcmd(resume_postcmd: Callable[[Any], str | None]) -> None:
|
|
286
|
+
"""
|
|
287
|
+
Set the function to call after the resume command.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
resume_postcmd (function): The function to call.
|
|
291
|
+
"""
|
|
292
|
+
__api.pre_post_cmd_manager.resume_postcmd = resume_postcmd
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@usercommand
|
|
296
|
+
@helparglist("")
|
|
297
|
+
def open_plot_window(
|
|
298
|
+
is_primary: bool = True, host: str | int | None = None, figures: list[int] | None = None
|
|
299
|
+
) -> None:
|
|
300
|
+
"""
|
|
301
|
+
Open the plot window in a locally running client
|
|
302
|
+
(even if this is called in a standalone genie_python)
|
|
303
|
+
Args:
|
|
304
|
+
is_primary: True to open primary plotting window; False open secondaty window
|
|
305
|
+
host: host to open plot from; Default None is localhost
|
|
306
|
+
figures: List of figures to open; Default opens all figures
|
|
307
|
+
"""
|
|
308
|
+
from genie_python.matplotlib_backend.ibex_websocket_backend import (
|
|
309
|
+
figure_numbers,
|
|
310
|
+
ibex_open_plot_window,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
ibex_open_plot_window(
|
|
314
|
+
figures=figure_numbers if figures is None else figures, is_primary=is_primary, host=host
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
@usercommand
|
|
319
|
+
@helparglist("")
|
|
320
|
+
@log_command_and_handle_exception
|
|
321
|
+
def get_instrument() -> str | None:
|
|
322
|
+
"""
|
|
323
|
+
Gets the name of the local instrument (e.g. NDW1234, DEMO, EMMA-A)
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
the name of the local instrument
|
|
327
|
+
"""
|
|
328
|
+
return __api.get_instrument()
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@usercommand
|
|
332
|
+
@helparglist("verbose")
|
|
333
|
+
@log_command_and_handle_exception
|
|
334
|
+
def set_dae_message_verbosity(verbose: bool) -> None:
|
|
335
|
+
"""
|
|
336
|
+
Set the verbosity of messages coming from the DAE.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
verbose (bool): set the verbosity, True to be more verbose
|
|
340
|
+
"""
|
|
341
|
+
__api.dae.set_verbose(verbose)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class _GetExpDataReturn(TypedDict):
|
|
345
|
+
rb_number: int | str
|
|
346
|
+
user: str
|
|
347
|
+
role: str
|
|
348
|
+
start_date: str
|
|
349
|
+
duration: float
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@usercommand
|
|
353
|
+
@helparglist("")
|
|
354
|
+
@log_command_and_handle_exception
|
|
355
|
+
def get_exp_data(
|
|
356
|
+
rb: int | str = "%", user: str = "%", role: str = "%", verbose: bool = False
|
|
357
|
+
) -> list[_GetExpDataReturn]:
|
|
358
|
+
"""
|
|
359
|
+
Returns the data of experiments that match the given criteria,
|
|
360
|
+
or all if none is given, from the exp_data
|
|
361
|
+
database. If verbose is enabled, only pretty-print the data.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
rb (int, optional): The RB number of the experiment to look for, Defaults to Any.
|
|
365
|
+
user (str, optional): The name of the user who is running/has
|
|
366
|
+
run the experiment, Defaults to Any.
|
|
367
|
+
role (str, optional): The user role, Defaults to Any.
|
|
368
|
+
verbose (bool, optional): Pretty-print the data, Defaults to False.
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
exp_data (list): The experiment(s) data as a list of dicts.
|
|
372
|
+
|
|
373
|
+
Raises:
|
|
374
|
+
NotFoundError: Thrown if a parameter's value was not found in the database.
|
|
375
|
+
|
|
376
|
+
"""
|
|
377
|
+
try:
|
|
378
|
+
if __api.exp_data is None:
|
|
379
|
+
raise EnvironmentError("Could not connect to instrument database")
|
|
380
|
+
return __api.exp_data.get_exp_data(rb, user, role, verbose)
|
|
381
|
+
except AttributeError as e:
|
|
382
|
+
raise NotImplementedError(
|
|
383
|
+
"get_exp_data is not implemented for this genie type. {}".format(e)
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
@usercommand
|
|
388
|
+
@helparglist("")
|
|
389
|
+
@log_command_and_handle_exception
|
|
390
|
+
def get_spectrum_data(with_spec_zero: bool = True, with_time_bin_zero: bool = False) -> npt.NDArray:
|
|
391
|
+
"""
|
|
392
|
+
Get the event mode spectrum data as ND array.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
with_spec_zero (bool, optional): Include or exclude diagnostic spectrum 0
|
|
396
|
+
if you have 10 spectra and include spectrum zero, your array will be
|
|
397
|
+
of size 11 and spectrum 5 will be at array[5]. If you exclude spectrum zero
|
|
398
|
+
then spectrum 5 would be at array[4]
|
|
399
|
+
with_time_bin_zero (bool, optional): Include or exclude diagnostic bin 0
|
|
400
|
+
if you have 1000 time channels and include time bin 0, your array will be
|
|
401
|
+
of size 1001 and data for your defined time bins will start at array[1]
|
|
402
|
+
rather than array[0]. This bin contents is only of use for diagnostic
|
|
403
|
+
issues, it contains data that does not fit into the defined time range
|
|
404
|
+
Returns:
|
|
405
|
+
numpy int array: spectrum data ND array
|
|
406
|
+
this is of dimensions [periods, spectra, time_bins]
|
|
407
|
+
"""
|
|
408
|
+
data = __api.dae.get_spec_data()
|
|
409
|
+
nper = __api.dae.get_num_periods()
|
|
410
|
+
nsp = __api.dae.get_num_spectra()
|
|
411
|
+
ntc = __api.dae.get_num_timechannels()
|
|
412
|
+
# this is (nsp + 1) and (ntc + 1) for spectrum 0 and time channel 0
|
|
413
|
+
data_reshaped = data.reshape((nper, nsp + 1, ntc + 1))
|
|
414
|
+
return data_reshaped[
|
|
415
|
+
slice(None),
|
|
416
|
+
slice(None) if with_spec_zero else slice(1, None),
|
|
417
|
+
slice(None) if with_time_bin_zero else slice(1, None),
|
|
418
|
+
]
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Genie Alerts module:
|
|
3
|
+
|
|
4
|
+
This module is used for setting alerts on blocks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import absolute_import, print_function
|
|
8
|
+
|
|
9
|
+
from genie_python.genie_api_setup import (
|
|
10
|
+
__api,
|
|
11
|
+
helparglist,
|
|
12
|
+
log_command_and_handle_exception,
|
|
13
|
+
usercommand,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
_ALERT_ENABLE = "CS:SB:{}:AC:ENABLE"
|
|
17
|
+
_ALERT_LOW = "CS:SB:{}:AC:LOW"
|
|
18
|
+
_ALERT_HIGH = "CS:SB:{}:AC:HIGH"
|
|
19
|
+
_ALERT_DELAY_OUT = "CS:SB:{}:AC:OUT:DELAY"
|
|
20
|
+
_ALERT_DELAY_IN = "CS:SB:{}:AC:IN:DELAY"
|
|
21
|
+
|
|
22
|
+
_ALERT_MOBILES = "CS:AC:ALERTS:MOBILES:SP"
|
|
23
|
+
_ALERT_EMAILS = "CS:AC:ALERTS:EMAILS:SP"
|
|
24
|
+
_ALERT_MESSAGE = "CS:AC:ALERTS:MESSAGE:SP"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@usercommand
|
|
28
|
+
@helparglist("block, lowlimit, highlimit, [delay_in, delay_out]")
|
|
29
|
+
@log_command_and_handle_exception
|
|
30
|
+
def set_range(block, lowlimit, highlimit, set_enable=True, delay_in=None, delay_out=None):
|
|
31
|
+
"""
|
|
32
|
+
Sets alert range on block.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
block (str): Block name
|
|
36
|
+
lowlimit (float): low limit
|
|
37
|
+
highlimit (float): high limit
|
|
38
|
+
set_enable (bool): (optional setting True will enable alerts on the block. Defaults to True.
|
|
39
|
+
delay_in (float): (optional) delay /s before triggering in range. If not specified the delay remains unchanged.
|
|
40
|
+
delay_out (float): (optional) delay /s before triggering out of range. If not specified the delay remains unchanged.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
if not __api.block_exists(block):
|
|
44
|
+
raise Exception('No block with the name "{}" exists'.format(block))
|
|
45
|
+
|
|
46
|
+
__api.set_pv_value(_ALERT_LOW.format(block), lowlimit, wait=False, is_local=True)
|
|
47
|
+
__api.set_pv_value(_ALERT_HIGH.format(block), highlimit, wait=False, is_local=True)
|
|
48
|
+
if delay_in is not None:
|
|
49
|
+
__api.set_pv_value(_ALERT_DELAY_IN.format(block), delay_in, wait=False, is_local=True)
|
|
50
|
+
if delay_out is not None:
|
|
51
|
+
__api.set_pv_value(_ALERT_DELAY_OUT.format(block), delay_out, wait=False, is_local=True)
|
|
52
|
+
if set_enable:
|
|
53
|
+
enable(block)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@usercommand
|
|
57
|
+
@helparglist("block [, is_enabled]")
|
|
58
|
+
@log_command_and_handle_exception
|
|
59
|
+
def enable(block, set_enabled=True):
|
|
60
|
+
"""
|
|
61
|
+
Enable alerts on a block.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
block (str): Block name
|
|
65
|
+
set_enabled (bool): whether to enable
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
if not __api.block_exists(block):
|
|
69
|
+
raise Exception('No block with the name "{}" exists'.format(block))
|
|
70
|
+
__api.set_pv_value(_ALERT_ENABLE.format(block), set_enabled, wait=False, is_local=True)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@usercommand
|
|
74
|
+
@helparglist("message")
|
|
75
|
+
@log_command_and_handle_exception
|
|
76
|
+
def send(message):
|
|
77
|
+
"""
|
|
78
|
+
Send a message to all alert recipients.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
message (str): message to send
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
__api.set_pv_value(_ALERT_MESSAGE, message, wait=False, is_local=True)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
## no log decorator so mobile numbers not sent to log file
|
|
88
|
+
@usercommand
|
|
89
|
+
@helparglist("numbers")
|
|
90
|
+
def set_sms(numbers):
|
|
91
|
+
"""
|
|
92
|
+
Set SMS text numbers for alerts on blocks.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
numbers (list): list of strings giving phone numbers
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
try:
|
|
99
|
+
if isinstance(numbers, list):
|
|
100
|
+
__api.set_pv_value(_ALERT_MOBILES, ";".join(numbers), wait=False, is_local=True)
|
|
101
|
+
else:
|
|
102
|
+
__api.set_pv_value(_ALERT_MOBILES, numbers, wait=False, is_local=True)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print("Unable to set alert SMS numbers: {}".format(e))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
## no log decorator so email addresses not sent to log file
|
|
108
|
+
@usercommand
|
|
109
|
+
@helparglist("emails")
|
|
110
|
+
def set_email(emails):
|
|
111
|
+
"""
|
|
112
|
+
Set email addresses for alerts on blocks.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
emails (list): list of strings giving email addresses
|
|
116
|
+
|
|
117
|
+
"""
|
|
118
|
+
try:
|
|
119
|
+
if isinstance(emails, list):
|
|
120
|
+
__api.set_pv_value(_ALERT_EMAILS, ";".join(emails), wait=False, is_local=True)
|
|
121
|
+
else:
|
|
122
|
+
__api.set_pv_value(_ALERT_EMAILS, emails, wait=False, is_local=True)
|
|
123
|
+
except Exception as e:
|
|
124
|
+
print("Unable to set alert email addresses: {}".format(e))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _print_block(block, only_if_enabled=False):
|
|
128
|
+
enabled = (
|
|
129
|
+
__api.get_pv_value(_ALERT_ENABLE.format(block), to_string=True, is_local=True) == "YES"
|
|
130
|
+
)
|
|
131
|
+
if only_if_enabled and not enabled:
|
|
132
|
+
return
|
|
133
|
+
print("Block: {}".format(block))
|
|
134
|
+
print(" Enabled: {}".format(enabled))
|
|
135
|
+
print(
|
|
136
|
+
" Low: {}".format(
|
|
137
|
+
__api.get_pv_value(_ALERT_LOW.format(block), to_string=False, is_local=True)
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
print(
|
|
141
|
+
" High: {}".format(
|
|
142
|
+
__api.get_pv_value(_ALERT_HIGH.format(block), to_string=False, is_local=True)
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
print(
|
|
146
|
+
" Delay In: {}".format(
|
|
147
|
+
__api.get_pv_value(_ALERT_DELAY_IN.format(block), to_string=False, is_local=True)
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
print(
|
|
151
|
+
" Delay Out: {}".format(
|
|
152
|
+
__api.get_pv_value(_ALERT_DELAY_OUT.format(block), to_string=False, is_local=True)
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@usercommand
|
|
158
|
+
@helparglist("[block, all]")
|
|
159
|
+
@log_command_and_handle_exception
|
|
160
|
+
def status(block=None, all=False):
|
|
161
|
+
"""
|
|
162
|
+
Prints the emails and mobiles used for alerts and the current status of specified block.
|
|
163
|
+
Args:
|
|
164
|
+
block (string): The block to print information about
|
|
165
|
+
all (bool): If True information about all the blocks is printed
|
|
166
|
+
"""
|
|
167
|
+
print("Emails: {}".format(__api.get_pv_value(_ALERT_EMAILS, to_string=False, is_local=True)))
|
|
168
|
+
print("Mobiles: {}".format(__api.get_pv_value(_ALERT_MOBILES, to_string=True, is_local=True)))
|
|
169
|
+
if block is not None:
|
|
170
|
+
if not __api.block_exists(block):
|
|
171
|
+
raise Exception('No block with the name "{}" exists'.format(block))
|
|
172
|
+
_print_block(block)
|
|
173
|
+
else:
|
|
174
|
+
blocks = __api.get_block_names()
|
|
175
|
+
for block in blocks:
|
|
176
|
+
_print_block(block, not all)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# used as part of tests, returns a dictionary of details
|
|
180
|
+
def _dump(block):
|
|
181
|
+
if not __api.block_exists(block):
|
|
182
|
+
raise Exception('No block with the name "{}" exists'.format(block))
|
|
183
|
+
res = {}
|
|
184
|
+
res["emails"] = __api.get_pv_value(_ALERT_EMAILS, to_string=True, is_local=True).split(";")
|
|
185
|
+
res["mobiles"] = __api.get_pv_value(_ALERT_MOBILES, to_string=True, is_local=True).split(";")
|
|
186
|
+
res["enabled"] = __api.get_pv_value(_ALERT_ENABLE.format(block), to_string=False, is_local=True)
|
|
187
|
+
res["lowlimit"] = __api.get_pv_value(_ALERT_LOW.format(block), to_string=False, is_local=True)
|
|
188
|
+
res["highlimit"] = __api.get_pv_value(_ALERT_HIGH.format(block), to_string=False, is_local=True)
|
|
189
|
+
res["delay_in"] = __api.get_pv_value(
|
|
190
|
+
_ALERT_DELAY_IN.format(block), to_string=False, is_local=True
|
|
191
|
+
)
|
|
192
|
+
res["delay_out"] = __api.get_pv_value(
|
|
193
|
+
_ALERT_DELAY_OUT.format(block), to_string=False, is_local=True
|
|
194
|
+
)
|
|
195
|
+
return res
|