mosca-client 2.0.1__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.
- mosca_client/__init__.py +0 -0
- mosca_client/tango/__init__.py +0 -0
- mosca_client/tango/devices/__init__.py +0 -0
- mosca_client/tango/devices/dante.py +116 -0
- mosca_client/tango/devices/danteplus.py +8 -0
- mosca_client/tango/devices/dummy.py +9 -0
- mosca_client/tango/devices/falconx.py +71 -0
- mosca_client/tango/devices/generator.py +58 -0
- mosca_client/tango/devices/simdante.py +9 -0
- mosca_client/tango/devices/simfalconx.py +9 -0
- mosca_client/tango/devices/simulation.py +18 -0
- mosca_client/tango/devices/xia.py +66 -0
- mosca_client/tango/mca.py +96 -0
- mosca_client/tango/mosca.py +639 -0
- mosca_client/tango/rois.py +262 -0
- mosca_client/tango/saving.py +135 -0
- mosca_client/tango/utils.py +78 -0
- mosca_client-2.0.1.dist-info/METADATA +8 -0
- mosca_client-2.0.1.dist-info/RECORD +20 -0
- mosca_client-2.0.1.dist-info/WHEEL +4 -0
mosca_client/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from ..mca import McaBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# from .base import McaController, TriggerMode
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Dante(McaBase):
|
|
11
|
+
"""XGLab's Dante mosca 2 client."""
|
|
12
|
+
|
|
13
|
+
# Bounds for config_file's wait loop, in seconds.
|
|
14
|
+
CONFIG_LOAD_START_WAIT = 5.
|
|
15
|
+
CONFIG_LOAD_TIMEOUT = 60.
|
|
16
|
+
|
|
17
|
+
def __init__(self, *args, **kwargs):
|
|
18
|
+
super().__init__(*args, **kwargs)
|
|
19
|
+
|
|
20
|
+
def __info__(self) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Add specific information for XIA controllers.
|
|
23
|
+
"""
|
|
24
|
+
txt = super().__info__()
|
|
25
|
+
txt += f"\n * config file: {self.config_file}\n"
|
|
26
|
+
txt += f" * gating mode: {self.gating_mode}\n"
|
|
27
|
+
return txt
|
|
28
|
+
|
|
29
|
+
# def _apply_settings(self, config_file=None, **kwargs):
|
|
30
|
+
# if config_file is not None:
|
|
31
|
+
# self.config_file = config_file
|
|
32
|
+
# return super()._apply_settings(**kwargs)
|
|
33
|
+
|
|
34
|
+
def stats_mapping(self):
|
|
35
|
+
return {
|
|
36
|
+
"realtime": "elapsed_time",
|
|
37
|
+
"trigger_livetime": "trigger_livetime",
|
|
38
|
+
"icr": "icr",
|
|
39
|
+
"ocr": "ocr",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def gating_mode(self):
|
|
44
|
+
return self.proxy.gating_mode
|
|
45
|
+
|
|
46
|
+
def available_configurations(self):
|
|
47
|
+
return self.proxy.available_configurations
|
|
48
|
+
|
|
49
|
+
def _load_configuration(self, configuration: str | None = None) -> None:
|
|
50
|
+
proxy = self.proxy
|
|
51
|
+
print(f"Setting config file to {configuration}. This may take a while.")
|
|
52
|
+
proxy.set_configuration_file_async(configuration)
|
|
53
|
+
|
|
54
|
+
# Wait for the server to actually start processing the request
|
|
55
|
+
# (mca_status flips to UNINITIALIZED) instead of blindly sleeping
|
|
56
|
+
# for a fixed amount of time -- avoids racing against slow thread
|
|
57
|
+
# scheduling on the server side, which could otherwise let a stale
|
|
58
|
+
# prior status (e.g. READY) slip past the checks below undetected.
|
|
59
|
+
start_deadline = time.time() + self.CONFIG_LOAD_START_WAIT
|
|
60
|
+
while proxy.mca_status != "UNINITIALIZED" and time.time() < start_deadline:
|
|
61
|
+
time.sleep(0.1)
|
|
62
|
+
|
|
63
|
+
if proxy.mca_status != "UNINITIALIZED":
|
|
64
|
+
raise RuntimeError(
|
|
65
|
+
f"Server did not start processing config [{configuration}] "
|
|
66
|
+
f"(mca_status still {proxy.mca_status!r} after "
|
|
67
|
+
f"{self.CONFIG_LOAD_START_WAIT}s)."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
deadline = time.time() + self.CONFIG_LOAD_TIMEOUT
|
|
71
|
+
spin = ["\\", "-", "/", "-"]
|
|
72
|
+
i = 0
|
|
73
|
+
while proxy.mca_status == "UNINITIALIZED":
|
|
74
|
+
if time.time() > deadline:
|
|
75
|
+
raise RuntimeError(
|
|
76
|
+
f"Timed out waiting for config [{configuration}] to load "
|
|
77
|
+
f"(still UNINITIALIZED after {self.CONFIG_LOAD_TIMEOUT}s)."
|
|
78
|
+
)
|
|
79
|
+
init_msg = proxy.init_message
|
|
80
|
+
print(f"\033[KIn progress [{spin[i%4]}] {init_msg} ", end="\r")
|
|
81
|
+
time.sleep(0.3)
|
|
82
|
+
i = (i + 1) % 4
|
|
83
|
+
|
|
84
|
+
init_msg = proxy.init_message
|
|
85
|
+
print(f"\033[K{init_msg}")
|
|
86
|
+
if proxy.mca_status != "READY":
|
|
87
|
+
raise RuntimeError(
|
|
88
|
+
f"Failed to load config [{configuration}]. Please check server logs."
|
|
89
|
+
)
|
|
90
|
+
return self.current_configuration()
|
|
91
|
+
|
|
92
|
+
def current_configuration(self):
|
|
93
|
+
return self.proxy.configuration_file
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def config_files_list(self):
|
|
97
|
+
# legacy code
|
|
98
|
+
return self.available_configurations()
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def config_file(self):
|
|
102
|
+
# legacy code
|
|
103
|
+
return self.current_configuration()
|
|
104
|
+
|
|
105
|
+
@config_file.setter
|
|
106
|
+
def config_file(self, config_file):
|
|
107
|
+
# legacy code
|
|
108
|
+
self.load_configuration(config_file)
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def config_dir(self):
|
|
112
|
+
# legacy code
|
|
113
|
+
return self.proxy.configuration_directory
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
Mca = Dante
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from .xia import Xia
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FalconX(Xia):
|
|
7
|
+
def __info__(self) -> str:
|
|
8
|
+
txt = super().__info__()
|
|
9
|
+
txt += f" * mca_refresh: {self.proxy.mca_refresh:.4f} s\n"
|
|
10
|
+
return txt
|
|
11
|
+
|
|
12
|
+
def scan_metadata(self) -> dict:
|
|
13
|
+
metadata = super().scan_metadata()
|
|
14
|
+
metadata["mca_refresh"] = self.proxy.mca_refresh
|
|
15
|
+
return metadata
|
|
16
|
+
|
|
17
|
+
def filter_parameters(self,
|
|
18
|
+
acq_realtime=None,
|
|
19
|
+
block_size=None,
|
|
20
|
+
trigger_mode=None,
|
|
21
|
+
mca_refresh=None,
|
|
22
|
+
**kwargs):
|
|
23
|
+
""" Computes mca_refresh and block_size if not provided.
|
|
24
|
+
* mca_refresh: 0.01 <= acq_realtime or 0.1 <= 0.1
|
|
25
|
+
* block_size: 1 <= 1 / acq_realtime <= 1020
|
|
26
|
+
Attention: block_size is not changed if acq_realtime
|
|
27
|
+
is None or nul!
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
params = super().filter_parameters(**kwargs)
|
|
31
|
+
|
|
32
|
+
acq_realtime = params.get("acq_realtime", acq_realtime)
|
|
33
|
+
block_size = params.get("block_size", block_size)
|
|
34
|
+
trigger_mode = params.get("trigger_mode", trigger_mode)
|
|
35
|
+
mca_refresh = params.get("mca_refresh", mca_refresh)
|
|
36
|
+
|
|
37
|
+
if trigger_mode == "INT_TRIG_READOUT":
|
|
38
|
+
params["acq_nb_points"] = 1
|
|
39
|
+
|
|
40
|
+
if mca_refresh is None:
|
|
41
|
+
mca_refresh = np.clip(acq_realtime or 0.1,
|
|
42
|
+
0.01, 0.1)
|
|
43
|
+
params["mca_refresh"] = float(mca_refresh)
|
|
44
|
+
|
|
45
|
+
if block_size is None and acq_realtime:
|
|
46
|
+
if trigger_mode in ("GATE", "EXT_TRIG"):
|
|
47
|
+
# aiming for a buffer swap every second
|
|
48
|
+
block_size = np.clip(
|
|
49
|
+
1. / acq_realtime,
|
|
50
|
+
1, 1020)
|
|
51
|
+
params["block_size"] = int(block_size)
|
|
52
|
+
|
|
53
|
+
return params
|
|
54
|
+
|
|
55
|
+
def set_parameters(self,
|
|
56
|
+
mca_refresh=None,
|
|
57
|
+
**kwargs) -> None:
|
|
58
|
+
super().set_parameters(**kwargs)
|
|
59
|
+
if mca_refresh is not None:
|
|
60
|
+
self.proxy.mca_refresh = mca_refresh
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def mca_refresh(self) -> float:
|
|
64
|
+
return self.proxy.mca_refresh
|
|
65
|
+
|
|
66
|
+
@mca_refresh.setter
|
|
67
|
+
def mca_refresh(self, value: float) -> None:
|
|
68
|
+
self.proxy.mca_refresh = value
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
Mca = FalconX
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from tango import DeviceProxy
|
|
2
|
+
|
|
3
|
+
from ..utils import is_ds_exported, Callback
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Generator:
|
|
7
|
+
def __init__(self, name, tg_url):
|
|
8
|
+
self._name = name
|
|
9
|
+
|
|
10
|
+
self._tg_url = tg_url
|
|
11
|
+
self._proxy = None
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def proxy(self):
|
|
15
|
+
if self._proxy is None:
|
|
16
|
+
if not is_ds_exported(self._tg_url):
|
|
17
|
+
raise RuntimeError(f"Server {self._tg_url} doesn't seem to be running.")
|
|
18
|
+
self._proxy = DeviceProxy(self._tg_url)
|
|
19
|
+
return self._proxy
|
|
20
|
+
|
|
21
|
+
# TODO: put this in bliss
|
|
22
|
+
def __info__(self):
|
|
23
|
+
return f"Generator {self.__class__.__name__} ({self.proxy.name()})"
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def acq_nb_points(self):
|
|
27
|
+
return self.proxy.acq_nb_points
|
|
28
|
+
|
|
29
|
+
@acq_nb_points.setter
|
|
30
|
+
def acq_nb_points(self, acq_nb_points):
|
|
31
|
+
self.proxy.acq_nb_points = acq_nb_points
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def trigger_mode(self):
|
|
35
|
+
return self.proxy.trigger_mode
|
|
36
|
+
|
|
37
|
+
@trigger_mode.setter
|
|
38
|
+
def trigger_mode(self, trigger_mode):
|
|
39
|
+
self.proxy.trigger_mode = trigger_mode
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def acq_realtime(self):
|
|
43
|
+
return self.proxy.acq_realtime
|
|
44
|
+
|
|
45
|
+
@acq_realtime.setter
|
|
46
|
+
def acq_realtime(self, acq_realtime):
|
|
47
|
+
self.proxy.acq_realtime = acq_realtime
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def use_external_trigger(self):
|
|
51
|
+
return self.proxy.use_external_trigger
|
|
52
|
+
|
|
53
|
+
@use_external_trigger.setter
|
|
54
|
+
def use_external_trigger(self, use_external_trigger):
|
|
55
|
+
self.proxy.use_external_trigger = use_external_trigger
|
|
56
|
+
|
|
57
|
+
def hw_trig(self):
|
|
58
|
+
self.proxy.hw_trig()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from tango import DeviceProxy
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SimulatorMixin:
|
|
5
|
+
def __init__(self, *args, **kwargs):
|
|
6
|
+
self._generator = None
|
|
7
|
+
super().__init__(*args, **kwargs)
|
|
8
|
+
|
|
9
|
+
def generator(self):
|
|
10
|
+
if self._generator is None:
|
|
11
|
+
# keep everything before "family/instance" as-is, so this
|
|
12
|
+
# also works with a fully-qualified tango://host:port/... URL
|
|
13
|
+
parts = self.tg_url.split("/")
|
|
14
|
+
prefix = "/".join(parts[:-2])
|
|
15
|
+
instance = parts[-1]
|
|
16
|
+
tg_name = f"{prefix}/generator/{instance}"
|
|
17
|
+
self._generator = DeviceProxy(tg_name)
|
|
18
|
+
return self._generator
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from ..mca import McaBase
|
|
2
|
+
from ..utils import ChangeTangoTimeout
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Xia(McaBase):
|
|
6
|
+
|
|
7
|
+
def __init__(self, *args, **kwargs):
|
|
8
|
+
super().__init__(*args, **kwargs)
|
|
9
|
+
|
|
10
|
+
def stats_mapping(self):
|
|
11
|
+
return {
|
|
12
|
+
# MOSCA : NxWriter
|
|
13
|
+
"output": "events",
|
|
14
|
+
"icr": "icr",
|
|
15
|
+
"ocr": "ocr",
|
|
16
|
+
"livetime": "trigger_livetime",
|
|
17
|
+
"deadtime": "deadtime",
|
|
18
|
+
"realtime": "realtime",
|
|
19
|
+
"triggers": "triggers",
|
|
20
|
+
"livetime_events": "energy_livetime",
|
|
21
|
+
"deadtime_correction": "deadtime_correction",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# def _apply_settings(self, ini_file=None, **kwargs):
|
|
25
|
+
# if ini_file:
|
|
26
|
+
# self.configuration_file = ini_file
|
|
27
|
+
# return super()._apply_settings(**kwargs)
|
|
28
|
+
|
|
29
|
+
def __info__(self) -> str:
|
|
30
|
+
txt = super().__info__()
|
|
31
|
+
txt += f"\n * ini file: {self.proxy.ini_file}\n"
|
|
32
|
+
txt += f" * gate ignore: {self.proxy.gate_ignore}\n"
|
|
33
|
+
txt += f" * pix per buf: {self.proxy.pixels_per_buffer}\n"
|
|
34
|
+
txt += f" * preset type: {self.proxy.preset_type}\n"
|
|
35
|
+
txt += f" * preset value: {self.proxy.preset_value}\n"
|
|
36
|
+
return txt
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def configuration_file(self) -> str:
|
|
40
|
+
return self.current_configuration()
|
|
41
|
+
|
|
42
|
+
@configuration_file.setter
|
|
43
|
+
def configuration_file(self, fname: str) -> None:
|
|
44
|
+
self.load_configuration(fname)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def configuration_dir(self) -> str:
|
|
48
|
+
return self.proxy.ini_dir
|
|
49
|
+
|
|
50
|
+
def available_configurations(self) -> list[str]:
|
|
51
|
+
return self.proxy.available_ini_files()
|
|
52
|
+
|
|
53
|
+
def _load_configuration(self, configuration: str | None = None) -> None:
|
|
54
|
+
try:
|
|
55
|
+
with ChangeTangoTimeout(self.proxy, 10):
|
|
56
|
+
print(f"Loading {configuration}...")
|
|
57
|
+
if configuration is None:
|
|
58
|
+
configuration = ""
|
|
59
|
+
self.proxy.ini_file = configuration
|
|
60
|
+
except Exception as ex:
|
|
61
|
+
# TODO: report server msg
|
|
62
|
+
raise RuntimeError(f"Loading configuration file '{configuration}' has failed.") from ex
|
|
63
|
+
return self.current_configuration()
|
|
64
|
+
|
|
65
|
+
def current_configuration(self):
|
|
66
|
+
return self.proxy.ini_file
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from tango import DeviceProxy
|
|
2
|
+
|
|
3
|
+
from .utils import is_ds_exported, Callback
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class McaBase:
|
|
7
|
+
|
|
8
|
+
def __init__(self, name, tg_url, on_change_cb=None, **kwargs):
|
|
9
|
+
self._name = name
|
|
10
|
+
|
|
11
|
+
# settings_name = f"mosca:{name}:{self.__class__.__name__}"
|
|
12
|
+
# self._settings2 = HashObjSetting(settings_name)
|
|
13
|
+
self._settings = kwargs.get("settings", {})
|
|
14
|
+
|
|
15
|
+
self._add_setting("configuration", None)
|
|
16
|
+
|
|
17
|
+
self._tg_url = tg_url
|
|
18
|
+
self._proxy = None
|
|
19
|
+
self._on_change_cb = Callback(on_change_cb, blocked=True)
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def mca_status(self):
|
|
23
|
+
return self.proxy.mca_status
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def tg_url(self):
|
|
27
|
+
return self._tg_url
|
|
28
|
+
|
|
29
|
+
def stats_mapping(self):
|
|
30
|
+
return {}
|
|
31
|
+
|
|
32
|
+
def changed(self):
|
|
33
|
+
self._on_change_cb()
|
|
34
|
+
|
|
35
|
+
def _add_setting(self, key, value=None):
|
|
36
|
+
if key not in self._settings:
|
|
37
|
+
self._settings[key] = value
|
|
38
|
+
|
|
39
|
+
def _set_settings(self, **kwargs):
|
|
40
|
+
for key, value in kwargs.items():
|
|
41
|
+
self._settings[key] = value
|
|
42
|
+
|
|
43
|
+
def initialize(self, load_configuration=True):
|
|
44
|
+
self._on_change_cb.blocked = True
|
|
45
|
+
# iterating on KEYS, because ITEMS doesn't work (returns #ERR)
|
|
46
|
+
settings = {key: (self._settings[key] if load_configuration or key != "configuration" else None)
|
|
47
|
+
for key in self._settings.keys()}
|
|
48
|
+
try:
|
|
49
|
+
self._apply_settings(**settings)
|
|
50
|
+
finally:
|
|
51
|
+
self._on_change_cb.blocked = False
|
|
52
|
+
|
|
53
|
+
def _apply_settings(self, configuration=None, **kwargs):
|
|
54
|
+
if configuration is not None:
|
|
55
|
+
self.load_configuration(configuration)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def proxy(self):
|
|
59
|
+
if self._proxy is None:
|
|
60
|
+
if not is_ds_exported(self._tg_url):
|
|
61
|
+
raise RuntimeError(f"Server {self._tg_url} doesn't seem to be running.")
|
|
62
|
+
self._proxy = DeviceProxy(self._tg_url)
|
|
63
|
+
return self._proxy
|
|
64
|
+
|
|
65
|
+
# TODO: put this in bliss
|
|
66
|
+
def __info__(self):
|
|
67
|
+
return f"MCA {self.__class__.__name__} ({self.proxy.name()})"
|
|
68
|
+
|
|
69
|
+
def load_configuration(self, *args, **kwargs):
|
|
70
|
+
reply = None
|
|
71
|
+
try:
|
|
72
|
+
reply = self._load_configuration(*args, **kwargs)
|
|
73
|
+
except NotImplementedError:
|
|
74
|
+
print(f"MCA doesn't implement load_configuration.")
|
|
75
|
+
else:
|
|
76
|
+
self._set_settings(configuration=reply)
|
|
77
|
+
self.initialize(load_configuration=False)
|
|
78
|
+
self.changed()
|
|
79
|
+
return reply
|
|
80
|
+
|
|
81
|
+
# To be overridden, if necessary
|
|
82
|
+
|
|
83
|
+
def _load_configuration(self, *args, **kwargs):
|
|
84
|
+
raise NotImplementedError
|
|
85
|
+
|
|
86
|
+
def filter_parameters(self, **kwargs):
|
|
87
|
+
return {}
|
|
88
|
+
|
|
89
|
+
def set_parameters(self, **kwargs):
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
def available_configurations(self, *args, **kwargs):
|
|
93
|
+
return []
|
|
94
|
+
|
|
95
|
+
def current_configuration(self, *args, **kwargs):
|
|
96
|
+
return "N/A"
|