Skip to content

Commit

Permalink
Faster guider stop (#7)
Browse files Browse the repository at this point in the history
* Add tracking of camera status

* Wait until cameras are idle

* Add stop --soft

* Do not rotate logs in watch_for_files

* Fix get_dark_subtracted_data when DARKFILE is None
  • Loading branch information
albireox authored Sep 12, 2023
1 parent d778f4d commit 5a7b0a3
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 22 deletions.
3 changes: 2 additions & 1 deletion src/lvmguider/actor/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
if TYPE_CHECKING:
from lvmguider.guider import Guider


__all__ = ["LVMGuiderActor"]


Expand Down Expand Up @@ -61,7 +62,7 @@ def __init__(self, *args, **kwargs):
self.guider: Guider | None = None
self.guide_task: asyncio.Task | None = None

# Track model of focuser associated to this telescope.
# Track model of the cameras and focuser associated to this telescope.
self.models.actors.append(f"lvm.{self.telescope}.foc")

@property
Expand Down
3 changes: 3 additions & 0 deletions src/lvmguider/actor/commands/expose.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from lvmguider.actor import lvmguider_parser
from lvmguider.maskbits import GuiderStatus
from lvmguider.tools import wait_until_cameras_are_idle


if TYPE_CHECKING:
Expand Down Expand Up @@ -51,6 +52,8 @@ async def expose(
):
"""Exposes the cameras without guiding."""

await wait_until_cameras_are_idle(command)

# Force the cameras to check the last image.
command.actor.cameras.reset_seqno()

Expand Down
3 changes: 3 additions & 0 deletions src/lvmguider/actor/commands/focus.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from lvmguider.actor import lvmguider_parser
from lvmguider.focus import Focuser
from lvmguider.tools import wait_until_cameras_are_idle


if TYPE_CHECKING:
Expand Down Expand Up @@ -68,6 +69,8 @@ async def focus(
):
"""Performs a focus sweep."""

await wait_until_cameras_are_idle(command)

# Force the cameras to check the last image.
command.actor.cameras.reset_seqno()

Expand Down
3 changes: 3 additions & 0 deletions src/lvmguider/actor/commands/guide.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from lvmguider.actor import lvmguider_parser
from lvmguider.guider import CriticalGuiderError, Guider
from lvmguider.maskbits import GuiderStatus
from lvmguider.tools import wait_until_cameras_are_idle


if TYPE_CHECKING:
Expand Down Expand Up @@ -86,6 +87,8 @@ async def guide(
if actor.status & GuiderStatus.NON_IDLE:
return command.finish("Guider is not idle. Stop the guide loop.")

await wait_until_cameras_are_idle(command)

# Force the cameras to check the last image.
command.actor.cameras.reset_seqno()

Expand Down
35 changes: 19 additions & 16 deletions src/lvmguider/actor/commands/stop.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,28 @@


@lvmguider_parser.command()
@click.option("--now", is_flag=True, help="Aggressively stops the loop.")
async def stop(command: GuiderCommand, now=False):
@click.option("--soft", is_flag=True, help="Stops the guider less aggressively.")
async def stop(command: GuiderCommand, soft: bool = False):
"""Stops the guide loop."""

status = command.actor.status
if status & GuiderStatus.IDLE:
if command.actor.guide_task is None or command.actor.guide_task.done():
command.actor.status = GuiderStatus.IDLE
return command.finish("Guider is not active.")

if now:
if command.actor.guide_task and not command.actor.guide_task.done():
command.actor.guide_task.cancel()
with suppress(asyncio.CancelledError):
await command.actor.guide_task
command.actor.guider = None
command.actor.status = GuiderStatus.IDLE
return command.finish("Guider was forcibly stopped.")
if soft:
if command.actor.status & GuiderStatus.STOPPING:
return command.fail("Guider loop is already stopping.")

command.actor.status |= GuiderStatus.STOPPING
return command.finish("Stopping the guide loop.")

command.actor.guide_task.cancel()
with suppress(asyncio.CancelledError):
await command.actor.guide_task

command.actor.guider = None
command.actor.guide_task = None

if command.actor.status & GuiderStatus.STOPPING:
return command.fail("Guider loop is already stopping.")
command.actor.status = GuiderStatus.IDLE

command.actor.status |= GuiderStatus.STOPPING
return command.finish("Stopping the guide loop.")
return command.finish("Guider has been stopped.")
40 changes: 35 additions & 5 deletions src/lvmguider/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,16 +529,16 @@ def get_dark_subtracted_data(
# Data in counts per second.
data: ARRAY_2D_F32 = raw.data.copy().astype("f4") / exptime

dark_file = ""
dark_path = None

if "PROC" in hdul:
# Get data and subtract dark or fit background.
dirname = hdul["PROC"].header.get("DIRNAME", path.parent)
dark_file = hdul["PROC"].header.get("DARKFILE", "")

dark_path = pathlib.Path(dirname) / dark_file

else:
dark_file = ""
dark_path = None
if dark_file and dark_file != "":
dark_path = pathlib.Path(dirname) / dark_file

if dark_file != "" and dark_path is not None and dark_path.exists():
dark_raw = get_raw_extension(str(dark_path))
Expand Down Expand Up @@ -844,6 +844,36 @@ def get_raw_extension(file: AnyPath | fits.HDUList):
return hdul[0]


async def wait_until_cameras_are_idle(
command: GuiderCommand, timeout: float | None = 30.0, interval: float = 2.0
):
"""Blocks until all the AG cameras for the telescope are idle."""

agcam: str = f"lvm.{command.actor.telescope}.agcam"
elapsed: float = 0.0

while True:
status_cmd = await command.send_command(agcam, "status", internal=True)
if status_cmd.status.did_fail:
raise ValueError(f"Command '{agcam} status' failed.")

states: list[str] = []
for reply in status_cmd.replies:
if "status" in reply.message:
states.append(reply.message["status"]["camera_state"])

if all([state == "idle" for state in states]):
return True

if elapsed == 0:
command.warning("Waiting until cameras are idle.")
elif timeout is not None and elapsed >= timeout:
raise TimeoutError("Timed out waiting for cameras to become idle.")

await asyncio.sleep(interval)
elapsed += interval


def isot_to_sjd(isot: str):
"""Converts ISOT format to SJD."""

Expand Down

0 comments on commit 5a7b0a3

Please sign in to comment.