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,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This type stub file was generated by pyright.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import TypeAlias
|
|
8
|
+
|
|
9
|
+
from caffi.ca import *
|
|
10
|
+
from caffi.constants import *
|
|
11
|
+
from caffi.macros import *
|
|
12
|
+
from numpy import NDArray
|
|
13
|
+
|
|
14
|
+
from ._ca import *
|
|
15
|
+
|
|
16
|
+
if os.environ.get("CACHANNEL_BACKEND") == "caffi": ...
|
|
17
|
+
else: ...
|
|
18
|
+
|
|
19
|
+
ECA_TIMEOUT: int
|
|
20
|
+
DBR_STRING: str
|
|
21
|
+
DBR_CHAR: str
|
|
22
|
+
DBR_CTRL_ENUM: Enum
|
|
23
|
+
cs_conn: Enum
|
|
24
|
+
CA_OP_CONN_DOWN: int
|
|
25
|
+
|
|
26
|
+
PVBaseValue: TypeAlias = bool | int | float | str
|
|
27
|
+
PVValue: TypeAlias = PVBaseValue | list[PVBaseValue] | NDArray | None
|
|
28
|
+
|
|
29
|
+
def dbr_type_is_CHAR(PVValue) -> bool: ...
|
|
30
|
+
def dbr_type_is_ENUM(PVValue) -> bool: ...
|
|
31
|
+
def dbr_type_is_STRING(PVValue) -> bool: ...
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
from __future__ import absolute_import, print_function
|
|
2
|
+
|
|
3
|
+
import codecs
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import unicodedata
|
|
8
|
+
import zlib
|
|
9
|
+
from builtins import object
|
|
10
|
+
from datetime import timedelta
|
|
11
|
+
from functools import wraps
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from nicos import session
|
|
15
|
+
|
|
16
|
+
def check_break(level):
|
|
17
|
+
session.breakpoint(level)
|
|
18
|
+
except ImportError:
|
|
19
|
+
|
|
20
|
+
def check_break(level):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## call to make sure we cleanup any subprocesses on process termination
|
|
25
|
+
## useful to call from e.g. ioc test framework
|
|
26
|
+
## it creates windows job object with kill on close property, which will be inherited by sub processes
|
|
27
|
+
## when returned handle is closed, all processes will die
|
|
28
|
+
## we make sure we detach the Py_HANDLE object from the underlying WIN32 handle
|
|
29
|
+
## so termination is done by windows and not when pythion obecjt goes out of scope
|
|
30
|
+
def cleanup_subprocs_on_process_exit():
|
|
31
|
+
if os.name == "nt":
|
|
32
|
+
try:
|
|
33
|
+
import win32api
|
|
34
|
+
import win32job
|
|
35
|
+
|
|
36
|
+
h = win32job.CreateJobObject(None, "")
|
|
37
|
+
info = win32job.QueryInformationJobObject(h, win32job.JobObjectExtendedLimitInformation)
|
|
38
|
+
info["BasicLimitInformation"]["LimitFlags"] |= (
|
|
39
|
+
win32job.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
|
|
40
|
+
)
|
|
41
|
+
win32job.SetInformationJobObject(h, win32job.JobObjectExtendedLimitInformation, info)
|
|
42
|
+
win32job.AssignProcessToJobObject(h, win32api.GetCurrentProcess())
|
|
43
|
+
h.Detach()
|
|
44
|
+
except Exception as err:
|
|
45
|
+
raise OSError(f"cleanup_subprocs_on_process_exit() failed: {err}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class PVReadException(Exception):
|
|
49
|
+
"""
|
|
50
|
+
Exception to throw when there is a problem reading a PV.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, message):
|
|
54
|
+
super(PVReadException, self).__init__(message)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def compress_and_hex(value):
|
|
58
|
+
compr = zlib.compress(bytearray(value, "utf-8"))
|
|
59
|
+
return codecs.encode(compr, "hex_codec")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def dehex_and_decompress(value):
|
|
63
|
+
"""
|
|
64
|
+
Dehex and decompress a string and return it
|
|
65
|
+
:param value: compressed hexed string
|
|
66
|
+
:return: value as a strinnng
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
# If it comes as bytes then cast to string
|
|
70
|
+
value = value.decode("utf-8")
|
|
71
|
+
except AttributeError:
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
return zlib.decompress(bytes.fromhex(value)).decode("utf-8")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def dehex_decompress_and_dejson(value):
|
|
78
|
+
"""
|
|
79
|
+
Convert string from zipped hexed json to a python representation
|
|
80
|
+
:param value: value to convert
|
|
81
|
+
:return: python representation of json
|
|
82
|
+
"""
|
|
83
|
+
return json.loads(dehex_and_decompress(value))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def waveform_to_string(data):
|
|
87
|
+
output = ""
|
|
88
|
+
for i in data:
|
|
89
|
+
if i == 0:
|
|
90
|
+
break
|
|
91
|
+
if isinstance(i, str):
|
|
92
|
+
output += i
|
|
93
|
+
else:
|
|
94
|
+
output += str(chr(i))
|
|
95
|
+
return output
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def convert_string_to_ascii(data):
|
|
99
|
+
"""
|
|
100
|
+
Converts a string to be ascii.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
data: the string to convert
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
string: the ascii equivalent
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def _make_ascii_mappings():
|
|
110
|
+
"""
|
|
111
|
+
Create mapping for characters not converted to 7-bit by NFKD.
|
|
112
|
+
"""
|
|
113
|
+
mappings_in = [ord(char) for char in "\xd0\xd7\xd8\xde\xdf\xf0\xf8\xfe"]
|
|
114
|
+
mappings_out = "DXOPBoop"
|
|
115
|
+
d = dict(list(zip(mappings_in, mappings_out)))
|
|
116
|
+
d[ord("\xc6")] = "AE"
|
|
117
|
+
d[ord("\xe6")] = "ae"
|
|
118
|
+
return d
|
|
119
|
+
|
|
120
|
+
# Replace all compatibility characters with their equivalents
|
|
121
|
+
normalised = unicodedata.normalize("NFKD", data)
|
|
122
|
+
# Keep non-combining chars only
|
|
123
|
+
extracted = "".join([c for c in normalised if not unicodedata.combining(c)])
|
|
124
|
+
# Finally translate to ascii
|
|
125
|
+
return extracted.translate(_make_ascii_mappings()).encode("ascii", "ignore").decode("utf-8")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_correct_path(path):
|
|
129
|
+
"""
|
|
130
|
+
Corrects the slashes and escapes any slash characters.
|
|
131
|
+
|
|
132
|
+
Note: does not check whether the file exists.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
path (string): the file path to correct
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
string : the corrected file path
|
|
139
|
+
"""
|
|
140
|
+
# Remove any unescaped chars
|
|
141
|
+
path = _convert_to_rawstring(path)
|
|
142
|
+
# Replace '\' with '/'
|
|
143
|
+
path = path.replace("\\", "/").replace("'", "")
|
|
144
|
+
# Remove multiple slashes
|
|
145
|
+
return re.sub("/+", "/", path)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_time_delta(seconds, minutes, hours):
|
|
149
|
+
"""
|
|
150
|
+
Returns a timedelta representation of the input seconds, minutes and hours. If all parameters are None, then
|
|
151
|
+
None returned, else None parameters are interpreted as 0
|
|
152
|
+
"""
|
|
153
|
+
if all(t is None for t in (seconds, minutes, hours)):
|
|
154
|
+
return None
|
|
155
|
+
else:
|
|
156
|
+
num_seconds, num_minutes, num_hours = (
|
|
157
|
+
0 if t is None else t for t in (seconds, minutes, hours)
|
|
158
|
+
)
|
|
159
|
+
return timedelta(hours=num_hours, minutes=num_minutes, seconds=num_seconds)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _correct_path_casing_existing(path):
|
|
163
|
+
"""
|
|
164
|
+
If the file exists it get the correct path with the correct casing.
|
|
165
|
+
"""
|
|
166
|
+
if os.name == "nt":
|
|
167
|
+
try:
|
|
168
|
+
# Correct path case for windows as Python needs correct casing
|
|
169
|
+
# Windows specific stuff
|
|
170
|
+
import win32api
|
|
171
|
+
|
|
172
|
+
return win32api.GetLongPathName(win32api.GetShortPathName(path))
|
|
173
|
+
except Exception as err:
|
|
174
|
+
raise OSError("Invalid file path entered: %s" % err)
|
|
175
|
+
else:
|
|
176
|
+
# Nothing to do for unix
|
|
177
|
+
return path
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _convert_to_rawstring(data):
|
|
181
|
+
escape_dict = {
|
|
182
|
+
"\a": r"\a",
|
|
183
|
+
"\b": r"\b",
|
|
184
|
+
"\c": r"\c",
|
|
185
|
+
"\f": r"\f",
|
|
186
|
+
"\n": r"\n",
|
|
187
|
+
"\r": r"\r",
|
|
188
|
+
"\t": r"\t",
|
|
189
|
+
"\v": r"\v",
|
|
190
|
+
"'": r"\'",
|
|
191
|
+
'"': r"\"",
|
|
192
|
+
}
|
|
193
|
+
raw_string = ""
|
|
194
|
+
for char in data:
|
|
195
|
+
try:
|
|
196
|
+
raw_string += escape_dict[char]
|
|
197
|
+
except KeyError:
|
|
198
|
+
raw_string += char
|
|
199
|
+
return raw_string
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def get_correct_filepath_existing(path):
|
|
203
|
+
"""
|
|
204
|
+
Corrects the file path to make it OS independent.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
path (string): the file path to correct
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
string : the corrected file path
|
|
211
|
+
|
|
212
|
+
Raises:
|
|
213
|
+
if the directory does not exist.
|
|
214
|
+
"""
|
|
215
|
+
path = get_correct_path(path)
|
|
216
|
+
return _correct_path_casing_existing(path)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def crc8(value):
|
|
220
|
+
"""
|
|
221
|
+
Generate a CRC 8 from the value (See EPICS\\utils_win32\\master\\src\\crc8.c).
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
value: the value to generate a CRC from
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
string: representation of the CRC8 of the value; two characters
|
|
228
|
+
|
|
229
|
+
"""
|
|
230
|
+
if value == "":
|
|
231
|
+
return ""
|
|
232
|
+
|
|
233
|
+
crc_size = 8
|
|
234
|
+
maximum_crc_value = 255
|
|
235
|
+
generator = 0x07
|
|
236
|
+
|
|
237
|
+
as_bytes = value.encode("utf-8")
|
|
238
|
+
|
|
239
|
+
crc = 0 # start with 0 so first byte can be 'xored' in
|
|
240
|
+
|
|
241
|
+
for byte in as_bytes:
|
|
242
|
+
crc ^= byte # XOR-in the next input byte
|
|
243
|
+
|
|
244
|
+
for i in range(8):
|
|
245
|
+
# unlike the c code we have to artifically restrict the maximum value wherever it is caluclated
|
|
246
|
+
if (crc >> (crc_size - 1)) & maximum_crc_value != 0:
|
|
247
|
+
crc = ((crc << 1 & maximum_crc_value) ^ generator) & maximum_crc_value
|
|
248
|
+
else:
|
|
249
|
+
crc <<= 1
|
|
250
|
+
|
|
251
|
+
return "{0:02X}".format(crc)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def get_json_pv_value(pv_name, api, attempts=3):
|
|
255
|
+
"""
|
|
256
|
+
Get the pv value decompress and convert from JSON.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
pv_name: name of the pv to read
|
|
260
|
+
api: the api to use to read it
|
|
261
|
+
attempts: number of attempts to try to read PV
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
pv value as python objects
|
|
265
|
+
|
|
266
|
+
Raises:
|
|
267
|
+
PVReadException: if value can not be read
|
|
268
|
+
|
|
269
|
+
"""
|
|
270
|
+
try:
|
|
271
|
+
raw = api.get_pv_value(pv_name, to_string=True, attempts=attempts)
|
|
272
|
+
except Exception:
|
|
273
|
+
raise PVReadException("Can not read '{0}'".format(pv_name))
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
raw = dehex_and_decompress(raw)
|
|
277
|
+
except Exception:
|
|
278
|
+
raise PVReadException("Can not decompress '{0}'".format(pv_name))
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
result = json.loads(raw)
|
|
282
|
+
except Exception:
|
|
283
|
+
raise PVReadException("Can not unmarshal '{0}'".format(pv_name))
|
|
284
|
+
|
|
285
|
+
return result
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def remove_field_from_pv(pv):
|
|
289
|
+
"""
|
|
290
|
+
Given a PV, return it with any field postfixes removed.
|
|
291
|
+
|
|
292
|
+
examples:
|
|
293
|
+
IN:TEST:FIELD.RVAL -> IN:TEST:FIELD
|
|
294
|
+
IN:TEST:NOFIELD -> IN:TEST:NOFIELD
|
|
295
|
+
|
|
296
|
+
args:
|
|
297
|
+
pv (str): the pv to remove the field from
|
|
298
|
+
|
|
299
|
+
returns:
|
|
300
|
+
(str) the pv name with the field postfix removed
|
|
301
|
+
"""
|
|
302
|
+
return pv.split(".")[0] if "." in pv else pv
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def check_lowlimit_against_highlimit(lowlimit, highlimit):
|
|
306
|
+
"""
|
|
307
|
+
Check the lowlimit is below the highlimit, and warns if this is the case
|
|
308
|
+
"""
|
|
309
|
+
if lowlimit is not None and highlimit is not None and lowlimit > highlimit:
|
|
310
|
+
print(
|
|
311
|
+
"WARNING: You have set the lowlimit({}) above the highlimit({})".format(
|
|
312
|
+
lowlimit, highlimit
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def require_runstate(runstates):
|
|
318
|
+
"""
|
|
319
|
+
Decorator that checks for needed runstates.
|
|
320
|
+
If skip_required_runstates is passed in as a keyword argument to the underlying function then it will be ignore this
|
|
321
|
+
check
|
|
322
|
+
"""
|
|
323
|
+
runstates_string = ", ".join(runstates)
|
|
324
|
+
|
|
325
|
+
def _check_runstate(func):
|
|
326
|
+
@wraps(func)
|
|
327
|
+
def _wrapper(self, *args, **kwargs):
|
|
328
|
+
if not kwargs.pop("skip_required_runstates", False):
|
|
329
|
+
run_state = self.get_run_state()
|
|
330
|
+
if run_state not in set(runstates):
|
|
331
|
+
e_string = "{} can only be run in the following runstates: {}".format(
|
|
332
|
+
func.__name__, runstates_string
|
|
333
|
+
)
|
|
334
|
+
raise ValueError(e_string)
|
|
335
|
+
return func(self, *args, **kwargs)
|
|
336
|
+
|
|
337
|
+
return _wrapper
|
|
338
|
+
|
|
339
|
+
return _check_runstate
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class EnvironmentDetails(object):
|
|
343
|
+
"""
|
|
344
|
+
Details of the computer environment the code is running in.
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
# PV which holds the live instrument list
|
|
348
|
+
INSTRUMENT_LIST_PV = "CS:INSTLIST"
|
|
349
|
+
|
|
350
|
+
# List of instruments dictionary similar to CS:INSTLIST
|
|
351
|
+
DEFAULT_INST_LIST = [
|
|
352
|
+
{"name": "LARMOR"},
|
|
353
|
+
{"name": "ALF"},
|
|
354
|
+
{"name": "DEMO"},
|
|
355
|
+
{"name": "IMAT"},
|
|
356
|
+
{"name": "MUONFE"},
|
|
357
|
+
{"name": "ZOOM"},
|
|
358
|
+
{"name": "IRIS"},
|
|
359
|
+
]
|
|
360
|
+
|
|
361
|
+
def __init__(self, host_name=None):
|
|
362
|
+
"""
|
|
363
|
+
Consturctor.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
host_name: computer host name to use; None to get it from the system
|
|
367
|
+
Returns:
|
|
368
|
+
|
|
369
|
+
"""
|
|
370
|
+
import socket
|
|
371
|
+
|
|
372
|
+
if host_name is None:
|
|
373
|
+
self._host_name = socket.gethostname()
|
|
374
|
+
else:
|
|
375
|
+
self._host_name = host_name
|
|
376
|
+
|
|
377
|
+
def get_host_name(self):
|
|
378
|
+
"""
|
|
379
|
+
Gets the name of the computer.
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
the host name of the computer
|
|
383
|
+
"""
|
|
384
|
+
return self._host_name
|
|
385
|
+
|
|
386
|
+
def get_instrument_list(self, api):
|
|
387
|
+
"""
|
|
388
|
+
Get the instrument list.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
api: api to use to get a pv value
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
the current instrument list
|
|
395
|
+
"""
|
|
396
|
+
try:
|
|
397
|
+
return get_json_pv_value(self.INSTRUMENT_LIST_PV, api, attempts=1)
|
|
398
|
+
except PVReadException as ex:
|
|
399
|
+
print("Error: {!r}. Using internal instrument list.".format(ex))
|
|
400
|
+
return self.DEFAULT_INST_LIST
|
|
401
|
+
|
|
402
|
+
def get_settings_directory(self):
|
|
403
|
+
default_directory = "C:/Instrument/Settings/config/{}/configurations".format(
|
|
404
|
+
self._host_name
|
|
405
|
+
)
|
|
406
|
+
return os.environ.get("ICPCONFIGROOT", default_directory)
|
genie_python/version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "0.0.0.qualifier"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, ISIS Experiment Controls Computing
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: genie_python
|
|
3
|
+
Version: 15.1.0rc1
|
|
4
|
+
Summary: Instrument control & scripting for the ISIS Neutron & Muon source
|
|
5
|
+
Author-email: ISIS Experiment Controls <ISISExperimentControls@stfc.ac.uk>
|
|
6
|
+
Maintainer-email: ISIS Experiment Controls <ISISExperimentControls@stfc.ac.uk>
|
|
7
|
+
License: BSD 3-Clause License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2024, ISIS Experiment Controls Computing
|
|
10
|
+
|
|
11
|
+
Redistribution and use in source and binary forms, with or without
|
|
12
|
+
modification, are permitted provided that the following conditions are met:
|
|
13
|
+
|
|
14
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
15
|
+
list of conditions and the following disclaimer.
|
|
16
|
+
|
|
17
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
18
|
+
this list of conditions and the following disclaimer in the documentation
|
|
19
|
+
and/or other materials provided with the distribution.
|
|
20
|
+
|
|
21
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
22
|
+
contributors may be used to endorse or promote products derived from
|
|
23
|
+
this software without specific prior written permission.
|
|
24
|
+
|
|
25
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
26
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
27
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
28
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
29
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
30
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
31
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
32
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
33
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
34
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35
|
+
|
|
36
|
+
Project-URL: Homepage, https://github.com/isiscomputinggroup/genie
|
|
37
|
+
Project-URL: Bug Reports, https://github.com/isiscomputinggroup/genie/issues
|
|
38
|
+
Project-URL: Source, https://github.com/isiscomputinggroup/genie
|
|
39
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
40
|
+
Classifier: Intended Audience :: Developers
|
|
41
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
43
|
+
Requires-Python: >=3.11
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
License-File: LICENSE
|
|
46
|
+
Requires-Dist: CaChannel
|
|
47
|
+
Requires-Dist: caffi
|
|
48
|
+
Requires-Dist: graypy
|
|
49
|
+
Requires-Dist: ipython
|
|
50
|
+
Requires-Dist: mysql-connector-python
|
|
51
|
+
Requires-Dist: numpy
|
|
52
|
+
Requires-Dist: p4p
|
|
53
|
+
Requires-Dist: psutil
|
|
54
|
+
Requires-Dist: pylint
|
|
55
|
+
Requires-Dist: pyright
|
|
56
|
+
Requires-Dist: pywin32 ; platform_system == "Windows"
|
|
57
|
+
Provides-Extra: dev
|
|
58
|
+
Requires-Dist: genie-python[doc,plot] ; extra == 'dev'
|
|
59
|
+
Requires-Dist: mock ; extra == 'dev'
|
|
60
|
+
Requires-Dist: parameterized ; extra == 'dev'
|
|
61
|
+
Requires-Dist: pyhamcrest ; extra == 'dev'
|
|
62
|
+
Requires-Dist: pytest ; extra == 'dev'
|
|
63
|
+
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
64
|
+
Requires-Dist: ruff >=0.6 ; extra == 'dev'
|
|
65
|
+
Provides-Extra: doc
|
|
66
|
+
Requires-Dist: sphinx ; extra == 'doc'
|
|
67
|
+
Requires-Dist: sphinx-rtd-theme ; extra == 'doc'
|
|
68
|
+
Requires-Dist: myst-parser ; extra == 'doc'
|
|
69
|
+
Requires-Dist: sphinx-autobuild ; extra == 'doc'
|
|
70
|
+
Provides-Extra: plot
|
|
71
|
+
Requires-Dist: matplotlib ==3.9.2 ; extra == 'plot'
|
|
72
|
+
Requires-Dist: py4j ; extra == 'plot'
|
|
73
|
+
Requires-Dist: tornado ; extra == 'plot'
|
|
74
|
+
|
|
75
|
+
# genie_python
|
|
76
|
+
|
|
77
|
+
The ISIS Python-based instrument control and scripting library.
|
|
78
|
+
|
|
79
|
+
## Instrument initialisation
|
|
80
|
+
|
|
81
|
+
By default when setting an instrument the init_default.py file is loaded.
|
|
82
|
+
This file checks for the existence of a folder called C:\Instrument\Settings\config\NDX%INSTNAME%\Python and adds this to the sys path if it does.
|
|
83
|
+
If this path exists and contains a file called init_%INSTNAME%.py, it will load it too.
|
|
84
|
+
|
|
85
|
+
On the NDX any files in C:\Instrument\Settings\config\NDX%INSTNAME%\Python can be added to SVN for safe keeping.
|
|
86
|
+
|
|
87
|
+
Python modules can be imported directly from the C:\Instrument\Settings\config\NDX%INSTNAME%\Python directory. If running on a client it is necessary to have a copy of the Python directory for the instrument being connected to in the correct location.
|
|
88
|
+
|
|
89
|
+
Folders inside the Python directory must have a `__init__.py` file for them to be available to be imported.
|
|
90
|
+
|
|
91
|
+
## Start-up
|
|
92
|
+
The line "from genie_python import *" in genie_startup is responsible for loading all the genie_python stuff!
|
|
93
|
+
This file also contains code for disabling quickedit and for making genie_python guess the instrument name.
|
|
94
|
+
|
|
95
|
+
As genie_python is running inside IPython we use c.TerminalIPythonApp.exec_files to run genie_start.py, so everything is imported correctly.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
genie_python/.pylintrc,sha256=_DRngouR1JvKbizT3AynF80UTgiXcBkqQ4ogCXNli5o,16340
|
|
2
|
+
genie_python/__init__.py,sha256=_6EmvSY6Mgea6kqFt6iavz4NDcUtXrozECUGUGAjcG0,41
|
|
3
|
+
genie_python/block_names.py,sha256=-1IolEpAq3XFfesNqnsp50UGEQPHI4N7cU7CYRrIhsU,5044
|
|
4
|
+
genie_python/channel_access_exceptions.py,sha256=5H_DC68sdLOVG0xSOpp1g3vrj7Jt9O5ftD4na3zJY-A,1271
|
|
5
|
+
genie_python/genie.py,sha256=DCRXo1_L-8JaO7XiOWi6sQxLeScd2ycFSrbr5bnbQ2U,74834
|
|
6
|
+
genie_python/genie_advanced.py,sha256=DzVPlAM3Dfr7WlAJo6SVk00XzADVlntHcgllBjreET0,11863
|
|
7
|
+
genie_python/genie_alerts.py,sha256=We2o_iSnhO0SWpaG1M-0qyikioAATFJBmyzFVXXzHng,6514
|
|
8
|
+
genie_python/genie_api_setup.py,sha256=OKGPRjprrbBvYgAO4W4r7Wu6K8lTr6-CH5YaFMGa52o,15882
|
|
9
|
+
genie_python/genie_blockserver.py,sha256=FB85zjQ1rHs5sD4cvopNdFrFwG9fnqmXP56uWoEkj5U,2267
|
|
10
|
+
genie_python/genie_cachannel_wrapper.py,sha256=gApFfCC1zsoYv92gSzRleQTi3WsjjTlB41Sb6SNAuOI,19678
|
|
11
|
+
genie_python/genie_change_cache.py,sha256=IcUpAGNE6ajrlQtLwOXX_02LLOg1IHooQZ_962kLt-4,6021
|
|
12
|
+
genie_python/genie_dae.py,sha256=LA5jzOseNSTM0MQWZ2I83p2O-A7kGiFkW6qka2RY1bY,77953
|
|
13
|
+
genie_python/genie_epics_api.py,sha256=HIR7QdW4KllWWmgljBcSrABr4a52NTNcH2nKCc06bA0,33199
|
|
14
|
+
genie_python/genie_experimental_data.py,sha256=aJsRHgtO2tENfBwnKc0OkND1MEhmTp_kHCPUp8TVL9I,6604
|
|
15
|
+
genie_python/genie_logging.py,sha256=lxobLji-RSXw0onMiSdXs9vQtNdqCBBQPhr85370jjc,6523
|
|
16
|
+
genie_python/genie_p4p_wrapper.py,sha256=eDgfspZ9K9qQUrGkLTCg0jY4TDzKjrt7TsDWBzEQtvU,6921
|
|
17
|
+
genie_python/genie_plot.py,sha256=K0IQIiNGzRImsXqNVd2TxB-uOWUDYL9VHSZEmTVe8oE,2392
|
|
18
|
+
genie_python/genie_pre_post_cmd_manager.py,sha256=fZAHHe9Ay_sG7fKYUSk46v2MXu3ity87tEFJxFcKRhA,787
|
|
19
|
+
genie_python/genie_pv_connection_protocol.py,sha256=f0IzmsBzOCZI47Wn6fgn8fa6R579L6k6CKQggLkOBxo,1048
|
|
20
|
+
genie_python/genie_script_checker.py,sha256=KCzf0OWsonmyIMZGrtWb-FLdhA5tyf4KiJ0HrKOXrB4,19334
|
|
21
|
+
genie_python/genie_script_generator.py,sha256=6J5rujmZ9GWMZ7w76U_Wh7mTnhqkjMrbR70xi_SePaQ,8927
|
|
22
|
+
genie_python/genie_simulate.py,sha256=yoFNnlmEveKaMr4WoY0y2WhlUbjRB8FXlW3bbxPg_5E,2704
|
|
23
|
+
genie_python/genie_simulate_impl.py,sha256=9OJmSPUqUw9Y1R0orobtjn48jQqIJV46zNOt6wcz2XI,44639
|
|
24
|
+
genie_python/genie_startup.py,sha256=AMGU37NYUI3DgffmialtYtzzgHZr-MKfE5KJor63Etk,1023
|
|
25
|
+
genie_python/genie_toggle_settings.py,sha256=jrbFMIS46n4dKrhUqpPlnhiRbyHxKvvndthFoT5ED2w,1771
|
|
26
|
+
genie_python/genie_wait_for_move.py,sha256=_Ihk0vQ2knwuBXcC5FNpbo0beMNO289vn3vqpCt0fYo,6358
|
|
27
|
+
genie_python/genie_waitfor.py,sha256=YtqyNeOe9pKOzKmkMqod5yMPlJRVvsgAZSAjKv9qnGo,22653
|
|
28
|
+
genie_python/mysql_abstraction_layer.py,sha256=1UllAEoCz_Yb6zSuDvIDjfi3wYH7Ep6s00e9niTwNeg,9818
|
|
29
|
+
genie_python/run_tests.py,sha256=VuKtVDdB0mx1doyX0-KEwpZr6oCRIGwE5B_jYCZ_NnY,2009
|
|
30
|
+
genie_python/scanning_instrument_pylint_plugin.py,sha256=rJ2Ss4k3O6GQW-fO80qh7HGlEVeN2rSA1ioMETusOos,1174
|
|
31
|
+
genie_python/utilities.py,sha256=_Dn_ieMOU7lxRDuZuy4V3OKMT0d1nS5O8UWKcPqHV18,11181
|
|
32
|
+
genie_python/version.py,sha256=foZqjv1s035uLKjlG_5PkcUFysvR2T31SGW0M_xeo48,28
|
|
33
|
+
genie_python/matplotlib_backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
genie_python/matplotlib_backend/ibex_websocket_backend.py,sha256=tvCffygzcay8Y2B6Wxlm-zy59o0snCPOfAtGL7N1LpA,12450
|
|
35
|
+
genie_python/typings/CaChannel/CaChannel.pyi,sha256=yvNHOtZZzfrrOI2ggxkUySXl889xoVerjPt-tfPD64U,42021
|
|
36
|
+
genie_python/typings/CaChannel/__init__.pyi,sha256=XVuVzAQJF8WSbzf0Q1pOOsEX6tAVBB7cjFtwqh-35aw,172
|
|
37
|
+
genie_python/typings/CaChannel/_version.pyi,sha256=g-keGfiquGWTLjf9HR3zCWszAtz6CrMcGT3otGJ6Cb0,92
|
|
38
|
+
genie_python/typings/CaChannel/ca.pyi,sha256=mAAbTk3rAC91ny7x128TaS6-7_nkPCd1YOpvQj-kLRM,665
|
|
39
|
+
genie_python-15.1.0rc1.dist-info/LICENSE,sha256=cASiBLOac74VMKBPq44ijTeGntmlx75qnUavxbZtqN4,1521
|
|
40
|
+
genie_python-15.1.0rc1.dist-info/METADATA,sha256=2mvjmr2NOjeDJLIwibt5xHnopJLdN9GcmlmUgPV3vZk,4781
|
|
41
|
+
genie_python-15.1.0rc1.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
|
42
|
+
genie_python-15.1.0rc1.dist-info/top_level.txt,sha256=VdV_RRsxfn--QiaIBZzB5AGVWSkb1LDUtu3b85qTN5s,13
|
|
43
|
+
genie_python-15.1.0rc1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
genie_python
|