lima2.client 2.1.1__tar.gz → 2.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-2.1.1/src/lima2.client.egg-info → lima2_client-2.1.2}/PKG-INFO +1 -1
  2. lima2_client-2.1.2/src/lima2/client/devencoded/smx_sparse_frame.py +128 -0
  3. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/pipeline.py +8 -1
  4. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/pipelines/smx.py +11 -1
  5. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/state_machine.py +20 -4
  6. {lima2_client-2.1.1 → lima2_client-2.1.2/src/lima2.client.egg-info}/PKG-INFO +1 -1
  7. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2.client.egg-info/SOURCES.txt +1 -0
  8. {lima2_client-2.1.1 → lima2_client-2.1.2}/LICENSE +0 -0
  9. {lima2_client-2.1.1 → lima2_client-2.1.2}/README.md +0 -0
  10. {lima2_client-2.1.1 → lima2_client-2.1.2}/pyproject.toml +0 -0
  11. {lima2_client-2.1.1 → lima2_client-2.1.2}/setup.cfg +0 -0
  12. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/__init__.py +0 -0
  13. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/client.py +0 -0
  14. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/convert.py +0 -0
  15. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/detector.py +0 -0
  16. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/devencoded/__init__.py +0 -0
  17. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/devencoded/dense_frame.py +0 -0
  18. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/devencoded/exception.py +0 -0
  19. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/devencoded/sparse_frame.py +0 -0
  20. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/devencoded/structured_array.py +0 -0
  21. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/pipelines/__init__.py +0 -0
  22. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/pipelines/legacy.py +0 -0
  23. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/pipelines/xpcs.py +0 -0
  24. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/progress_counter.py +0 -0
  25. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/py.typed +0 -0
  26. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/shell.py +0 -0
  27. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/topology.py +0 -0
  28. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/client/utils.py +0 -0
  29. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/master_file/generate.py +0 -0
  30. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/server/__init__.py +0 -0
  31. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/server/control.py +0 -0
  32. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/server/receiver.py +0 -0
  33. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2/server/timer.py +0 -0
  34. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2.client.egg-info/dependency_links.txt +0 -0
  35. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2.client.egg-info/entry_points.txt +0 -0
  36. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2.client.egg-info/requires.txt +0 -0
  37. {lima2_client-2.1.1 → lima2_client-2.1.2}/src/lima2.client.egg-info/top_level.txt +0 -0
  38. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_client.py +0 -0
  39. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_convert.py +0 -0
  40. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_dense_frame.py +0 -0
  41. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_detector.py +0 -0
  42. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_pipeline.py +0 -0
  43. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_pipelines.py +0 -0
  44. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_progress_counter.py +0 -0
  45. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_sparse_frame.py +0 -0
  46. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_state_machine.py +0 -0
  47. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_timer.py +0 -0
  48. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_topology.py +0 -0
  49. {lima2_client-2.1.1 → lima2_client-2.1.2}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: lima2.client
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Python client library for Lima2 detector
5
5
  Home-page: https://gitlab.esrf.fr/limagroup/lima2-client
6
6
  Author: BCU (ESRF)
