Illumination Things for sangaboard and simulator
This commit is contained in:
parent
8435f6ae98
commit
5682b4d28d
3 changed files with 65 additions and 27 deletions
|
|
@ -140,6 +140,7 @@ class SimulatedCamera(BaseCamera):
|
||||||
self._capture_thread: Optional[Thread] = None
|
self._capture_thread: Optional[Thread] = None
|
||||||
self._capture_enabled = False
|
self._capture_enabled = False
|
||||||
self.generate_sprites()
|
self.generate_sprites()
|
||||||
|
self.mult = 1
|
||||||
|
|
||||||
repeating: bool = lt.property(default=False)
|
repeating: bool = lt.property(default=False)
|
||||||
|
|
||||||
|
|
@ -339,14 +340,20 @@ class SimulatedCamera(BaseCamera):
|
||||||
pl_img = Image.fromarray(np_img.astype("uint8"))
|
pl_img = Image.fromarray(np_img.astype("uint8"))
|
||||||
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
|
||||||
|
def set_led(self, led_on: bool = True, led_channel=None):
|
||||||
|
if led_on:
|
||||||
|
self.mult = 1
|
||||||
|
else:
|
||||||
|
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
|
||||||
if not self._stage.led_on:
|
return frame * self.mult
|
||||||
frame = np.full((self.shape[0], self.shape[1], 3), 0, dtype=np.uint8)
|
|
||||||
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."""
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,12 @@ from typing import Literal
|
||||||
|
|
||||||
import labthings_fastapi as lt
|
import labthings_fastapi as lt
|
||||||
|
|
||||||
|
from .stage import BaseStage
|
||||||
|
from .camera import BaseCam
|
||||||
|
|
||||||
class Illumination(lt.Thing):
|
class Illumination(lt.Thing):
|
||||||
"""Abstract illumination controller."""
|
"""Abstract illumination controller."""
|
||||||
|
|
||||||
brightness: float = lt.property(
|
|
||||||
default=0.0,
|
|
||||||
description="Illumination brightness (0-1)",
|
|
||||||
)
|
|
||||||
|
|
||||||
@lt.action
|
|
||||||
def set_brightness(self, brightness: float) -> None:
|
|
||||||
self.brightness = brightness
|
|
||||||
|
|
||||||
@lt.action
|
@lt.action
|
||||||
def flash(
|
def flash(
|
||||||
self,
|
self,
|
||||||
|
|
@ -23,22 +16,14 @@ class Illumination(lt.Thing):
|
||||||
dt: float = 0.5,
|
dt: float = 0.5,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Flash the illumination source."""
|
"""Flash the illumination source."""
|
||||||
for _ in range(number_of_flashes):
|
raise NotImplementedError(
|
||||||
self.set_brightness(1.0)
|
'Flashing the LED can only be done from the simulator or sangaboard'
|
||||||
time.sleep(dt)
|
)
|
||||||
self.set_brightness(0.0)
|
|
||||||
time.sleep(dt)
|
|
||||||
|
|
||||||
|
|
||||||
class SangaIllumination(Illumination):
|
class SangaIllumination(Illumination):
|
||||||
"""Illumination driven by a Sangaboard."""
|
"""Illumination driven by a Sangaboard."""
|
||||||
|
|
||||||
sangaboard = lt.thing_slot(description="Sangaboard providing LED control")
|
_stage: BaseStage = lt.thing_slot()
|
||||||
|
|
||||||
@lt.action
|
|
||||||
def set_brightness(self, brightness: float) -> None:
|
|
||||||
self.sangaboard.set_led_brightness(brightness)
|
|
||||||
self.brightness = brightness
|
|
||||||
|
|
||||||
@lt.action
|
@lt.action
|
||||||
def flash(
|
def flash(
|
||||||
|
|
@ -48,7 +33,25 @@ class SangaIllumination(Illumination):
|
||||||
led_channel: Literal["cc"] = "cc",
|
led_channel: Literal["cc"] = "cc",
|
||||||
) -> None:
|
) -> None:
|
||||||
for _ in range(number_of_flashes):
|
for _ in range(number_of_flashes):
|
||||||
self.sangaboard.set_led(True, led_channel)
|
self._stage.set_led(False, led_channel)
|
||||||
time.sleep(dt)
|
time.sleep(dt)
|
||||||
self.sangaboard.set_led(False, led_channel)
|
self._stage.set_led(True, led_channel)
|
||||||
|
time.sleep(dt)
|
||||||
|
|
||||||
|
class SimulatorIllumination(Illumination):
|
||||||
|
"""Illumination control in the simulator."""
|
||||||
|
|
||||||
|
_cam: BaseCam = lt.thing_slot()
|
||||||
|
|
||||||
|
@lt.action
|
||||||
|
def flash(
|
||||||
|
self,
|
||||||
|
number_of_flashes: int = 10,
|
||||||
|
dt: float = 0.5,
|
||||||
|
led_channel: Literal["cc"] = "cc",
|
||||||
|
) -> None:
|
||||||
|
for _ in range(number_of_flashes):
|
||||||
|
self._cam.set_led(False, led_channel)
|
||||||
|
time.sleep(dt)
|
||||||
|
self._cam.set_led(True, led_channel)
|
||||||
time.sleep(dt)
|
time.sleep(dt)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import threading
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import Any, Iterator, Optional, Self
|
from typing import Any, Iterator, Literal, Optional, Self
|
||||||
|
|
||||||
import semver
|
import semver
|
||||||
|
|
||||||
|
|
@ -172,3 +172,31 @@ class SangaboardThing(BaseStage):
|
||||||
with self.sangaboard() as sb:
|
with self.sangaboard() as sb:
|
||||||
sb.zero_position()
|
sb.zero_position()
|
||||||
self.update_position()
|
self.update_position()
|
||||||
|
|
||||||
|
@lt.action
|
||||||
|
def set_led(
|
||||||
|
self,
|
||||||
|
led_on: bool = True,
|
||||||
|
led_channel: Literal["cc"] = "cc",
|
||||||
|
) -> None:
|
||||||
|
"""Flash the LED to identify the board.
|
||||||
|
|
||||||
|
This is intended to be useful in situations where there are multiple
|
||||||
|
Sangaboards in use, and it is necessary to identify which one is
|
||||||
|
being addressed.
|
||||||
|
"""
|
||||||
|
led_command = f"led_{led_channel}"
|
||||||
|
with self.sangaboard() as sb:
|
||||||
|
return_value = sb.query(f"{led_command}?")
|
||||||
|
if not return_value.startswith("CC LED:"):
|
||||||
|
raise IOError("The sangaboard does not support LED control")
|
||||||
|
|
||||||
|
# Reading and setting LED brightness suffers from repeated reads and writes
|
||||||
|
# decreasing the value. Rather than use the value the code warns that the value
|
||||||
|
# cannot be used.
|
||||||
|
if led_on:
|
||||||
|
on_brightness = 0.32
|
||||||
|
sb.query(f"{led_command} {on_brightness}")
|
||||||
|
else:
|
||||||
|
sb.query(f"{led_command} 0")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue