Skip to content

Commit

Permalink
Merge pull request #170 from lsst-ts/tickets/DM-46179
Browse files Browse the repository at this point in the history
DM-46179 Extend TCS readiness check to other image types beyond OBJECT
  • Loading branch information
iglesu authored Oct 18, 2024
2 parents 17cbe73 + f3cf295 commit 5959b48
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 33 deletions.
1 change: 1 addition & 0 deletions doc/news/DM-46179.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix MTRotator enumeration from INITIALIZING to STATIONARY
2 changes: 2 additions & 0 deletions doc/news/DM-46179.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Extend TCS readiness check to other image types beyond OBJECT, such as:
ENGTEST, CWFS and ACQ.
1 change: 1 addition & 0 deletions doc/news/DM-46179.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In MTCSAsyncMock remove old idl.enums import in favor of new xml.enums
5 changes: 3 additions & 2 deletions python/lsst/ts/observatory/control/base_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,8 @@ async def take_imgtype(
if imgtype not in ["BIAS", "DARK"]:
await self.setup_instrument(**kwargs)

if imgtype == "OBJECT" and self.ready_to_take_data is not None:
tcs_ready_imgtypes = ["OBJECT", "ENGTEST", "ACQ", "CWFS"]
if imgtype in tcs_ready_imgtypes and self.ready_to_take_data is not None:
self.log.debug(f"imagetype: {imgtype}, wait for TCS to be ready.")
try:
await asyncio.wait_for(
Expand All @@ -977,7 +978,7 @@ async def take_imgtype(
"Timeout waiting for TCS to report as ready to take data "
f"(timeout={self.max_tcs_wait_time})."
)
elif imgtype == "OBJECT" and self.ready_to_take_data is None:
elif imgtype in tcs_ready_imgtypes and self.ready_to_take_data is None:
self.log.debug(f"imagetype: {imgtype}, TCS synchronization not configured.")
else:
self.log.debug(f"imagetype: {imgtype}, skip TCS synchronization.")
Expand Down
55 changes: 55 additions & 0 deletions python/lsst/ts/observatory/control/mock/base_camera_async_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import typing

import astropy
import pytest
from lsst.ts import utils
from lsst.ts.observatory.control import CameraExposure
from lsst.ts.observatory.control.mock import RemoteGroupAsyncMock
Expand Down Expand Up @@ -785,6 +786,60 @@ def assert_take_calibration(self, expected_camera_exposure: CameraExposure) -> N
"await counts for cmd_discardRows.set_start."
)

async def assert_take_image_tcs_sync(
self,
image_type: str,
exptime: float = 1.0,
n: int = 1,
should_fail: bool = False,
**kwargs: typing.Any,
) -> None:
"""Test that taking an image of a given type waits for TCS readiness
and asserts the camera commands.
Parameters
----------
image_type : str
The image type to test (e.g., "OBJECT", "ENGTEST", "ACQ","CWFS").
exptime : float
Exposure time.
n : int
Number of images to take.
should_fail : bool
If True, simulate TCS failure and expect a RuntimeError.
**kwargs
Additional arguments to pass to the `assert_take_<type>` method.
"""
image_type_lower = image_type.lower()

assert_method_name = f"assert_take_{image_type_lower}"

assert_take_method = getattr(self, assert_method_name, None)

if assert_take_method is None:
raise AttributeError(
f"No method found for image type '{image_type}'. "
f"Ensure that '{assert_method_name}' exist."
)

with self.get_fake_tcs() as fake_tcs:
fake_tcs.fail = should_fail

if should_fail:
with pytest.raises(RuntimeError):
await assert_take_method(
exptime=exptime,
n=n,
**kwargs,
)
else:
await assert_take_method(
exptime=exptime,
n=n,
**kwargs,
)
assert fake_tcs.called == 1

@contextlib.contextmanager
def get_fake_tcs(self) -> typing.Generator[FakeTCS, None, None]:
fake_tcs = FakeTCS(self.log)
Expand Down
61 changes: 30 additions & 31 deletions python/lsst/ts/observatory/control/mock/mtcs_async_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@
import typing

import numpy as np
from lsst.ts import idl, utils
from lsst.ts.idl.enums import MTM1M3
from lsst.ts import utils, xml
from lsst.ts.observatory.control.maintel.mtcs import MTCS, MTCSUsages
from lsst.ts.observatory.control.mock import RemoteGroupAsyncMock
from lsst.ts.xml.enums import MTDome
from lsst.ts.xml.enums import MTM1M3, MTDome


class MTCSAsyncMock(RemoteGroupAsyncMock):
Expand Down Expand Up @@ -106,7 +105,7 @@ async def setup_types(self) -> None:
)
self._mtrotator_evt_in_position = types.SimpleNamespace(inPosition=True)
self._mtrotator_evt_controller_state = types.SimpleNamespace(
enabledSubstate=idl.enums.MTRotator.EnabledSubstate.INITIALIZING
enabledSubstate=xml.enums.MTRotator.EnabledSubstate.STATIONARY
)

