Simplify JogCommand class structure, and remove unneeded event logic
This commit is contained in:
parent
eb79905581
commit
f4d11b3c83
2 changed files with 16 additions and 52 deletions
|
|
@ -33,40 +33,22 @@ class RedefinedBaseMovementError(RuntimeError):
|
||||||
|
|
||||||
|
|
||||||
class JogCommand:
|
class JogCommand:
|
||||||
"""A base class for jog operations.
|
"""A base class for jog operations."""
|
||||||
|
|
||||||
This class handles threading events used to interrupt previous jog commands. There
|
def __init__(self, displacement: Optional[Sequence[int]]) -> None:
|
||||||
are two subclasses that are used by jogging `JogMoveCommand` and `JogStopCommand`.
|
"""Initialise a JogCommand.
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
:param displacement: The distances as a sequence of the move for each axis.
|
||||||
"""Initialise a JogCommand."""
|
None for stop motion.
|
||||||
super().__init__()
|
|
||||||
self.finished = threading.Event()
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
"""Represent the command as a string."""
|
|
||||||
return f"<{self.__class__.__name__}>"
|
|
||||||
|
|
||||||
|
|
||||||
class JogMoveCommand(JogCommand):
|
|
||||||
"""A command to make a jog move."""
|
|
||||||
|
|
||||||
def __init__(self, displacement: Sequence[int]) -> None:
|
|
||||||
"""Initialise a JogMoveCommand.
|
|
||||||
|
|
||||||
:param displacement: The displacement for the jog move, in hardware coordinates.
|
|
||||||
"""
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.displacement = displacement
|
self.displacement = displacement
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""Represent the command as a string."""
|
"""Represent the command as a string."""
|
||||||
return f"<{self.__class__.__name__} {self.displacement}>"
|
if self.displacement is None:
|
||||||
|
return "<JogCommand>STOP"
|
||||||
|
return f"<JogCommand>{self.displacement}"
|
||||||
class JogStopCommand(JogCommand):
|
|
||||||
"""A command to stop a jog move."""
|
|
||||||
|
|
||||||
|
|
||||||
class JogQueue(queue.Queue[JogCommand]):
|
class JogQueue(queue.Queue[JogCommand]):
|
||||||
|
|
@ -283,7 +265,7 @@ class BaseStage(lt.Thing):
|
||||||
:param kwargs: Keyword arguments should be axis names.
|
:param kwargs: Keyword arguments should be axis names.
|
||||||
"""
|
"""
|
||||||
if stop:
|
if stop:
|
||||||
self._send_jog_command(JogStopCommand(), timeout=1)
|
self._send_jog_command(JogCommand(None))
|
||||||
else:
|
else:
|
||||||
self._hardware_jog(**self._apply_axis_direction(kwargs))
|
self._hardware_jog(**self._apply_axis_direction(kwargs))
|
||||||
|
|
||||||
|
|
@ -299,14 +281,9 @@ class BaseStage(lt.Thing):
|
||||||
:param kwargs: Keyword arguments should be axis names.
|
:param kwargs: Keyword arguments should be axis names.
|
||||||
"""
|
"""
|
||||||
move = [kwargs.get(axis, 0) for axis in self.axis_names]
|
move = [kwargs.get(axis, 0) for axis in self.axis_names]
|
||||||
self._send_jog_command(
|
self._send_jog_command(JogCommand(move))
|
||||||
JogMoveCommand(move),
|
|
||||||
timeout=self._estimate_move_duration(move) + 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _send_jog_command(
|
def _send_jog_command(self, command: JogCommand) -> None:
|
||||||
self, command: JogCommand, timeout: Optional[float] = None
|
|
||||||
) -> None:
|
|
||||||
"""Send a jog command to the background jog thread.
|
"""Send a jog command to the background jog thread.
|
||||||
|
|
||||||
This function will start the background thread if it is not running.
|
This function will start the background thread if it is not running.
|
||||||
|
|
@ -315,8 +292,6 @@ class BaseStage(lt.Thing):
|
||||||
function should never block for a long time.
|
function should never block for a long time.
|
||||||
|
|
||||||
:param command: the jog command to send.
|
:param command: the jog command to send.
|
||||||
:param timeout: how long to wait for the command to be completed, or ``None``
|
|
||||||
to skip waiting.
|
|
||||||
"""
|
"""
|
||||||
if not self._jog_lock.acquire(timeout=0.1):
|
if not self._jog_lock.acquire(timeout=0.1):
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
|
|
@ -337,10 +312,6 @@ class BaseStage(lt.Thing):
|
||||||
self._jog_queue.put(command)
|
self._jog_queue.put(command)
|
||||||
finally:
|
finally:
|
||||||
self._jog_lock.release()
|
self._jog_lock.release()
|
||||||
# The final wait happens after releasing the lock: this allows jog moves to
|
|
||||||
# be interrupted.
|
|
||||||
if timeout is not None:
|
|
||||||
command.finished.wait(timeout)
|
|
||||||
|
|
||||||
def _jog_loop(self, first_command: JogCommand) -> None:
|
def _jog_loop(self, first_command: JogCommand) -> None:
|
||||||
"""Execute jog commands in a background thread.
|
"""Execute jog commands in a background thread.
|
||||||
|
|
@ -350,25 +321,18 @@ class BaseStage(lt.Thing):
|
||||||
"""
|
"""
|
||||||
# Timeout for checking queue
|
# Timeout for checking queue
|
||||||
timeout = 0.1
|
timeout = 0.1
|
||||||
previous_command: Optional[JogCommand] = None
|
|
||||||
command: Optional[JogCommand] = first_command
|
command: Optional[JogCommand] = first_command
|
||||||
|
|
||||||
# prevent others using the stage while jogging.
|
# prevent others using the stage while jogging.
|
||||||
with self._hardware_lock:
|
with self._hardware_lock:
|
||||||
while command is not None:
|
while command is not None:
|
||||||
if previous_command:
|
if command.displacement is not None:
|
||||||
# Notify the last command it's superseded
|
|
||||||
previous_command.finished.set()
|
|
||||||
previous_command = command
|
|
||||||
if isinstance(command, JogMoveCommand):
|
|
||||||
self._hardware_start_move_relative(command.displacement)
|
self._hardware_start_move_relative(command.displacement)
|
||||||
timeout = self._estimate_move_duration(command.displacement)
|
timeout = self._estimate_move_duration(command.displacement)
|
||||||
elif isinstance(command, JogStopCommand):
|
else:
|
||||||
self._hardware_stop()
|
self._hardware_stop()
|
||||||
# Next iteration, we will probably time out.
|
# Next iteration, we will probably time out.
|
||||||
timeout = 0.1
|
timeout = 0.1
|
||||||
else:
|
|
||||||
raise RuntimeError(f"Unknown jog command: {command}")
|
|
||||||
self.update_position()
|
self.update_position()
|
||||||
command = self._get_from_jog_queue(timeout)
|
command = self._get_from_jog_queue(timeout)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from labthings_fastapi.testing import create_thing_without_server
|
||||||
from openflexure_microscope_server.things.camera.simulation import SimulatedCamera
|
from openflexure_microscope_server.things.camera.simulation import SimulatedCamera
|
||||||
from openflexure_microscope_server.things.stage import (
|
from openflexure_microscope_server.things.stage import (
|
||||||
BaseStage,
|
BaseStage,
|
||||||
JogMoveCommand,
|
JogCommand,
|
||||||
RedefinedBaseMovementError,
|
RedefinedBaseMovementError,
|
||||||
)
|
)
|
||||||
from openflexure_microscope_server.things.stage.dummy import DummyStage
|
from openflexure_microscope_server.things.stage.dummy import DummyStage
|
||||||
|
|
@ -307,10 +307,10 @@ def test_get_jog_from_queue_most_recent(dummy_stage):
|
||||||
"""Test that the jog queue gives the most recent Jog Command."""
|
"""Test that the jog queue gives the most recent Jog Command."""
|
||||||
# Try to stack 4 moves in the queue, only 1 should be queued.
|
# Try to stack 4 moves in the queue, only 1 should be queued.
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
dummy_stage._jog_queue.put(JogMoveCommand([i, i, i]))
|
dummy_stage._jog_queue.put(JogCommand([i, i, i]))
|
||||||
|
|
||||||
command = dummy_stage._get_from_jog_queue(0.001)
|
command = dummy_stage._get_from_jog_queue(0.001)
|
||||||
assert isinstance(command, JogMoveCommand)
|
assert isinstance(command, JogCommand)
|
||||||
# Should be the last one queued
|
# Should be the last one queued
|
||||||
assert command.displacement == [3, 3, 3]
|
assert command.displacement == [3, 3, 3]
|
||||||
# Nothing else is queued
|
# Nothing else is queued
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue