Skip to content

Commit

Permalink
Merge pull request #180 from lsst-ts/tickets/DM-47381
Browse files Browse the repository at this point in the history
Tickets/dm 47381: Support Summit Observing Weeks 45-46 of 2024
  • Loading branch information
edennihy authored Nov 20, 2024
2 parents 4319525 + d7736b0 commit 5050117
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 146 deletions.
1 change: 1 addition & 0 deletions doc/news/DM-47381.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``maintel/mtcs.py``, fix set_azel_slew_checks to take into account value of the check flag for mtdome and mtdometrajectory.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement simple TCS synchronization in MTCS.
6 changes: 6 additions & 0 deletions doc/news/DM-47381.feature.2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Add the following to MTCSUsages.All:
- mirrorCoversMotionState
- compensationMode
- m1m3 events
- mirrorCoversSystemState
- mirrorCoversLocksMotionState
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``maintel/comcam.py``, add CCOOD.evt_imageInOODS to TakeImage usage.
6 changes: 6 additions & 0 deletions doc/news/DM-47381.feature.4.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
In ``maintel/mtcs.py``, make the following updates the open_m1_cover and close_m1_cover methods:
- Refactor open_m1_cover.
- Refactor close_m1_cover.
- Add stop_tracking later in the close_m1_cover operation.
- Add stop_tracking to the slew_to_m1_cover_operational_range method after pointing the telescope.
- Update open_m1_cover to stop tracking if not repositioning the telescope.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.5.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``maintel/mtcs.py``, create a local copy of the check attribute in the _slew method.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.6.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``maintel/mtcs.py``, increase m1m3 settling time.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IN ``base_camera.py``, remove ROI spec splitting.
37 changes: 5 additions & 32 deletions python/lsst/ts/observatory/control/base_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import json
import logging
import typing
from math import ceil

