lima2.client 4.0.7__tar.gz → 4.0.8__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-4.0.7/src/lima2_client.egg-info → lima2_client-4.0.8}/PKG-INFO +1 -1
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/devencoded/dense_frame.py +4 -2
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/acquisition.py +138 -48
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/acquisition_system.py +265 -180
- lima2_client-4.0.8/src/lima2/conductor/detector.py +38 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/processing/__init__.py +0 -22
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/processing/master_file.py +65 -36
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/processing/pipeline.py +98 -268
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/processing/reduced_data.py +82 -92
- lima2_client-4.0.8/src/lima2/conductor/tango/container.py +183 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/tango/control.py +33 -9
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/tango/processing.py +1 -32
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/tango/receiver.py +34 -8
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/tango/utils.py +9 -1
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/utils.py +8 -4
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/acquisition.py +2 -2
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/pipeline.py +9 -15
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/utils.py +1 -1
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/webapp.py +7 -18
- {lima2_client-4.0.7 → lima2_client-4.0.8/src/lima2_client.egg-info}/PKG-INFO +1 -1
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2_client.egg-info/SOURCES.txt +2 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/LICENSE +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/README.md +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/pyproject.toml +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/setup.cfg +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/client/__init__.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/client/exceptions.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/client/py.typed +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/client/services.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/client/session.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/client/shell.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/__init__.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/devencoded/__init__.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/devencoded/exception.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/devencoded/smx_sparse_frame.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/devencoded/sparse_frame.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/devencoded/structured_array.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/exceptions.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/pipelines.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/progress_counter.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/py.typed +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/state.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/common/types.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/__init__.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/processing/failing.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/processing/legacy.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/processing/smx.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/processing/xpcs.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/py.typed +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/tango/__init__.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/topology.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/__init__.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/benchmark.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/benchmark_generators/__init__.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/benchmark_generators/rois.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/cli.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/detector.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2/conductor/webservice/main.py +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2_client.egg-info/dependency_links.txt +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2_client.egg-info/entry_points.txt +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2_client.egg-info/requires.txt +0 -0
- {lima2_client-4.0.7 → lima2_client-4.0.8}/src/lima2_client.egg-info/top_level.txt +0 -0
|
@@ -125,7 +125,7 @@ def decode(raw_data: EncodedFrame) -> Frame:
|
|
|
125
125
|
except Exception:
|
|
126
126
|
raise DevEncodedFormatNotSupported(
|
|
127
127
|
"Image format from Lima Tango device not supported (found %s)." % data_type
|
|
128
|
-
)
|
|
128
|
+
) from None
|
|
129
129
|
if endianness != 0:
|
|
130
130
|
raise DevEncodedFormatNotSupported(
|
|
131
131
|
"Unsupported endianness (found %s)." % endianness
|
|
@@ -139,7 +139,9 @@ def decode(raw_data: EncodedFrame) -> Frame:
|
|
|
139
139
|
try:
|
|
140
140
|
dtype = MODE_TO_NUMPY[mode]
|
|
141
141
|
except Exception:
|
|
142
|
-
raise DevEncodedFormatNotSupported(
|
|
142
|
+
raise DevEncodedFormatNotSupported(
|
|
143
|
+
"Data format %s is not supported" % mode
|
|
144
|
+
) from None
|
|
143
145
|
|
|
144
146
|
data = numpy.frombuffer(raw_data, offset=header_size, dtype=dtype)
|
|
145
147
|
data.shape = dim3, dim2, dim1
|
|
@@ -10,15 +10,25 @@ Contains state logic for running acquisitions.
|
|
|
10
10
|
|
|
11
11
|
import asyncio
|
|
12
12
|
import logging
|
|
13
|
+
import time
|
|
13
14
|
import traceback
|
|
15
|
+
import weakref
|
|
14
16
|
from dataclasses import dataclass
|
|
15
17
|
from enum import Enum, auto
|
|
16
18
|
from typing import Awaitable, Callable
|
|
17
|
-
from uuid import UUID
|
|
18
|
-
import weakref
|
|
19
|
-
|
|
20
|
-
from lima2.conductor.processing.pipeline import Pipeline
|
|
21
19
|
|
|
20
|
+
from lima2.common.exceptions import Lima2BackendError
|
|
21
|
+
from lima2.common.types import FrameInfo
|
|
22
|
+
from lima2.conductor.processing import pipeline_classes
|
|
23
|
+
from lima2.conductor.processing.master_file import (
|
|
24
|
+
FrameSavingChannel,
|
|
25
|
+
MasterFileMetadata,
|
|
26
|
+
)
|
|
27
|
+
from lima2.conductor.processing.reduced_data import RoiParams
|
|
28
|
+
from lima2.conductor.tango.container import TangoProcessingGroup
|
|
29
|
+
from lima2.conductor.tango.processing import ProcessingErrorEvent, TangoProcessing
|
|
30
|
+
from lima2.conductor.tango.utils import format_exception
|
|
31
|
+
from lima2.conductor.utils import naturaltime
|
|
22
32
|
|
|
23
33
|
logger = logging.getLogger(__name__)
|
|
24
34
|
|
|
@@ -171,17 +181,6 @@ class AcquisitionState:
|
|
|
171
181
|
await self.event_queue.put((event, fut))
|
|
172
182
|
return fut
|
|
173
183
|
|
|
174
|
-
async def process(self, event: AcquisitionEvent) -> None:
|
|
175
|
-
"""Enqueue an event and wait for it to be processed.
|
|
176
|
-
|
|
177
|
-
If other events are already in the queue, they will be processed first.
|
|
178
|
-
|
|
179
|
-
The returned future can be awaited to pause until the event has been
|
|
180
|
-
processed.
|
|
181
|
-
"""
|
|
182
|
-
fut = await self.enqueue(event=event)
|
|
183
|
-
await fut
|
|
184
|
-
|
|
185
184
|
async def _handle(self, event: AcquisitionEvent) -> list[AcquisitionEvent]:
|
|
186
185
|
"""Handle an event.
|
|
187
186
|
|
|
@@ -295,32 +294,58 @@ class AcquisitionState:
|
|
|
295
294
|
f"Error while handling {evt}: {traceback.format_exc()}"
|
|
296
295
|
)
|
|
297
296
|
fut.set_exception(e)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
297
|
+
|
|
298
|
+
# NOTE: break condition
|
|
299
|
+
if evt is AcquisitionEvent.FINISHED:
|
|
300
|
+
break
|
|
302
301
|
|
|
303
302
|
|
|
304
303
|
class Acquisition:
|
|
305
|
-
"""
|
|
306
|
-
|
|
307
|
-
|
|
304
|
+
"""Prepared acquisition, ready to start.
|
|
305
|
+
|
|
306
|
+
Holds an internal state, a set of processing devices, and a Pipeline instance.
|
|
307
|
+
|
|
308
|
+
Listens to events from all devices in order to update its state and properly manage
|
|
309
|
+
local tasks.
|
|
308
310
|
"""
|
|
309
311
|
|
|
310
312
|
def __init__(
|
|
311
313
|
self,
|
|
312
|
-
|
|
313
|
-
num_receivers: int,
|
|
314
|
+
processing_devices: TangoProcessingGroup,
|
|
314
315
|
on_end: OnEndCallback,
|
|
315
316
|
on_failure: OnFailureCallback,
|
|
317
|
+
num_frames: int,
|
|
318
|
+
frame_infos: dict[str, FrameInfo],
|
|
319
|
+
roi_params: RoiParams,
|
|
320
|
+
saving_channels: dict[str, FrameSavingChannel],
|
|
321
|
+
masterfile_metadata: MasterFileMetadata,
|
|
316
322
|
) -> None:
|
|
317
|
-
self.uuid = uuid
|
|
323
|
+
self.uuid = processing_devices.uuid
|
|
318
324
|
self.state = AcquisitionState(
|
|
319
|
-
num_receivers=
|
|
325
|
+
num_receivers=len(processing_devices),
|
|
320
326
|
on_end=on_end,
|
|
321
327
|
on_failure=on_failure,
|
|
322
328
|
)
|
|
323
|
-
self.
|
|
329
|
+
self.proc_group = processing_devices
|
|
330
|
+
"""Processing device group."""
|
|
331
|
+
|
|
332
|
+
# Instantiate a Pipeline
|
|
333
|
+
pipeline_class = pipeline_classes[self.proc_group.class_name]
|
|
334
|
+
self.pipeline = pipeline_class(
|
|
335
|
+
devices=processing_devices,
|
|
336
|
+
num_frames=num_frames,
|
|
337
|
+
frame_infos=frame_infos,
|
|
338
|
+
roi_params=roi_params,
|
|
339
|
+
saving_channels=saving_channels,
|
|
340
|
+
masterfile_metadata=masterfile_metadata,
|
|
341
|
+
)
|
|
342
|
+
"""Processing pipeline."""
|
|
343
|
+
|
|
344
|
+
self.processing_errors: list[str] = []
|
|
345
|
+
"""Holds processing error messages that occurred during the run, if any."""
|
|
346
|
+
|
|
347
|
+
self.done_devices: set[TangoProcessing] = set()
|
|
348
|
+
"""Set of processing devices that have finished (successfully or not)."""
|
|
324
349
|
|
|
325
350
|
def __del__(self) -> None:
|
|
326
351
|
logger.debug(f"Acquisition instance {self.uuid} destroyed")
|
|
@@ -333,45 +358,110 @@ class Acquisition:
|
|
|
333
358
|
|
|
334
359
|
def start(self) -> None:
|
|
335
360
|
"""Start the pipeline subtasks and listen for device events."""
|
|
336
|
-
if self.pipeline is None:
|
|
337
|
-
raise RuntimeError(f"Pipeline is not attached for acquisition {self.uuid}")
|
|
338
|
-
|
|
339
361
|
self.pipeline.start()
|
|
340
362
|
self.state.start()
|
|
341
363
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
364
|
+
@staticmethod
|
|
365
|
+
async def attach(
|
|
366
|
+
processing_devices: TangoProcessingGroup,
|
|
367
|
+
on_end: OnEndCallback,
|
|
368
|
+
on_failure: OnFailureCallback,
|
|
369
|
+
num_frames: int,
|
|
370
|
+
frame_infos: dict[str, FrameInfo],
|
|
371
|
+
roi_params: RoiParams,
|
|
372
|
+
saving_channels: dict[str, FrameSavingChannel],
|
|
373
|
+
masterfile_metadata: MasterFileMetadata,
|
|
374
|
+
) -> "Acquisition":
|
|
375
|
+
"""
|
|
376
|
+
Instantiate a complete Acquisition instance listening to processing device
|
|
377
|
+
events.
|
|
378
|
+
"""
|
|
379
|
+
instance = Acquisition(
|
|
380
|
+
processing_devices=processing_devices,
|
|
381
|
+
on_end=on_end,
|
|
382
|
+
on_failure=on_failure,
|
|
383
|
+
num_frames=num_frames,
|
|
384
|
+
frame_infos=frame_infos,
|
|
385
|
+
roi_params=roi_params,
|
|
386
|
+
saving_channels=saving_channels,
|
|
387
|
+
masterfile_metadata=masterfile_metadata,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
await instance.listen()
|
|
391
|
+
|
|
392
|
+
return instance
|
|
393
|
+
|
|
394
|
+
def detach(self) -> None:
|
|
395
|
+
"""Stop listening to events."""
|
|
396
|
+
self.state.detach()
|
|
397
|
+
|
|
398
|
+
async def notify(self, event: AcquisitionEvent) -> None:
|
|
399
|
+
"""Enqueue an event in the AcquisitionState and return."""
|
|
400
|
+
await self.state.enqueue(event=event)
|
|
401
|
+
|
|
402
|
+
async def listen(self) -> None:
|
|
403
|
+
"""Listen to processing device events to update the AcquisitionState.
|
|
404
|
+
|
|
405
|
+
Should be called just after instantiating the Acquisition instance.
|
|
406
|
+
"""
|
|
345
407
|
|
|
346
408
|
# NOTE: Using weakref in event handlers is necessary to allow the
|
|
347
|
-
# garbage collector to destroy the
|
|
409
|
+
# garbage collector to destroy the Pipeline object.
|
|
348
410
|
# This is CRUCIAL for proper cleanup of the tango DeviceProxy
|
|
349
411
|
# instances.
|
|
350
412
|
# See https://gitlab.esrf.fr/limagroup/lima2-client/-/issues/90.
|
|
351
413
|
wself = weakref.ref(self)
|
|
352
414
|
|
|
353
|
-
async def
|
|
415
|
+
async def on_device_finished(device: TangoProcessing) -> None:
|
|
416
|
+
"""Adds a processing device to the done_devices set.
|
|
417
|
+
|
|
418
|
+
When the set is complete, call the on_finished callback registered to
|
|
419
|
+
this pipeline instance (see constructor).
|
|
420
|
+
"""
|
|
421
|
+
logger.info(f"Processing device {device.name} is done")
|
|
422
|
+
|
|
354
423
|
self_ref = wself()
|
|
355
|
-
if
|
|
424
|
+
if self_ref is None:
|
|
356
425
|
return
|
|
426
|
+
|
|
357
427
|
await self_ref.notify(event=AcquisitionEvent.PROC_DEVICE_DONE)
|
|
358
428
|
|
|
359
|
-
|
|
429
|
+
self_ref.done_devices.add(device)
|
|
430
|
+
if self_ref.done_devices == set(self_ref.proc_group):
|
|
431
|
+
logger.info("Closing pipeline")
|
|
432
|
+
await self_ref.pipeline.close()
|
|
433
|
+
|
|
434
|
+
async def on_device_error(evt: ProcessingErrorEvent) -> None:
|
|
435
|
+
"""Reports a processing error to the registered callback."""
|
|
360
436
|
self_ref = wself()
|
|
361
|
-
if
|
|
437
|
+
if self_ref is None:
|
|
362
438
|
return
|
|
439
|
+
|
|
440
|
+
err_msg = format_exception(
|
|
441
|
+
where=evt.device_name,
|
|
442
|
+
when="while processing frames",
|
|
443
|
+
what=evt.what,
|
|
444
|
+
info=evt.info,
|
|
445
|
+
)
|
|
446
|
+
logger.warning(err_msg)
|
|
447
|
+
self_ref.processing_errors.append(err_msg)
|
|
448
|
+
|
|
363
449
|
await self_ref.notify(event=AcquisitionEvent.PROC_DEVICE_FAILED)
|
|
364
450
|
|
|
365
|
-
|
|
366
|
-
|
|
451
|
+
reg_t0 = time.perf_counter()
|
|
452
|
+
|
|
453
|
+
results = await self.proc_group.listen(
|
|
454
|
+
on_finished=on_device_finished, on_error=on_device_error
|
|
367
455
|
)
|
|
368
456
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
457
|
+
errors = [res for res in results if res is not None]
|
|
458
|
+
if len(errors) > 0:
|
|
459
|
+
raise Lima2BackendError(
|
|
460
|
+
"Couldn't register event handlers on some processing devices:\n- "
|
|
461
|
+
+ "\n- ".join([str(err) for err in errors])
|
|
462
|
+
)
|
|
374
463
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
464
|
+
logger.info(
|
|
465
|
+
f"Registered to processing device events in "
|
|
466
|
+
f"{naturaltime(time.perf_counter() - reg_t0)}"
|
|
467
|
+
)
|