Coverage for /opt/conda/envs/apienv/lib/python3.10/site-packages/daiquiri/implementors/tomo/tomo_helper.py: 0%
97 statements
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-15 02:12 +0000
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-15 02:12 +0000
1from __future__ import annotations
3from typing import Any
4import contextlib
5import logging
6from bliss.common import cleanup
7from tomo.controllers.tomo_config import TomoConfig
8from tomo.utils import configure_shift, ShiftType
9from bliss.config.static import get_config
10from daiquiri.core.components import (
11 ComponentActorSchema,
12)
13from bliss.common.measurementgroup import MeasurementGroup
15_logger = logging.getLogger(__name__)
18try:
19 from tomo.globals import ACTIVE_TOMOCONFIG
20except ImportError:
21 ACTIVE_TOMOCONFIG = None
24def get_active_tomo_config() -> TomoConfig | None:
25 tomo_config = (
26 ACTIVE_TOMOCONFIG is not None and ACTIVE_TOMOCONFIG.deref_active_object()
27 )
28 return tomo_config
31def patch_actor_validator(func):
32 """Patch the validation function from the actor schema to make is a bit more
33 easy to use
35 .. code-block::
37 @tomo_helper.patch_actor_validator
38 def calculated(self, expo_time: float | None = None, **kwargs):
39 ...
40 """
42 def new_func(self, data):
43 try:
44 try:
45 return func(self, **data)
46 except TypeError as e:
47 if f"{func.name()}" in e.args[0]:
48 # The function signature is not right, it means we expect
49 # params which are not yet part of the function
50 _logger.debug(f"{func.name} failed with: {e}", exc_info=True)
51 else:
52 raise
53 except Exception as e:
54 # Use an error instead of a debug
55 _logger.error(f"{func.name} failed with: {e}", exc_info=True)
56 raise
57 return None
59 return new_func
62def get_sequence_from_name(
63 tomo_config: TomoConfig, object_names: str | list[str], expected_class
64):
65 if isinstance(object_names, str):
66 object_names = [object_names]
68 bliss_config = get_config()
70 for name in object_names:
71 sequence = bliss_config.get(name)
72 if not isinstance(sequence, expected_class):
73 raise RuntimeError(
74 f"'Object {name} is expected to be a {expected_class.name}. Found {type(sequence)}"
75 )
76 if sequence.tomo_config is tomo_config:
77 break
78 else:
79 raise RuntimeError(
80 f"No {expected_class.__name__} object was found for tomo config '{tomo_config.name}'"
81 )
82 return sequence
85def create_daiquiri_scan_info(
86 actor: ComponentActorSchema,
87 share_data_collection: bool = False,
88 share_data_collection_group: bool = False,
89) -> dict[str, Any]:
90 scan_info = {
91 "daiquiri": {"sampleid": actor["sampleid"], "sessionid": actor["sessionid"]}
92 }
93 if share_data_collection:
94 datacollectionid = actor.get("datacollectionid")
95 if datacollectionid:
96 scan_info["daiquiri"]["datacollectionid"] = datacollectionid
97 if share_data_collection_group:
98 datacollectiongroupid = actor.get("datacollectiongroupid")
99 if datacollectiongroupid is not None:
100 scan_info["daiquiri"]["datacollectiongroupid"] = datacollectiongroupid
101 return scan_info
104@contextlib.contextmanager
105def y_axis_at(config: TomoConfig, position: float, enabled: bool = True):
106 """
107 Move and restore the rotation axis
108 """
109 if enabled:
110 context = cleanup.cleanup(
111 config.y_axis,
112 restore_list=(cleanup.axis.POS,),
113 )
114 else:
115 context = contextlib.nullcontext
117 with context:
118 if enabled:
119 config.y_axis.move(position)
120 yield
123def create_mg(tomo_config: TomoConfig):
124 tomocam = tomo_config.detectors.active_detector
125 if tomocam is None:
126 raise RuntimeError(f"No active detector selected in `{tomo_config.name}`")
127 cam = tomocam.detector
128 mg = MeasurementGroup("daiquiri_mg", {"counters": []})
129 try:
130 mg.add(cam.image)
131 except Exception: # nosec
132 pass
133 mg.enable(cam.image.fullname)
134 if tomo_config.machinfo is not None:
135 try:
136 mg.add(tomo_config.machinfo.counters.current)
137 except Exception: # nosec
138 pass
139 mg.enable(tomo_config.machinfo.counters.current.fullname)
140 return mg
143def setup_main_pars(
144 sequence,
145 scan_type: str,
146 use_sinogram: bool,
147 n_dark: int,
148 n_flat: int,
149 axis_displacement: float,
150 comment: str,
151):
152 sequence.pars.scan_type = scan_type.upper()
153 sequence.pars.activate_sinogram = use_sinogram
154 if use_sinogram:
155 # A sinogram is expected so a dark will be acquired
156 sequence.pars.dark_at_start = True
157 sequence.pars.flat_at_start = True
158 sequence.pars.flat_n = n_flat
159 sequence.pars.dark_n = n_dark
160 sequence.pars.comment = comment
161 if axis_displacement == 0:
162 sequence.pars.half_acquisition = False
163 sequence.pars.shift_in_fov_factor = 0
164 sequence.pars.shift_in_mm = 0
165 else:
166 configure_shift(sequence, axis_displacement / 100, ShiftType.FOV_FACTOR)
167 sequence.pars.half_acquisition = True