Skip to content

Commit 960ba85

Browse files
committed
add trigger and sensor mode settings
1 parent 715d9c6 commit 960ba85

File tree

1 file changed

+41
-22
lines changed

1 file changed

+41
-22
lines changed

src/navigate/model/devices/camera/daheng.py

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,23 @@
2929
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3030
# POSSIBILITY OF SUCH DAMAGE.
3131

32-
# NOTE: This module depends on Daheng's proprietary 'gxipy' SDK.
33-
# To use this camera class, 'gxipy' must be installed manually.
34-
# See the ImportError message below for installation instructions.
32+
# ################################################################################
33+
# WARNING:
34+
# This camera class has not been internally tested by our team.
35+
# Users are advised to exercise caution when using it.
36+
# NOTE:
37+
# This module depends on Daheng's proprietary 'gxipy' SDK.
38+
# To use this camera class, 'gxipy' must be installed manually.
39+
# See the ImportError message below for installation instructions.
40+
# ################################################################################
3541

3642
# Standard Library Imports
3743
import logging
3844
from typing import Union, Any, List
3945

4046
# Third Party Imports
4147
try:
42-
from gxipy import DeviceManager, Device
48+
import gxipy as gx
4349
except ImportError:
4450
raise ImportError(
4551
"Missing required module 'gxipy'.\n"
@@ -56,15 +62,16 @@
5662
# Local Imports
5763
from navigate.model.utils.exceptions import UserVisibleException
5864
from navigate.model.devices.camera.base import CameraBase
59-
from navigate.model.devices.device_types import SequenceDevice
6065
from navigate.tools.decorators import log_initialization
6166

6267

63-
logger = logging.getLogger(__name__)
68+
# Logger Setup
69+
p = __name__.split(".")[1]
70+
logger = logging.getLogger(p)
6471

6572

6673
@log_initialization
67-
class DahengCamera(SequenceDevice):
74+
class DahengCamera(CameraBase):
6875
"""
6976
Daheng camera implementation for the MER2-1220-32U3C model.
7077
@@ -86,6 +93,8 @@ def __init__(self, microscope_name, device_connection, configuration):
8693
configuration : dict
8794
Device configuration settings (e.g. resolution, exposure).
8895
"""
96+
super().__init__(microscope_name, device_connection, configuration)
97+
8998
self.microscope_name = microscope_name
9099
self.device_connection = device_connection # Store raw connection for compatibility
91100
self.configuration = configuration
@@ -122,6 +131,8 @@ def __init__(self, microscope_name, device_connection, configuration):
122131
# Finish hardware setup (initialize feature_control etc.)
123132
self.initialize_sdk_state()
124133

134+
self.camera_parameters["supported_readout_directions"] = ["Top-to-Bottom"]
135+
125136
def __str__(self) -> str:
126137
"""
127138
Return a human-readable string representation of the camera status.
@@ -157,10 +168,10 @@ def get_connect_params(cls) -> list:
157168
list
158169
An empty list since no parameters are required for Daheng connection.
159170
"""
160-
return []
171+
return ["serial_number"]
161172

162173
@classmethod
163-
def connect(cls, serial_number: str = None) -> Device:
174+
def connect(cls, serial_number: str = None) -> gx.Device:
164175
"""
165176
Connect to a Daheng camera using the gxipy SDK.
166177
@@ -181,7 +192,7 @@ def connect(cls, serial_number: str = None) -> Device:
181192
If no camera is found, or if the specified serial number does not match any camera.
182193
"""
183194
# Discover and list available devices using the Daheng SDK
184-
device_manager = DeviceManager()
195+
device_manager = gx.DeviceManager()
185196
device_manager.update_device_list()
186197
dev_info_list = device_manager.get_device_list()
187198

@@ -228,8 +239,14 @@ def initialize_sdk_state(self) -> None:
228239
self.device_serial_number = self.feature_control.get_string_feature("DeviceSerialNumber").get()
229240
self.payload_size = self.feature_control.get_int_feature("PayloadSize").get()
230241

231-
# Set default acquisition mode
232-
self.feature_control.get_enum_feature("AcquisitionMode").set("Continuous")
242+
# Set trigger source (GPIO line 2) for external triggering
243+
self.device.TriggerSource.set(gx.GxTriggerSourceEntry.LINE2)
244+
# Set trigger active
245+
self.device.TriggerActivation.set(gx.GxTriggerActivationEntry.RISINGEDGE)
246+
# Set trigger mode to on
247+
self.device.TriggerMode.set(gx.GxSwitchEntry.ON)
248+
# Set acquisition mode to single frame
249+
self.device.AcquisitionMode.set(gx.GxAcquisitionModeEntry.SINGLEFRAME)
233250

234251
# Get current image dimensions from hardware
235252
width = self.feature_control.get_int_feature("Width").get()
@@ -290,6 +307,7 @@ def report_settings(self) -> None:
290307

291308
try:
292309
# Retrieve current settings from camera hardware
310+
sensor_mode = self.device.SensorShutterMode.get()
293311
sensor_width = self.feature_control.get_int_feature("Width").get()
294312
sensor_height = self.feature_control.get_int_feature("Height").get()
295313
bin_x = self.feature_control.get_int_feature("BinningHorizontal").get()
@@ -300,7 +318,7 @@ def report_settings(self) -> None:
300318

301319
# Log all settings
302320
logger.info("Camera Settings:")
303-
logger.info(" sensor_mode: N/A (fixed to Continuous)")
321+
logger.info(f" sensor_mode: {sensor_mode} (0: Normal, 1: Light-Sheet)")
304322
logger.info(f" binning: {bin_x}x{bin_y}")
305323
logger.info(" readout_speed: N/A")
306324
logger.info(" trigger_active: N/A")
@@ -322,6 +340,13 @@ def disconnect(self) -> None:
322340
This method safely closes the device connection and clears
323341
all associated resources, even if errors occur during shutdown.
324342
"""
343+
# Reset associated handles and status flags
344+
self.feature_control = None # release device handler
345+
self.data_stream = None
346+
self.device_serial_number = None
347+
self.payload_size = None
348+
self.is_connected = False
349+
325350
if self.device is not None:
326351
try:
327352
# Attempt to close the hardware connection
@@ -331,13 +356,6 @@ def disconnect(self) -> None:
331356
finally:
332357
self.device = None
333358

334-
# Reset associated handles and status flags
335-
self.feature_control = None
336-
self.data_stream = None
337-
self.device_serial_number = None
338-
self.payload_size = None
339-
self.is_connected = False
340-
341359
logger.info("Daheng camera disconnected and internal state cleared.")
342360

343361
def set_sensor_mode(self, mode: str) -> None:
@@ -350,8 +368,9 @@ def set_sensor_mode(self, mode: str) -> None:
350368
Requested sensor mode (e.g., 'Normal', 'Light-Sheet').
351369
This value is ignored as Daheng does not support sensor mode switching.
352370
"""
353-
logger.warning(f"Sensor mode '{mode}' is not supported on Daheng cameras.")
354-
371+
modes_dict = {"Normal": 0, "Light-Sheet": 1}
372+
self.device.SensorShutterMode.set(modes_dict.get(mode, 0))
373+
355374
# Default to scan mode 0 for compatibility
356375
self._scan_mode = 0
357376

0 commit comments

Comments
 (0)