Save useful ROM data to data directory

This commit is contained in:
Chish36 2026-05-14 11:14:40 +01:00
parent 182c67e105
commit 03dc0a065f

View file

@ -13,10 +13,13 @@ is tracked and an error is raised if it exceeds a minimum amount. Currently
this is 10% of the expected motion is the measured axis. this is 10% of the expected motion is the measured axis.
""" """
import json
import os
import time import time
from copy import copy from copy import copy
from datetime import datetime
from threading import Lock from threading import Lock
from typing import Any, Literal, Mapping, Optional, overload from typing import Any, Literal, Mapping, Optional, Self, overload
import numpy as np import numpy as np
@ -24,6 +27,8 @@ import labthings_fastapi as lt
from camera_stage_mapping import fft_image_tracking from camera_stage_mapping import fft_image_tracking
# Things # Things
from openflexure_microscope_server.things import OFMThing
from .autofocus import AutofocusThing from .autofocus import AutofocusThing
from .camera import BaseCamera from .camera import BaseCamera
from .camera_stage_mapping import CameraStageMapper from .camera_stage_mapping import CameraStageMapper
@ -114,7 +119,7 @@ class ParasiticMotionError(lt.exceptions.InvocationError):
""" """
class RangeofMotionThing(lt.Thing): class RangeofMotionThing(OFMThing):
"""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."""
_class_settings = {"validate_properties_on_set": True} _class_settings = {"validate_properties_on_set": True}
@ -128,6 +133,14 @@ class RangeofMotionThing(lt.Thing):
default=None, readonly=True default=None, readonly=True
) )
csm_matrix: Optional[list[list[float]]] = lt.setting(default=None, readonly=True)
stage_coords: Optional[Any] = lt.setting(default=None, readonly=True)
correlations: Optional[Any] = lt.setting(default=None, readonly=True)
time: Optional[float] = lt.setting(default=None, readonly=True)
def __init__(self, thing_server_interface: lt.ThingServerInterface) -> None: def __init__(self, thing_server_interface: lt.ThingServerInterface) -> None:
"""Initialise and create the lock.""" """Initialise and create the lock."""
super().__init__(thing_server_interface) super().__init__(thing_server_interface)
@ -135,6 +148,11 @@ class RangeofMotionThing(lt.Thing):
self._stream_resolution: Optional[tuple[int, int]] = None self._stream_resolution: Optional[tuple[int, int]] = None
self._rom_data = RomDataTracker() self._rom_data = RomDataTracker()
def __enter__(self) -> Self:
"""Open hardware connection when the Thing context manager is opened."""
super().__enter__()
return self
@lt.action @lt.action
def perform_rom_test(self) -> Mapping[str, Any]: def perform_rom_test(self) -> Mapping[str, Any]:
"""Measures the range of motion of the stage across the x and y axes. """Measures the range of motion of the stage across the x and y axes.
@ -162,6 +180,9 @@ class RangeofMotionThing(lt.Thing):
axes: tuple[Literal["x"], Literal["y"]] = ("x", "y") axes: tuple[Literal["x"], Literal["y"]] = ("x", "y")
directions: tuple[Literal[1], Literal[-1]] = (1, -1) directions: tuple[Literal[1], Literal[-1]] = (1, -1)
stage_dic = {}
cor_dic = {}
for axis in axes: for axis in axes:
# The final position of the axis under test in the given direction # The final position of the axis under test in the given direction
axis_limits = [] axis_limits = []
@ -171,14 +192,37 @@ class RangeofMotionThing(lt.Thing):
self._move_until_edge(axis=axis, direction=axis_dir) self._move_until_edge(axis=axis, direction=axis_dir)
# Append final position from tracker # Append final position from tracker
axis_limits.append(self._rom_data.final_position[axis]) axis_limits.append(self._rom_data.final_position[axis])
axis_string = "positive" if axis_dir == 1 else "negative"
stage_dic[f"{axis}_{axis_string}"] = self._rom_data.stage_coords
cor_dic[f"{axis}_{axis_string}"] = self._rom_data.offsets
# Calculate step range. # Calculate step range.
step_range.append(abs(axis_limits[0] - axis_limits[1])) step_range.append(abs(axis_limits[0] - axis_limits[1]))
total_time = time.time() - start_time total_time = (time.time() - start_time) / 60
self.logger.info( self.logger.info(
f"Range of motion is {step_range[0]} x {step_range[1]} steps" f"Range of motion is {step_range[0]} x {step_range[1]} steps"
) )
self.calibrated_range = (step_range[0], step_range[1]) data = {
"calibrated_range": [step_range[0], step_range[1]],
"stage_coords": self.stage_coords,
"correlations": self.correlations,
"csm_matrix": self._csm.image_to_stage_displacement_matrix,
"time": self.time,
}
if not os.path.exists(self.data_dir):
os.makedirs(self.data_dir)
with open(
os.path.join(
self.data_dir,
f"rom_data_{datetime.now().strftime('%Y_%m_%d_%H_%M')}.json",
),
"w",
encoding="utf-8",
) as f:
json.dump(data, f, indent=4)
finally: finally:
self._lock.release() self._lock.release()
return { return {