lima2.client 1.0.9__tar.gz → 2.0rc2__tar.gz
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.
- {lima2_client-1.0.9/src/lima2.client.egg-info → lima2_client-2.0rc2}/PKG-INFO +37 -7
- lima2_client-2.0rc2/README.md +43 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/setup.cfg +21 -4
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/__init__.py +6 -3
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/channel.py +1 -1
- lima2_client-2.0rc2/src/lima2/client/client.py +276 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/convert.py +1 -1
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/detector.py +31 -199
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/devencoded/__init__.py +1 -1
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/devencoded/dense_frame.py +9 -4
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/devencoded/exception.py +1 -1
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/devencoded/sparse_frame.py +8 -7
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/devencoded/sparse_frame_with_background.py +8 -6
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/devencoded/structured_array.py +2 -2
- lima2_client-2.0rc2/src/lima2/client/pipeline.py +555 -0
- lima2_client-2.0rc2/src/lima2/client/pipelines/__init__.py +46 -0
- lima2_client-2.0rc2/src/lima2/client/pipelines/legacy.py +56 -0
- lima2_client-2.0rc2/src/lima2/client/pipelines/smx.py +120 -0
- lima2_client-2.0rc2/src/lima2/client/pipelines/xpcs.py +127 -0
- lima2_client-2.0rc2/src/lima2/client/progress_counter.py +69 -0
- lima2_client-2.0rc2/src/lima2/client/py.typed +0 -0
- lima2_client-2.0rc2/src/lima2/client/shell.py +150 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/client/state_machine.py +33 -25
- lima2_client-2.0rc2/src/lima2/client/topology.py +162 -0
- lima2_client-2.0rc2/src/lima2/client/utils.py +49 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/server/__init__.py +1 -1
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/server/control.py +28 -12
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/server/receiver.py +56 -12
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2/server/timer.py +1 -1
- {lima2_client-1.0.9 → lima2_client-2.0rc2/src/lima2.client.egg-info}/PKG-INFO +37 -7
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2.client.egg-info/SOURCES.txt +15 -4
- lima2_client-2.0rc2/src/lima2.client.egg-info/entry_points.txt +2 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2.client.egg-info/requires.txt +6 -0
- lima2_client-2.0rc2/tests/test_client.py +162 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/tests/test_detector.py +3 -30
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/tests/test_import.py +1 -1
- lima2_client-2.0rc2/tests/test_pipeline.py +486 -0
- lima2_client-2.0rc2/tests/test_pipelines.py +20 -0
- lima2_client-2.0rc2/tests/test_progress_counter.py +35 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/tests/test_state_machine.py +3 -3
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/tests/test_timer.py +1 -1
- lima2_client-2.0rc2/tests/test_topology.py +145 -0
- lima2_client-2.0rc2/tests/test_utils.py +30 -0
- lima2_client-1.0.9/README.md +0 -18
- lima2_client-1.0.9/src/lima2/client/cli.py +0 -321
- lima2_client-1.0.9/src/lima2/client/pipelines/__init__.py +0 -6
- lima2_client-1.0.9/src/lima2/client/pipelines/legacy.py +0 -111
- lima2_client-1.0.9/src/lima2/client/pipelines/smx.py +0 -103
- lima2_client-1.0.9/src/lima2/client/pipelines/xpcs.py +0 -137
- lima2_client-1.0.9/src/lima2/client/processing.py +0 -180
- lima2_client-1.0.9/src/lima2.client.egg-info/entry_points.txt +0 -2
- /lima2_client-1.0.9/LICENCE → /lima2_client-2.0rc2/LICENSE +0 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/pyproject.toml +0 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2.client.egg-info/dependency_links.txt +0 -0
- {lima2_client-1.0.9 → lima2_client-2.0rc2}/src/lima2.client.egg-info/top_level.txt +0 -0
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: lima2.client
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0rc2
|
|
4
4
|
Summary: Python client library for Lima2 detector
|
|
5
|
-
Home-page: https://gitlab.esrf.fr/limagroup/
|
|
5
|
+
Home-page: https://gitlab.esrf.fr/limagroup/lima2-client
|
|
6
6
|
Author: BCU (ESRF)
|
|
7
|
-
License:
|
|
7
|
+
License: MIT
|
|
8
8
|
Classifier: Intended Audience :: Science/Research
|
|
9
|
-
Classifier: License :: OSI Approved ::
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
License-File:
|
|
12
|
+
License-File: LICENSE
|
|
13
13
|
Requires-Dist: pytango
|
|
14
14
|
Requires-Dist: gevent
|
|
15
15
|
Requires-Dist: jsonschema
|
|
16
16
|
Requires-Dist: jsonschema-default
|
|
17
17
|
Requires-Dist: finite-state-machine
|
|
18
18
|
Requires-Dist: beartype
|
|
19
|
+
Requires-Dist: numpy
|
|
20
|
+
Requires-Dist: pyyaml
|
|
21
|
+
Requires-Dist: typing-extensions
|
|
22
|
+
Provides-Extra: shell
|
|
23
|
+
Requires-Dist: ipython; extra == "shell"
|
|
19
24
|
|
|
20
25
|
# LImA 2 Client library
|
|
21
26
|
|
|
@@ -23,6 +28,31 @@ This project provides a Python interface to the LImA 2 distributed system.
|
|
|
23
28
|
|
|
24
29
|
Checkout the [documentation here](https://limagroup.gitlab-pages.esrf.fr/lima2-client/).
|
|
25
30
|
|
|
31
|
+
### Development setup
|
|
32
|
+
|
|
33
|
+
In order to install lima2-client in a new conda environment:
|
|
34
|
+
```sh
|
|
35
|
+
conda create -n l2c -c esrf-bcu python pytango finite-state-machine
|
|
36
|
+
pip install .
|
|
37
|
+
```
|
|
38
|
+
and, if you need the lima2-client shell:
|
|
39
|
+
```sh
|
|
40
|
+
pip install .[shell]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Quickstart with blissdemo
|
|
44
|
+
|
|
45
|
+
With `bliss-demo-servers` running, you can run an interactive Lima2 client shell to manipulate the
|
|
46
|
+
Lima2 simulator devices and test the client API:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
export TANGO_HOST="localhost:10000" # Assuming you are running bliss-demo-servers locally
|
|
50
|
+
lima2_shell
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The `lima2_shell` utility instantiates a `Client` object using config from `l2c_config.yaml`,
|
|
54
|
+
creates a set of default control, acquisition and processing parameters, and starts an ipython session.
|
|
55
|
+
|
|
26
56
|
### Bootstrapping the documentation
|
|
27
57
|
|
|
28
58
|
The source for the documentation is in the `docs` folder. Here are the instructions to built and read it locally. The documentation is built with [Doxygen](http://www.doxygen.org/) and [Sphinx](http://www.sphinx-doc.org). The sphinx template is from [Sphinx Material](https://bashtage.github.io/sphinx-material/).
|
|
@@ -30,7 +60,7 @@ The source for the documentation is in the `docs` folder. Here are the instructi
|
|
|
30
60
|
```
|
|
31
61
|
conda create -n doc --file docs/requirements.txt -c conda-forge
|
|
32
62
|
conda activate doc
|
|
33
|
-
cd docs
|
|
63
|
+
cd docs
|
|
34
64
|
make html
|
|
35
65
|
```
|
|
36
66
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# LImA 2 Client library
|
|
2
|
+
|
|
3
|
+
This project provides a Python interface to the LImA 2 distributed system.
|
|
4
|
+
|
|
5
|
+
Checkout the [documentation here](https://limagroup.gitlab-pages.esrf.fr/lima2-client/).
|
|
6
|
+
|
|
7
|
+
### Development setup
|
|
8
|
+
|
|
9
|
+
In order to install lima2-client in a new conda environment:
|
|
10
|
+
```sh
|
|
11
|
+
conda create -n l2c -c esrf-bcu python pytango finite-state-machine
|
|
12
|
+
pip install .
|
|
13
|
+
```
|
|
14
|
+
and, if you need the lima2-client shell:
|
|
15
|
+
```sh
|
|
16
|
+
pip install .[shell]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Quickstart with blissdemo
|
|
20
|
+
|
|
21
|
+
With `bliss-demo-servers` running, you can run an interactive Lima2 client shell to manipulate the
|
|
22
|
+
Lima2 simulator devices and test the client API:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
export TANGO_HOST="localhost:10000" # Assuming you are running bliss-demo-servers locally
|
|
26
|
+
lima2_shell
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The `lima2_shell` utility instantiates a `Client` object using config from `l2c_config.yaml`,
|
|
30
|
+
creates a set of default control, acquisition and processing parameters, and starts an ipython session.
|
|
31
|
+
|
|
32
|
+
### Bootstrapping the documentation
|
|
33
|
+
|
|
34
|
+
The source for the documentation is in the `docs` folder. Here are the instructions to built and read it locally. The documentation is built with [Doxygen](http://www.doxygen.org/) and [Sphinx](http://www.sphinx-doc.org). The sphinx template is from [Sphinx Material](https://bashtage.github.io/sphinx-material/).
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
conda create -n doc --file docs/requirements.txt -c conda-forge
|
|
38
|
+
conda activate doc
|
|
39
|
+
cd docs
|
|
40
|
+
make html
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The html documentation is generated in `docs/.build/html`.
|
|
@@ -4,11 +4,11 @@ author = BCU (ESRF)
|
|
|
4
4
|
description = Python client library for Lima2 detector
|
|
5
5
|
long_description = file: README.md
|
|
6
6
|
long_description_content_type = text/markdown
|
|
7
|
-
license =
|
|
8
|
-
url = https://gitlab.esrf.fr/limagroup/
|
|
7
|
+
license = MIT
|
|
8
|
+
url = https://gitlab.esrf.fr/limagroup/lima2-client
|
|
9
9
|
classifiers =
|
|
10
10
|
Intended Audience :: Science/Research
|
|
11
|
-
License :: OSI Approved ::
|
|
11
|
+
License :: OSI Approved :: MIT License
|
|
12
12
|
Programming Language :: Python :: 3
|
|
13
13
|
|
|
14
14
|
[options]
|
|
@@ -19,16 +19,33 @@ install_requires =
|
|
|
19
19
|
jsonschema-default
|
|
20
20
|
finite-state-machine
|
|
21
21
|
beartype
|
|
22
|
+
numpy
|
|
23
|
+
pyyaml
|
|
24
|
+
typing-extensions
|
|
22
25
|
|
|
23
26
|
[options.entry_points]
|
|
24
27
|
console_scripts =
|
|
25
|
-
|
|
28
|
+
lima2_shell = lima2.client.shell:main [shell]
|
|
29
|
+
|
|
30
|
+
[options.extras_require]
|
|
31
|
+
shell =
|
|
32
|
+
ipython
|
|
26
33
|
|
|
27
34
|
[flake8]
|
|
28
35
|
ignore = E501,W503,E203,W504,E251,E262,E265,E266,W291,W293
|
|
36
|
+
per-file-ignores =
|
|
37
|
+
src/lima2/client/shell.py:E402
|
|
29
38
|
|
|
30
39
|
[tool:pytest]
|
|
40
|
+
required_plugins =
|
|
41
|
+
pytest-cov>=6.0.0
|
|
42
|
+
pytest-forked>=1.6.0
|
|
31
43
|
addopts = -v --forked
|
|
44
|
+
pythonpath = src
|
|
45
|
+
|
|
46
|
+
[coverage:run]
|
|
47
|
+
omit =
|
|
48
|
+
src/lima2/client/shell.py
|
|
32
49
|
|
|
33
50
|
[egg_info]
|
|
34
51
|
tag_build =
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# This file is part of the Lima2 project
|
|
4
4
|
#
|
|
5
5
|
# Copyright (c) 2020-2024 Beamline Control Unit, ESRF
|
|
6
|
-
# Distributed under the MIT
|
|
6
|
+
# Distributed under the MIT license. See LICENSE for more info.
|
|
7
7
|
|
|
8
8
|
"""Lima2.Client package
|
|
9
9
|
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
state_machine.State
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
from lima2.client.
|
|
16
|
+
from lima2.client.client import Client
|
|
17
|
+
from lima2.client.detector import CommError
|
|
17
18
|
from lima2.client.state_machine import State
|
|
18
|
-
from lima2.client.
|
|
19
|
+
from lima2.client.pipeline import Pipeline
|
|
20
|
+
|
|
21
|
+
__all__ = ["Client", "CommError", "State", "Pipeline"]
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# This file is part of the Lima2 project
|
|
4
4
|
#
|
|
5
5
|
# Copyright (c) 2020-2024 Beamline Control Unit, ESRF
|
|
6
|
-
# Distributed under the MIT
|
|
6
|
+
# Distributed under the MIT license. See LICENSE for more info.
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
from abc import ABC, abstractmethod
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# This file is part of the Lima2 project
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2020-2024 Beamline Control Unit, ESRF
|
|
6
|
+
# Distributed under the MIT license. See LICENSE for more info.
|
|
7
|
+
|
|
8
|
+
"""User-facing interface exposing Lima2's control and acquisition API.
|
|
9
|
+
|
|
10
|
+
The role of the client is to connect the user to the Lima2 devices via Tango.
|
|
11
|
+
|
|
12
|
+
The Client object instantiates a `Detector` object, allowing the user to control the acquisition
|
|
13
|
+
via the standard prepare/start/stop sequence.
|
|
14
|
+
|
|
15
|
+
The client hides away the topology by providing aggregated stats and counters in the form of
|
|
16
|
+
`ProgressCounter` objects. If needed, these can be explored to trace the information back to
|
|
17
|
+
individual receivers, for instance if one of them is stuck on a frame while the rest are still
|
|
18
|
+
processing data.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
from uuid import UUID
|
|
23
|
+
|
|
24
|
+
import tango as tg
|
|
25
|
+
import yaml
|
|
26
|
+
from beartype import beartype
|
|
27
|
+
from typing_extensions import Any, Optional
|
|
28
|
+
|
|
29
|
+
from lima2.client import pipelines, progress_counter, state_machine, topology
|
|
30
|
+
from lima2.client.detector import Detector
|
|
31
|
+
from lima2.client.pipeline import Pipeline
|
|
32
|
+
from lima2.client.progress_counter import ProgressCounter, SingleCounter
|
|
33
|
+
from lima2.client.topology import TopologyKind
|
|
34
|
+
from lima2.client.utils import docstring_from
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@beartype
|
|
38
|
+
class Client:
|
|
39
|
+
"""Lima2 user-facing client."""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
ctl_dev: tg.DeviceProxy,
|
|
44
|
+
rcv_devs: list[tg.DeviceProxy],
|
|
45
|
+
):
|
|
46
|
+
"""Construct a Client given tango device proxies.
|
|
47
|
+
|
|
48
|
+
The Client can also be built from a yaml config file (see `from_yaml`).
|
|
49
|
+
The topology is queried from the control device properties.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
self._detector = Detector(ctl_dev, *rcv_devs)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
# Get the topology property from control
|
|
56
|
+
topology_prop = ctl_dev.get_property("receiver_topology")
|
|
57
|
+
topology_name = topology_prop["receiver_topology"][0]
|
|
58
|
+
self.topology_kind = TopologyKind(topology_name)
|
|
59
|
+
except IndexError:
|
|
60
|
+
# Property does not exist
|
|
61
|
+
raise RuntimeError(
|
|
62
|
+
"Could not find 'receiver_topology' property in control device"
|
|
63
|
+
) from None
|
|
64
|
+
except ValueError as e:
|
|
65
|
+
# Invalid topology (no matching name in Topology enum)
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"No matching topology type for {repr(topology_name)}"
|
|
68
|
+
) from e
|
|
69
|
+
|
|
70
|
+
self.recv_devs = rcv_devs
|
|
71
|
+
|
|
72
|
+
# Cache of processing pipelines by uuid (see method `pipeline`)
|
|
73
|
+
self.__pipelines: dict[UUID, Pipeline] = {}
|
|
74
|
+
|
|
75
|
+
self.tango_db = tg.Database()
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_yaml(
|
|
79
|
+
cls,
|
|
80
|
+
config_filename: str = "l2c_config.yaml",
|
|
81
|
+
) -> "Client":
|
|
82
|
+
with open(os.path.abspath(config_filename)) as config_f:
|
|
83
|
+
# Raises on io/parsing error
|
|
84
|
+
config = yaml.safe_load(config_f)
|
|
85
|
+
|
|
86
|
+
ctl_url = config["ctl_url"]
|
|
87
|
+
rcv_urls = config["rcv_urls"]
|
|
88
|
+
|
|
89
|
+
ctl_dev = tg.DeviceProxy(ctl_url)
|
|
90
|
+
rcv_devs = [tg.DeviceProxy(url) for url in rcv_urls]
|
|
91
|
+
|
|
92
|
+
return Client(ctl_dev=ctl_dev, rcv_devs=rcv_devs)
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def detector(self) -> Detector:
|
|
96
|
+
return self._detector
|
|
97
|
+
|
|
98
|
+
####################################################################################
|
|
99
|
+
# Detector properties
|
|
100
|
+
####################################################################################
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
@docstring_from(Detector.det_info)
|
|
104
|
+
def det_info(self) -> dict[str, Any]:
|
|
105
|
+
return self.detector.det_info
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
@docstring_from(Detector.det_status)
|
|
109
|
+
def det_status(self) -> dict[str, Any]:
|
|
110
|
+
return self.detector.det_status
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
@docstring_from(Detector.det_capabilities)
|
|
114
|
+
def det_capabilities(self) -> dict[str, Any]:
|
|
115
|
+
return self.detector.det_capabilities
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
@docstring_from(Detector.state)
|
|
119
|
+
def state(self) -> state_machine.State:
|
|
120
|
+
return self.detector.state
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
@docstring_from(Detector.params_default)
|
|
124
|
+
def params_default(self) -> dict[str, Any]:
|
|
125
|
+
return self.detector.params_default
|
|
126
|
+
|
|
127
|
+
####################################################################################
|
|
128
|
+
# Control
|
|
129
|
+
####################################################################################
|
|
130
|
+
|
|
131
|
+
def prepare_acq(
|
|
132
|
+
self, uuid: UUID, ctrl_params: dict, acq_params: dict, proc_params: dict
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Prepare acquisition with a given uuid and set of params."""
|
|
135
|
+
|
|
136
|
+
# Adjust params for distributed acquisition
|
|
137
|
+
ctl, acq, proc = topology.distribute_acq(
|
|
138
|
+
ctl_params=ctrl_params,
|
|
139
|
+
acq_params=acq_params,
|
|
140
|
+
proc_params=proc_params,
|
|
141
|
+
num_receivers=len(self.recv_devs),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
self.detector.prepare_acq(uuid, ctl, acq, proc)
|
|
145
|
+
|
|
146
|
+
@docstring_from(Detector.start_acq)
|
|
147
|
+
def start_acq(self) -> None:
|
|
148
|
+
self.detector.start_acq()
|
|
149
|
+
|
|
150
|
+
@docstring_from(Detector.trigger)
|
|
151
|
+
def trigger(self) -> None:
|
|
152
|
+
self.detector.trigger()
|
|
153
|
+
|
|
154
|
+
@docstring_from(Detector.stop_acq)
|
|
155
|
+
def stop_acq(self) -> None:
|
|
156
|
+
self.detector.stop_acq()
|
|
157
|
+
|
|
158
|
+
@docstring_from(Detector.reset_acq)
|
|
159
|
+
def reset_acq(self) -> None:
|
|
160
|
+
self.detector.reset_acq()
|
|
161
|
+
|
|
162
|
+
####################################################################################
|
|
163
|
+
# Progress counters
|
|
164
|
+
####################################################################################
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def nb_frames_acquired(self) -> ProgressCounter:
|
|
168
|
+
"""Number of frames acquired"""
|
|
169
|
+
return ProgressCounter.from_single(
|
|
170
|
+
SingleCounter(
|
|
171
|
+
name="nb_frames_acquired",
|
|
172
|
+
value=self.detector.nb_frames_acquired,
|
|
173
|
+
source=self.detector.ctrl.name(),
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def nb_frames_xferred(self) -> ProgressCounter:
|
|
179
|
+
"""Aggregated number of frames transferred"""
|
|
180
|
+
return progress_counter.aggregate(
|
|
181
|
+
[
|
|
182
|
+
SingleCounter(
|
|
183
|
+
name="nb_frames_xferred",
|
|
184
|
+
value=recv.nb_frames_xferred,
|
|
185
|
+
source=recv.name(),
|
|
186
|
+
)
|
|
187
|
+
for recv in self.detector.recvs
|
|
188
|
+
]
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
####################################################################################
|
|
192
|
+
# Pipelines
|
|
193
|
+
####################################################################################
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def pipelines(self) -> list[UUID]:
|
|
197
|
+
"""Get list of available pipelines UUID"""
|
|
198
|
+
uuids = [recv.pipelines for recv in self.recv_devs]
|
|
199
|
+
if all(uuid is None for uuid in uuids):
|
|
200
|
+
return []
|
|
201
|
+
return [UUID(u) for u in {y for x in uuids for y in x}]
|
|
202
|
+
|
|
203
|
+
def pipeline(self, uuid: Optional[UUID] = None) -> Pipeline:
|
|
204
|
+
"""Get a specific pipeline by uuid. Return the current one by default (uuid=None)"""
|
|
205
|
+
if uuid is None:
|
|
206
|
+
return self.current_pipeline
|
|
207
|
+
|
|
208
|
+
if uuid in self.__pipelines:
|
|
209
|
+
return self.__pipelines[uuid]
|
|
210
|
+
|
|
211
|
+
# uuid isn't in the local cache of pipelines: get it from Tango
|
|
212
|
+
instances = self.tango_db.get_device_exported(f"*/limaprocessing/{uuid}*")
|
|
213
|
+
if not instances:
|
|
214
|
+
raise ValueError(f"Pipeline not found in tango database: {uuid=}")
|
|
215
|
+
|
|
216
|
+
class_names = [
|
|
217
|
+
self.tango_db.get_device_info(instance).class_name for instance in instances
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
# Select the pipeline according to the Tango class (Homogeneous processing for now)
|
|
221
|
+
pipeline_class = pipelines.get_class(tango_class_name=class_names[0])
|
|
222
|
+
|
|
223
|
+
# Note: sort list of pipeline instances by name such that pipelines are ordered
|
|
224
|
+
# by receiver index. This is necessary e.g. in a strict round-robin topology to
|
|
225
|
+
# be able to find a frame by index.
|
|
226
|
+
proc_devs = [tg.DeviceProxy(url) for url in sorted(list(instances))]
|
|
227
|
+
|
|
228
|
+
# Instantiate pipeline
|
|
229
|
+
self.__pipelines[uuid] = pipeline_class(uuid, proc_devs, self.topology_kind)
|
|
230
|
+
|
|
231
|
+
return self.__pipelines[uuid]
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def current_pipeline(self) -> Pipeline:
|
|
235
|
+
"""Return the current pipeline as a Pipeline object.
|
|
236
|
+
|
|
237
|
+
Raises if no pipeline has been created, or if receivers disagree on the
|
|
238
|
+
current pipeline's uuid.
|
|
239
|
+
"""
|
|
240
|
+
uuids: list[str] = [recv.current_pipeline for recv in self.recv_devs]
|
|
241
|
+
|
|
242
|
+
if all([uuid == "" for uuid in uuids]):
|
|
243
|
+
raise ValueError("No pipeline present in the tango database")
|
|
244
|
+
|
|
245
|
+
uuids: list[UUID] = [UUID(uuid) for uuid in uuids]
|
|
246
|
+
|
|
247
|
+
if not all([uuid == uuids[0] for uuid in uuids]):
|
|
248
|
+
raise ValueError(f"Inconsistent pipeline uuids on all receivers: {uuids=}")
|
|
249
|
+
|
|
250
|
+
return self.pipeline(uuids[0])
|
|
251
|
+
|
|
252
|
+
def erase_pipeline(self, uuid: UUID) -> None:
|
|
253
|
+
"""Erase a pipeline instance"""
|
|
254
|
+
self.__pipelines.pop(uuid, None)
|
|
255
|
+
for recv in self.recv_devs:
|
|
256
|
+
recv.erasePipeline(str(uuid))
|
|
257
|
+
|
|
258
|
+
####################################################################################
|
|
259
|
+
# Utility
|
|
260
|
+
####################################################################################
|
|
261
|
+
|
|
262
|
+
def __repr__(self) -> str:
|
|
263
|
+
return "\n".join(
|
|
264
|
+
[
|
|
265
|
+
"Lima2 client",
|
|
266
|
+
f"State: {self.detector.state}",
|
|
267
|
+
"Controller:",
|
|
268
|
+
f" {self.detector.ctrl}",
|
|
269
|
+
f"Receivers ({self.topology_kind}):",
|
|
270
|
+
*[f" - {recv}" for recv in self.detector.recvs],
|
|
271
|
+
"Progress counters:",
|
|
272
|
+
f" - {self.nb_frames_acquired}",
|
|
273
|
+
f" - {self.nb_frames_xferred}",
|
|
274
|
+
str(self.current_pipeline) if self.__pipelines else "",
|
|
275
|
+
]
|
|
276
|
+
)
|