import astropy
from lsst.ts import salobj
Expand Down Expand Up @@ -104,8 +103,6 @@ def __init__(

self.max_n_snaps_warning = 2

self.init_guider_roi_spec_max_size = 254

async def take_bias(
self,
nbias: int,
Expand Down Expand Up @@ -1151,35 +1148,11 @@ async def init_guider(self, roi_spec: ROISpec) -> None:
roi = roi_spec_dict.pop("roi")
roi_spec_dict.update(roi)
roi_spec_json = json.dumps(roi_spec_dict, separators=(",", ":"))
# the roiSpec attribute on the initGuiders command has a limit of
# 256 characters. Tony implemented a mechanism to allow us to
# split it by appending a - at the end of the string.
cmd_max_size = self.init_guider_roi_spec_max_size
n_commands = int(ceil(len(roi_spec_json) / cmd_max_size))
if n_commands > 1:
self.log.info(
f"Splitting ROISpec(len:{len(roi_spec_json)}) in {n_commands}."
)
roi_spec_split = [
roi_spec_json[i * cmd_max_size : (i + 1) * cmd_max_size]
for i in range(n_commands)
]
for rs in roi_spec_split[:-1]:
await self.camera.cmd_initGuiders.set_start(
roiSpec=f"{rs}-",
timeout=self.long_timeout,
)
await self.camera.cmd_initGuiders.set_start(
roiSpec=roi_spec_split[-1],
timeout=self.long_timeout,
)

else:
self.log.info(f"ROISpec(len:{len(roi_spec_json)}) fits in one command.")
await self.camera.cmd_initGuiders.set_start(
roiSpec=roi_spec_json,
timeout=self.long_timeout,
)
self.log.info(f"ROISpec(len:{len(roi_spec_json)}) fits in one command.")
await self.camera.cmd_initGuiders.set_start(
roiSpec=roi_spec_json,
timeout=self.long_timeout,
)

async def _handle_take_images(
self, camera_exposure: CameraExposure
Expand Down
5 changes: 4 additions & 1 deletion python/lsst/ts/observatory/control/maintel/comcam.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ def usages(self) -> typing.Dict[int, UsagesResources]:
)

usages[self.valid_use_cases.TakeImage] = UsagesResources(
components_attr=["cccamera"],
components_attr=["cccamera", "ccoods"],
readonly=False,
cccamera=[
"takeImages",
Expand All @@ -337,6 +337,9 @@ def usages(self) -> typing.Dict[int, UsagesResources]:
"endSetFilter",
"availableFilters",
],
ccoods=[
"imageInOODS",
],
)

usages[self.valid_use_cases.TakeImageFull] = UsagesResources(
Expand Down
179 changes: 138 additions & 41 deletions python/lsst/ts/observatory/control/maintel/mtcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def __init__(
# timeout to raise m1m3, in seconds.
self.m1m3_raise_timeout = 600.0
# time it takes for m1m3 to settle after a slew finishes.
self.m1m3_settle_time = 2.0
self.m1m3_settle_time = 5.0

# Tolerance on the stability of the balance force magnitude
self.m1m3_force_magnitude_stable_tolerance = 50.0
Expand Down Expand Up @@ -237,7 +237,7 @@ async def _slew_to(

assert self._dome_az_in_position is not None

_check = self.check if check is None else check
_check = copy.copy(self.check) if check is None else copy.copy(check)

ccw_following = await self.rem.mtmount.evt_cameraCableWrapFollowing.aget(
timeout=self.fast_timeout
Expand Down Expand Up @@ -285,6 +285,14 @@ async def _slew_to(
)

async with self.m1m3_booster_valve():
for comp in self.components_attr:
if getattr(_check, comp):
self.log.debug(f"Checking state of {comp}.")
getattr(self.rem, comp).evt_summaryState.flush()
self.scheduled_coro.append(
asyncio.create_task(self.check_component_state(comp))
)

await slew_cmd.start(timeout=slew_timeout)
self._dome_az_in_position.clear()
if offset_cmd is not None:
Expand All @@ -301,13 +309,6 @@ async def _slew_to(
)
self.scheduled_coro.append(asyncio.create_task(self.monitor_position()))

for comp in self.components_attr:
if getattr(_check, comp):
getattr(self.rem, comp).evt_summaryState.flush()
self.scheduled_coro.append(
asyncio.create_task(self.check_component_state(comp))
)

await self.process_as_completed(self.scheduled_coro)

async def wait_for_inposition(
Expand Down Expand Up @@ -738,8 +739,8 @@ def set_azel_slew_checks(self, wait_dome: bool) -> typing.Any:
Reformated check namespace.
"""
check = copy.copy(self.check)
check.mtdome = wait_dome
check.mtdometrajectory = wait_dome
check.mtdome = wait_dome and self.check.mtdome
check.mtdometrajectory = wait_dome and self.check.mtdometrajectory
return check

async def slew_dome_to(self, az: float, check: typing.Any = None) -> None:
Expand Down Expand Up @@ -795,8 +796,6 @@ async def close_m1_cover(self) -> None:
If mirror system state is FAULT.
"""

await self.stop_tracking()

self.rem.mtmount.evt_mirrorCoversMotionState.flush()
cover_state = await self.rem.mtmount.evt_mirrorCoversMotionState.aget(
timeout=self.fast_timeout
Expand All @@ -813,28 +812,70 @@ async def close_m1_cover(self) -> None:
# Mirror covers shall close at zenith pointing.
if not await self.in_m1_cover_operational_range():
await self.slew_to_m1_cover_operational_range()
else:
await self.stop_tracking()

await self.rem.mtmount.cmd_closeMirrorCovers.start(
timeout=self.long_long_timeout
)
try:
await self.rem.mtmount.cmd_closeMirrorCovers.start(
timeout=self.long_long_timeout
)
except salobj.AckError as ack:

self.log.error(
f"Closing mirror cover command failed with {ack.ack!r}::{ack.error}. "
"Checking state of the system."
)
cover_state = await self.rem.mtmount.evt_mirrorCoversMotionState.aget(
timeout=self.mirror_covers_timeout
)
cover_locks_state = (
await self.rem.mtmount.evt_mirrorCoverLocksMotionState.aget(
timeout=self.mirror_covers_timeout
)
)
if (
cover_state.state == MTMount.DeployableMotionState.DEPLOYED
and cover_locks_state.state
== MTMount.DeployableMotionState.RETRACTED
):
self.log.warning(
f"Close mirror cover command failed {ack.ack!r}::{ack.error} "
"but mirror cover in the correct state."
)
else:
cover_locks_element_state = [
MTMount.DeployableMotionState(state)
for state in cover_locks_state.elementsState
]
cover_element_state = [
MTMount.DeployableMotionState(state)
for state in cover_state.elementsState
]
raise RuntimeError(
f"Close mirror cover command failed with {ack.ack!r}::{ack.error}. "
f"Mirror cover state: {cover_element_state} expected all to be DEPLOYED. "
f"Mirror cover locks state: {cover_locks_element_state} expected all to be RETRACTED."
)
cover_state = await self.rem.mtmount.evt_mirrorCoversMotionState.aget(
timeout=self.mirror_covers_timeout
)
cover_locks_state = (
await self.rem.mtmount.evt_mirrorCoverLocksMotionState.aget(
timeout=self.mirror_covers_timeout
)
)
self.log.info(
f"Cover state: {MTMount.DeployableMotionState(cover_state.state)!r}"
f"Cover locks state: {MTMount.DeployableMotionState(cover_locks_state.state)!r}"
)
while cover_state.state != MTMount.DeployableMotionState.DEPLOYED:
cover_state = await self.rem.mtmount.evt_mirrorCoversMotionState.next(
flush=False, timeout=self.mirror_covers_timeout
)
self.log.debug(
f"Cover state: {MTMount.DeployableMotionState(cover_state.state)!r}"
)
cover_system_state = (
await self.rem.mtmount.evt_mirrorCoversSystemState.aget(
timeout=self.fast_timeout
)
)
if cover_system_state.state == MTMount.PowerState.FAULT:
raise RuntimeError(
"Close cover failed. Cover system state: "
f"{MTMount.PowerState(cover_system_state.state)!r}"
)

self.log.info(
f"Cover state: {MTMount.DeployableMotionState(cover_state.state)!r}"
)
Expand Down Expand Up @@ -919,31 +960,62 @@ async def open_m1_cover(self) -> None:
# Mirror covers shall open at zenith pointing.
if not await self.in_m1_cover_operational_range():
await self.slew_to_m1_cover_operational_range()
else:
await self.stop_tracking()

await self.rem.mtmount.cmd_openMirrorCovers.set_start(
leaf=MTMount.MirrorCover.ALL, timeout=self.long_long_timeout
)

while cover_state.state != MTMount.DeployableMotionState.RETRACTED:
cover_state = await self.rem.mtmount.evt_mirrorCoversMotionState.next(
flush=False, timeout=self.mirror_covers_timeout
try:
await self.rem.mtmount.cmd_openMirrorCovers.set_start(
leaf=MTMount.MirrorCover.ALL, timeout=self.long_long_timeout
)
self.log.debug(
f"Cover state: {MTMount.DeployableMotionState(cover_state.state)!r}"
except salobj.AckError as ack:
self.log.error(
f"Open mirror cover command failed with {ack.ack!r}::{ack.error}. "
"Checking state of the system."
)
cover_system_state = (
await self.rem.mtmount.evt_mirrorCoversSystemState.aget(
timeout=self.fast_timeout
cover_state = await self.rem.mtmount.evt_mirrorCoversMotionState.aget(
timeout=self.mirror_covers_timeout
)
cover_locks_state = (
await self.rem.mtmount.evt_mirrorCoverLocksMotionState.aget(
timeout=self.mirror_covers_timeout
)
)
if cover_system_state.state == MTMount.PowerState.FAULT:
if (
cover_state.state == MTMount.DeployableMotionState.RETRACTED
and cover_locks_state.state
== MTMount.DeployableMotionState.DEPLOYED
):
self.log.warning(
f"Open mirror cover command failed {ack.ack!r}::{ack.error} "
"but mirror cover in the correct state."
)
else:
cover_locks_element_state = [
MTMount.DeployableMotionState(state)
for state in cover_locks_state.elementsState
]
cover_element_state = [
MTMount.DeployableMotionState(state)
for state in cover_state.elementsState
]
raise RuntimeError(
"Open cover failed. Cover system state: "
f"{MTMount.PowerState(cover_system_state.state)!r}"
f"Open mirror cover command failed with {ack.ack!r}::{ack.error}. "
f"Mirror cover state: {cover_element_state} "
f"Mirror cover locks state: {cover_locks_element_state} "
)
cover_state = await self.rem.mtmount.evt_mirrorCoversMotionState.aget(
timeout=self.mirror_covers_timeout
)
cover_locks_state = (
await self.rem.mtmount.evt_mirrorCoverLocksMotionState.aget(
timeout=self.mirror_covers_timeout
)
)
self.log.info(
f"Cover state: {MTMount.DeployableMotionState(cover_state.state)!r}"
f"Cover locks state: {MTMount.DeployableMotionState(cover_locks_state.state)!r}"
)

else:
raise RuntimeError(
f"Mirror covers in {MTMount.DeployableMotionState(cover_state.state)!r} "
Expand Down Expand Up @@ -974,6 +1046,7 @@ async def slew_to_m1_cover_operational_range(self) -> None:
rot_tel=rotation_data.actualPosition,
wait_dome=False,
)
await self.stop_tracking()

async def in_m1_cover_operational_range(self) -> bool:
"""Check if MTMount is in safe range for mirror covers operation.
Expand Down Expand Up @@ -2514,6 +2587,19 @@ async def _ready_to_take_data(self) -> None:
"""Placeholder, still needs to be implemented."""
# TODO: Finish implementation.

try:
await asyncio.gather(
self.wait_for_mtmount_inposition(self.long_timeout, False),
self._handle_in_position(
in_position_event=self.rem.mthexapod_1.evt_inPosition,
timeout=self.long_timeout,
settle_time=0.0,
component_name="Camera Hexapod",
),
)
except asyncio.TimeoutError:
self.log.warning("Mount, Camera Hexapod or Rotator not in position.")

async def open_m1m3_booster_valve(self) -> None:
"""Open M1M3 booster valves."""
if self.check.mtm1m3:
Expand Down Expand Up @@ -2777,8 +2863,19 @@ def usages(self) -> typing.Dict[int, UsagesResources]:
"elevationInPosition",
"azimuthInPosition",
"cameraCableWrapFollowing",
"mirrorCoversMotionState",
"mirrorCoversSystemState",
"mirrorCoverLocksMotionState",
],
mtm1m3=[
"boosterValveStatus",
"forceActuatorState",
"detailedState",
"forceControllerState",
],
mtdome=["azimuth", "lightWindScreen"],
mthexapod_1=["compensationMode"],
mthexapod_2=["compensationMode"],
)

usages[self.valid_use_cases.Slew] = UsagesResources(
Expand Down
Loading

0 comments on commit 5050117

Please sign in to comment.