From c0831210c801c7238d5417ecdaa3e2da35523a73 Mon Sep 17 00:00:00 2001 From: gac-x02da Date: Mon, 16 Jun 2025 21:21:40 +0200 Subject: [PATCH 01/10] wip --- .../devices/std_daq/std_daq_live_processing.py | 3 ++- tomcat_bec/scans/simple_scans.py | 12 ++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tomcat_bec/devices/std_daq/std_daq_live_processing.py b/tomcat_bec/devices/std_daq/std_daq_live_processing.py index 9f3f491..714c5ea 100644 --- a/tomcat_bec/devices/std_daq/std_daq_live_processing.py +++ b/tomcat_bec/devices/std_daq/std_daq_live_processing.py @@ -122,8 +122,9 @@ class StdDaqLiveProcessing: return corrected_data # Ensure that the division does not lead to division by zero + flat_corr = np.abs(flat-dark) corrected_data = np.divide( - data - dark, flat - dark, out=np.zeros_like(data), where=(flat - dark) != 0 + data - dark, flat_corr, out=np.zeros_like(data, dtype=np.float32), where=flat_corr != 0 ) return corrected_data diff --git a/tomcat_bec/scans/simple_scans.py b/tomcat_bec/scans/simple_scans.py index 243aa05..06a9358 100644 --- a/tomcat_bec/scans/simple_scans.py +++ b/tomcat_bec/scans/simple_scans.py @@ -102,12 +102,10 @@ class TomoComponents: self.connector.send_client_info(f"Acquiring {num_images} dark images.") yield from self.restart_cameras( - name=name, prefix=name, num_images=num_images, frames_per_trigger=1 + name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images ) # yield from self.close_shutter() - for i in range(num_images): - logger.debug(f"Acquiring dark image {i+1}/{num_images}.") - yield from self.stubs.trigger(min_wait=exposure_time) + yield from self.stubs.trigger(min_wait=exposure_time) yield from self.complete() yield from self.update_live_processing_references(ref_type="dark") yield from self.restore_configs(name=name) @@ -130,12 +128,10 @@ class TomoComponents: self.connector.send_client_info(f"Acquiring {num_images} flat images.") yield from self.restart_cameras( - name=name, prefix=name, num_images=num_images, frames_per_trigger=1 + name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images ) # yield from self.open_shutter() - for i in range(num_images): - logger.debug(f"Acquiring flat image {i+1}/{num_images}.") - yield from self.stubs.trigger(min_wait=exposure_time) + yield from self.stubs.trigger(min_wait=exposure_time) yield from self.complete() yield from self.update_live_processing_references(ref_type="flat") yield from self.restore_configs(name=name) -- 2.49.1 From f49018754e302cb3e39361cffd10144419a88b4f Mon Sep 17 00:00:00 2001 From: wakonig_k Date: Mon, 16 Jun 2025 21:26:58 +0200 Subject: [PATCH 02/10] wip --- tomcat_bec/scans/simple_scans.py | 39 +++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/tomcat_bec/scans/simple_scans.py b/tomcat_bec/scans/simple_scans.py index 06a9358..ad22654 100644 --- a/tomcat_bec/scans/simple_scans.py +++ b/tomcat_bec/scans/simple_scans.py @@ -70,7 +70,7 @@ class TomoComponents: for cam in self.cameras: yield from self.stubs.send_rpc_and_wait( device=cam, func_name="restore_config", name=name - ) + ) def update_live_processing_references(self, ref_type: Literal["dark", "flat"]): """ @@ -143,6 +143,43 @@ class TomoComponents: yield from self.acquire_flat(num_flats, exposure_time=exp_time, name=name) +class AcquireDark(ScanBase): + scan_name = "acquire_dark" + + def __init__(self, exp_time: float, frames_per_trigger: int = 1, **kwargs): + """ + Acquire dark images. + + Args: + num_images (int): Number of dark images to acquire. + exposure_time (float): Exposure time for each dark image in seconds. + name (str): Name for the dark image acquisition. Default: "dark" + + Returns: + ScanReport + """ + super().__init__(frames_per_trigger=frames_per_trigger, exp_time=exp_time, **kwargs) + self.components = TomoComponents(self) + + def scan_report_instructions(self): + """ + Generate scan report instructions for the dark image acquisition. + This method provides the necessary instructions to listen to the camera progress during the scan. + """ + if not self.components.cameras: + yield from super().scan_report_instructions() + return + + # Use the first camera or "gfcam" if available for reporting + report_camera = ( + "gfcam" if "gfcam" in self.components.cameras else self.components.cameras[0] + ) + yield from self.stubs.scan_report_instruction({"device_progress": [report_camera]}) + + def scan_core(self): + yield from self.components.acquire_dark(self.frames_per_trigger, self.exp_time, name="dark") + + class TomoScan(LineScan): scan_name = "tomo_line_scan" -- 2.49.1 From a9b275c0b9628b9ee40666291451c06a0f5bf938 Mon Sep 17 00:00:00 2001 From: gac-x02da Date: Mon, 16 Jun 2025 22:20:42 +0200 Subject: [PATCH 03/10] wip --- tomcat_bec/devices/pco_edge/pcoedgecamera.py | 9 +- tomcat_bec/scans/__init__.py | 16 +-- tomcat_bec/scans/simple_scans.py | 113 ++++++++++++++----- 3 files changed, 99 insertions(+), 39 deletions(-) diff --git a/tomcat_bec/devices/pco_edge/pcoedgecamera.py b/tomcat_bec/devices/pco_edge/pcoedgecamera.py index 75ea0cb..2bc810e 100644 --- a/tomcat_bec/devices/pco_edge/pcoedgecamera.py +++ b/tomcat_bec/devices/pco_edge/pcoedgecamera.py @@ -434,11 +434,16 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): "/gpfs/test/test-beamline" # FIXME: This should be from the scan message ) if "file_prefix" not in scan_args: - scan_args["file_prefix"] = scan_msg.info["file_components"][0].split("/")[-1] + "_" + file_base = scan_msg.info["file_components"][0].split("/")[-1] + file_suffix = scan_msg.info.get("file_suffix") or "" + comps = [file_base, self.name] + if file_suffix: + comps.append(file_suffix) + scan_args["file_prefix"] = "_".join(comps) self.configure(scan_args) if scan_msg.scan_type == "step": - num_points = self.frames_per_trigger.get() * scan_msg.num_points # type: ignore + num_points = self.frames_per_trigger.get() * max(scan_msg.num_points, 1) # type: ignore else: num_points = self.frames_per_trigger.get() diff --git a/tomcat_bec/scans/__init__.py b/tomcat_bec/scans/__init__.py index ad3c815..fdb7f1f 100644 --- a/tomcat_bec/scans/__init__.py +++ b/tomcat_bec/scans/__init__.py @@ -1,9 +1,9 @@ -from .simple_scans import TomoFlyScan, TomoScan +from .simple_scans import TomoFlyScan, TomoScan, AcquireDark from .tomcat_scans import TomcatSimpleSequence, TomcatSnapNStep -from .tutorial_fly_scan import ( - AcquireDark, - AcquireProjections, - AcquireRefs, - AcquireWhite, - TutorialFlyScanContLine, -) +# from .tutorial_fly_scan import ( +# # AcquireDark, +# AcquireProjections, +# AcquireRefs, +# AcquireWhite, +# TutorialFlyScanContLine, +# ) diff --git a/tomcat_bec/scans/simple_scans.py b/tomcat_bec/scans/simple_scans.py index ad22654..96a758a 100644 --- a/tomcat_bec/scans/simple_scans.py +++ b/tomcat_bec/scans/simple_scans.py @@ -62,6 +62,21 @@ class TomoComponents: frames_per_trigger=frames_per_trigger, ) + def scan_report_instructions(self): + """ + Generate scan report instructions for the dark image acquisition. + This method provides the necessary instructions to listen to the camera progress during the scan. + """ + if not self.components.cameras: + yield from super().scan_report_instructions() + return + + # Use the first camera or "gfcam" if available for reporting + report_camera = ( + "gfcam" if "gfcam" in self.components.cameras else self.components.cameras[0] + ) + yield from self.stubs.scan_report_instruction({"device_progress": [report_camera]}) + def complete(self): for cam in self.cameras: yield from self.stubs.send_rpc_and_wait(device=cam, func_name="on_complete") @@ -88,7 +103,7 @@ class TomoComponents: device=cam, func_name="update_live_processing_reference", reference_type=ref_type ) - def acquire_dark(self, num_images: int, exposure_time: float, name="dark"): + def acquire_dark(self, num_images: int, exposure_time: float, name="dark", restart=True): """ Acquire dark images. @@ -101,20 +116,22 @@ class TomoComponents: logger.info(f"Acquiring {num_images} dark images with exposure time {exposure_time}s.") self.connector.send_client_info(f"Acquiring {num_images} dark images.") - yield from self.restart_cameras( - name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images - ) + if restart: + yield from self.restart_cameras( + name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images + ) # yield from self.close_shutter() yield from self.stubs.trigger(min_wait=exposure_time) yield from self.complete() yield from self.update_live_processing_references(ref_type="dark") - yield from self.restore_configs(name=name) + if restart: + yield from self.restore_configs(name=name) # yield from self.open_shutter() self.connector.send_client_info("") logger.info("Dark image acquisition complete.") - def acquire_flat(self, num_images: int, exposure_time: float, name="flat"): + def acquire_flat(self, num_images: int, exposure_time: float, name="flat", restart=True): """ Acquire flat images. @@ -127,20 +144,23 @@ class TomoComponents: logger.info(f"Acquiring {num_images} flat images with exposure time {exposure_time}s.") self.connector.send_client_info(f"Acquiring {num_images} flat images.") - yield from self.restart_cameras( - name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images - ) + if restart: + yield from self.restart_cameras( + name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images + ) # yield from self.open_shutter() yield from self.stubs.trigger(min_wait=exposure_time) yield from self.complete() yield from self.update_live_processing_references(ref_type="flat") - yield from self.restore_configs(name=name) + + if restart: + yield from self.restore_configs(name=name) logger.info("Flat image acquisition complete.") self.connector.send_client_info("") - def acquire_references(self, num_darks: int, num_flats: int, exp_time: float, name: str): - yield from self.acquire_dark(num_darks, exposure_time=exp_time, name=name) - yield from self.acquire_flat(num_flats, exposure_time=exp_time, name=name) + def acquire_references(self, num_darks: int, num_flats: int, exp_time: float, restart=True): + yield from self.acquire_dark(num_darks, exposure_time=exp_time, restart=restart) + yield from self.acquire_flat(num_flats, exposure_time=exp_time, restart=restart) class AcquireDark(ScanBase): @@ -152,8 +172,7 @@ class AcquireDark(ScanBase): Args: num_images (int): Number of dark images to acquire. - exposure_time (float): Exposure time for each dark image in seconds. - name (str): Name for the dark image acquisition. Default: "dark" + exp_time (float): Exposure time for each dark image in seconds. Returns: ScanReport @@ -162,23 +181,59 @@ class AcquireDark(ScanBase): self.components = TomoComponents(self) def scan_report_instructions(self): - """ - Generate scan report instructions for the dark image acquisition. - This method provides the necessary instructions to listen to the camera progress during the scan. - """ - if not self.components.cameras: - yield from super().scan_report_instructions() - return - - # Use the first camera or "gfcam" if available for reporting - report_camera = ( - "gfcam" if "gfcam" in self.components.cameras else self.components.cameras[0] - ) - yield from self.stubs.scan_report_instruction({"device_progress": [report_camera]}) + yield from self.components.scan_report_instructions() def scan_core(self): - yield from self.components.acquire_dark(self.frames_per_trigger, self.exp_time, name="dark") + yield from self.components.acquire_dark(self.frames_per_trigger, self.exp_time, restart=False) +class AcquireFlat(ScanBase): + scan_name = "acquire_flat" + + def __init__(self, exp_time: float, frames_per_trigger: int = 1, **kwargs): + """ + Acquire flat images. + + Args: + num_images (int): Number of flat images to acquire. + exp_time (float): Exposure time for each flat image in seconds. + + Returns: + ScanReport + """ + super().__init__(frames_per_trigger=frames_per_trigger, exp_time=exp_time, **kwargs) + self.components = TomoComponents(self) + + def scan_report_instructions(self): + yield from self.components.scan_report_instructions() + + def scan_core(self): + yield from self.components.acquire_flat(self.frames_per_trigger, self.exp_time, restart=False) + +class AcquireReferences(ScanBase): + scan_name = "acquire_refs" + + def __init__(self, num_darks:int, num_flats:int, exp_time: float, frames_per_trigger: int = 1, **kwargs): + """ + Acquire flats and darks. + + Args: + num_darks (int): Number of dark images to acquire. + num_flats (int): Number of flat images to acquire. + exp_time (float): Exposure time for each flat image in seconds. + + Returns: + ScanReport + """ + super().__init__(frames_per_trigger=frames_per_trigger, exp_time=exp_time, **kwargs) + self.num_darks = num_darks + self.num_flats = num_flats + self.components = TomoComponents(self) + + def scan_report_instructions(self): + yield from self.components.scan_report_instructions() + + def scan_core(self): + yield from self.components.acquire_references(self.num_darks, self.num_flats, self.frames_per_trigger, self.exp_time, restart=False) class TomoScan(LineScan): scan_name = "tomo_line_scan" -- 2.49.1 From e11fb31386a4dd3f16fccb58162ee3b47ef85f22 Mon Sep 17 00:00:00 2001 From: wakonig_k Date: Mon, 16 Jun 2025 22:36:00 +0200 Subject: [PATCH 04/10] wip --- tomcat_bec/scans/simple_scans.py | 64 ++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/tomcat_bec/scans/simple_scans.py b/tomcat_bec/scans/simple_scans.py index 96a758a..7344d0e 100644 --- a/tomcat_bec/scans/simple_scans.py +++ b/tomcat_bec/scans/simple_scans.py @@ -49,6 +49,16 @@ class TomoComponents: file_path: str = "", frames_per_trigger: int = 1, ): + """ + Restart the cameras with a new configuration. + This is typically used to reset the cameras during another scan, e.g. before acquiring dark or flat images. + Args: + name (str): Name of the configuration to restart with. + num_images (int): Number of images to acquire. + prefix (str): Prefix for the file names. + file_path (str): Path where the files will be saved. + frames_per_trigger (int): Number of frames to acquire per trigger. + """ if not prefix: return for cam in self.cameras: @@ -64,24 +74,32 @@ class TomoComponents: def scan_report_instructions(self): """ - Generate scan report instructions for the dark image acquisition. + Generate scan report instructions for the acquisition. This method provides the necessary instructions to listen to the camera progress during the scan. """ - if not self.components.cameras: - yield from super().scan_report_instructions() + if not self.cameras: return # Use the first camera or "gfcam" if available for reporting - report_camera = ( - "gfcam" if "gfcam" in self.components.cameras else self.components.cameras[0] - ) + report_camera = "gfcam" if "gfcam" in self.cameras else self.cameras[0] yield from self.stubs.scan_report_instruction({"device_progress": [report_camera]}) def complete(self): + """ + Complete the acquisition by sending an RPC to each camera. + This method is typically called after the acquisition is done to finalize the process and start + writing the virtual dataset. + """ for cam in self.cameras: yield from self.stubs.send_rpc_and_wait(device=cam, func_name="on_complete") def restore_configs(self, name: str): + """ + Restore the camera configurations after an acquisition. + + Args: + name (str): Name of the configuration to restore. + """ for cam in self.cameras: yield from self.stubs.send_rpc_and_wait( device=cam, func_name="restore_config", name=name @@ -121,7 +139,7 @@ class TomoComponents: name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images ) # yield from self.close_shutter() - yield from self.stubs.trigger(min_wait=exposure_time) + yield from self.stubs.trigger(min_wait=exposure_time * num_images) yield from self.complete() yield from self.update_live_processing_references(ref_type="dark") if restart: @@ -149,7 +167,7 @@ class TomoComponents: name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images ) # yield from self.open_shutter() - yield from self.stubs.trigger(min_wait=exposure_time) + yield from self.stubs.trigger(min_wait=exposure_time * num_images) yield from self.complete() yield from self.update_live_processing_references(ref_type="flat") @@ -165,8 +183,9 @@ class TomoComponents: class AcquireDark(ScanBase): scan_name = "acquire_dark" + gui_config = {"Acquisition Parameters": ["num_images", "exp_time"]} - def __init__(self, exp_time: float, frames_per_trigger: int = 1, **kwargs): + def __init__(self, num_images: int, exp_time: float, **kwargs): """ Acquire dark images. @@ -177,6 +196,7 @@ class AcquireDark(ScanBase): Returns: ScanReport """ + frames_per_trigger = num_images if num_images > 0 else 1 super().__init__(frames_per_trigger=frames_per_trigger, exp_time=exp_time, **kwargs) self.components = TomoComponents(self) @@ -184,22 +204,28 @@ class AcquireDark(ScanBase): yield from self.components.scan_report_instructions() def scan_core(self): - yield from self.components.acquire_dark(self.frames_per_trigger, self.exp_time, restart=False) + yield from self.components.acquire_dark( + self.frames_per_trigger, self.exp_time, restart=False + ) + class AcquireFlat(ScanBase): scan_name = "acquire_flat" + gui_config = {"Acquisition Parameters": ["num_images", "exp_time"]} - def __init__(self, exp_time: float, frames_per_trigger: int = 1, **kwargs): + def __init__(self, num_images: int, exp_time: float, **kwargs): """ Acquire flat images. Args: num_images (int): Number of flat images to acquire. exp_time (float): Exposure time for each flat image in seconds. + frames_per_trigger (int): Number of frames to acquire per trigger. Returns: ScanReport """ + frames_per_trigger = num_images if num_images > 0 else 1 super().__init__(frames_per_trigger=frames_per_trigger, exp_time=exp_time, **kwargs) self.components = TomoComponents(self) @@ -207,24 +233,29 @@ class AcquireFlat(ScanBase): yield from self.components.scan_report_instructions() def scan_core(self): - yield from self.components.acquire_flat(self.frames_per_trigger, self.exp_time, restart=False) + yield from self.components.acquire_flat( + self.frames_per_trigger, self.exp_time, restart=False + ) + class AcquireReferences(ScanBase): scan_name = "acquire_refs" + gui_config = {"Acquisition Parameters": ["num_darks", "num_flats", "exp_time"]} - def __init__(self, num_darks:int, num_flats:int, exp_time: float, frames_per_trigger: int = 1, **kwargs): + def __init__(self, num_darks: int, num_flats: int, exp_time: float, **kwargs): """ - Acquire flats and darks. + Acquire flats and darks. Args: num_darks (int): Number of dark images to acquire. num_flats (int): Number of flat images to acquire. exp_time (float): Exposure time for each flat image in seconds. + frames_per_trigger (int): Number of frames to acquire per trigger. Returns: ScanReport """ - super().__init__(frames_per_trigger=frames_per_trigger, exp_time=exp_time, **kwargs) + super().__init__(exp_time=exp_time, **kwargs) self.num_darks = num_darks self.num_flats = num_flats self.components = TomoComponents(self) @@ -233,7 +264,8 @@ class AcquireReferences(ScanBase): yield from self.components.scan_report_instructions() def scan_core(self): - yield from self.components.acquire_references(self.num_darks, self.num_flats, self.frames_per_trigger, self.exp_time, restart=False) + yield from self.components.acquire_references(self.num_darks, self.num_flats, self.exp_time) + class TomoScan(LineScan): scan_name = "tomo_line_scan" -- 2.49.1 From 11251841c3b56a0fc39d0fd27430fffb73eb1f80 Mon Sep 17 00:00:00 2001 From: wakonig_k Date: Mon, 16 Jun 2025 22:39:58 +0200 Subject: [PATCH 05/10] wip --- tomcat_bec/scans/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tomcat_bec/scans/__init__.py b/tomcat_bec/scans/__init__.py index fdb7f1f..46e6b20 100644 --- a/tomcat_bec/scans/__init__.py +++ b/tomcat_bec/scans/__init__.py @@ -1,5 +1,6 @@ -from .simple_scans import TomoFlyScan, TomoScan, AcquireDark +from .simple_scans import AcquireDark, AcquireFlat, AcquireReferences, TomoFlyScan, TomoScan from .tomcat_scans import TomcatSimpleSequence, TomcatSnapNStep + # from .tutorial_fly_scan import ( # # AcquireDark, # AcquireProjections, -- 2.49.1 From ab57c482506965d3b0630e0ca8167c44af63d8fe Mon Sep 17 00:00:00 2001 From: gac-x02da Date: Tue, 17 Jun 2025 10:35:29 +0200 Subject: [PATCH 06/10] wip --- tomcat_bec/devices/pco_edge/pcoedgecamera.py | 13 ++++++-- tomcat_bec/scans/simple_scans.py | 31 ++++++++++---------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/tomcat_bec/devices/pco_edge/pcoedgecamera.py b/tomcat_bec/devices/pco_edge/pcoedgecamera.py index 2bc810e..417ffad 100644 --- a/tomcat_bec/devices/pco_edge/pcoedgecamera.py +++ b/tomcat_bec/devices/pco_edge/pcoedgecamera.py @@ -245,7 +245,8 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): self, name: str, file_path: str = "", - file_prefix: str = "", + file_name: str | None = None, + file_suffix:str = "", num_images: int | None = None, frames_per_trigger: int | None = None, ) -> StatusBase: @@ -263,14 +264,20 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): Returns: DeviceStatus: The status of the restart operation. It resolves when the camera is ready to receive the first image. """ + if file_name is not None and file_suffix: + raise ValueError("Both file_name and file_suffix are specified. Please choose one.") + self.acq_configs[name] = {} conf = {} if file_path: self.acq_configs[name]["file_path"] = self.file_path.get() conf["file_path"] = file_path - if file_prefix: + if file_suffix: self.acq_configs[name]["file_prefix"] = self.file_prefix.get() - conf["file_prefix"] = file_prefix + conf["file_prefix"] = "_".join([self.file_prefix.get(), file_suffix]) + if file_name: + self.acq_configs[name]["file_prefix"] = self.file_prefix.get() + conf["file_prefix"] = file_name if num_images is not None: self.acq_configs[name]["num_images"] = self.num_images.get() conf["num_images"] = num_images diff --git a/tomcat_bec/scans/simple_scans.py b/tomcat_bec/scans/simple_scans.py index 7344d0e..432278f 100644 --- a/tomcat_bec/scans/simple_scans.py +++ b/tomcat_bec/scans/simple_scans.py @@ -45,7 +45,7 @@ class TomoComponents: self, name: str, num_images: int, - prefix: str = "", + file_suffix: str = "", file_path: str = "", frames_per_trigger: int = 1, ): @@ -55,18 +55,16 @@ class TomoComponents: Args: name (str): Name of the configuration to restart with. num_images (int): Number of images to acquire. - prefix (str): Prefix for the file names. + file_suffix (str): Suffix for the file names. file_path (str): Path where the files will be saved. frames_per_trigger (int): Number of frames to acquire per trigger. """ - if not prefix: - return for cam in self.cameras: yield from self.stubs.send_rpc_and_wait( device=cam, func_name="restart_with_new_config", name=name, - file_prefix=prefix, + file_suffix=file_suffix, file_path=file_path, num_images=num_images, frames_per_trigger=frames_per_trigger, @@ -121,7 +119,7 @@ class TomoComponents: device=cam, func_name="update_live_processing_reference", reference_type=ref_type ) - def acquire_dark(self, num_images: int, exposure_time: float, name="dark", restart=True): + def acquire_dark(self, num_images: int, exposure_time: float, name="dark", restart=True, restore=True): """ Acquire dark images. @@ -136,20 +134,20 @@ class TomoComponents: if restart: yield from self.restart_cameras( - name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images + name=name, file_suffix=name, num_images=num_images, frames_per_trigger=num_images ) # yield from self.close_shutter() yield from self.stubs.trigger(min_wait=exposure_time * num_images) yield from self.complete() yield from self.update_live_processing_references(ref_type="dark") - if restart: + if restore: yield from self.restore_configs(name=name) # yield from self.open_shutter() self.connector.send_client_info("") logger.info("Dark image acquisition complete.") - def acquire_flat(self, num_images: int, exposure_time: float, name="flat", restart=True): + def acquire_flat(self, num_images: int, exposure_time: float, name="flat", restart=True, restore=True): """ Acquire flat images. @@ -164,21 +162,21 @@ class TomoComponents: if restart: yield from self.restart_cameras( - name=name, prefix=name, num_images=num_images, frames_per_trigger=num_images + name=name, file_suffix=name, num_images=num_images, frames_per_trigger=num_images ) # yield from self.open_shutter() yield from self.stubs.trigger(min_wait=exposure_time * num_images) yield from self.complete() yield from self.update_live_processing_references(ref_type="flat") - if restart: + if restore: yield from self.restore_configs(name=name) logger.info("Flat image acquisition complete.") self.connector.send_client_info("") - def acquire_references(self, num_darks: int, num_flats: int, exp_time: float, restart=True): - yield from self.acquire_dark(num_darks, exposure_time=exp_time, restart=restart) - yield from self.acquire_flat(num_flats, exposure_time=exp_time, restart=restart) + def acquire_references(self, num_darks: int, num_flats: int, exp_time: float, restart=True, restore=True): + yield from self.acquire_dark(num_darks, exposure_time=exp_time, restart=restart, restore=restore) + yield from self.acquire_flat(num_flats, exposure_time=exp_time, restart=restart, restore=restore) class AcquireDark(ScanBase): @@ -263,8 +261,11 @@ class AcquireReferences(ScanBase): def scan_report_instructions(self): yield from self.components.scan_report_instructions() + def pre_scan(self): + yield from self.components.acquire_references(self.num_darks, self.num_flats, self.exp_time, restart=True, restore=False) + def scan_core(self): - yield from self.components.acquire_references(self.num_darks, self.num_flats, self.exp_time) + pass class TomoScan(LineScan): -- 2.49.1 From 058de83fbed767b9677ebb3f7f9ba2830210288a Mon Sep 17 00:00:00 2001 From: wakonig_k Date: Tue, 17 Jun 2025 10:38:29 +0200 Subject: [PATCH 07/10] wip --- tomcat_bec/devices/pco_edge/pcoedgecamera.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tomcat_bec/devices/pco_edge/pcoedgecamera.py b/tomcat_bec/devices/pco_edge/pcoedgecamera.py index 417ffad..1900536 100644 --- a/tomcat_bec/devices/pco_edge/pcoedgecamera.py +++ b/tomcat_bec/devices/pco_edge/pcoedgecamera.py @@ -70,6 +70,12 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): analysis_signal = Cpt(Signal, name="analysis_signal", kind=Kind.hinted, doc="Analysis Signal") analysis_signal2 = Cpt(Signal, name="analysis_signal2", kind=Kind.hinted, doc="Analysis Signal") preview = Cpt(PreviewSignal, ndim=2, name="preview", doc="Camera raw data preview signal") + preview_corrected = Cpt( + PreviewSignal, + ndim=2, + name="preview_corrected", + doc="Camera preview signal with flat and dark correction", + ) progress = Cpt( ProgressSignal, name="progress", @@ -207,8 +213,8 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): def _on_preview_update(self, img: np.ndarray): corrected_img = self.live_processing.apply_flat_dark_correction(img) self.live_processing.on_new_data(corrected_img) - self.preview.put(corrected_img) - self._run_subs(sub_type=self.SUB_DEVICE_MONITOR_2D, obj=self, value=corrected_img) + self.preview.put(img) + self.preview_corrected.put(corrected_img) def _on_count_update(self, count: int): """ @@ -246,7 +252,7 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): name: str, file_path: str = "", file_name: str | None = None, - file_suffix:str = "", + file_suffix: str = "", num_images: int | None = None, frames_per_trigger: int | None = None, ) -> StatusBase: @@ -266,7 +272,7 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): """ if file_name is not None and file_suffix: raise ValueError("Both file_name and file_suffix are specified. Please choose one.") - + self.acq_configs[name] = {} conf = {} if file_path: @@ -376,7 +382,9 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): status = DeviceStatus(self) if self.backend.status != StdDaqStatus.IDLE: self.backend.add_status_callback( - status, success=[StdDaqStatus.IDLE], error=[StdDaqStatus.REJECTED, StdDaqStatus.ERROR] + status, + success=[StdDaqStatus.IDLE], + error=[StdDaqStatus.REJECTED, StdDaqStatus.ERROR], ) self.backend.stop() else: -- 2.49.1 From 5699363acf848c08cb9dfa890c7cb99f1c521534 Mon Sep 17 00:00:00 2001 From: wakonig_k Date: Tue, 17 Jun 2025 11:01:53 +0200 Subject: [PATCH 08/10] wip --- tomcat_bec/devices/pco_edge/pcoedgecamera.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tomcat_bec/devices/pco_edge/pcoedgecamera.py b/tomcat_bec/devices/pco_edge/pcoedgecamera.py index 1900536..abc5798 100644 --- a/tomcat_bec/devices/pco_edge/pcoedgecamera.py +++ b/tomcat_bec/devices/pco_edge/pcoedgecamera.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +from collections import deque from typing import TYPE_CHECKING, Literal, cast import numpy as np @@ -123,6 +124,7 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): self.backend = StdDaqClient(parent=self, ws_url=std_daq_ws, rest_url=std_daq_rest) self.backend.add_count_callback(self._on_count_update) self.live_preview = None + self.converted_files = deque(maxlen=100) # Store the last 10 converted files self.acq_configs = {} if std_daq_live is not None: self.live_preview = StdDaqPreview(url=std_daq_live, cb=self._on_preview_update) @@ -539,6 +541,12 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): """Called to inquire if a device has completed a scans.""" def _create_dataset(_status: DeviceStatus): + if self.target_file in self.converted_files: + logger.info( + f"File {self.target_file} already exists in the converted files list. " + "Skipping dataset creation." + ) + return self.backend.create_virtual_datasets( self.file_path.get(), file_prefix=self.file_prefix.get() # type: ignore ) @@ -547,8 +555,9 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): file_path=self.target_file, done=True, successful=True, - hinted_location={"data": "data"}, + hinted_location={"data": "tomcat-pco/data"}, ) + self.converted_files.append(self.target_file) logger.info(f"Finished writing to {self.target_file}") status = self.acq_done() -- 2.49.1 From da9004b4873883a1a377ffd2b146f5cacf0bbab7 Mon Sep 17 00:00:00 2001 From: wakonig_k Date: Tue, 17 Jun 2025 11:08:10 +0200 Subject: [PATCH 09/10] wip --- tomcat_bec/devices/pco_edge/pcoedgecamera.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tomcat_bec/devices/pco_edge/pcoedgecamera.py b/tomcat_bec/devices/pco_edge/pcoedgecamera.py index abc5798..b80faaa 100644 --- a/tomcat_bec/devices/pco_edge/pcoedgecamera.py +++ b/tomcat_bec/devices/pco_edge/pcoedgecamera.py @@ -125,6 +125,7 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): self.backend.add_count_callback(self._on_count_update) self.live_preview = None self.converted_files = deque(maxlen=100) # Store the last 10 converted files + self.target_files = deque(maxlen=100) # Store the last 10 target files self.acq_configs = {} if std_daq_live is not None: self.live_preview = StdDaqPreview(url=std_daq_live, cb=self._on_preview_update) @@ -361,6 +362,7 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): num_images=self.num_images.get(), # type: ignore ) self.camera_status.set(CameraStatus.RUNNING).wait() + self.target_files.append(self.target_file) def is_running(*, value, timestamp, **_): return bool(value == CameraStatusCode.RUNNING) @@ -541,11 +543,11 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): """Called to inquire if a device has completed a scans.""" def _create_dataset(_status: DeviceStatus): - if self.target_file in self.converted_files: - logger.info( - f"File {self.target_file} already exists in the converted files list. " - "Skipping dataset creation." - ) + if ( + self.target_file in self.converted_files + or self.target_file not in self.target_files + ): + logger.info(f"File {self.target_file} already processed or not in target files.") return self.backend.create_virtual_datasets( self.file_path.get(), file_prefix=self.file_prefix.get() # type: ignore -- 2.49.1 From 26c52e21bea485efc011b5741f67e7289b065db9 Mon Sep 17 00:00:00 2001 From: gac-x02da Date: Wed, 18 Jun 2025 15:25:30 +0200 Subject: [PATCH 10/10] wip --- tomcat_bec/devices/pco_edge/pcoedgecamera.py | 3 ++- tomcat_bec/devices/std_daq/std_daq_live_processing.py | 2 +- tomcat_bec/scans/simple_scans.py | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tomcat_bec/devices/pco_edge/pcoedgecamera.py b/tomcat_bec/devices/pco_edge/pcoedgecamera.py index b80faaa..aad85f7 100644 --- a/tomcat_bec/devices/pco_edge/pcoedgecamera.py +++ b/tomcat_bec/devices/pco_edge/pcoedgecamera.py @@ -70,12 +70,13 @@ class PcoEdge5M(PSIDeviceBase, PcoEdgeBase): analysis_signal = Cpt(Signal, name="analysis_signal", kind=Kind.hinted, doc="Analysis Signal") analysis_signal2 = Cpt(Signal, name="analysis_signal2", kind=Kind.hinted, doc="Analysis Signal") - preview = Cpt(PreviewSignal, ndim=2, name="preview", doc="Camera raw data preview signal") + preview = Cpt(PreviewSignal, ndim=2, name="preview", doc="Camera raw data preview signal", num_rotation_90=1, transpose=False) preview_corrected = Cpt( PreviewSignal, ndim=2, name="preview_corrected", doc="Camera preview signal with flat and dark correction", + num_rotation_90=1, transpose=False ) progress = Cpt( ProgressSignal, diff --git a/tomcat_bec/devices/std_daq/std_daq_live_processing.py b/tomcat_bec/devices/std_daq/std_daq_live_processing.py index 714c5ea..5368d24 100644 --- a/tomcat_bec/devices/std_daq/std_daq_live_processing.py +++ b/tomcat_bec/devices/std_daq/std_daq_live_processing.py @@ -126,7 +126,7 @@ class StdDaqLiveProcessing: corrected_data = np.divide( data - dark, flat_corr, out=np.zeros_like(data, dtype=np.float32), where=flat_corr != 0 ) - return corrected_data + return np.clip(corrected_data, a_min=0, a_max=None) @typechecked def _load_and_update_reference( diff --git a/tomcat_bec/scans/simple_scans.py b/tomcat_bec/scans/simple_scans.py index 432278f..8419ee6 100644 --- a/tomcat_bec/scans/simple_scans.py +++ b/tomcat_bec/scans/simple_scans.py @@ -262,11 +262,11 @@ class AcquireReferences(ScanBase): yield from self.components.scan_report_instructions() def pre_scan(self): - yield from self.components.acquire_references(self.num_darks, self.num_flats, self.exp_time, restart=True, restore=False) + yield from self.components.acquire_references(self.num_darks, self.num_flats, self.exp_time) def scan_core(self): - pass - + yield None + class TomoScan(LineScan): scan_name = "tomo_line_scan" -- 2.49.1