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.
Files changed (43) hide show
  1. genie_python/.pylintrc +539 -0
  2. genie_python/__init__.py +1 -0
  3. genie_python/block_names.py +123 -0
  4. genie_python/channel_access_exceptions.py +45 -0
  5. genie_python/genie.py +2462 -0
  6. genie_python/genie_advanced.py +418 -0
  7. genie_python/genie_alerts.py +195 -0
  8. genie_python/genie_api_setup.py +451 -0
  9. genie_python/genie_blockserver.py +64 -0
  10. genie_python/genie_cachannel_wrapper.py +545 -0
  11. genie_python/genie_change_cache.py +151 -0
  12. genie_python/genie_dae.py +2218 -0
  13. genie_python/genie_epics_api.py +906 -0
  14. genie_python/genie_experimental_data.py +186 -0
  15. genie_python/genie_logging.py +200 -0
  16. genie_python/genie_p4p_wrapper.py +203 -0
  17. genie_python/genie_plot.py +77 -0
  18. genie_python/genie_pre_post_cmd_manager.py +21 -0
  19. genie_python/genie_pv_connection_protocol.py +36 -0
  20. genie_python/genie_script_checker.py +507 -0
  21. genie_python/genie_script_generator.py +212 -0
  22. genie_python/genie_simulate.py +69 -0
  23. genie_python/genie_simulate_impl.py +1265 -0
  24. genie_python/genie_startup.py +29 -0
  25. genie_python/genie_toggle_settings.py +58 -0
  26. genie_python/genie_wait_for_move.py +154 -0
  27. genie_python/genie_waitfor.py +576 -0
  28. genie_python/matplotlib_backend/__init__.py +0 -0
  29. genie_python/matplotlib_backend/ibex_websocket_backend.py +366 -0
  30. genie_python/mysql_abstraction_layer.py +272 -0
  31. genie_python/run_tests.py +56 -0
  32. genie_python/scanning_instrument_pylint_plugin.py +31 -0
  33. genie_python/typings/CaChannel/CaChannel.pyi +893 -0
  34. genie_python/typings/CaChannel/__init__.pyi +9 -0
  35. genie_python/typings/CaChannel/_version.pyi +6 -0
  36. genie_python/typings/CaChannel/ca.pyi +31 -0
  37. genie_python/utilities.py +406 -0
  38. genie_python/version.py +1 -0
  39. genie_python-15.1.0rc1.dist-info/LICENSE +28 -0
  40. genie_python-15.1.0rc1.dist-info/METADATA +95 -0
  41. genie_python-15.1.0rc1.dist-info/RECORD +43 -0
  42. genie_python-15.1.0rc1.dist-info/WHEEL +5 -0
  43. 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