Remove flash from stage baseclass, add to sim config

This commit is contained in:
Joe Knapper 2026-02-10 22:13:09 +00:00
parent 5682b4d28d
commit 4d788a2817
5 changed files with 26 additions and 26 deletions

View file

@ -4,6 +4,7 @@
"stage": "openflexure_microscope_server.things.stage.dummy:DummyStage", "stage": "openflexure_microscope_server.things.stage.dummy:DummyStage",
"autofocus": "openflexure_microscope_server.things.autofocus:AutofocusThing", "autofocus": "openflexure_microscope_server.things.autofocus:AutofocusThing",
"camera_stage_mapping": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper", "camera_stage_mapping": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
"illumination": "openflexure_microscope_server.things.illumination.SimulatorIllumination",
"system": "openflexure_microscope_server.things.system:OpenFlexureSystem", "system": "openflexure_microscope_server.things.system:OpenFlexureSystem",
"smart_scan": { "smart_scan": {
"class": "openflexure_microscope_server.things.smart_scan:SmartScanThing", "class": "openflexure_microscope_server.things.smart_scan:SmartScanThing",

View file

@ -341,19 +341,21 @@ class SimulatedCamera(BaseCamera):
return pl_img.resize((self.shape[1], self.shape[0]), Image.Resampling.BILINEAR) return pl_img.resize((self.shape[1], self.shape[0]), Image.Resampling.BILINEAR)
@lt.action @lt.action
def set_led(self, led_on: bool = True, led_channel=None): def set_led(self, led_on: bool = True, led_channel: str = None) -> None: # noqa: ARG002
"""Set the simulated LED to on or off."""
if led_on: if led_on:
self.mult = 1 self.mult = 1
else: else:
self.mult = 0 self.mult = 0
def generate_frame(self) -> Image.Image: def generate_frame(self) -> Image.Image:
"""Generate a frame with blobs based on the stage coordinates.""" """Generate a frame with blobs based on the stage coordinates."""
pos = self._stage.instantaneous_position pos = self._stage.instantaneous_position
frame = self.generate_image((pos["y"], pos["x"], pos["z"])) frame = self.generate_image((pos["y"], pos["x"], pos["z"]))
# Simulate LED turning off by setting all channels to 0 # Simulate LED turning off by setting all channels to 0
return frame * self.mult if self.mult == 0:
return Image.new(frame.mode, frame.size, 0)
return frame
def __enter__(self) -> Self: def __enter__(self) -> Self:
"""Start the capture thread when the Thing context manager is opened.""" """Start the capture thread when the Thing context manager is opened."""

View file

@ -1,10 +1,13 @@
"""A module to handle the illumination in LabThings."""
import time import time
from typing import Literal from typing import Literal
import labthings_fastapi as lt import labthings_fastapi as lt
from .camera import BaseCamera
from .stage import BaseStage from .stage import BaseStage
from .camera import BaseCam
class Illumination(lt.Thing): class Illumination(lt.Thing):
"""Abstract illumination controller.""" """Abstract illumination controller."""
@ -17,9 +20,10 @@ class Illumination(lt.Thing):
) -> None: ) -> None:
"""Flash the illumination source.""" """Flash the illumination source."""
raise NotImplementedError( raise NotImplementedError(
'Flashing the LED can only be done from the simulator or sangaboard' "Flashing the LED can only be done from the simulator or sangaboard"
) )
class SangaIllumination(Illumination): class SangaIllumination(Illumination):
"""Illumination driven by a Sangaboard.""" """Illumination driven by a Sangaboard."""
@ -32,16 +36,22 @@ class SangaIllumination(Illumination):
dt: float = 0.5, dt: float = 0.5,
led_channel: Literal["cc"] = "cc", led_channel: Literal["cc"] = "cc",
) -> None: ) -> None:
"""Flash an LED a given number of times.
Flashes the LED on channel led_channel number_of_flashes times,
for a duration of dt each.
"""
for _ in range(number_of_flashes): for _ in range(number_of_flashes):
self._stage.set_led(False, led_channel) self._stage.set_led(False, led_channel)
time.sleep(dt) time.sleep(dt)
self._stage.set_led(True, led_channel) self._stage.set_led(True, led_channel)
time.sleep(dt) time.sleep(dt)
class SimulatorIllumination(Illumination): class SimulatorIllumination(Illumination):
"""Illumination control in the simulator.""" """Illumination control in the simulator."""
_cam: BaseCam = lt.thing_slot() _cam: BaseCamera = lt.thing_slot()
@lt.action @lt.action
def flash( def flash(
@ -50,6 +60,11 @@ class SimulatorIllumination(Illumination):
dt: float = 0.5, dt: float = 0.5,
led_channel: Literal["cc"] = "cc", led_channel: Literal["cc"] = "cc",
) -> None: ) -> None:
"""Flash an LED a given number of times.
Flashes the LED on channel led_channel number_of_flashes times,
for a duration of dt each.
"""
for _ in range(number_of_flashes): for _ in range(number_of_flashes):
self._cam.set_led(False, led_channel) self._cam.set_led(False, led_channel)
time.sleep(dt) time.sleep(dt)

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import time import time
from types import TracebackType from types import TracebackType
from typing import Any, Literal, Optional, Self from typing import Any, Optional, Self
import labthings_fastapi as lt import labthings_fastapi as lt
@ -18,8 +18,6 @@ class DummyStage(BaseStage):
hardware attached. hardware attached.
""" """
led_on: bool = lt.property(default=True)
def __init__( def __init__(
self, self,
thing_server_interface: lt.ThingServerInterface, thing_server_interface: lt.ThingServerInterface,
@ -117,18 +115,3 @@ class DummyStage(BaseStage):
""" """
self._hardware_position = dict.fromkeys(self.axis_names, 0) self._hardware_position = dict.fromkeys(self.axis_names, 0)
self.instantaneous_position = self._hardware_position self.instantaneous_position = self._hardware_position
# led_channel is unused in the simulation, but must exist to match the real API
@lt.action
def flash_led(
self,
number_of_flashes: int = 10,
dt: float = 0.5,
led_channel: Literal["cc"] = "cc", # noqa: ARG002
) -> None:
"""Flash the LED to identify the board (simulated)."""
for _ in range(number_of_flashes):
self.led_on = False
time.sleep(dt)
self.led_on = True
time.sleep(dt)

View file

@ -197,6 +197,5 @@ class SangaboardThing(BaseStage):
if led_on: if led_on:
on_brightness = 0.32 on_brightness = 0.32
sb.query(f"{led_command} {on_brightness}") sb.query(f"{led_command} {on_brightness}")
else: else:
sb.query(f"{led_command} 0") sb.query(f"{led_command} 0")