Files
navigate/test/config/test_configuration.py
2025-12-04 16:07:30 +08:00

427 lines
16 KiB
Python

# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted for academic and research use only
# (subject to the limitations in the disclaimer below)
# provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Standard Imports
import unittest
import yaml
import os
# Third Party Imports
# Local Imports
class TestConfiguration(unittest.TestCase):
def setUp(self):
current_path = os.path.abspath(os.path.dirname(__file__))
root_path = os.path.dirname(os.path.dirname(current_path))
yaml_path = os.path.join(
root_path, "src", "navigate", "config", "configuration.yaml"
)
with open(yaml_path) as file:
self.data = yaml.safe_load(file)
def tearDown(self):
pass
# # hardware head section has been removed
# def test_hardware_section(self):
# expected_hardware = ["daq", "camera", "filter_wheel", "stage", "zoom"]
# hardware_types = self.data["hardware"].keys()
# for hardware_type in hardware_types:
# self.assertIn(hardware_type, expected_hardware)
# if isinstance(self.data["hardware"][hardware_type], dict):
# hardware_keys = self.data["hardware"][hardware_type].keys()
# for key in hardware_keys:
# self.assertIn("type", self.data["hardware"][hardware_type])
# elif isinstance(self.data["hardware"][hardware_type], list):
# for i in range(len(self.data["hardware"][hardware_type])):
# self.assertIn("type", self.data["hardware"][hardware_type][i])
def test_gui_section(self):
expected_keys = ["channels"]
expected_channel_keys = [
"count",
# "laser_power",
# "exposure_time",
# "interval_time",
]
expected_stack_keys = ["step_size", "start_pos", "end_pos"]
min_max_step_keys = ["min", "max", "step"]
gui_keys = self.data["gui"].keys()
for key in gui_keys:
self.assertIn(key, expected_keys)
# Channels Entry
if key == "channels":
channel_keys = self.data["gui"][key].keys()
for channel_key in channel_keys:
self.assertIn(channel_key, expected_channel_keys)
if channel_key != "count":
channel_key_keys = self.data["gui"][key][channel_key].keys()
for channel_key_key in channel_key_keys:
self.assertIn(channel_key_key, min_max_step_keys)
# Stack Acquisition Entry
elif key == "stack_acquisition":
stack_keys = self.data["gui"][key].keys()
for stack_key in stack_keys:
self.assertIn(stack_key, expected_stack_keys)
stack_key_keys = self.data["gui"][key][stack_key].keys()
for stack_key_key in stack_key_keys:
self.assertIn(stack_key_key, min_max_step_keys)
# Timepoint Entry
elif key == "timepoint":
timepoint_keys = self.data["gui"][key].keys()
for timepoint_key in timepoint_keys:
timepoint_key_keys = self.data["gui"][key][timepoint_key].keys()
for timepoint_key_key in timepoint_key_keys:
self.assertIn(timepoint_key_key, min_max_step_keys)
else:
raise ValueError("Unexpected key in gui section")
def test_microscope_section(self):
expected_hardware = [
"daq",
"camera",
"remote_focus",
"galvo",
"shutter",
"laser",
"filter_wheel",
"stage",
"zoom",
]
microscopes = self.data["microscopes"].keys()
for microscope in microscopes:
hardware = self.data["microscopes"][microscope].keys()
for hardware_type in hardware:
self.assertIn(hardware_type, expected_hardware)
if hardware_type == "daq":
self.daq_section(microscope=microscope, hardware_type=hardware_type)
elif hardware_type == "camera":
self.camera_section(
microscope=microscope, hardware_type=hardware_type
)
elif hardware_type == "remote_focus":
self.remote_focus_section(
microscope=microscope, hardware_type=hardware_type
)
elif hardware_type == "galvo":
self.galvo_section(
microscope=microscope, hardware_type=hardware_type
)
elif hardware_type == "filter_wheel":
self.filter_wheel_section(
microscope=microscope, hardware_type=hardware_type
)
elif hardware_type == "stage":
self.stage_section(
microscope=microscope, hardware_type=hardware_type
)
elif hardware_type == "zoom":
self.zoom_section(
microscope=microscope, hardware_type=hardware_type
)
elif hardware_type == "shutter":
self.shutter_section(
microscope=microscope, hardware_type=hardware_type
)
elif hardware_type == "laser":
self.laser_section(
microscope=microscope, hardware_type=hardware_type
)
else:
raise ValueError("Unexpected hardware type")
def daq_section(self, microscope, hardware_type):
expected_daq_keys = [
"hardware",
"sample_rate",
"sweep_time",
"master_trigger_out_line",
"camera_trigger_out_line",
"trigger_source",
"laser_port_switcher",
"laser_switch_state",
]
type_keys = ["type"]
daq_keys = self.data["microscopes"][microscope][hardware_type].keys()
for key in daq_keys:
if key == "hardware":
for type_key in type_keys:
self.assertIn(
type_key,
self.data["microscopes"][microscope][hardware_type][key],
)
else:
self.assertIn(key, expected_daq_keys)
def camera_section(self, microscope, hardware_type):
expected_keys = [
"hardware",
"defect_correct_mode",
"delay",
"settle_down",
"flip_x",
"flip_y",
]
type_keys = ["type", "serial_number", "camera_connection"]
camera_keys = self.data["microscopes"][microscope][hardware_type].keys()
for key in camera_keys:
if key == "hardware":
for type_key in type_keys:
self.assertIn(
type_key,
self.data["microscopes"][microscope][hardware_type][key],
)
else:
self.assertIn(key, expected_keys)
def remote_focus_section(self, microscope, hardware_type):
expected_keys = [
"hardware",
]
type_keys = ["type", "channel", "min", "max", "port", "baudrate"]
remote_focus_keys = self.data["microscopes"][microscope][hardware_type].keys()
for key in remote_focus_keys:
if key == "hardware":
for type_key in type_keys:
self.assertIn(
type_key,
self.data["microscopes"][microscope][hardware_type][key],
)
else:
self.assertIn(key, expected_keys)
def galvo_section(self, microscope, hardware_type):
expected_keys = [
"hardware",
"waveform",
"phase",
]
type_keys = ["type", "channel", "min", "max"]
if isinstance(self.data["microscopes"][microscope][hardware_type], list):
for i in range(len(self.data["microscopes"][microscope][hardware_type])):
galvo_keys = self.data["microscopes"][microscope][hardware_type][
i
].keys()
for key in galvo_keys:
if key == "hardware":
for type_key in type_keys:
self.assertIn(
type_key,
self.data["microscopes"][microscope][hardware_type][i][
key
],
)
else:
self.assertIn(key, expected_keys)
else:
raise ValueError("Galvo section is not a list")
def filter_wheel_section(self, microscope, hardware_type):
expected_keys = [
"hardware",
"filter_wheel_delay",
"available_filters",
]
type_keys = ["type", "wheel_number", "port", "baudrate"]
keys = self.data["microscopes"][microscope][hardware_type].keys()
for key in keys:
if key == "hardware":
for type_key in type_keys:
self.assertIn(
type_key,
self.data["microscopes"][microscope][hardware_type][key],
)
elif key == "available_filters":
assert isinstance(
self.data["microscopes"][microscope][hardware_type][key], dict
)
else:
self.assertIn(key, expected_keys)
def stage_section(self, microscope, hardware_type):
expected_keys = [
"hardware",
"x_max",
"x_min",
"y_max",
"y_min",
"z_max",
"z_min",
"f_max",
"f_min",
"theta_max",
"theta_min",
"x_offset",
"y_offset",
"z_offset",
"theta_offset",
"f_offset",
"joystick_axes",
"flip_x",
"flip_y",
"flip_z",
"flip_f",
]
type_keys = [
"type",
"serial_number",
"axes",
"volts_per_micron",
"axes_mapping",
"max",
"min",
"controllername",
"stages",
"refmode",
"port",
"baudrate",
"timeout",
]
for key in self.data["microscopes"][microscope][hardware_type].keys():
if key == "hardware":
if isinstance(
self.data["microscopes"][microscope][hardware_type][key], list
):
for i in range(
len(self.data["microscopes"][microscope][hardware_type][key])
):
for type_key in type_keys:
self.assertIn(
type_key,
self.data["microscopes"][microscope][hardware_type][
key
][i],
)
elif isinstance(
self.data["microscopes"][microscope][hardware_type][key], dict
):
for type_key in type_keys:
self.assertIn(
type_key,
self.data["microscopes"][microscope][hardware_type][key],
)
else:
raise ValueError("Stage hardware is not a list or dict")
else:
self.assertIn(key, expected_keys)
def zoom_section(self, microscope, hardware_type):
expected_keys = ["hardware", "position", "pixel_size", "stage_positions"]
type_keys = ["type", "servo_id", "port", "baudrate"]
for key in self.data["microscopes"][microscope][hardware_type].keys():
if key == "hardware":
for type_key in type_keys:
self.assertIn(
type_key,
self.data["microscopes"][microscope][hardware_type][key],
)
elif key == "position":
assert isinstance(
self.data["microscopes"][microscope][hardware_type][key], dict
)
elif key == "pixel_size":
assert isinstance(
self.data["microscopes"][microscope][hardware_type][key], dict
)
elif key == "stage_positions":
assert isinstance(
self.data["microscopes"][microscope][hardware_type][key], dict
)
else:
self.assertIn(key, expected_keys)
def shutter_section(self, microscope, hardware_type):
expected_keys = ["hardware"]
type_keys = ["type", "channel", "min", "max"]
for key in self.data["microscopes"][microscope][hardware_type].keys():
if key == "hardware":
for type_key in type_keys:
self.assertIn(
type_key,
self.data["microscopes"][microscope][hardware_type][key],
)
else:
self.assertIn(key, expected_keys)
def laser_section(self, microscope, hardware_type):
expected_keys = [
"wavelength",
"onoff",
"power",
"type",
]
hardware_keys = ["type", "channel", "min", "max"]
if isinstance(self.data["microscopes"][microscope][hardware_type], list):
for i in range(len(self.data["microscopes"][microscope][hardware_type])):
laser_keys = self.data["microscopes"][microscope][hardware_type][
i
].keys()
for key in laser_keys:
if key == "onoff" or key == "power":
onoff_keys = self.data["microscopes"][microscope][
hardware_type
][i][key]["hardware"].keys()
for onoff_key in onoff_keys:
self.assertIn(onoff_key, hardware_keys)
else:
self.assertIn(key, expected_keys)
else:
raise ValueError("Laser section is not a list")