From 3e4bf95318b578e61ff8fd3ca43090a276f2787b Mon Sep 17 00:00:00 2001 From: Parker Fagrelius Date: Fri, 11 Oct 2024 13:42:30 -0600 Subject: [PATCH 1/4] initial commit of power off scripts --- .../calibration/power_off_tunablelaser.py | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 python/lsst/ts/standardscripts/maintel/calibration/power_off_tunablelaser.py diff --git a/python/lsst/ts/standardscripts/maintel/calibration/power_off_tunablelaser.py b/python/lsst/ts/standardscripts/maintel/calibration/power_off_tunablelaser.py new file mode 100644 index 00000000..3c7daed8 --- /dev/null +++ b/python/lsst/ts/standardscripts/maintel/calibration/power_off_tunablelaser.py @@ -0,0 +1,144 @@ +# 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__ = ["PowerOffTunableLaser"] + + +import yaml +from lsst.ts import salobj +from lsst.ts.observatory.control.maintel.mtcalsys import MTCalsys + + +class PowerOffTunableLaser(salobj.BaseScript): + """Starts propagating the Tunable Laser for functional + testing. + + Parameters + ---------- + index : `int` + Index of Script SAL component. + + """ + + def __init__(self, index): + super().__init__( + index=index, + descr="Power On Tunable Laser", + ) + + self.laser = None + self.mtcalsys = None + + @classmethod + def get_schema(cls): + schema_yaml = """ + $schema: http://json-schema.org/draft-07/schema# + $id: https://github.com/lsst-ts/ts_standardscripts/maintel/calibrations/power_on_tunablelaser.yaml + title: PowerOffTunableLaser v1 + description: Configuration for PowerOffTunableLaser. + Each attribute can be specified as a scalar or array. + All arrays must have the same length (one item per image). + type: object + properties: + sequence_name: + description: Name of sequence in MTCalsys + type: string + default: laser_functional + + additionalProperties: false + """ + return yaml.safe_load(schema_yaml) + + def set_metadata(self, metadata): + metadata.duration = 30 + + async def configure(self, config): + """Configure the script. + + Parameters + ---------- + config : ``self.cmd_configure.DataType`` + + """ + self.log.info("Configure started") + if self.mtcalsys is None: + self.log.debug("Creating MTCalSys.") + self.mtcalsys = MTCalsys(domain=self.domain, log=self.log) + await self.mtcalsys.start_task + + self.sequence_name = config.sequence_name + self.mtcalsys.load_calibration_config_file() + self.mtcalsys.assert_valid_configuration_option(name=self.sequence_name) + + self.config_data = self.mtcalsys.get_calibration_configuration( + self.sequence_name + ) + + if self.laser is None: + self.laser = salobj.Remote( + domain=self.domain, + name="TunableLaser", + ) + + self.laser.start_task + + self.log.info("Configure completed") + + async def run(self): + """Run script.""" + await self.assert_components_enabled() + + await self.checkpoint("Configuring TunableLaser") + + params = await self.mtcalsys.get_laser_parameters() + + self.log.info( + f"Laser Configuration is {params[0]}, \n" + f"wavelength is {params[1]}, \n" + f"Interlock is {params[2]}, \n" + f"Burst mode is {params[3]}, \n" + f"Cont. mode is {params[4]}" + ) + + await self.checkpoint("Stopping laser propagation") + await self.start_propagation_off() + + async def start_propagation_off(self): + """Starts propagation of the laser""" + + await self.mtcalsys.laser_stop_propagate() + + async def assert_components_enabled(self): + """Checks if TunableLaser is ENABLED + + Raises + ------ + RunTimeError: + If either component is not ENABLED""" + + comps = [self.laser] + + for comp in comps: + summary_state = await comp.evt_summaryState.aget() + if salobj.State(summary_state.summaryState) != salobj.State( + salobj.State.ENABLED + ): + raise RuntimeError(f"{comp} is not ENABLED") From f05edda5ac4d137529d44497e554c56f15081da4 Mon Sep 17 00:00:00 2001 From: Parker Fagrelius Date: Fri, 11 Oct 2024 13:51:31 -0600 Subject: [PATCH 2/4] adding other files --- doc/news/DM-45743.feature.rst | 1 + .../maintel/calibration/__init__.py | 22 ++++ tests/test_maintel_power_off_tunablelaser.py | 123 ++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 doc/news/DM-45743.feature.rst create mode 100644 python/lsst/ts/standardscripts/maintel/calibration/__init__.py create mode 100644 tests/test_maintel_power_off_tunablelaser.py diff --git a/doc/news/DM-45743.feature.rst b/doc/news/DM-45743.feature.rst new file mode 100644 index 00000000..e48e6836 --- /dev/null +++ b/doc/news/DM-45743.feature.rst @@ -0,0 +1 @@ +New script to turn the Tunable Laser off, i.e. stop propagating diff --git a/python/lsst/ts/standardscripts/maintel/calibration/__init__.py b/python/lsst/ts/standardscripts/maintel/calibration/__init__.py new file mode 100644 index 00000000..2fe118c0 --- /dev/null +++ b/python/lsst/ts/standardscripts/maintel/calibration/__init__.py @@ -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 . + +from .power_off_tunablelaser import * diff --git a/tests/test_maintel_power_off_tunablelaser.py b/tests/test_maintel_power_off_tunablelaser.py new file mode 100644 index 00000000..e6e5f176 --- /dev/null +++ b/tests/test_maintel_power_off_tunablelaser.py @@ -0,0 +1,123 @@ +# 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 logging +import os +import random +import types +import unittest +import warnings + +from lsst.ts import salobj, standardscripts, utils +from lsst.ts.standardscripts.maintel.calibration import PowerOffTunableLaser +from lsst.ts.xml.enums.TunableLaser import LaserDetailedState + +# TODO: (DM-46168) Revert workaround for TunableLaser XML changes +try: + from lsst.ts.xml.enums.TunableLaser import ( + OpticalConfiguration as LaserOpticalConfiguration, + ) +except ImportError: + warnings.warn( + "OpticalConfiguration enumeration not availble in ts-xml. Using local version." + ) + from lsst.ts.observatory.control.utils.enums import LaserOpticalConfiguration + +random.seed(47) # for set_random_lsst_dds_partition_prefix + +logging.basicConfig() + + +class TestPowerOffTunableLaser( + standardscripts.BaseScriptTestCase, unittest.IsolatedAsyncioTestCase +): + async def basic_make_script(self, index): + self.script = PowerOffTunableLaser(index=index) + + await self.configure_mocks() + + return [ + self.script, + ] + + async def configure_mocks(self): + self.script.laser = unittest.mock.AsyncMock() + self.script.laser.start_task = utils.make_done_future() + + # Configure mocks + + self.script.laser.configure_mock( + **{ + "evt_summaryState.aget.side_effect": self.mock_get_laser_summary_state, + "cmd_startPropagateLaser.start.side_effect": self.mock_start_laser, + } + ) + + async def mock_get_laser_summary_state(self, **kwargs): + return types.SimpleNamespace(summaryState=salobj.State.ENABLED) + + async def mock_stop_laser(self, **kwargs): + self.laser_state = types.SimpleNamespace( + detailedState=LaserDetailedState.NONPROPAGATING_CONTINUOUS_MODE + ) + + async def test_configure(self): + # Try to configure with only some of the optional parameters + async with self.make_script(): + mode = LaserDetailedState.NONPROPAGATING_CONTINUOUS_MODE + optical_configuration = LaserOpticalConfiguration.SCU.name + wavelength = 500.0 + + await self.configure_script() + + assert self.script.laser_mode == mode + assert self.script.optical_configuration == optical_configuration + assert self.script.wavelength == wavelength + + async def test_run_without_failures(self): + async with self.make_script(): + await self.configure_script() + + await self.run_script() + + self.script.laser.cmd_stopPropagateLaser.start.assert_awaited_with( + timeout=self.script.laser_warmup, + ) + + # Summary State + self.script.laser.evt_summaryState.aget.assert_awaited_once_with() + + # Assert states are OK + assert ( + self.laser_status.detailedState + == LaserDetailedState.NONPROPAGATING_CONTINUOUS_MODE + ) + + async def test_executable(self): + scripts_dir = standardscripts.get_scripts_dir() + script_path = os.path.join( + scripts_dir, "maintel", "calibration", "power_on_tunablelaser.py" + ) + await self.check_executable(script_path) + + +if __name__ == "__main__": + unittest.main() From eca9aed1ee884eb1979122045383a2543b28356a Mon Sep 17 00:00:00 2001 From: Parker Fagrelius Date: Fri, 11 Oct 2024 14:28:31 -0600 Subject: [PATCH 3/4] changes to test --- tests/test_maintel_power_off_tunablelaser.py | 31 ++------------------ 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/tests/test_maintel_power_off_tunablelaser.py b/tests/test_maintel_power_off_tunablelaser.py index e6e5f176..b5db947c 100644 --- a/tests/test_maintel_power_off_tunablelaser.py +++ b/tests/test_maintel_power_off_tunablelaser.py @@ -24,23 +24,11 @@ import random import types import unittest -import warnings from lsst.ts import salobj, standardscripts, utils from lsst.ts.standardscripts.maintel.calibration import PowerOffTunableLaser from lsst.ts.xml.enums.TunableLaser import LaserDetailedState -# TODO: (DM-46168) Revert workaround for TunableLaser XML changes -try: - from lsst.ts.xml.enums.TunableLaser import ( - OpticalConfiguration as LaserOpticalConfiguration, - ) -except ImportError: - warnings.warn( - "OpticalConfiguration enumeration not availble in ts-xml. Using local version." - ) - from lsst.ts.observatory.control.utils.enums import LaserOpticalConfiguration - random.seed(47) # for set_random_lsst_dds_partition_prefix logging.basicConfig() @@ -67,7 +55,7 @@ async def configure_mocks(self): self.script.laser.configure_mock( **{ "evt_summaryState.aget.side_effect": self.mock_get_laser_summary_state, - "cmd_startPropagateLaser.start.side_effect": self.mock_start_laser, + "cmd_stopPropagateLaser.start.side_effect": self.mock_stop_laser, } ) @@ -76,22 +64,9 @@ async def mock_get_laser_summary_state(self, **kwargs): async def mock_stop_laser(self, **kwargs): self.laser_state = types.SimpleNamespace( - detailedState=LaserDetailedState.NONPROPAGATING_CONTINUOUS_MODE + detailedState=LaserDetailedState.PROPAGATING_CONTINUOUS_MODE ) - async def test_configure(self): - # Try to configure with only some of the optional parameters - async with self.make_script(): - mode = LaserDetailedState.NONPROPAGATING_CONTINUOUS_MODE - optical_configuration = LaserOpticalConfiguration.SCU.name - wavelength = 500.0 - - await self.configure_script() - - assert self.script.laser_mode == mode - assert self.script.optical_configuration == optical_configuration - assert self.script.wavelength == wavelength - async def test_run_without_failures(self): async with self.make_script(): await self.configure_script() @@ -114,7 +89,7 @@ async def test_run_without_failures(self): async def test_executable(self): scripts_dir = standardscripts.get_scripts_dir() script_path = os.path.join( - scripts_dir, "maintel", "calibration", "power_on_tunablelaser.py" + scripts_dir, "maintel", "calibration", "power_off_tunablelaser.py" ) await self.check_executable(script_path) From 3cd8ea1b10c71b8a7d250d740d4cc50d653b5e97 Mon Sep 17 00:00:00 2001 From: Parker Fagrelius Date: Fri, 11 Oct 2024 14:35:11 -0600 Subject: [PATCH 4/4] adding script --- .../calibration/power_off_tunablelaser.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100755 python/lsst/ts/standardscripts/data/scripts/maintel/calibration/power_off_tunablelaser.py diff --git a/python/lsst/ts/standardscripts/data/scripts/maintel/calibration/power_off_tunablelaser.py b/python/lsst/ts/standardscripts/data/scripts/maintel/calibration/power_off_tunablelaser.py new file mode 100755 index 00000000..6e050970 --- /dev/null +++ b/python/lsst/ts/standardscripts/data/scripts/maintel/calibration/power_off_tunablelaser.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.calibration import PowerOffTunableLaser + +asyncio.run(PowerOffTunableLaser.amain())