@@ -0,0 +1,128 @@
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
+ """Utility functions to decode Lima data from device server.
9
+ """
10
+
11
+ import struct
12
+ import numpy as np
13
+ import typing
14
+ from dataclasses import dataclass
15
+
16
+ from .exception import DevEncodedFormatNotSupported
17
+
18
+ DATA_HEADER_FORMAT = "<HHIII"
19
+ DATA_HEADER_SIZE = struct.calcsize(DATA_HEADER_FORMAT)
20
+
21
+
22
+ @dataclass
23
+ class SmxSparseFrame:
24
+ """
25
+ Provide sparse data frame from Lima including few metadata
26
+ """
27
+
28
+ index: np.ndarray
29
+ intensity: np.ndarray
30
+ background_avg: np.ndarray
31
+ background_std: np.ndarray
32
+
33
+ idx: typing.Optional[int]
34
+ """Index of the frame where 0 is the first frame"""
35
+
36
+ shape: tuple[int, ...]
37
+ """Shape of the frame"""
38
+
39
+ def __bool__(self):
40
+ return not self.isEmpty()
41
+
42
+ def isEmpty(self):
43
+ return self.index is None
44
+
45
+ def densify(self, mask, radius, dummy, normalization=None):
46
+ """Generate a dense image of its sparse representation with background
47
+
48
+ :param mask: 2D array with NaNs for mask and pixel radius for the valid pixels
49
+ :param radius: 1D array with the radial distance
50
+ :param dummy: numerical value for masked-out pixels in dense image
51
+ :param normalization: flat*solidangle*polarization*... array
52
+ :return dense array
53
+ """
54
+ dense = np.interp(mask, radius, self.background_avg)
55
+ if self.background_std is not None:
56
+ std = np.interp(mask, radius, self.background_std)
57
+ np.maximum(0.0, np.random.normal(dense, std), out=dense)
58
+ if normalization is not None:
59
+ dense *= normalization
60
+
61
+ flat = dense.ravel()
62
+ flat[self.index] = self.intensity
63
+ dtype = self.intensity.dtype
64
+ if np.issubdtype(dtype, np.integer):
65
+ dense = np.round(dense)
66
+ dense = np.ascontiguousarray(dense, dtype=dtype)
67
+ dense[np.logical_not(np.isfinite(mask))] = dummy
68
+ return dense
69
+
70
+ def densify_peaks(self):
71
+ """Generate a dense image of its sparse representation with peaks only
72
+
73
+ :param mask: 2D array with NaNs for mask and pixel radius for the valid pixels
74
+ :return dense array
75
+ """
76
+ dense = np.zeros(self.shape, self.intensity.dtype)
77
+ flat = dense.ravel()
78
+ flat[self.index] = self.intensity
79
+ return dense
80
+
81
+ @property
82
+ def data(self):
83
+ return self.densify_peaks()
84
+
85
+
86
+ def decode_sparse_frame(raw_data: bytes) -> SmxSparseFrame:
87
+ """Decode data provided by Lima2
88
+
89
+ Argument:
90
+ raw_data: DevEncoded data
91
+
92
+ Returns:
93
+ A SmxSparseFrame.
94
+ """
95
+
96
+ if isinstance(raw_data, tuple):
97
+ # Support the direct output from proxy.readImage
98
+ if raw_data[0] != "SMX_SPARSE_FRAME":
99
+ raise DevEncodedFormatNotSupported(
100
+ "Data type SMX_SPARSE_FRAME expected (found %s)." % raw_data[0]
101
+ )
102
+ raw_data = raw_data[1]
103
+
104
+ (
105
+ width,
106
+ height,
107
+ nb_bins,
108
+ nb_peaks,
109
+ frame_idx,
110
+ ) = struct.unpack_from(DATA_HEADER_FORMAT, raw_data)
111
+
112
+ offset = DATA_HEADER_SIZE
113
+ index = np.frombuffer(raw_data, count=nb_peaks, offset=offset, dtype=np.int32)
114
+ offset += index.nbytes
115
+ intensity = np.frombuffer(raw_data, count=nb_peaks, offset=offset, dtype=np.float32)
116
+ offset += intensity.nbytes
117
+ background_avg = np.frombuffer(
118
+ raw_data, count=nb_bins, offset=offset, dtype=np.float32
119
+ )
120
+ offset += background_avg.nbytes
121
+ background_std = np.frombuffer(
122
+ raw_data, count=nb_bins, offset=offset, dtype=np.float32
123
+ )
124
+
125
+ shape = (1, height, width)
126
+ return SmxSparseFrame(
127
+ index, intensity, background_avg, background_std, frame_idx, shape
128
+ )
@@ -28,7 +28,12 @@ from typing_extensions import Optional
28
28
 
29
29
  from lima2.client import progress_counter, utils
30
30
  from lima2.client.convert import frame_info_to_shape_dtype
31
- from lima2.client.devencoded import dense_frame, sparse_frame, structured_array
31
+ from lima2.client.devencoded import (
32
+ dense_frame,
33
+ sparse_frame,
34
+ smx_sparse_frame,
35
+ structured_array,
36
+ )
32
37
  from lima2.client.progress_counter import ProgressCounter, SingleCounter