# MTDome data
Expand All @@ -127,19 +126,19 @@ async def setup_types(self) -> None:

# MTM1M3 data
self._mtm1m3_evt_detailed_state = types.SimpleNamespace(
detailedState=idl.enums.MTM1M3.DetailedState.PARKED
detailedState=xml.enums.MTM1M3.DetailedState.PARKED
)
self._mtm1m3_evt_applied_balance_forces = types.SimpleNamespace(
forceMagnitude=0.0
)
self._mtm1m3_evt_hp_test_status = types.SimpleNamespace(
testState=[idl.enums.MTM1M3.HardpointTest.NOTTESTED] * 6
testState=[xml.enums.MTM1M3.HardpointTest.NOTTESTED] * 6
)
self._mtm1m3_evt_force_actuator_bump_test_status = types.SimpleNamespace(
actuatorId=0,
primaryTest=[idl.enums.MTM1M3.BumpTest.NOTTESTED]
primaryTest=[xml.enums.MTM1M3.BumpTest.NOTTESTED]
* len(self.mtcs.get_m1m3_actuator_ids()),
secondaryTest=[idl.enums.MTM1M3.BumpTest.NOTTESTED]
secondaryTest=[xml.enums.MTM1M3.BumpTest.NOTTESTED]
* len(self.mtcs.get_m1m3_actuator_secondary_ids()),
primaryTestTimestamps=[0.0] * len(self.mtcs.get_m1m3_actuator_ids()),
secondaryTestTimestamps=[0.0]
Expand All @@ -149,8 +148,8 @@ async def setup_types(self) -> None:
slewFlag=False,
balanceForcesApplied=False,
)
self.desired_hp_test_final_status = idl.enums.MTM1M3.HardpointTest.PASSED
self.desired_bump_test_final_status = idl.enums.MTM1M3.BumpTest.PASSED
self.desired_hp_test_final_status = xml.enums.MTM1M3.HardpointTest.PASSED
self.desired_bump_test_final_status = xml.enums.MTM1M3.BumpTest.PASSED

self.m1m3_actuator_offset = 101

Expand Down Expand Up @@ -437,11 +436,11 @@ async def mtrotator_cmd_stop(self, *args: typing.Any, **kwargs: typing.Any) -> N

async def _mtrotator_stop(self) -> None:
self._mtrotator_evt_controller_state.enabledSubstate = (
idl.enums.MTRotator.EnabledSubstate.CONSTANT_VELOCITY
xml.enums.MTRotator.EnabledSubstate.CONSTANT_VELOCITY
)
await asyncio.sleep(1.0)
self._mtrotator_evt_controller_state.enabledSubstate = (
idl.enums.MTRotator.EnabledSubstate.STATIONARY
xml.enums.MTRotator.EnabledSubstate.STATIONARY
)

