Compare commits
6 Commits
fix/contro
...
fix/rate_l
| Author | SHA1 | Date | |
|---|---|---|---|
| 149af32ab1 | |||
|
|
47f0b66791 | ||
| 2c0fced9b7 | |||
| d99b44b619 | |||
|
|
dbab981ac2 | ||
|
|
bdd7f1767f |
Binary file not shown.
@@ -20,6 +20,7 @@ class flomniGuiTools:
|
||||
def __init__(self):
|
||||
self.text_box = None
|
||||
self.progressbar = None
|
||||
self.pdf_viewer = None
|
||||
|
||||
def set_client(self, client):
|
||||
self.client = client
|
||||
@@ -97,6 +98,7 @@ class flomniGuiTools:
|
||||
self.gui.flomni.delete_all()
|
||||
self.progressbar = None
|
||||
self.text_box = None
|
||||
self.pdf_viewer = None
|
||||
|
||||
def flomnigui_idle(self):
|
||||
self.flomnigui_show_gui()
|
||||
@@ -114,6 +116,27 @@ class flomniGuiTools:
|
||||
)
|
||||
idle_text_box.set_html_text(text)
|
||||
|
||||
def flomnigui_docs(self):
|
||||
|
||||
import csaxs_bec
|
||||
from pathlib import Path
|
||||
|
||||
# Ensure this is a Path object, not a string
|
||||
csaxs_bec_basepath = Path(csaxs_bec.__file__)
|
||||
|
||||
pdf_file = "FZP_flomni_with_CS.pdf"
|
||||
|
||||
# Build the absolute path correctly
|
||||
pdf_file = (
|
||||
csaxs_bec_basepath.parent / 'bec_ipython_client' / 'plugins' / 'flomni' / 'docs' / pdf_file
|
||||
).resolve()
|
||||
|
||||
if self.pdf_viewer is None:
|
||||
self.flomnigui_show_gui()
|
||||
self.flomnigui_remove_all_docks()
|
||||
self.pdf_viewer = self.gui.flomni.new(widget="PdfViewerWidget")
|
||||
self.pdf_viewer.PdfViewerWidget.load_pdf(str(pdf_file))
|
||||
|
||||
def _flomnicam_check_device_exists(self, device):
|
||||
try:
|
||||
device
|
||||
@@ -156,8 +179,8 @@ class flomniGuiTools:
|
||||
)
|
||||
self.progressbar.set_value([progress, subtomo_progress, 0])
|
||||
if self.text_box is not None:
|
||||
text = f"Progress report:\n Tomo type: ....................... {self.progress['tomo_type']}\n Projection: ...................... {self.progress['projection']:.0f}\n Total projections expected ....... {self.progress['total_projections']}\n Angle: ........................... {self.progress['angle']}\n Current subtomo: ................. {self.progress['subtomo']}\n Current projection within subtomo: {self.progress['subtomo_projection']}\n Total projections per subtomo: ... {self.progress['subtomo_total_projections']}"
|
||||
self.text_box.set_plain_text(text)
|
||||
text = f"Progress report:\n Tomo type: ....................... {self.progress['tomo_type']}\n Projection: ...................... {self.progress['projection']:.0f}\n Total projections expected ....... {self.progress['total_projections']}\n Angle: ........................... {self.progress['angle']}\n Current subtomo: ................. {self.progress['subtomo']}\n Current projection within subtomo: {self.progress['subtomo_projection']}\n Total projections per subtomo: ... {self.progress['subtomo_total_projections']}"
|
||||
self.text_box.set_plain_text(text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -115,7 +115,7 @@ samy:
|
||||
softwareTrigger: false
|
||||
micfoc:
|
||||
description: Focusing motor of Microscope stage
|
||||
deviceClass: ophyd_devices.devices.EpicsMotorEx
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES06
|
||||
motor_resolution: 0.00125
|
||||
@@ -133,7 +133,7 @@ micfoc:
|
||||
softwareTrigger: false
|
||||
owis_samx:
|
||||
description: Owis motor stage samx
|
||||
deviceClass: ophyd_devices.devices.EpicsMotorEx
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES01
|
||||
motor_resolution: 0.00125
|
||||
@@ -151,7 +151,7 @@ owis_samx:
|
||||
softwareTrigger: false
|
||||
owis_samy:
|
||||
description: Owis motor stage samx
|
||||
deviceClass: ophyd_devices.devices.EpicsMotorEx
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES02
|
||||
motor_resolution: 0.00125
|
||||
@@ -169,7 +169,7 @@ owis_samy:
|
||||
softwareTrigger: false
|
||||
rotx:
|
||||
description: Rotation stage rotx
|
||||
deviceClass: ophyd_devices.devices.EpicsMotorEx
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES05
|
||||
motor_resolution: 0.0025
|
||||
@@ -190,7 +190,7 @@ rotx:
|
||||
softwareTrigger: false
|
||||
roty:
|
||||
description: Rotation stage rotx
|
||||
deviceClass: ophyd_devices.devices.EpicsMotorEx
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES04
|
||||
motor_resolution: 0.0025
|
||||
|
||||
96
csaxs_bec/device_configs/sastt.yaml
Normal file
96
csaxs_bec/device_configs/sastt.yaml
Normal file
@@ -0,0 +1,96 @@
|
||||
samx:
|
||||
description: Owis motor stage samx
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES01
|
||||
motor_resolution: 0.00125
|
||||
base_velocity: 0.0625
|
||||
velocity: 10
|
||||
backlash_distance: 0.125
|
||||
acceleration: 0.2
|
||||
user_offset_dir: 1
|
||||
deviceTags:
|
||||
- cSAXS
|
||||
- owis_samx
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: baseline
|
||||
softwareTrigger: false
|
||||
samy:
|
||||
description: Owis motor stage samy
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES02
|
||||
motor_resolution: 0.00125
|
||||
base_velocity: 0.0625
|
||||
velocity: 10
|
||||
backlash_distance: 0.125
|
||||
acceleration: 0.2
|
||||
user_offset_dir: 0
|
||||
deviceTags:
|
||||
- cSAXS
|
||||
- owis_samx
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: baseline
|
||||
softwareTrigger: false
|
||||
rotx:
|
||||
description: Rotation stage rotx
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES03
|
||||
motor_resolution: 0.0025
|
||||
base_velocity: 0.5
|
||||
velocity: 7.5
|
||||
backlash_distance: 0.25
|
||||
acceleration: 0.2
|
||||
user_offset_dir: 1
|
||||
limits:
|
||||
- -0.1
|
||||
- 0.1
|
||||
deviceTags:
|
||||
- cSAXS
|
||||
- rotx
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: baseline
|
||||
softwareTrigger: false
|
||||
roty:
|
||||
description: Rotation stage roty
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES04
|
||||
motor_resolution: 0.0025
|
||||
base_velocity: 0.5
|
||||
velocity: 7.5
|
||||
backlash_distance: 0.25
|
||||
acceleration: 0.2
|
||||
user_offset_dir: 0
|
||||
limits:
|
||||
- -0.1
|
||||
- 0.1
|
||||
deviceTags:
|
||||
- cSAXS
|
||||
- roty
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: baseline
|
||||
softwareTrigger: false
|
||||
micfoc:
|
||||
description: Focusing motor of Microscope stage
|
||||
deviceClass: ophyd_devices.devices.psi_motor.EpicsUserMotorVME
|
||||
deviceConfig:
|
||||
prefix: X12SA-ES2-ES05
|
||||
motor_resolution: 0.00125
|
||||
base_velocity: 0.25
|
||||
velocity: 2.5
|
||||
backlash_distance: 0.125
|
||||
acceleration: 0.4
|
||||
user_offset_dir: 0
|
||||
deviceTags:
|
||||
- cSAXS
|
||||
- micfoc
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: baseline
|
||||
softwareTrigger: false
|
||||
@@ -15,10 +15,10 @@ CI/CD pipelines can run without the pyueye library or the related DLLs installed
|
||||
from __future__ import annotations
|
||||
|
||||
import atexit
|
||||
import time
|
||||
from typing import Literal
|
||||
|
||||
import numpy as np
|
||||
import time
|
||||
from bec_lib.logger import bec_logger
|
||||
|
||||
from csaxs_bec.devices.ids_cameras.base_integration.utils import check_error
|
||||
@@ -67,8 +67,8 @@ class IDSCameraObject:
|
||||
check_error(ueye.is_SetDisplayMode(self.h_cam, ueye.IS_SET_DM_DIB), "IDSCameraObject")
|
||||
|
||||
if (
|
||||
int.from_bytes(self.s_info.nColorMode.value, byteorder="big")
|
||||
== self.ueye.IS_COLORMODE_BAYER
|
||||
int.from_bytes(self.s_info.nColorMode.value, byteorder="big")
|
||||
== self.ueye.IS_COLORMODE_BAYER
|
||||
):
|
||||
logger.info("Bayer color mode detected.")
|
||||
# setup the color depth to the current windows setting
|
||||
@@ -77,16 +77,16 @@ class IDSCameraObject:
|
||||
) # TODO This raises an error - maybe check the m_n_colormode value
|
||||
self.bytes_per_pixel = int(self.n_bits_per_pixel / 8)
|
||||
elif (
|
||||
int.from_bytes(self.s_info.nColorMode.value, byteorder="big")
|
||||
== self.ueye.IS_COLORMODE_CBYCRY
|
||||
int.from_bytes(self.s_info.nColorMode.value, byteorder="big")
|
||||
== self.ueye.IS_COLORMODE_CBYCRY
|
||||
):
|
||||
# for color camera models use RGB32 mode
|
||||
self.m_n_colormode = self.ueye.IS_CM_BGRA8_PACKED
|
||||
self.n_bits_per_pixel = self.ueye.INT(32)
|
||||
self.bytes_per_pixel = int(self.n_bits_per_pixel / 8)
|
||||
elif (
|
||||
int.from_bytes(self.s_info.nColorMode.value, byteorder="big")
|
||||
== self.ueye.IS_COLORMODE_MONOCHROME
|
||||
int.from_bytes(self.s_info.nColorMode.value, byteorder="big")
|
||||
== self.ueye.IS_COLORMODE_MONOCHROME
|
||||
):
|
||||
# for color camera models use RGB32 mode
|
||||
self.m_n_colormode = self.ueye.IS_CM_MONO8
|
||||
@@ -160,12 +160,12 @@ class Camera:
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
camera_id: int,
|
||||
m_n_colormode: Literal[0, 1, 2, 3] = 1,
|
||||
bits_per_pixel: int = 24,
|
||||
connect: bool = True,
|
||||
force_monochrome: bool = False,
|
||||
self,
|
||||
camera_id: int,
|
||||
m_n_colormode: Literal[0, 1, 2, 3] = 1,
|
||||
bits_per_pixel: int = 24,
|
||||
connect: bool = True,
|
||||
force_monochrome: bool = False,
|
||||
):
|
||||
self.ueye = ueye
|
||||
self.camera_id = camera_id
|
||||
@@ -173,8 +173,13 @@ class Camera:
|
||||
self.force_monochrome = force_monochrome
|
||||
self._connected = False
|
||||
self.cam = None
|
||||
|
||||
atexit.register(self.on_disconnect)
|
||||
|
||||
self._enable_warning_rate_limit: bool = False
|
||||
self._last_rate_limited_log: float = 0
|
||||
self._warning_log_rate_limit_s: float = 10
|
||||
|
||||
if connect:
|
||||
self.on_connect()
|
||||
|
||||
@@ -255,7 +260,7 @@ class Camera:
|
||||
def get_image_data(self) -> np.ndarray | None:
|
||||
"""Get the image data from the camera."""
|
||||
if not self._connected:
|
||||
logger.warning("Camera is not connected.")
|
||||
self._rate_limited_warning_log("Camera is not connected.")
|
||||
return None
|
||||
array = self.ueye.get_data(
|
||||
self.cam.pc_image_mem,
|
||||
@@ -282,6 +287,22 @@ class Camera:
|
||||
|
||||
return img
|
||||
|
||||
def set_camera_rate_limiting(self, enabled: bool, rate_limit_s: float | None = None):
|
||||
if rate_limit_s is not None:
|
||||
if rate_limit_s <= 0:
|
||||
raise ValueError(f"Invalid rate limit: {rate_limit_s}, must be positive nonzero.")
|
||||
self._warning_log_rate_limit_s = rate_limit_s
|
||||
self._enable_warning_rate_limit = enabled
|
||||
|
||||
def _rate_limited_warning_log(self, msg: "str"):
|
||||
if (
|
||||
self._enable_warning_rate_limit
|
||||
and time.monotonic() < self._last_rate_limited_log + self._warning_log_rate_limit_s
|
||||
):
|
||||
return
|
||||
self._last_rate_limited_log = time.monotonic()
|
||||
logger.warning(msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
|
||||
@@ -29,8 +29,14 @@ class IDSCamera(PSIDeviceBase):
|
||||
to interact with the IDS camera using the pyueye library.
|
||||
"""
|
||||
|
||||
image = Cpt(PreviewSignal, name="image", ndim=2, doc="Preview signal for the camera.", num_rotation_90=0,
|
||||
transpose=False)
|
||||
image = Cpt(
|
||||
PreviewSignal,
|
||||
name="image",
|
||||
ndim=2,
|
||||
doc="Preview signal for the camera.",
|
||||
num_rotation_90=0,
|
||||
transpose=False,
|
||||
)
|
||||
roi_signal = Cpt(
|
||||
AsyncSignal,
|
||||
name="roi_signal",
|
||||
@@ -43,19 +49,19 @@ class IDSCamera(PSIDeviceBase):
|
||||
USER_ACCESS = ["live_mode", "mask", "set_rect_roi", "get_last_image"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
name: str,
|
||||
camera_id: int,
|
||||
prefix: str = "",
|
||||
scan_info: ScanInfo | None = None,
|
||||
m_n_colormode: Literal[0, 1, 2, 3] = 1,
|
||||
bits_per_pixel: Literal[8, 24] = 24,
|
||||
live_mode: bool = False,
|
||||
num_rotation_90: int = 0,
|
||||
transpose: bool = False,
|
||||
force_monochrome: bool = False,
|
||||
**kwargs,
|
||||
self,
|
||||
*,
|
||||
name: str,
|
||||
camera_id: int,
|
||||
prefix: str = "",
|
||||
scan_info: ScanInfo | None = None,
|
||||
m_n_colormode: Literal[0, 1, 2, 3] = 1,
|
||||
bits_per_pixel: Literal[8, 24] = 24,
|
||||
live_mode: bool = False,
|
||||
num_rotation_90: int = 0,
|
||||
transpose: bool = False,
|
||||
force_monochrome: bool = False,
|
||||
**kwargs,
|
||||
):
|
||||
"""Initialize the IDS Camera.
|
||||
|
||||
@@ -133,7 +139,7 @@ class IDSCamera(PSIDeviceBase):
|
||||
if x + width > img_shape[1] or y + height > img_shape[0]:
|
||||
raise ValueError("ROI exceeds camera dimensions.")
|
||||
mask = np.zeros(img_shape, dtype=np.uint8)
|
||||
mask[y: y + height, x: x + width] = 1
|
||||
mask[y : y + height, x : x + width] = 1
|
||||
self.mask = mask
|
||||
|
||||
def _start_live(self):
|
||||
@@ -162,6 +168,7 @@ class IDSCamera(PSIDeviceBase):
|
||||
|
||||
def _live_mode_loop(self, stop_event: threading.Event):
|
||||
"""Loop to capture images in live mode."""
|
||||
self.cam.set_camera_rate_limiting(True)
|
||||
while not stop_event.is_set():
|
||||
try:
|
||||
self.process_data(self.cam.get_image_data())
|
||||
@@ -169,6 +176,7 @@ class IDSCamera(PSIDeviceBase):
|
||||
logger.error(f"Error in live mode loop: {e}")
|
||||
break
|
||||
stop_event.wait(0.2) # 5 Hz
|
||||
self.cam.set_camera_rate_limiting(False)
|
||||
|
||||
def process_data(self, image: np.ndarray | None):
|
||||
"""Process the image data before sending it to the preview signal."""
|
||||
|
||||
Reference in New Issue
Block a user