diff --git a/doc/news/DM-47363.feature.rst b/doc/news/DM-47363.feature.rst new file mode 100644 index 000000000..d6669a1ac --- /dev/null +++ b/doc/news/DM-47363.feature.rst @@ -0,0 +1 @@ +Add new `set_dof.py`` to set absolute DOF position \ No newline at end of file diff --git a/python/lsst/ts/standardscripts/data/scripts/maintel/set_dof.py b/python/lsst/ts/standardscripts/data/scripts/maintel/set_dof.py new file mode 100755 index 000000000..f3c48b07d --- /dev/null +++ b/python/lsst/ts/standardscripts/data/scripts/maintel/set_dof.py @@ -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 . + +import asyncio + +from lsst.ts.standardscripts.maintel import SetDOF + +asyncio.run(SetDOF.amain()) diff --git a/python/lsst/ts/standardscripts/maintel/__init__.py b/python/lsst/ts/standardscripts/maintel/__init__.py index 9017f90d7..27c01a834 100644 --- a/python/lsst/ts/standardscripts/maintel/__init__.py +++ b/python/lsst/ts/standardscripts/maintel/__init__.py @@ -39,6 +39,7 @@ from .offset_mtcs import * from .open_mirror_covers import * from .point_azel import * +from .set_dof import * from .setup_mtcs import * from .standby_comcam import * from .standby_mtcs import * diff --git a/python/lsst/ts/standardscripts/maintel/set_dof.py b/python/lsst/ts/standardscripts/maintel/set_dof.py new file mode 100644 index 000000000..2024bde82 --- /dev/null +++ b/python/lsst/ts/standardscripts/maintel/set_dof.py @@ -0,0 +1,57 @@ +# 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 . + +__all__ = ["SetDOF"] + +from .apply_dof import ApplyDOF + +STD_TIMEOUT = 30 + + +class SetDOF(ApplyDOF): + """Set absolute positions DOF to the main telescope, either bending + mode or hexapod position. + + Parameters + ---------- + index : `int` + Index of Script SAL component. + + Notes + ----- + **Checkpoints** + "Setting DOF..." - The DOF absolute position is being applied. + + """ + + async def run(self) -> None: + """Run script.""" + # Assert feasibility + await self.assert_feasibility() + + await self.checkpoint("Setting DOF...") + current_dof = await self.mtcs.rem.mtaos.evt_degreeOfFreedom.aget( + timeout=STD_TIMEOUT + ) + dof_data = self.mtcs.rem.mtaos.cmd_offsetDOF.DataType() + for i, dof_absolute in enumerate(self.dofs): + dof_data.value[i] = dof_absolute - current_dof.aggregatedDoF[i] + await self.mtcs.rem.mtaos.cmd_offsetDOF.start(data=dof_data) diff --git a/tests/test_maintel_set_dof.py b/tests/test_maintel_set_dof.py new file mode 100644 index 000000000..19c1246e0 --- /dev/null +++ b/tests/test_maintel_set_dof.py @@ -0,0 +1,87 @@ +# 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 . + +import asyncio +import random +import types +import unittest + +import numpy as np +from lsst.ts import standardscripts +from lsst.ts.observatory.control.maintel.mtcs import MTCS, MTCSUsages +from lsst.ts.observatory.control.utils.enums import DOFName +from lsst.ts.standardscripts.maintel import SetDOF + +random.seed(47) # for set_random_lsst_dds_partition_prefix + + +class TestSetDOF(standardscripts.BaseScriptTestCase, unittest.IsolatedAsyncioTestCase): + async def basic_make_script(self, index): + self.script = SetDOF(index=index) + + # Mock the MTCS + self.script.mtcs = MTCS( + domain=self.script.domain, + intended_usage=MTCSUsages.DryTest, + log=self.script.log, + ) + self.script.mtcs.rem.mtaos = unittest.mock.AsyncMock() + self.script.mtcs.rem.mtaos.cmd_offsetDOF.attach_mock( + unittest.mock.Mock( + return_value=types.SimpleNamespace(value=np.zeros(len(DOFName))) + ), + "DataType", + ) + self.script.mtcs.rem.mtaos.configure_mock( + **{ + "evt_degreeOfFreedom.aget": self.get_current_dof, + } + ) + + self.script.mtcs.assert_all_enabled = unittest.mock.AsyncMock() + + return (self.script,) + + async def get_current_dof(self, timeout): + await asyncio.sleep(0.5) + return np.zeros(len(DOFName)) + + async def test_run(self) -> None: + # Start the test itself + async with self.make_script(): + config_dofs = {"M2_dz": 0.2, "Cam_dy": 0.3, "M1M3_B1": 0.5, "M2_B14": 0.7} + + await self.configure_script(**config_dofs) + + # Run the script + await self.run_script() + + self.script.mtcs.rem.mtaos.cmd_offsetDOF.DataType.assert_called() + self.script.mtcs.rem.mtaos.cmd_offsetDOF.start.assert_awaited_once() + + async def test_executable(self) -> None: + scripts_dir = standardscripts.get_scripts_dir() + script_path = scripts_dir / "maintel" / "set_dof.py" + await self.check_executable(script_path) + + +if __name__ == "__main__": + unittest.main()