diff --git a/python/lsst/ts/standardscripts/data/scripts/maintel/mtrotator/move_rotator.py b/python/lsst/ts/standardscripts/data/scripts/maintel/mtrotator/move_rotator.py old mode 100644 new mode 100755 diff --git a/python/lsst/ts/standardscripts/maintel/mtrotator/move_rotator.py b/python/lsst/ts/standardscripts/maintel/mtrotator/move_rotator.py index 6d26d1c22..b92cda214 100644 --- a/python/lsst/ts/standardscripts/maintel/mtrotator/move_rotator.py +++ b/python/lsst/ts/standardscripts/maintel/mtrotator/move_rotator.py @@ -19,10 +19,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -__all__ = ["EnableComCam"] - -import asyncio -import warnings +__all__ = ["MoveRotator"] import yaml from lsst.ts.observatory.control.maintel.mtcs import MTCS, MTCSUsages @@ -52,15 +49,19 @@ class MoveRotator(BaseBlockScript): def __init__(self, index: int, add_remotes: bool = True) -> None: super().__init__(index=index, descr="Move Rotator") - mtcs_usage = None if add_remotes else MTCSUsages.DryTest - - self.mtcs = MTCS(self.domain, intended_usage=mtcs_usage, log=self.log) + self.mtcs = MTCS( + domain=self.domain, + intended_usage=None if add_remotes else MTCSUsages.DryTest, + log=self.log, + ) for comp in self.mtcs.components_attr: if comp not in ["mtrotator", "mtmount"]: setattr(self.mtcs.check, comp, False) - self.rotator_time_per_angle = 5.0 # seconds per degree + self.rotator_velocity = 3.5 # degrees per second + self.short_timeout = 10 # seconds + self.long_timeout = 120 # seconds @classmethod def get_schema(cls): @@ -82,14 +83,14 @@ def get_schema(cls): minimum: -90 maximum: 90 wait_for_complete: - description: >- - whether wait for the rotator to reach the desired angle or - complete the script before the rotator reaches the desired + description: >- + whether wait for the rotator to reach the desired angle or + complete the script before the rotator reaches the desired angle. type: boolean default: true required: - - angle + - angle additionalProperties: false """ schema_dict = yaml.safe_load(schema_yaml) @@ -102,3 +103,24 @@ def get_schema(cls): ] return schema_dict + + async def configure(self, config): + self.target_angle = config.angle + self.wait_for_complete = config.wait_for_complete + + await super().configure(config=config) + + def set_metadata(self, metadata): + metadata.duration = self.long_timeout + + async def run_block(self): + await self.checkpoint(f"Start moving rotator to {self.target_angle} degrees.") + await self.mtcs.move_rotator( + angle=self.target_angle, wait_for_complete=self.wait_for_complete + ) + if self.wait_for_complete: + await self.checkpoint_message( + f"Rotator reached {self.target_angle} degrees." + ) + else: + await self.checkpoint("Stop script and keep rotator moving.") diff --git a/tests/test_maintel_mtrotator_move_rotator.py b/tests/test_maintel_mtrotator_move_rotator.py index c263894ff..c2691b0eb 100644 --- a/tests/test_maintel_mtrotator_move_rotator.py +++ b/tests/test_maintel_mtrotator_move_rotator.py @@ -19,6 +19,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import asyncio import unittest from lsst.ts import standardscripts @@ -31,8 +32,74 @@ class TestMoveRotator( async def basic_make_script(self, index): self.script = MoveRotator(index=index, add_remotes=False) + self.start_angle = 0.0 # degrees + self.very_short_sleep = 0.1 # seconds + self.script.mtcs.move_rotator = unittest.mock.AsyncMock( + side_effect=self.mock_move_rotator + ) + return (self.script,) + async def mock_move_rotator(self, angle, wait_for_complete): + if wait_for_complete is False: + await asyncio.sleep(self.very_short_sleep) + else: + current_angle = self.start_angle + while current_angle < angle: + await asyncio.sleep(self.very_short_sleep) + current_angle += self.script.rotator_velocity * self.very_short_sleep + + async def test_configure_default(self): + """Test the default configuration""" + + async with self.make_script(): + target_angle = 45.0 + + await self.configure_script(angle=target_angle) + + assert self.script.target_angle == target_angle + assert self.script.wait_for_complete is True + assert self.script.program is None + assert self.script.reason is None + assert self.script.checkpoint_message is None + + async def test_configure_dont_wait_for_complete(self): + """Test with the configuration where ``wait_for_complete`` is False""" + + async with self.make_script(): + target_angle = 45.0 + wait_for_complete = False + + await self.configure_script(angle=target_angle, wait_for_complete=False) + + assert self.script.target_angle == target_angle + assert self.script.wait_for_complete is wait_for_complete + assert self.script.program is None + assert self.script.reason is None + assert self.script.checkpoint_message is None + + async def test_configure_with_program_reason(self): + """Testing a valid configuration: with program and reason""" + + # Try configure with a list of valid actuators ids + async with self.make_script(): + self.script.get_obs_id = unittest.mock.AsyncMock( + side_effect=["202306060001"] + ) + await self.configure_script( + angle=10.0, + wait_for_complete=True, + program="BLOCK-123", + reason="SITCOM-321", + ) + + assert self.script.program == "BLOCK-123" + assert self.script.reason == "SITCOM-321" + assert ( + self.script.checkpoint_message + == "MoveRotator BLOCK-123 202306060001 SITCOM-321" + ) + async def test_executable(self): scripts_dir = standardscripts.get_scripts_dir() script_path = scripts_dir / "maintel" / "mtrotator" / "move_rotator.py"