async def test_ignore_feature(self): async with self.make_script(): ignore = ["mtdome", "mtdometrajectory"] await self.configure_script(ignore=ignore) await self.run_script() for comp in self.script.group.components_attr: if getattr(self.script.group.check, comp): current_state = salobj.State( getattr(self.mtcs_mock.controllers, comp).evt_summaryState.data.summaryState) with self.subTest(f"{comp} summary state", comp=comp): assert current_state == salobj.State.ENABLED for comp in ignore: current_state = salobj.State( getattr(self.mtcs_mock.controllers, comp).evt_summaryState.data.summaryState) with self.subTest(f"{comp} summary state", comp=comp): assert current_state == salobj.State.STANDBY
async def test_no_queue(self): """Test the simple target production loop. This test makes sure the scheduler will go to a fault state if it is enabled and the queue is not enabled. """ data = await self.scheduler_remote.evt_errorCode.next( flush=False, timeout=STD_TIMEOUT) assert data.errorCode == 0 # Test 1 - Enable scheduler, Queue is not enable. Scheduler should go # to ENABLE and then to FAULT It may take some time for the scheduler # to go to FAULT state. # Make sure Queue is in STANDBY await salobj.set_summary_state(self.queue_remote, salobj.State.STANDBY) # Enable Scheduler await salobj.set_summary_state( self.scheduler_remote, salobj.State.ENABLED, override="simple_target_loop_sequential.yaml", ) # Resume scheduler operation await self.scheduler_remote.cmd_resume.start(timeout=STD_TIMEOUT) # Wait until summary state is FAULT or Timeout state = None while True: try: evt_state = await self.scheduler_remote.evt_summaryState.next( flush=False, timeout=STD_TIMEOUT) state = salobj.State(evt_state.summaryState) if state == salobj.State.FAULT: break except asyncio.TimeoutError: break self.assertEqual(state, salobj.State.FAULT, f"Scheduler in {state}, expected FAULT. ") self.assertEqual( self.scheduler.summary_state, salobj.State.FAULT, f"Scheduler in {state}, expected FAULT. ", ) # Check error code data = await self.scheduler_remote.evt_errorCode.next( flush=False, timeout=STD_TIMEOUT) assert data.errorCode == NO_QUEUE # recover from fault state sending it to STANDBY await self.scheduler_remote.cmd_standby.start(timeout=STD_TIMEOUT) self.assertEqual( self.scheduler.summary_state, salobj.State.STANDBY, "Scheduler in %s, expected STANDBY. " % salobj.State(self.scheduler.summary_state), )
async def get_state(self, component: str, ignore_timeout: bool = False) -> salobj.State: """Get summary state for component. Parameters ---------- component : `str` Name of the component. ignore_timeout : `bool` If `True` will return None in case it times out getting the state. Default is `False`, which means raise `TimeoutError`. Returns ------- state : `salobj.State` or `None` Current state of component. Raises ------ `asyncio.TimeoutError` If can not get state in `self.fast_timeout` seconds. """ try: ss = await getattr( self.rem, component).evt_summaryState.aget(timeout=self.fast_timeout) return salobj.State(ss.summaryState) except asyncio.TimeoutError as e: if ignore_timeout: return None else: raise e
def assert_summary_state(self, state, isbefore=None): """Assert that the current summary state is as specified. First check that CSC can command the low-level controller. Used in do_xxx methods to check that a command is allowed. Parameters ---------- state : `lsst.ts.salobj.State` Expected summary state. isbefore : `bool`, optional Deprecated. The only allowed values are False (which raises a deprecation warning) and None. """ if isbefore: raise ValueError( f"isbefore={isbefore}; this deprecated argument must be None or False" ) elif isbefore is False: warnings.warn(f"isbefore={isbefore} is deprecated", DeprecationWarning) state = salobj.State(state) self.assert_connected() if self.summary_state != state: raise salobj.ExpectedError( f"Rejected: initial state is {self.summary_state!r} instead of {state!r}" )
def assert_enable(data): """Callback function to make sure scheduler is enabled""" self.assertEqual( data.summaryState, salobj.State.ENABLED, "Scheduler unexpectedly transitioned from " "ENABLE to %s" % salobj.State(data.summaryState), )
async def check_component_state( self, component: str, desired_state: salobj.State = salobj.State.ENABLED) -> None: """Monitor the summary state of a component and raises an exception if it is or goes to a state different than the desired state. This method will run forever as long as the summary state remains unchanged. The intention is that this can run alongside an operation that require the component to be in a certain state, when the operation is completed, the task can be canceled. Parameters ---------- component : `str` Name of the component to follow. Must be one of: atmcs, atptg, ataos, atpneumatics, athexapod, atdome, atdometrajectory desired_state : `salobj.State` Desired state of the CSC. Raises ------ RuntimeError If state is not `desired_state`. KeyError If component is not found. """ desired_state = salobj.State(desired_state) state_topic = getattr(self.rem, component).evt_summaryState state_topic.flush() data = await state_topic.aget() while True: state = salobj.State(data.summaryState) if state != desired_state: self.log.warning( f"{component} not in {desired_state!r}: {state!r}") raise RuntimeError( f"{component} state is {state!r}, expected {desired_state!r}" ) else: self.log.debug(f"{component}: {state!r}") data = await state_topic.next(flush=False)
def __call__(self, topic_callback): state = topic_callback.get().summaryState if state == salobj.State.ENABLED: return base.NoneNoReason elif state == salobj.State.FAULT: return AlarmSeverity.SERIOUS, "FAULT state" else: try: state_name = salobj.State(state).name except Exception: state_name = str(state) return AlarmSeverity.WARNING, f"{state_name} state"
async def next_state(self, component: str) -> salobj.State: """Get summary state for component. Parameters ---------- component : `str` Name of the component. Returns ------- state : `salobj.State` Current state of component. """ ss = await getattr(self.rem, component).evt_summaryState.next( flush=False, timeout=self.fast_timeout) return salobj.State(ss.summaryState)
async def test_run(self): async with self.make_script(): await self.configure_script() await self.run_script() for comp in self.script.group.components_attr: if getattr(self.script.group.check, comp): current_state = salobj.State( getattr(self.mtcs_mock.controllers, comp).evt_summaryState.data.summaryState) with self.subTest(f"{comp} summary state", comp=comp): assert current_state == salobj.State.STANDBY
async def wait_summary_state(self, state, max_telem=MAX_STATE_CHANGE_TELEMETRY_MESSAGES ): """Wait for the summary state to be as specified. Parameters ---------- state : `lsst.ts.salobj.State` Allowed summary states. max_telem : `int` Maximum number of low-level telemetry messages to wait for. """ state = salobj.State(state) for i in range(max_telem): self.assert_connected() await self.client.next_telemetry() if self.summary_state == state: return raise salobj.ExpectedError( f"Failed: final state is {self.summary_state!r} instead of {state!r}" )
async def set_state( self, state: salobj.State, overrides: typing.Optional[typing.Dict[str, str]] = None, components: typing.Optional[typing.List[str]] = None, ) -> None: """Set summary state for all components. Parameters ---------- state : `salobj.State` Desired state. overrides : `dict` or None Settings to apply for each component. components : `list[`str`]` List of components to set state, as they appear in `self.components_attr`. Raises ------ RuntimeError * If a component in `components` is not part of the group. * If it fails to transition one or more components. """ work_components = self.get_work_components(components) if overrides is None: overrides = dict() set_ss_tasks = [] for comp in work_components: if getattr(self.check, comp): set_ss_tasks.append( salobj.set_summary_state( remote=getattr(self.rem, comp), state=salobj.State(state), override=overrides.get(comp, ""), timeout=self.long_long_timeout, )) else: set_ss_tasks.append(self.get_state(comp, ignore_timeout=True)) ret_val = (await asyncio.gather(*set_ss_tasks, return_exceptions=True) if self._concurrent_operation else [(await asyncio.gather(task, return_exceptions=True))[0] for task in set_ss_tasks]) error_flag = False failed_components = [] for i, comp in enumerate(work_components): if isinstance(ret_val[i], Exception): error_flag = True failed_components.append(comp) err_message = ( f"Unable to transition {comp} to " f"{salobj.State(state)!r} {traceback.format_exc()}.\n") etype: typing.Type[BaseException] = type(ret_val[i]) value: BaseException = ret_val[i] tb: types.TracebackType = ret_val[i].__traceback__ err_traceback = traceback.format_exception( etype, value, tb, ) for trace in err_traceback: err_message += trace self.log.error(err_message) else: self.log.debug(f"[{comp}]::{ret_val[i]!r}") if error_flag: raise RuntimeError(f"Failed to transition {failed_components} to " f"{salobj.State(state)!r}.") else: self.log.info(f"All components in {salobj.State(state)!r}.")
def get_summary_state(self) -> typing.Optional[salobj.State]: """Get Scheduler summary state.""" current_summary_state = self.scheduler_remote.evt_summaryState.get() return (None if current_summary_state is None else salobj.State( current_summary_state.summaryState))