diff --git a/src/openflexure_microscope_server/things/stage/__init__.py b/src/openflexure_microscope_server/things/stage/__init__.py index a42190c6..f70eabfe 100644 --- a/src/openflexure_microscope_server/things/stage/__init__.py +++ b/src/openflexure_microscope_server/things/stage/__init__.py @@ -166,6 +166,15 @@ class BaseStage(lt.Thing): ) """Used to convert coordinates between the program frame and the hardware frame.""" + @lt.property + def calibration_required(self) -> bool: + """Whether the stage requires calibration. + + Currently the only calibration method for the stage is the z-gear rotation + direction in the sangaboard stage. + """ + return False + def update_position(self) -> None: """Update the position property from the stage.""" # Copy the position before the move. diff --git a/src/openflexure_microscope_server/things/stage/sangaboard.py b/src/openflexure_microscope_server/things/stage/sangaboard.py index b8ad994c..7ab3103f 100644 --- a/src/openflexure_microscope_server/things/stage/sangaboard.py +++ b/src/openflexure_microscope_server/things/stage/sangaboard.py @@ -80,6 +80,37 @@ class SangaboardThing(BaseStage): ) """Used to convert coordinates between the program frame and the hardware frame.""" + z_axis_calibrated: bool = lt.setting(default=False, readonly=True) + + @lt.property + def calibration_required(self) -> bool: + """Whether the stage requires calibration. + + Return False only if z-direction has been calibrated. + """ + return not self.z_axis_calibrated + + @lt.action + def calibrate_z_direction( + self, positive_motion: Literal["clockwise", "anti-clockwise"] + ) -> None: + """Calibrate the z motor direction based on the gear rotation direction. + + Positive z motion should turn the exposed gear anti-clockwise. Calling this + action marks the z motor as calibrated. + + :param positive_motion: The direction the gear moved during pisitive z motion. + """ + if positive_motion == "clockwise": + # Wrong direction, mark as such. + self.invert_axis_direction(axis="z") + elif positive_motion != "anti-clockwise": + raise ValueError( + "Motion should be reported as either 'clockwise' or 'anti-clockwise'." + ) + # Set z_axis_calibrated to true once this function is called. + self.z_axis_calibrated = True + def _hardware_update_position(self) -> None: """Read position from the stage and set internal attribute _hardware_position. diff --git a/tests/unit_tests/test_stage.py b/tests/unit_tests/test_stage.py index f922ef65..84c82305 100644 --- a/tests/unit_tests/test_stage.py +++ b/tests/unit_tests/test_stage.py @@ -603,10 +603,13 @@ def test_thing_description_equivalence(dummy_stage, mocker): dummy_properties = set(dummy_td.properties.keys()) sanga_td = create_thing_without_server(SangaboardThing).thing_description() - # Remove known extra actions - sanga_actions = list(sanga_td.actions.keys()) - sanga_actions = set(sanga_actions) - sanga_properties = set(sanga_td.properties.keys()) + + # Sangaboard has one extra action for calibrating z-motor direction. This is + # handled in the webapp calibration wizard, and an extra setting for persisting it + sanga_extra_actions = {"calibrate_z_direction"} + sanga_extra_properties = {"z_axis_calibrated"} + sanga_actions = set(sanga_td.actions.keys()) - sanga_extra_actions + sanga_properties = set(sanga_td.properties.keys()) - sanga_extra_properties assert sanga_actions == dummy_actions == base_actions assert sanga_properties == dummy_properties == base_properties diff --git a/webapp/public/direction_AC.png b/webapp/public/direction_AC.png new file mode 100644 index 00000000..214c0efd Binary files /dev/null and b/webapp/public/direction_AC.png differ diff --git a/webapp/public/direction_CW.png b/webapp/public/direction_CW.png new file mode 100644 index 00000000..3758dc4b Binary files /dev/null and b/webapp/public/direction_CW.png differ diff --git a/webapp/src/components/modalComponents/calibrationWizard.vue b/webapp/src/components/modalComponents/calibrationWizard.vue index 1e54ec6b..cc5993f0 100644 --- a/webapp/src/components/modalComponents/calibrationWizard.vue +++ b/webapp/src/components/modalComponents/calibrationWizard.vue @@ -28,6 +28,7 @@ diff --git a/webapp/src/components/modalComponents/calibrationWizardComponents/zMotorDirectionStep.vue b/webapp/src/components/modalComponents/calibrationWizardComponents/zMotorDirectionStep.vue new file mode 100644 index 00000000..b0d97e4f --- /dev/null +++ b/webapp/src/components/modalComponents/calibrationWizardComponents/zMotorDirectionStep.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/webapp/src/tests/unit/overrides.js b/webapp/src/tests/unit/overrides.js index 53797e78..90ebdcaa 100644 --- a/webapp/src/tests/unit/overrides.js +++ b/webapp/src/tests/unit/overrides.js @@ -133,4 +133,17 @@ export const componentOverrides = { "cameraCalibrationSettings.vue": { props: { cameraUri: "" }, }, + "zMotorDirectionStep.vue": { + mocks: { + thingDescription: () => ({ + properties: { + test_property_1: {}, + test_property_2: {}, + }, + actions: { + calibrate_z_direction: {}, + }, + }), + }, + }, };