async def reconnect(command: Command, controller: ArchonController, timeout: float): """Restarts the socket connection to the controller(s).""" name = controller.name connect_timeout = timeout or command.actor.config["timeouts"][ "controller_connect"] if controller.is_connected(): try: await asyncio.wait_for(controller.stop(), timeout=timeout or 1) except BaseException as err: command.warning(text=f"Failed disconnecting from {name!r} with " f"error {err}. Will try to reconnect.") try: await asyncio.wait_for(controller.start(), timeout=connect_timeout) except asyncio.TimeoutError: command.error( error=f"Timed-out while reconnecting to controller {name!r}.") return False except BaseException as err: command.error( error= f"Unexpected error while connecting to controller {name!r}: {err}") return False return True
def error_controller(command: Command, controller: ArchonController, message: str): """Issues a ``error_controller`` message.""" command.error(text={ "controller": controller.name, "text": message, }) return False
async def _do_exposures( command: Command, controllers: list[ArchonController], exposure_params: dict[str, Any], ) -> bool: """Manages exposing several controllers.""" config = command.actor.config now = astropy.time.Time.now() mjd = int(now.mjd) # Get data directory or create it if it doesn't exist. data_dir = pathlib.Path(config["files"]["data_dir"]) if not data_dir.exists(): data_dir.mkdir(parents=True) # We store the next exposure number in a file at the root of the data directory. next_exp_file = data_dir / "nextExposureNumber" if not next_exp_file.exists(): next_exp_file.touch() # Get the directory for this MJD or create it. mjd_dir = data_dir / str(mjd) if not mjd_dir.exists(): mjd_dir.mkdir(parents=True) try: with open_with_lock(next_exp_file, "r+") as fd: fd.seek(0) data = fd.read().strip() next_exp_no: int = int(data) if data != "" else 1 _jobs: list[asyncio.Task] = [] for controller in controllers: _jobs.append( asyncio.create_task( _do_one_controller( command, controller, exposure_params, next_exp_no, mjd_dir, ))) try: results = await asyncio.gather(*_jobs, return_exceptions=False) except BaseException as err: command.error(error=str(err)) command.error( "One controller failed. Cancelling remaining tasks.") for job in _jobs: if not job.done(): with suppress(asyncio.CancelledError): job.cancel() await job return False if not all(results): return False # Increment nextExposureNumber # TODO: Should we increase the sequence regardless of whether the exposure # fails or not? fd.seek(0) fd.truncate() fd.seek(0) fd.write(str(next_exp_no + 1)) except BlockingIOError: command.error( error="The nextExposureNumber file is locked. This probably " "indicates that another spectrograph process is running.") return False return True