diff --git a/python/lsst/ts/observatory/control/maintel/mtcs.py b/python/lsst/ts/observatory/control/maintel/mtcs.py index 2c8079ac..98deaa9c 100644 --- a/python/lsst/ts/observatory/control/maintel/mtcs.py +++ b/python/lsst/ts/observatory/control/maintel/mtcs.py @@ -579,6 +579,61 @@ async def dome_el_in_position(self) -> str: await self._dome_el_in_position.wait() return "Dome elevation in position." + async def wait_for_dome_state( + self, + expected_states: set[MTDome.MotionState], + bad_states: set[MTDome.MotionState], + timeout: float, + check_in_position: bool = False, + ) -> None: + """Wait for a specific dome state. + + Parameters + ---------- + expected_states : set[MTDome.MotionState] + Valid states to transition into while un-parking. + bad_states : set[MTDome.MotionState] + States that are not allowed while un-parking and should raise an + error. + timeout : float + Maximum time to wait for the correct state. + + Raises + ------ + RuntimeError + If a bad state is encountered or the expected state is not reached + in time. + """ + az_motion = await self.rem.mtdome.evt_azMotion.aget(timeout=self.long_timeout) + while az_motion.state not in expected_states: + az_motion = await self.rem.mtdome.evt_azMotion.next( + Flush=False, timeout=timeout + ) + + if az_motion.state in bad_states: + raise RuntimeError( + f"Dome transitioned to an invalid state:" f" {az_motion.state}" + ) + + # If check_in_position is True, verify the dome is in the correct + # position. + if check_in_position and not az_motion.inPosition: + self.log.debug( + f"Dome not yet in position. Current state: " f"{az_motion.state}." + ) + + self.log.debug( + f"Dome state: {az_motion.state}, inPosition: " f"{az_motion.inPosition}" + ) + + # If we reach this point, the dome has successfully reached an + # expected state. + if check_in_position and not az_motion.inPosition: + raise RuntimeError( + f"Dome reached the expected state " + f"{az_motion.state} but is not yet in position." + ) + async def park_dome(self) -> None: """Park the dome by moving it to the park azimuth.""" self.log.info(f"Parking dome at azimuth {self.dome_park_az}.") @@ -588,19 +643,36 @@ async def park_dome(self) -> None: message="All components need to be enabled for parking the Dome." ) - # Move the dome to the park position - await self.rem.mtdome.cmd_park.start(timeout=self.long_timeout) + # check first if Dome is already in PARKED state + az_motion = await self.rem.mtdome.evt_azMotion.aget(timeout=self.fast_timeout) - # Wait for the dome to emit the evt_azMotion event in the PARKED state - self.log.info("Waiting for dome to reach the PARKED status.") + if az_motion.state == MTDome.MotionState.PARKED: + self.log.info("Dome is already in PARKED state.") + else: + # Move the dome to the park position + await self.rem.mtdome.cmd_park.start(timeout=self.long_timeout) + + # Define expected and bad states for parking + expected_states = {MTDome.MotionState.PARKED} + bad_states = { + MTDome.MotionState.ERROR, + MTDome.MotionState.UNDETERMINED, + MTDome.MotionState.DISABLED, + MTDome.MotionState.DISABLING, + } - az_motion = await self.rem.mtdome.evt_azMotion.next() + # Wait for the dome to emit the evt_azMotion event in the PARKED + # state + self.log.info("Waiting for dome to reach the PARKED state.") - # Check if the dome is in the PARKED state and in position - if az_motion.state == MTDome.MotionState.PARKED and az_motion.inPosition: - self.log.info("Dome parked successfully.") - else: - raise RuntimeError("Dome did not reach the park azimuth.") + # Wait for the dome to park, ensuring it doesn't reach an invalid + # state + await self.wait_for_dome_state( + expected_states, + bad_states, + timeout=self.long_timeout, + check_in_position=True, + ) def set_azel_slew_checks(self, wait_dome: bool) -> typing.Any: """Handle azEl slew to wait or not for the dome.