Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new move_rotator.py SAL Script #146

Merged
merged 2 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/news/DM-41081.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Add new ``maintel/mtrotator/move_rotator.py`` SAL Script.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env python
# This file is part of ts_standardscripts
#
# Developed for the LSST Telescope and Site Systems.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

import asyncio

from lsst.ts.standardscripts.maintel.mtrotator import MoveRotator

asyncio.run(MoveRotator.amain())
22 changes: 22 additions & 0 deletions python/lsst/ts/standardscripts/maintel/mtrotator/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This file is part of ts_standardscripts
#
# Developed for the LSST Telescope and Site Systems.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

from .move_rotator import MoveRotator
141 changes: 141 additions & 0 deletions python/lsst/ts/standardscripts/maintel/mtrotator/move_rotator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# This file is part of ts_standardscripts
#
# Developed for the LSST Telescope and Site Systems.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

__all__ = ["MoveRotator"]

import yaml
from lsst.ts.observatory.control.maintel.mtcs import MTCS

from ...base_block_script import BaseBlockScript


class MoveRotator(BaseBlockScript):
"""Move the rotator to a given angle. It has the option of completing the
script before the rotator reaches the desired angle.

Parameters
----------
index : `int`
Index of Script SAL component.

Notes
-----
**Checkpoints**

- "Start moving rotator to {angle} degrees.": Start moving rotator.
- "Stop script and keep rotator moving.": Stop script.
- "Rotator reached {angle} degrees.": Rotator reached angle.

"""

def __init__(self, index: int) -> None:
super().__init__(index=index, descr="Move Rotator")

self.mtcs = None

self.rotator_velocity = 3.5 # degrees per second
self.short_timeout = 10 # seconds
self.long_timeout = 120 # seconds

@classmethod
def get_schema(cls):
url = "https://github.com/lsst-ts/"
path = (
"ts_standardscripts/blob/main/python/lsst/ts/standardscripts/"
"maintel/mtrotator/move_rotator.py"
)
schema_yaml = f"""
$schema: http://json-schema.org/draft-07/schema#
$id: {url}{path}
title: MoveRotator v1
description: Configuration for Maintel move rotator SAL Script.
type: object
properties:
angle:
description: final angle of the rotator.
type: number
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
angle.
type: boolean
default: true
required:
- angle
additionalProperties: false
"""
schema_dict = yaml.safe_load(schema_yaml)

base_schema_dict = super().get_schema()

for properties in base_schema_dict["properties"]:
schema_dict["properties"][properties] = base_schema_dict["properties"][
properties
]

return schema_dict

async def configure(self, config):
"""
Configure the script.

Parameters
----------
config : `dict`
Dictionary containing the configuration parameters.
"""
await self.configure_tcs()

self.target_angle = config.angle
self.wait_for_complete = config.wait_for_complete

await super().configure(config=config)

async def configure_tcs(self) -> None:
"""
Handle creating MTCS object and waiting for remote to start.
"""
if self.mtcs is None:
self.log.debug("Creating MTCS.")
self.mtcs = MTCS(
domain=self.domain,
log=self.log,
)
await self.mtcs.start_task
else:
self.log.debug("MTCS already defined, skipping.")

def set_metadata(self, metadata):
"""Set the metadata for the script."""
metadata.duration = self.long_timeout

async def run_block(self):
"""Run the script."""
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
)
await self.checkpoint(
f"Move rotator returned. Wait for complete: {self.wait_for_complete}."
)
118 changes: 118 additions & 0 deletions tests/test_maintel_mtrotator_move_rotator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# This file is part of ts_standardscripts
#
# Developed for the LSST Telescope and Site Systems.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

import unittest

from lsst.ts import standardscripts
from lsst.ts.observatory.control.maintel.mtcs import MTCS, MTCSUsages
from lsst.ts.standardscripts.maintel.mtrotator import MoveRotator


class TestMoveRotator(
standardscripts.BaseScriptTestCase, unittest.IsolatedAsyncioTestCase
):
async def basic_make_script(self, index):
self.script = MoveRotator(index=index)

self.script.mtcs = MTCS(
domain=self.script.domain,
intended_usage=MTCSUsages.DryTest,
log=self.script.log,
)

self.start_angle = 0.0 # degrees
self.very_short_sleep = 0.1 # seconds
self.script.mtcs.move_rotator = unittest.mock.AsyncMock()

return (self.script,)

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_run_with_default_config(self):
async with self.make_script():
target_angle = 45.0

await self.configure_script(angle=target_angle)

await self.run_script()

self.script.mtcs.move_rotator.assert_called_once_with(
angle=target_angle, wait_for_complete=True
)

async def test_executable(self):
scripts_dir = standardscripts.get_scripts_dir()
script_path = scripts_dir / "maintel" / "mtrotator" / "move_rotator.py"
print(script_path)
await self.check_executable(script_path)


if __name__ == "__main__":
unittest.main()
Loading