Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/navigate/controller/sub_controllers/acquire_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ def toggle_bdv_widgets(self, main_widget: str, dependent_widgets: list) -> None:
state = self.acquire_pop.tab_frame.inputs[main_widget].get_variable().get()
for widget in dependent_widgets:
self.acquire_pop.tab_frame.inputs[widget].widget.config(
state="readonly" if state else "disabled"
state="normal" if state else "disabled"
)

def update_microscope_mode(self, *args: Iterable) -> None:
Expand Down
2 changes: 1 addition & 1 deletion src/navigate/model/devices/camera/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def __init__(
# readout time in model and controller
self.camera_parameters["trigger_source"] = 2.0 # external trigger
self.camera_parameters["readout_speed"] = 1.0
self.camera_parameters["pixel_size_in_microns"] = 6.5
# self.camera_parameters["pixel_size_in_microns"] = 6.5
self.camera_parameters["trigger_active"] = 1.0
self.camera_parameters["trigger_mode"] = 1.0 # standard trigger mode
self.camera_parameters["trigger_polarity"] = 2.0
Expand Down
41 changes: 35 additions & 6 deletions src/navigate/model/devices/camera/ximea.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

# Third Party Imports
from ximea import xiapi
import numpy as np

# Local Imports
from navigate.model.devices.camera.base import CameraBase
Expand Down Expand Up @@ -73,6 +74,7 @@ def __init__(
super().__init__(microscope_name, device_connection, configuration)

#: str: Name of the microscope
self._frames_received = None
self.microscope_name = microscope_name

#: object: Camera Object
Expand All @@ -81,6 +83,15 @@ def __init__(
#: dict: Configuration settings
self.configuration = configuration

#: bool: Auto restart flag
self.auto_restart = True

#: int: Auto restart counter
self.auto_restart_counter = 0

#: int: Timeout counter
self.timeout_counter = 0

#: dict: Camera parameters
self.camera_parameters["x_pixels"] = self.cam.get_param("width:max")
self.camera_parameters["y_pixels"] = self.cam.get_param("height:max")
Expand Down Expand Up @@ -379,7 +390,7 @@ def set_ROI_and_binning(self, roi_width=2048, roi_height=2048, center_x=1024, ce
result = self.set_ROI(roi_width, roi_height, center_x, center_y)
return result

def initialize_image_series(self, data_buffer=None, number_of_frames=100):
def initialize_image_series(self, data_buffer=None, number_of_frames=1000):
"""Initialize Ximea Camera image series.

Parameters
Expand All @@ -395,8 +406,12 @@ def initialize_image_series(self, data_buffer=None, number_of_frames=100):
self._number_of_frames = number_of_frames
self._frames_received = 0

# set buffer policy: XI_BP_SAFE
self.cam.set_param("buffer_policy", "XI_BP_SAFE")
# # set buffer policy: XI_BP_SAFE
# self.cam.set_param("buffer_policy", "XI_BP_SAFE")

# *** use UNSAFE
self.cam.set_param("buffer_policy", "XI_BP_UNSAFE")

# set image data format to XI_MONO16, this value can be set only if acquisition is stopped.
self.cam.set_param('imgdataformat', "XI_MONO16")
#imgpayloadsize changes automatically after setting imgdataformat
Expand All @@ -422,15 +437,29 @@ def get_new_frame(self):
frame : numpy.ndarray
Frame ids from Ximea camera.
"""
# attach buffer to image object
self._image.bp = self._data_buffer[self._frames_received].ctypes.data
self._image.bp_size = self._data_buffer[self._frames_received].nbytes
# # attach buffer to image object
# self._image.bp = self._data_buffer[self._frames_received].ctypes.data
# self._image.bp_size = self._data_buffer[self._frames_received].nbytes
# get data from camera
try:
self.cam.get_image(self._image, 500)
except xiapi.Xi_error as e:
if e.status == 10: # XI_ERR_TIMEOUT
self.timeout_counter += 1
if self.auto_restart and self.timeout_counter >= 5 and self.auto_restart_counter < 3:
self.auto_restart_counter += 1
logger.warning(f"Timeout error while getting image. Restarting acquisition. Auto restart it {self.auto_restart_counter}")
self.cam.stop_acquisition()
self.cam.start_acquisition()
return [-1]
logger.error(f"Error getting image from camera: {e}")
return []
self.auto_restart_counter = 0
self.timeout_counter = 0
# *** copy image to data buffer
self._data_buffer[self._frames_received][:, :] = np.copy(
self._image.get_image_data_numpy()
)

frames_received = [self._frames_received]

Expand Down
38 changes: 37 additions & 1 deletion src/navigate/model/features/common_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from threading import Lock
import logging
from multiprocessing.managers import ListProxy
from queue import Queue, Empty

# Third party imports

Expand Down Expand Up @@ -1119,7 +1120,7 @@ def __init__(
# TODO: distance > 1000 should not be hardcoded and somehow related to
# different kinds of stage devices.
#: int: The stage distance threshold for pausing the data thread.
self.stage_distance_threshold = 1000
self.stage_distance_threshold = 200

#: dict: A dictionary of the previous position in the multi-position table.
self.pre_position = None
Expand Down Expand Up @@ -1156,6 +1157,7 @@ def __init__(

self.prepare_next_channel = PrepareNextChannel(model)


#: dict: A dictionary defining the configuration for the z-stack acquisition
self.config_table = {
"signal": {
Expand All @@ -1172,6 +1174,12 @@ def __init__(
"node": {"node_type": "multi-step", "device_related": True},
}

if self.model.active_microscope_name == "Macroscale":
self.data_queue = Queue()
self.config_table["signal"]["response"] = self.signal_response_func

self.resend_trigger = False

def get_microscope_state(self, microscope_state: dict) -> None:
"""Get the microscope state from the configuration.

Expand Down Expand Up @@ -1313,6 +1321,9 @@ def signal_func(self):
A boolean value indicating whether to continue the z-stack acquisition
process.
"""
if self.resend_trigger:
return True

if self.model.stop_acquisition:
return False
data_thread_is_paused = False
Expand Down Expand Up @@ -1414,6 +1425,8 @@ def signal_end(self) -> bool:
bool
A boolean value indicating whether to end the current node.
"""
if self.resend_trigger:
return False

# end this node
if self.model.stop_acquisition:
Expand Down Expand Up @@ -1483,6 +1496,21 @@ def signal_end(self) -> bool:

return False

def signal_response_func(self, *args) -> bool:
try:
r = self.data_queue.get(timeout=100)
if r == -1:
self.resend_trigger = True
else:
self.resend_trigger = False
except Empty:
logger.warning(
"ZStackAcquisition: No data received within the timeout period."
)
return False

return True

def update_channel(self) -> None:
"""Update the active channel during multichannel acquisition.

Expand Down Expand Up @@ -1519,10 +1547,18 @@ def in_data_func(self, frame_ids: list) -> None:
A list of frame IDs received during data acquisition.

"""

if -1 in frame_ids and hasattr(self, "data_queue"):
self.data_queue.put(-1)
self.received_frames += frame_ids.index(-1)
return
self.received_frames += len(frame_ids)
if self.image_writer is not None:
self.image_writer.save_image(frame_ids)

if hasattr(self, "data_queue"):
self.data_queue.put(True)

def end_data_func(self) -> bool:
"""Check if all expected data frames have been received.

Expand Down
8 changes: 4 additions & 4 deletions src/navigate/view/popups/acquire_popup.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ def __init__(self, parent: AcquirePopUp, frame: ttk.Frame) -> None:
input_class=ValidatedSpinbox,
input_var=tk.StringVar(),
input_args={
"from_": 0,
"from_": -360,
"to": 360,
"increment": 1,
},
Expand All @@ -443,7 +443,7 @@ def __init__(self, parent: AcquirePopUp, frame: ttk.Frame) -> None:
input_class=ValidatedSpinbox,
input_var=tk.StringVar(),
input_args={
"from_": 0,
"from_": -360,
"to": 360,
"increment": 1,
},
Expand All @@ -455,11 +455,11 @@ def __init__(self, parent: AcquirePopUp, frame: ttk.Frame) -> None:
self.inputs["rotate_angle_z"] = LabelInput(
parent=rotate_notebook,
label_pos="top",
label="Y Angle",
label="Z Angle",
input_class=ValidatedSpinbox,
input_var=tk.StringVar(),
input_args={
"from_": 0,
"from_": -360,
"to": 360,
"increment": 1,
},
Expand Down
Loading