33
38
  from lima2.client.topology import (
34
39
  RoundRobinTopology,
@@ -45,11 +50,13 @@ logger = logging.getLogger(__name__)
45
50
  class FrameType(Enum):
46
51
  DENSE = auto()
47
52
  SPARSE = auto()
53
+ SMX_SPARSE = auto()
48
54
 
49
55
 
50
56
  decoder_by_type: dict[FrameType, Callable] = {
51
57
  FrameType.DENSE: dense_frame.decode,
52
58
  FrameType.SPARSE: sparse_frame.decode,
59
+ FrameType.SMX_SPARSE: smx_sparse_frame.decode_sparse_frame,
53
60
  }
54
61
  """Mapping from frame type to associated decode function"""
55
62
 
@@ -36,7 +36,7 @@ class Smx(Pipeline):
36
36
  ),
37
37
  "sparse_frame": FrameSource(
38
38
  getter_name="getSparseFrame",
39
- frame_type=FrameType.SPARSE,
39
+ frame_type=FrameType.SMX_SPARSE,
40
40
  saving_channel="saving_sparse",
41
41
  ),
42
42
  "acc_corrected": FrameSource(
@@ -121,3 +121,13 @@ class Smx(Pipeline):
121
121
  logger.debug(f"Returning peak counters for frames: {data['frame_idx']}")
122
122
 
123
123
  return (data["frame_idx"], data["nb_peaks"])
124
+
125
+ @property
126
+ def channels(self):
127
+ """Return the channel descriptions"""
128
+ return {
129
+ "frame": self.input_frame_info[0],
130
+ "sparse_frame": self.processed_frame_info[0],
131
+ "acc_corrected": self.processed_frame_info[0],
132
+ "acc_peaks": self.processed_frame_info[0],
133
+ }
@@ -83,7 +83,10 @@ class StateMachine(fsm.StateMachine):
83
83
  State.FAULT,
84
84
  State.UNKNOWN,
85
85
  ]:
86
- self.error()
86
+ _logger.error(
87
+ f"Device {evt.device.dev_name()} in FAULT state. Stopping all devices."
88
+ )
89
+ self.abort()
87
90
 
88
91
  if self.state == State.DISCONNECTED:
89
92
  _logger.info(
@@ -123,6 +126,8 @@ class StateMachine(fsm.StateMachine):
123
126
  # Tolerate a mix of prepared and idle devices. In this case,
124
127
  # the global state shall be IDLE (forcing user to prepare again).
125
128
  self.state = State.IDLE
129
+ elif any(s == ServerState.fault for s in states):
130
+ self.state = State.FAULT
126
131
  elif not all(s == ctl_state for s in states):
127
132
  # Otherwise, if states are divergent, sync cannot be done
128
133
  self._state = State.UNKNOWN
@@ -240,8 +245,18 @@ class StateMachine(fsm.StateMachine):
240
245
  ],
241
246
  target=State.FAULT,
242
247
  )
243
- def error(self):
244
- # Send close to control device only (recv close by themselves)
248
+ def abort(self):
249
+ """Called when a device enters FAULT state."""
250
+ self.ctrl.Stop()
251
+
252
+ # Stop acquisition on receivers that are running
253
+ greenlets = [
254
+ gevent.spawn(recv.Stop)
255
+ for recv in self.recvs
256
+ if recv.acq_state.name == "running"
257
+ ]
258
+ gevent.joinall(greenlets, raise_error=False)
259
+
245
260
  self.ctrl.Close()
246
261
 
247
262
  def __subscribe_events(self):
@@ -271,7 +286,8 @@ class StateMachine(fsm.StateMachine):
271
286
  r.unsubscribe_event(e)
272
287
 
273
288
  _logger.debug(f"on_end_acq while {self.state}")
274
- gevent.spawn(self.close)
289
+ if self.state == State.RUNNING:
290
+ gevent.spawn(self.close)
275
291
 
276
292
  nb_frames_xferreds = []
277
293
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: lima2.client
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Python client library for Lima2 detector
5
5
  Home-page: https://gitlab.esrf.fr/limagroup/lima2-client
6
6
  Author: BCU (ESRF)
@@ -22,6 +22,7 @@ src/lima2/client/utils.py
22
22
  src/lima2/client/devencoded/__init__.py
23
23
  src/lima2/client/devencoded/dense_frame.py
24
24
  src/lima2/client/devencoded/exception.py
25
+ src/lima2/client/devencoded/smx_sparse_frame.py
25
26
  src/lima2/client/devencoded/sparse_frame.py
26
27
  src/lima2/client/devencoded/structured_array.py
27
28
  src/lima2/client/pipelines/__init__.py
File without changes
File without changes
File without changes