async def mtrotator_tel_rotation_next(
Expand Down Expand Up @@ -587,7 +586,7 @@ async def mtm1m3_cmd_raise_m1m3(
if (
raise_m1m3
and self._mtm1m3_evt_detailed_state.detailedState
== idl.enums.MTM1M3.DetailedState.PARKED
== xml.enums.MTM1M3.DetailedState.PARKED
):
self._mtm1m3_raise_task = asyncio.create_task(self.execute_raise_m1m3())
else:
Expand All @@ -604,7 +603,7 @@ async def mtm1m3_cmd_enter_engineering(
# since it could change considerably from what the m1m3 actually does.
asyncio.create_task(
self._set_m1m3_detailed_state(
idl.enums.MTM1M3.DetailedState.PARKEDENGINEERING
xml.enums.MTM1M3.DetailedState.PARKEDENGINEERING
)
)

Expand All @@ -626,7 +625,7 @@ async def mtm1m3_cmd_exit_engineering(
# simply put m1m3 in PARKED state, regardless of which engineering
# state it was before.
asyncio.create_task(
self._set_m1m3_detailed_state(idl.enums.MTM1M3.DetailedState.PARKED)
self._set_m1m3_detailed_state(xml.enums.MTM1M3.DetailedState.PARKED)
)

async def mtm1m3_cmd_test_hardpoint(
Expand Down Expand Up @@ -658,8 +657,8 @@ async def mtm1m3_cmd_abort_raise_m1m3(
self, *args: typing.Any, **kwargs: typing.Any
) -> None:
if self._mtm1m3_evt_detailed_state.detailedState in {
idl.enums.MTM1M3.DetailedState.RAISINGENGINEERING,
idl.enums.MTM1M3.DetailedState.RAISING,
xml.enums.MTM1M3.DetailedState.RAISINGENGINEERING,
xml.enums.MTM1M3.DetailedState.RAISING,
}:
self._mtm1m3_abort_raise_task = asyncio.create_task(
self.execute_abort_raise_m1m3()
Expand All @@ -670,12 +669,12 @@ async def mtm1m3_cmd_abort_raise_m1m3(
async def execute_raise_m1m3(self) -> None:
self.log.debug("Start raising M1M3...")
self._mtm1m3_evt_detailed_state.detailedState = (
idl.enums.MTM1M3.DetailedState.RAISING
xml.enums.MTM1M3.DetailedState.RAISING
)
await asyncio.sleep(self.execute_raise_lower_m1m3_time)
self.log.debug("Done raising M1M3...")
self._mtm1m3_evt_detailed_state.detailedState = (
idl.enums.MTM1M3.DetailedState.ACTIVE
xml.enums.MTM1M3.DetailedState.ACTIVE
)

async def mtm1m3_cmd_lower_m1m3(
Expand All @@ -686,7 +685,7 @@ async def mtm1m3_cmd_lower_m1m3(
if (
lower_m1m3
and self._mtm1m3_evt_detailed_state.detailedState
== idl.enums.MTM1M3.DetailedState.ACTIVE
== xml.enums.MTM1M3.DetailedState.ACTIVE
):
self._mtm1m3_lower_task = asyncio.create_task(self.execute_lower_m1m3())
else:
Expand All @@ -697,12 +696,12 @@ async def mtm1m3_cmd_lower_m1m3(
async def execute_lower_m1m3(self) -> None:
self.log.debug("Start lowering M1M3...")
self._mtm1m3_evt_detailed_state.detailedState = (
idl.enums.MTM1M3.DetailedState.LOWERING
xml.enums.MTM1M3.DetailedState.LOWERING
)
await asyncio.sleep(self.execute_raise_lower_m1m3_time)
self.log.debug("Done lowering M1M3...")
self._mtm1m3_evt_detailed_state.detailedState = (
idl.enums.MTM1M3.DetailedState.PARKED
xml.enums.MTM1M3.DetailedState.PARKED
)

async def execute_abort_raise_m1m3(self) -> None:
Expand All @@ -712,12 +711,12 @@ async def execute_abort_raise_m1m3(self) -> None:

self.log.debug("Set m1m3 detailed state to lowering...")
self._mtm1m3_evt_detailed_state.detailedState = (
idl.enums.MTM1M3.DetailedState.LOWERING
xml.enums.MTM1M3.DetailedState.LOWERING
)
await asyncio.sleep(self.execute_raise_lower_m1m3_time)
self.log.debug("M1M3 raise task done, set detailed state to parked...")
self._mtm1m3_evt_detailed_state.detailedState = (
idl.enums.MTM1M3.DetailedState.PARKED
xml.enums.MTM1M3.DetailedState.PARKED
)

async def mtm1m3_cmd_enable_hardpoint_corrections(
Expand All @@ -741,7 +740,7 @@ async def mtm1m3_cmd_disable_hp_corrections(
)

async def _set_m1m3_detailed_state(
self, detailed_state: idl.enums.MTM1M3.DetailedState
self, detailed_state: xml.enums.MTM1M3.DetailedState
) -> None:
self.log.debug(
f"M1M3 detailed state: {self._mtm1m3_evt_detailed_state.detailedState!r} -> {detailed_state!r}"
Expand All @@ -751,8 +750,8 @@ async def _set_m1m3_detailed_state(

async def _mtm1m3_cmd_test_hardpoint(self, hp: int) -> None:
hp_test_status = [
idl.enums.MTM1M3.HardpointTest.TESTINGPOSITIVE,
idl.enums.MTM1M3.HardpointTest.TESTINGNEGATIVE,
xml.enums.MTM1M3.HardpointTest.TESTINGPOSITIVE,
xml.enums.MTM1M3.HardpointTest.TESTINGNEGATIVE,
self.desired_hp_test_final_status,
]

Expand All @@ -765,10 +764,10 @@ async def _mtm1m3_cmd_force_actuator_bump_test(
self, actuator_id: int, test_primary: bool, test_secondary: bool
) -> None:
bump_test_states = [
idl.enums.MTM1M3.BumpTest.TESTINGPOSITIVE,
idl.enums.MTM1M3.BumpTest.TESTINGPOSITIVEWAIT,
idl.enums.MTM1M3.BumpTest.TESTINGNEGATIVE,
idl.enums.MTM1M3.BumpTest.TESTINGNEGATIVEWAIT,
xml.enums.MTM1M3.BumpTest.TESTINGPOSITIVE,
xml.enums.MTM1M3.BumpTest.TESTINGPOSITIVEWAIT,
xml.enums.MTM1M3.BumpTest.TESTINGNEGATIVE,
xml.enums.MTM1M3.BumpTest.TESTINGNEGATIVEWAIT,
]

if test_primary:
Expand Down
Loading

0 comments on commit 5959b48

Please sign in to comment.