lima2.client 3.1a0__tar.gz → 3.1.2__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.
Files changed (49) hide show
  1. {lima2_client-3.1a0/src/lima2_client.egg-info → lima2_client-3.1.2}/PKG-INFO +2 -2
  2. {lima2_client-3.1a0 → lima2_client-3.1.2}/pyproject.toml +1 -1
  3. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/client.py +27 -1
  4. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/detector.py +16 -2
  5. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/state_machine.py +3 -0
  6. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/master_file/generate.py +53 -2
  7. {lima2_client-3.1a0 → lima2_client-3.1.2/src/lima2_client.egg-info}/PKG-INFO +2 -2
  8. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2_client.egg-info/requires.txt +1 -1
  9. {lima2_client-3.1a0 → lima2_client-3.1.2}/LICENSE +0 -0
  10. {lima2_client-3.1a0 → lima2_client-3.1.2}/README.md +0 -0
  11. {lima2_client-3.1a0 → lima2_client-3.1.2}/setup.cfg +0 -0
  12. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/__init__.py +0 -0
  13. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/draw_state_diagram.py +0 -0
  14. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/examples/__init__.py +0 -0
  15. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/examples/async_github_pull_request.py +0 -0
  16. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/examples/async_turnstile.py +0 -0
  17. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/examples/boolean_field.py +0 -0
  18. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/examples/github_pull_request.py +0 -0
  19. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/examples/turnstile.py +0 -0
  20. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/exceptions.py +0 -0
  21. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/finite_state_machine/state_machine.py +0 -0
  22. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/__init__.py +0 -0
  23. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/convert.py +0 -0
  24. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/devencoded/__init__.py +0 -0
  25. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/devencoded/dense_frame.py +0 -0
  26. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/devencoded/exception.py +0 -0
  27. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/devencoded/smx_sparse_frame.py +0 -0
  28. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/devencoded/sparse_frame.py +0 -0
  29. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/devencoded/structured_array.py +0 -0
  30. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/pipeline.py +0 -0
  31. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/pipelines/__init__.py +0 -0
  32. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/pipelines/failing.py +0 -0
  33. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/pipelines/legacy.py +0 -0
  34. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/pipelines/smx.py +0 -0
  35. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/pipelines/xpcs.py +0 -0
  36. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/progress_counter.py +0 -0
  37. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/py.typed +0 -0
  38. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/shell.py +0 -0
  39. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/topology.py +0 -0
  40. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/client/utils.py +0 -0
  41. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/master_file/py.typed +0 -0
  42. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/server/__init__.py +0 -0
  43. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/server/control.py +0 -0
  44. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/server/receiver.py +0 -0
  45. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2/server/timer.py +0 -0
  46. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2_client.egg-info/SOURCES.txt +0 -0
  47. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2_client.egg-info/dependency_links.txt +0 -0
  48. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2_client.egg-info/entry_points.txt +0 -0
  49. {lima2_client-3.1a0 → lima2_client-3.1.2}/src/lima2_client.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lima2-client
3
- Version: 3.1a0
3
+ Version: 3.1.2
4
4
  Summary: Python client library for Lima2 detector
5
5
  Author: BCU (ESRF)
6
6
  License: Copyright (c) 2020-2024 Beamline Control Unit, ESRF
@@ -24,7 +24,7 @@ Requires-Dist: gevent
24
24
  Requires-Dist: jsonschema
25
25
  Requires-Dist: jsonschema-default
26
26
  Requires-Dist: beartype
27
- Requires-Dist: numpy
27
+ Requires-Dist: numpy<2.3
28
28
  Requires-Dist: pyyaml
29
29
  Requires-Dist: typing-extensions
30
30
  Requires-Dist: h5py
