Reduce argument numbers with data class and constants

This commit is contained in:
Julian Stirling 2025-10-16 11:28:31 +01:00
parent 8a0836f368
commit 9fe6ad8efb

View file

@ -15,6 +15,7 @@ this is 10% of the expected motion is the measured axis.
from typing import Literal, Any, Optional from typing import Literal, Any, Optional
import time import time
from dataclasses import dataclass
from scipy.optimize import curve_fit from scipy.optimize import curve_fit
from PIL import Image from PIL import Image
@ -38,6 +39,11 @@ CSMDep = lt.deps.direct_thing_client_dependency(
) )
AutofocusDep = lt.deps.direct_thing_client_dependency(AutofocusThing, "/autofocus/") AutofocusDep = lt.deps.direct_thing_client_dependency(AutofocusThing, "/autofocus/")
## Size of movement in percentage of field of view
SMALL_STEP = 20
MEDIUM_STEP = 50
BIG_STEP = 200
class RomDataTracker: class RomDataTracker:
"""Class for tracking range of motion data.""" """Class for tracking range of motion data."""
@ -64,6 +70,20 @@ class RomDataTracker:
self.cor_lat_steps.clear() self.cor_lat_steps.clear()
@dataclass
class RomDeps:
"""Grouped dependencies for the Range of motion Thing.
These are used to pass the dependencies from actions to other sub-functions.
"""
autofocus: AutofocusDep
stage: StageDep
cam: CamDep
csm: CSMDep
logger: lt.deps.InvocationLogger
class ParasiticMotionError(Exception): class ParasiticMotionError(Exception):
"""Custom exception raised when parasitic motion is detected. """Custom exception raised when parasitic motion is detected.
@ -71,8 +91,6 @@ class ParasiticMotionError(Exception):
is too high. is too high.
""" """
pass
def _parasitic_detect(delta: float, max_allowed_delta: float) -> None: def _parasitic_detect(delta: float, max_allowed_delta: float) -> None:
"""Compare two values and raise parasitic motion error.""" """Compare two values and raise parasitic motion error."""
@ -132,9 +150,9 @@ def _predict_z(
lateral_positions = [i[axis] for i in positions] # x or y positions lateral_positions = [i[axis] for i in positions] # x or y positions
z_positions = [i["z"] for i in positions] z_positions = [i["z"] for i in positions]
parameters, _ = curve_fit(quadratic, lateral_positions, z_positions) fit_params, *_others = curve_fit(quadratic, lateral_positions, z_positions)
z_dest = quadratic( z_dest = quadratic(
stage.position[axis] + (relative_move / pixel_step[axis]), *parameters stage.position[axis] + (relative_move / pixel_step[axis]), *fit_params
) )
return z_dest - stage.position["z"] return z_dest - stage.position["z"]
@ -146,9 +164,7 @@ def _move_and_measure(
data: RomDataTracker, data: RomDataTracker,
image1: npt.ArrayLike, image1: npt.ArrayLike,
autofocus_proc: bool, autofocus_proc: bool,
csm: CSMDep, rom_deps: RomDeps,
autofocus: AutofocusDep,
cam: CamDep,
) -> tuple[npt.ArrayLike, str]: ) -> tuple[npt.ArrayLike, str]:
"""Move the stage and measure the offset between the two positions. """Move the stage and measure the offset between the two positions.
@ -156,22 +172,19 @@ def _move_and_measure(
:param axis: The axis in which the stage is moving. This must be 'x' or 'y'. :param axis: The axis in which the stage is moving. This must be 'x' or 'y'.
:param data: The object used to track stage coordinates, correlation and delta. :param data: The object used to track stage coordinates, correlation and delta.
:param image1: An image taken before moving to be correlated with image2. :param image1: An image taken before moving to be correlated with image2.
:param autofocus_proc: If true, looping.autofocus will be used after the stage moves. :param rom_deps: All dependencies that were passed to the calling Action
:param csm: A direct_thing_client dependency for camera stage mapping.
:param autofocus: A direct_thing_client dependency for autofocus.
:param cam: A raw_thing_client depeendency for the camera.
:return: All required data for the next move. This includes the updated delta value and offset. :return: All required data for the next move. This includes the updated delta value and offset.
Also returns what wrong_axis is i.e. if the direction is 'x', wrong_axis = 'y'. Also returns what wrong_axis is i.e. if the direction is 'x', wrong_axis = 'y'.
""" """
if axis == "x": if axis == "x":
csm.move_in_image_coordinates(x=step_size["x"], y=0) rom_deps.csm.move_in_image_coordinates(x=step_size["x"], y=0)
wrong_axis = "y" wrong_axis = "y"
else: else:
csm.move_in_image_coordinates(x=0, y=step_size["y"]) rom_deps.csm.move_in_image_coordinates(x=0, y=step_size["y"])
wrong_axis = "x" wrong_axis = "x"
if autofocus_proc: if autofocus_proc:
autofocus.looping_autofocus(dz=800) rom_deps.autofocus.looping_autofocus(dz=800)
image2 = np.array(Image.open(cam.grab_jpeg().open())) image2 = np.array(Image.open(rom_deps.cam.grab_jpeg().open()))
offset = fft_image_tracking.displacement_between_images( offset = fft_image_tracking.displacement_between_images(
image_0=image1, image_1=image2, sigma=10, fractional_threshold=0.1, pad=True image_0=image1, image_1=image2, sigma=10, fractional_threshold=0.1, pad=True
) # Units is pixels ) # Units is pixels
@ -186,11 +199,7 @@ def _acquire_z_predict_points(
direction: Literal[1, -1], direction: Literal[1, -1],
axis: Literal["x", "y"], axis: Literal["x", "y"],
data: RomDataTracker, data: RomDataTracker,
csm: CSMDep, rom_deps: RomDeps,
cam: CamDep,
stage: StageDep,
autofocus: AutofocusDep,
logger: lt.deps.InvocationLogger,
) -> None: ) -> None:
"""Complete 5 medium sized steps to collect stage coordinates for prediction. """Complete 5 medium sized steps to collect stage coordinates for prediction.
@ -200,32 +209,26 @@ def _acquire_z_predict_points(
:param direction: The direction the stage moves. :param direction: The direction the stage moves.
:params axis: The axis which is being measured. This must be 'x' or 'y'. :params axis: The axis which is being measured. This must be 'x' or 'y'.
:params data: The object used to track stage coordinates, correlation and delta. :params data: The object used to track stage coordinates, correlation and delta.
:param csm: A direct_thing_client dependency for camera stage mapping. :param rom_deps: All dependencies that were passed to the calling Action
:param camera: A raw_thing_client depeendency for the camera.
:param stage: A raw_thing_client depeendency for the microscope stage.
:param autofocus: A direct_thing_client dependency for autofocus.
:return: Stage_coords and cor_lat_steps are lists of data tracked throughout the test. :return: Stage_coords and cor_lat_steps are lists of data tracked throughout the test.
""" """
medium_step = 50
wrong_axis_max_medium = _generate_move_dicts( wrong_axis_max_medium = _generate_move_dicts(
medium_step, stream_resolution, direction, factor=0.1 MEDIUM_STEP, stream_resolution, direction, factor=0.1
) )
for _loop in range(5): for _loop in range(5):
image1 = np.array(Image.open(cam.grab_jpeg().open())) image1 = rom_deps.cam.grab_as_array()
offset, wrong_axis = _move_and_measure( offset, wrong_axis = _move_and_measure(
step_size=_generate_move_dicts(medium_step, stream_resolution, direction), step_size=_generate_move_dicts(MEDIUM_STEP, stream_resolution, direction),
axis=axis, axis=axis,
data=data, data=data,
image1=image1, image1=image1,
autofocus_proc=True, autofocus_proc=True,
csm=csm, rom_deps=rom_deps,
autofocus=autofocus,
cam=cam,
) )
logger.info(f"Offset measured as {data.delta[axis]}") rom_deps.logger.info(f"Offset measured as {data.delta[axis]}")
data.measure(stage.position, offset) data.measure(rom_deps.stage.position, offset)
_parasitic_detect( _parasitic_detect(
delta=abs(data.delta[wrong_axis]), delta=abs(data.delta[wrong_axis]),
@ -234,17 +237,12 @@ def _acquire_z_predict_points(
def _check_stage_operation( def _check_stage_operation(
small_step: int,
stream_resolution: list[int], stream_resolution: list[int],
direction: Literal[1, -1], direction: Literal[1, -1],
axis: Literal["x", "y"], axis: Literal["x", "y"],
data: RomDataTracker, data: RomDataTracker,
minimum_offset_small: dict[str, float], minimum_offset_small: dict[str, float],
csm: CSMDep, rom_deps: RomDeps,
cam: CamDep,
stage: StageDep,
autofocus: AutofocusDep,
logger: lt.deps.InvocationLogger,
) -> None: ) -> None:
"""Carries out 3 small moves in a given direction and axis. """Carries out 3 small moves in a given direction and axis.
@ -254,46 +252,41 @@ def _check_stage_operation(
image quality is not causing the correlation to be unsuccessful. If the correlation image quality is not causing the correlation to be unsuccessful. If the correlation
is still too low then the edge is found. is still too low then the edge is found.
:params small_step: The integer value used to generate the small step sizes. Delta is updated and tracked after each move.
:params stream_resolution: The resolution of the stream from the camera. :params stream_resolution: The resolution of the stream from the camera.
:param direction: The direction the stage moves. :param direction: The direction the stage moves.
:params axis: The axis which is being measured. This must be 'x' or 'y'. :params axis: The axis which is being measured. This must be 'x' or 'y'.
:params data: The object used to track stage coordinates, correlation and delta. :params data: The object used to track stage coordinates, correlation and delta.
:params minimum_offset_small: A dictionary containing the minimum values for :params minimum_offset_small: A dictionary containing the minimum values for
a successful correlation. a successful correlation.
:param csm: A direct_thing_client dependency for camera stage mapping. :param rom_deps: All dependencies that were passed to the calling Action
:param camera: A raw_thing_client depeendency for the camera.
:param stage: A raw_thing_client depeendency for the microscope stage.
:param autofocus: A direct_thing_client dependency for autofocus.
:return: Stage_coords and cor_lat_steps are lists of data tracked throughout the test. :return: Stage_coords and cor_lat_steps are lists of data tracked throughout the test.
Delta is updated and tracked after each move.
""" """
failure_count = 0 failure_count = 0
for _loop in range(3): for _loop in range(3):
image1 = np.array(Image.open(cam.grab_jpeg().open())) image1 = rom_deps.cam.grab_as_array()
offset, wrong_axis = _move_and_measure( offset, wrong_axis = _move_and_measure(
step_size=_generate_move_dicts(small_step, stream_resolution, direction), step_size=_generate_move_dicts(SMALL_STEP, stream_resolution, direction),
axis=axis, axis=axis,
data=data, data=data,
image1=image1, image1=image1,
autofocus_proc=False, autofocus_proc=False,
csm=csm, rom_deps=rom_deps,
autofocus=autofocus,
cam=cam,
) )
logger.info(f"Offset measured as {data.delta[axis]}") rom_deps.logger.info(f"Offset measured as {data.delta[axis]}")
# If correlation is too small, refocuses and capture new image 3 times. # If correlation is too small, refocuses and capture new image 3 times.
while ( while (
np.abs(data.delta[axis]) < np.abs(minimum_offset_small[axis]) np.abs(data.delta[axis]) < np.abs(minimum_offset_small[axis])
and failure_count < 3 and failure_count < 3
): ):
logger.info( rom_deps.logger.info(
f"Correlation failed. Refocusing to check. Attempt {failure_count + 1}/3" f"Correlation failed. Refocusing to check. Attempt {failure_count + 1}/3"
) )
autofocus.looping_autofocus(dz=1000) rom_deps.autofocus.looping_autofocus(dz=1000)
image2 = np.array(Image.open(cam.grab_jpeg().open())) image2 = rom_deps.cam.grab_as_array()
failure_count += 1 failure_count += 1
offset = fft_image_tracking.displacement_between_images( offset = fft_image_tracking.displacement_between_images(
image_0=image1, image_0=image1,
@ -305,45 +298,39 @@ def _check_stage_operation(
# Units is pixels # Units is pixels
data.delta["x"] = int(offset[1]) data.delta["x"] = int(offset[1])
data.delta["y"] = int(offset[0]) data.delta["y"] = int(offset[0])
logger.info( rom_deps.logger.info(
f"Displacement found was {data.delta[axis]}.\ f"Displacement found was {data.delta[axis]}.\
Minimum offset is {minimum_offset_small[axis]}" Minimum offset is {minimum_offset_small[axis]}"
) )
data.measure(stage.position, offset) data.measure(rom_deps.stage.position, offset)
_parasitic_detect( _parasitic_detect(
abs(data.delta[wrong_axis]), abs(data.delta[wrong_axis]),
abs( abs(
_generate_move_dicts( _generate_move_dicts(
small_step, stream_resolution, direction, factor=0.1 SMALL_STEP, stream_resolution, direction, factor=0.1
)[wrong_axis] )[wrong_axis]
), ),
) )
# this means the edge has been found # this means the edge has been found
if np.abs(data.delta[axis]) < np.abs(minimum_offset_small[axis]): if np.abs(data.delta[axis]) < np.abs(minimum_offset_small[axis]):
logger.info("Edge has been found.") rom_deps.logger.info("Edge has been found.")
break break
def _motion_detection( def _motion_detection(
axis: Literal["x", "y"], axis: Literal["x", "y"],
direction: Literal[1, -1], direction: Literal[1, -1],
csm: CSMDep, rom_deps: RomDeps,
stage: StageDep,
cam: CamDep,
logger: lt.deps.InvocationLogger,
) -> dict[str, int]: ) -> dict[str, int]:
"""Move the stage until motion is detected along a specified axis and direction. """Move the stage until motion is detected along a specified axis and direction.
:params axis: The axis in which the stage is moving. This must be 'x' or 'y'. :params axis: The axis in which the stage is moving. This must be 'x' or 'y'.
:params direction: The direction in which the stage was moving :params direction: The direction in which the stage was moving
previous to motion detection being used. previous to motion detection being used.
:param csm: A direct_thing_client dependency for camera stage mapping. :param rom_deps: All dependencies that were passed to the calling Action
:param stage: A raw_thing_client depeendency for the microscope stage.
:param camera: A raw_thing_client depeendency for the camera.
:return: The stage coordinates where motion was detected.
""" """
# Array of increasing pixel sizes (powers of 2 from 1 to 512) # Array of increasing pixel sizes (powers of 2 from 1 to 512)
displacements = [2**i for i in range(10)] displacements = [2**i for i in range(10)]
@ -356,10 +343,12 @@ def _motion_detection(
for loop in range(np.shape(displacements)[0]): for loop in range(np.shape(displacements)[0]):
this_motion_step[axis] = displacements[loop] * direction * -1 this_motion_step[axis] = displacements[loop] * direction * -1
logger.info(f"Testing with step size {this_motion_step[axis]}") rom_deps.logger.info(f"Testing with step size {this_motion_step[axis]}")
image1 = np.array(Image.open(cam.grab_jpeg().open())) image1 = rom_deps.cam.grab_as_array()
csm.move_in_image_coordinates(x=this_motion_step["x"], y=this_motion_step["y"]) rom_deps.csm.move_in_image_coordinates(
image2 = np.array(Image.open(cam.grab_jpeg().open())) x=this_motion_step["x"], y=this_motion_step["y"]
)
image2 = rom_deps.cam.grab_as_array()
offset = fft_image_tracking.displacement_between_images( offset = fft_image_tracking.displacement_between_images(
image_0=image1, image_0=image1,
image_1=image2, image_1=image2,
@ -369,19 +358,19 @@ def _motion_detection(
) )
delta["x"] = int(offset[1]) delta["x"] = int(offset[1])
delta["y"] = int(offset[0]) delta["y"] = int(offset[0])
logger.info(f"Offset measured as {np.abs(delta[axis])}") rom_deps.logger.info(f"Offset measured as {np.abs(delta[axis])}")
if np.abs(delta[axis]) > motion_minimum: if np.abs(delta[axis]) > motion_minimum:
logger.info("Motion detected.") rom_deps.logger.info("Motion detected.")
break break
return stage.position return rom_deps.stage.position
def _collate_data(data: dict, time: float, csm: CSMDep) -> dict[str, Any]: def _collate_data(data: dict, total_time: float, csm: CSMDep) -> dict[str, Any]:
"""Collate and calculate all useful data from ROM test. """Collate and calculate all useful data from ROM test.
:param data: Dictionary created from ROM test. :param data: Dictionary created from ROM test.
:param time: Total time of the range of motion test. :param total_time: Total time of the range of motion test.
:param csm: A direct_thing_client dependency for camera stage mapping. :param csm: A direct_thing_client dependency for camera stage mapping.
""" """
x_range = abs( x_range = abs(
@ -394,7 +383,7 @@ def _collate_data(data: dict, time: float, csm: CSMDep) -> dict[str, Any]:
) )
step_range = [x_range, y_range] step_range = [x_range, y_range]
data["Time"] = time data["Time"] = total_time
data["CSM Matrix"] = csm.image_to_stage_displacement_matrix data["CSM Matrix"] = csm.image_to_stage_displacement_matrix
data["Step Range"] = step_range data["Step Range"] = step_range
return data return data
@ -403,67 +392,56 @@ def _collate_data(data: dict, time: float, csm: CSMDep) -> dict[str, Any]:
class RangeofMotionThing(lt.Thing): class RangeofMotionThing(lt.Thing):
"""A class used to measure the range of motion of the stage in X and Y.""" """A class used to measure the range of motion of the stage in X and Y."""
last_calibration = lt.ThingSetting(initial_value=None, model=dict, readonly=True)
def rom_axis( def rom_axis(
self, self,
autofocus: AutofocusDep,
stage: StageDep,
cam: CamDep,
csm: CSMDep,
logger: lt.deps.InvocationLogger,
axis: Literal["x", "y"], axis: Literal["x", "y"],
direction: Literal[1, -1], direction: Literal[1, -1],
rom_deps: RomDeps,
) -> dict: ) -> dict:
"""Measure the range of motion in a single axis and direction. """Measure the range of motion in a single axis and direction.
:param stage: A raw_thing_client depeendency for the microscope stage. :param rom_deps: All dependencies that were passed to the calling Action
:param cam: A raw_thing_client depeendency for the camera.
:param csm: A raw_thing_client depeendency for camera stage mapping.
:param logger: A raw_thing_client depeendency for the logger.
:param axis: The axis which is being measured. This must be 'x' or 'y'. :param axis: The axis which is being measured. This must be 'x' or 'y'.
:param direction: The direction which is being measured. This must be 1 or -1. :param direction: The direction which is being measured. This must be 1 or -1.
:return: Results dictionary containing stage positions, :return: Results dictionary containing stage positions,
correlations and the final position. correlations and the final position.
""" """
autofocus.looping_autofocus(dz=1000) rom_deps.autofocus.looping_autofocus(dz=1000)
starting_position = list(stage.position.values()) starting_position = list(rom_deps.stage.position.values())
rom_data = RomDataTracker() # initialise data tracking object rom_data = RomDataTracker() # initialise data tracking object
axis_results = {} axis_results = {}
try: try:
logger.info( dir_str = "positive" if direction == 1 else "negative"
f"Beginning the {axis}-axis in the {'positive' if direction == 1 else 'negative'} " rom_deps.logger.info(
"direction" f"Beginning the {axis}-axis in the {dir_str} direction"
) )
# Generate required dictionaries for step sizes and minimum offsets # Generate required dictionaries for step sizes and minimum offsets
stream_resolution = [820, 616] stream_resolution = [820, 616]
big_step = 200
small_step = 20
step_sizes_big = _generate_move_dicts( step_sizes_big = _generate_move_dicts(
big_step, stream_resolution, direction BIG_STEP, stream_resolution, direction
) )
rom_data.stage_coords.append(stage.position) rom_data.stage_coords.append(rom_deps.stage.position)
logger.info("Moving the stage in 5 medium sized steps.") rom_deps.logger.info("Moving the stage in 5 medium sized steps.")
_acquire_z_predict_points( _acquire_z_predict_points(
stream_resolution=stream_resolution, stream_resolution=stream_resolution,
direction=direction, direction=direction,
axis=axis, axis=axis,
data=rom_data, data=rom_data,
csm=csm, rom_deps=rom_deps,
cam=cam,
stage=stage,
autofocus=autofocus,
logger=logger,
) )
# 1 big step followed by 3 small steps # 1 big step followed by 3 small steps
minimum_offset_small = _generate_move_dicts( minimum_offset_small = _generate_move_dicts(
small_step, stream_resolution, direction, factor=0.65 SMALL_STEP, stream_resolution, direction, factor=0.65
) )
while np.abs(rom_data.delta[axis]) > np.abs(minimum_offset_small[axis]): while np.abs(rom_data.delta[axis]) > np.abs(minimum_offset_small[axis]):
@ -471,46 +449,38 @@ class RangeofMotionThing(lt.Thing):
positions=rom_data.stage_coords, positions=rom_data.stage_coords,
axis=axis, axis=axis,
relative_move=step_sizes_big[axis], relative_move=step_sizes_big[axis],
stage=stage, stage=rom_deps.stage,
csm=csm, csm=rom_deps.csm,
) )
logger.info("Z calibration complete.") rom_deps.logger.info("Z calibration complete.")
stage.move_relative(z=z_diff) rom_deps.stage.move_relative(z=z_diff)
logger.info(f"Moved in z by {z_diff}") rom_deps.logger.info(f"Moved in z by {z_diff}")
# Big step # Big step
if axis == "x": if axis == "x":
csm.move_in_image_coordinates(x=step_sizes_big["x"], y=0) rom_deps.csm.move_in_image_coordinates(x=step_sizes_big["x"], y=0)
else: else:
csm.move_in_image_coordinates(x=0, y=step_sizes_big["y"]) rom_deps.csm.move_in_image_coordinates(x=0, y=step_sizes_big["y"])
autofocus.looping_autofocus(dz=800) rom_deps.autofocus.looping_autofocus(dz=800)
rom_data.stage_coords.append(stage.position) rom_data.stage_coords.append(rom_deps.stage.position)
_check_stage_operation( _check_stage_operation(
small_step=small_step,
stream_resolution=stream_resolution, stream_resolution=stream_resolution,
direction=direction, direction=direction,
axis=axis, axis=axis,
data=rom_data, data=rom_data,
minimum_offset_small=minimum_offset_small, minimum_offset_small=minimum_offset_small,
csm=csm, rom_deps=rom_deps,
cam=cam,
stage=stage,
autofocus=autofocus,
logger=logger,
) )
# Motion detection # Motion detection
logger.info("Running motion detection") rom_deps.logger.info("Running motion detection")
final_pos = _motion_detection( final_pos = _motion_detection(
axis=axis, axis=axis,
direction=direction, direction=direction,
csm=csm, rom_deps=rom_deps,
stage=stage,
cam=cam,
logger=logger,
) )
rom_data.stage_coords[np.shape(np.array(rom_data.stage_coords))[0] - 1] = ( rom_data.stage_coords[np.shape(np.array(rom_data.stage_coords))[0] - 1] = (
final_pos final_pos
@ -525,14 +495,14 @@ class RangeofMotionThing(lt.Thing):
rom_data.reset_tracker() rom_data.reset_tracker()
except ParasiticMotionError: except ParasiticMotionError:
logger.info("Parasitic motion detected.") rom_deps.logger.error("Parasitic motion detected.")
return { return {
"correlation_lateral_steps": 0, "correlation_lateral_steps": 0,
"stage_positions": 0, "stage_positions": 0,
"final_position": {"x": 0, "y": 0, "z": 0}, "final_position": {"x": 0, "y": 0, "z": 0},
} }
finally: finally:
stage.move_absolute( rom_deps.stage.move_absolute(
x=starting_position[0], x=starting_position[0],
y=starting_position[1], y=starting_position[1],
z=starting_position[2], z=starting_position[2],
@ -559,6 +529,9 @@ class RangeofMotionThing(lt.Thing):
:param logger: A raw_thing_client depeendency for the logger. :param logger: A raw_thing_client depeendency for the logger.
:return: Results dictionary separated into keys of each axis and direction. :return: Results dictionary separated into keys of each axis and direction.
""" """
rom_deps = RomDeps(
autofocus=autofocus, stage=stage, csm=csm, cam=cam, logger=logger
)
logger.info( logger.info(
"Using the stage to measure the Range of Motion. " "Using the stage to measure the Range of Motion. "
"Please ensure you are using a big enough sample." "Please ensure you are using a big enough sample."
@ -569,13 +542,9 @@ class RangeofMotionThing(lt.Thing):
# Loop through all axes and directions. # Loop through all axes and directions.
for axis_dir in [["x", 1], ["x", -1], ["y", 1], ["y", -1]]: for axis_dir in [["x", 1], ["x", -1], ["y", 1], ["y", -1]]:
axis_dir_results = self.rom_axis( axis_dir_results = self.rom_axis(
autofocus,
stage,
cam,
csm,
logger,
axis=axis_dir[0], axis=axis_dir[0],
direction=axis_dir[1], direction=axis_dir[1],
rom_deps=rom_deps,
) )
# Save results from single axis and direction # Save results from single axis and direction
rom_json[ rom_json[
@ -585,7 +554,7 @@ class RangeofMotionThing(lt.Thing):
end_time = time.time() end_time = time.time()
total_time = (end_time - start_time) / 60 total_time = (end_time - start_time) / 60
rom_results = _collate_data(data=rom_json, time=total_time, csm=csm) rom_results = _collate_data(data=rom_json, total_time=total_time, csm=csm)
logger.info( logger.info(
f"Range of motion is {rom_results['Step Range'][0]} x {rom_results['Step Range'][1]} steps" f"Range of motion is {rom_results['Step Range'][0]} x {rom_results['Step Range'][1]} steps"
@ -594,5 +563,3 @@ class RangeofMotionThing(lt.Thing):
self.last_calibration = DenumpifyingDict(rom_results).model_dump() self.last_calibration = DenumpifyingDict(rom_results).model_dump()
return rom_results return rom_results
last_calibration = lt.ThingSetting(initial_value=None, model=dict, readonly=True)