@@ -24,7 +24,7 @@ dependencies = [
24
24
  "jsonschema",
25
25
  "jsonschema-default",
26
26
  "beartype",
27
- "numpy",
27
+ "numpy<2.3", # CI runner (CentOS 7) has glibc2.17, numpy>=2.3 needs glibc2.28+
28
28
  "pyyaml",
29
29
  "typing-extensions",
30
30
  "h5py",
@@ -89,6 +89,22 @@ class Client:
89
89
  {saving channel name: list of tasks}.
90
90
  """
91
91
 
92
+ def cleanup(self):
93
+ """Unregister all state loggers and tango event handlers.
94
+
95
+ This method serves only in specific cases where event handlers must for
96
+ some reason be unregistered. In a normal use case, a Client is
97
+ instantiated once per Lima2 instance, and no cleanup is necessary.
98
+
99
+ NOTE: After this call, the Client instance should no longer be used.
100
+ """
101
+ self._detector.unregister_events()
102
+ self._detector = None
103
+
104
+ for pipeline in self.__pipelines.values():
105
+ pipeline.unregister_events()
106
+ self.__pipelines.clear()
107
+
92
108
  @classmethod
93
109
  def from_yaml(
94
110
  cls,
@@ -167,6 +183,15 @@ class Client:
167
183
  ) -> None:
168
184
  """Prepare acquisition with a given uuid and set of params."""
169
185
 
186
+ # Check parameters required for master file generation
187
+ checked_params = mfg.check_generation(
188
+ ctrl_params=ctrl_params,
189
+ recv_params=acq_params,
190
+ proc_params=proc_params,
191
+ topology_kind=self.topology_kind,
192
+ )
193
+ ctrl_params, acq_params, proc_params = checked_params
194
+
170
195
  # Adjust params for distributed acquisition
171
196
  ctl, acq, proc = topology.distribute_acq(
172
197
  ctl_params=ctrl_params,
@@ -209,6 +234,7 @@ class Client:
209
234
  ctrl_params=ctl,
210
235
  recv_params=acq[0],
211
236
  proc_params=proc[0],
237
+ topology_kind=self.topology_kind,
212
238
  )
213
239
 
214
240
  @docstring_from(Detector.start_acq)
@@ -364,7 +390,7 @@ class Client:
364
390
 
365
391
  cleared = []
366
392
  for uuid in receiver.pipelines:
367
- if uuid != str(current):
393
+ if uuid != str(current) and all(self.pipeline(UUID(uuid)).is_finished):
368
394
  logger.debug(
369
395
  f"Erasing pipeline {uuid} from recv {receiver.dev_name()}"
370
396
  )
@@ -73,6 +73,7 @@ class Detector:
73
73
 
74
74
  self.__ctrl = ctrl_dev
75
75
  self.__recvs = list(recv_devs)
76
+ self._state_event_ids: dict[tango.DeviceProxy, int] = {}
76
77
 
77
78
  self.ping()
78
79
 
@@ -100,6 +101,17 @@ class Detector:
100
101
  d.set_green_mode(tango.GreenMode.Gevent)
101
102
  d.set_timeout_millis(timeout * 1000)
102
103
 
104
+ def unregister_events(self):
105
+ """Unregister all state loggers and tango event handlers.
106
+
107
+ This method serves only in specific cases where event handlers must for
108
+ some reason be unregistered. In a normal use case, a Client is
109
+ instantiated once per Lima2 instance, and no cleanup is necessary.
110
+ """
111
+ for device, event_id in self._state_event_ids.items():
112
+ device.unsubscribe_event(event_id)
113
+ self._state_event_ids.clear()
114
+
103
115
  @contextlib.contextmanager
104
116
  def _locked_fsm(self) -> Iterator[fsm.StateMachine]:
105
117
  self.__fsm_lock.acquire()
@@ -633,12 +645,14 @@ class Detector:
633
645
  finally:
634
646
  _logger.info(f"Current state: {fsm.state}")
635
647
 
636
- for dev in self.devs:
637
- dev.subscribe_event(
648
+ self._state_event_ids = {
649
+ dev: dev.subscribe_event(
638
650
  "acq_state",
639
651
  tango.EventType.CHANGE_EVENT,
640
652
  on_acq_state_change,
641
653
  )
654
+ for dev in self.devs
655
+ }
642
656
 
643
657
  def _register_on_end_acq_handlers(self):
644
658
  """Register an "end of acquisition" callback on each receiver.
@@ -239,6 +239,9 @@ class StateMachine(fsm.StateMachine):
239
239
  if logger in self.__loggers:
240
240
  self.__loggers.remove(logger)
241
241
 
242
+ def clear_transition_loggers(self):
243
+ self.__loggers.clear()
244
+
242
245
  def on_state_change(self, state):
243
246
  _logger.debug(f"on_state_change {state}")
244
247
  futures = []
@@ -27,6 +27,7 @@ import gevent
27
27
  import gevent.event
28
28
 
29
29
  from lima2.client.pipeline import FrameType, Pipeline
30
+ from lima2.client.topology import TopologyKind
30
31
 
31
32
  logger = logging.getLogger(__name__)
32
33
 
@@ -66,12 +67,54 @@ class MasterFileTask:
66
67
  self.task.join()
67
68
 
68
69
 
70
+ def check_generation(
71
+ ctrl_params: dict,
72
+ recv_params: dict,
73
+ proc_params: dict,
74
+ topology_kind: TopologyKind,
75
+ ) -> list[dict]:
76
+ """Check the parameters to be sent to prepare to ensure master file info"""
77
+
78
+ # group all active saving subsystems
79
+ active_saving_params: list[dict] = []
80
+
81
+ # first check receiver raw saving or SDK saving
82
+ for params in [recv_params, ctrl_params]:
83
+ if "saving" in params and params["saving"]["enabled"]:
84
+ active_saving_params.append(params["saving"])
85
+ break
86
+
87
+ # then check the saving streams of the current pipeline
88
+ for source, params in proc_params.items():
89
+ # Skip other subsystems
90
+ if not source.startswith("saving"):
91
+ continue
92
+ # Skip source if channel is disabled
93
+ if params["enabled"]:
94
+ active_saving_params.append(params)
95
+
96
+ need_frame_lut = topology_kind == TopologyKind.UNEVEN
97
+
98
+ # include detector frame_idx dataset in HDF5 files
99
+ for saving_params in active_saving_params:
100
+ if need_frame_lut and "include_frame_idx" in saving_params:
101
+ saving_params["include_frame_idx"] = True
102
+
103
+ # allow retreiving recv-det-frame-idx mapping
104
+ if need_frame_lut and "frame_idx_enabled" in proc_params:
105
+ logger.debug("Enabling frame_idx generation")
106
+ proc_params["frame_idx_enabled"] = True
107
+
108
+ return ctrl_params, recv_params, proc_params
109
+
110
+
69
111
  def enable_generation(
70
112
  pipeline: Pipeline,
71
113
  num_frames: int,
72
114
  ctrl_params: dict,
73
115
  recv_params: dict,
74
116
  proc_params: dict,
117
+ topology_kind: TopologyKind,
75
118
  rr_offset: int = 0,
76
119
  lut_source: LUTSource = LUTSource.AUTO,
77
120
  ) -> dict[str, MasterFileTask]:
@@ -144,6 +187,11 @@ def enable_generation(
144
187
  proc_params=proc_params,
145
188
  )
146
189
 
190
+ need_frame_lut = topology_kind == TopologyKind.UNEVEN
191
+
192
+ # Check strict Round-Robin only when needed
193
+ check_strict_rr = topology_kind == TopologyKind.ROUND_ROBIN
194
+
147
195
  for source, frame_info, saving_params, cnt_name in active_saving_params:
148
196
  base_path = saving_params["base_path"]
149
197
  filename_prefix = saving_params["filename_prefix"]
@@ -171,10 +219,12 @@ def enable_generation(
171
219
  this_lut_source = LUTSource.ROUND_ROBIN
172
220
  if this_lut_source is None:
173
221
  raise ValueError(f"Cannot satisfy {lut_source=}: {file_lut=}, {recv_lut=}")
222
+ elif this_lut_source == LUTSource.ROUND_ROBIN and need_frame_lut:
223
+ raise ValueError(f"Cannot use {this_lut_source} with {topology_kind}")
174
224
 
175
225
  logger.info(
176
226
  f"Master file (channel={source}) will be generated "
177
- f"at {master_file_path}..."
227
+ f"at {master_file_path} [{this_lut_source}]..."
178
228
  )
179
229
 
180
230
  if frame_info is not None:
@@ -201,6 +251,7 @@ def enable_generation(
201
251
  end_event=end_event,
202
252
  cnt_name=cnt_name,
203
253
  config=config,
254
+ check_strict_rr=check_strict_rr,
204
255
  )
205
256
 
206
257
  lut_sources = [s["lut_source"] for s in generate_params.values()]
@@ -260,7 +311,7 @@ class MasterFileGeneration:
260
311
  end_event: Optional[gevent.event.Event] = None
261
312
  cnt_name: Optional[str] = None
262
313
  config: Optional[dict] = None
263
- check_strict_rr: bool = True
314
+ check_strict_rr: bool = False
264
315
 
265
316
  def __init__(self, params: Params):
266
317
  self._p = params
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lima2-client
3
- Version: 3.1a0
3
+ Version: 3.1.2
4
4
  Summary: Python client library for Lima2 detector
5
5
  Author: BCU (ESRF)
6
6
  License: Copyright (c) 2020-2024 Beamline Control Unit, ESRF
@@ -24,7 +24,7 @@ Requires-Dist: gevent
24
24
  Requires-Dist: jsonschema
25
25
  Requires-Dist: jsonschema-default
26
26
  Requires-Dist: beartype
27
- Requires-Dist: numpy
27
+ Requires-Dist: numpy<2.3
28
28
  Requires-Dist: pyyaml
29
29
  Requires-Dist: typing-extensions
30
30
  Requires-Dist: h5py
@@ -3,7 +3,7 @@ gevent
3
3
  jsonschema
4
4
  jsonschema-default
5
5
  beartype
6
- numpy
6
+ numpy<2.3
7
7
  pyyaml
8
8
  typing-extensions
9
9
  h5py
File without changes
File without changes
File without changes