Пример #1
0
    async def basic_make_script(self, index):
        self.script = Stop(index=index)

        # A dict of name: number of calls
        # where name is {controller_name_index}.{command_name}
        self.num_calls = dict()

        # A dict of name_index: numer of calls
        # where name_index means the SAL component name and index
        # in the form name[:index] and [:index] is only wanted for
        # indexed SAL components.
        self.controllers = dict()

        for name_index in ("ATDome", "ATDomeTrajectory", "ATPtg", "ATMCS"):
            name, index = salobj.name_to_name_index(name_index)
            controller = salobj.Controller(name=name, index=index)
            self.controllers[name_index] = controller
            await controller.evt_summaryState.set_write(
                summaryState=salobj.State.ENABLED)
            for command_name in controller.salinfo.command_names:
                name = f"{name_index}.{command_name}"
                self.num_calls[name] = 0
                command = getattr(controller, f"cmd_{command_name}")
                command.callback = functools.partial(self.callback, name)

        return (self.script, ) + tuple(self.controllers.values())
Пример #2
0
    def test_name_to_name_index(self) -> None:
        for name, expected_result in (
            ("Script", ("Script", 0)),
            ("Script:0", ("Script", 0)),
            ("Script:15", ("Script", 15)),
            ("MTM1M3", ("MTM1M3", 0)),
            ("MTM1M3:47", ("MTM1M3", 47)),
        ):
            with self.subTest(name=name):
                result = salobj.name_to_name_index(name)
                assert result == expected_result

        for bad_name in (
            (" Script:15"),  # leading space
            ("Script:15 "),  # trailing space
            ("Script:"),  # colon with no index
            ("Script:zero"),  # index is not an integer
        ):
            with self.subTest(bad_name=bad_name):
                with pytest.raises(ValueError):
                    salobj.name_to_name_index(bad_name)
Пример #3
0
 def __init__(self, config):
     remote_name, remote_index = salobj.name_to_name_index(config.name)
     remote_info = base.RemoteInfo(
         name=remote_name,
         index=remote_index,
         callback_names=["evt_summaryState"],
         poll_names=[],
     )
     super().__init__(
         config=config,
         name=f"Enabled.{remote_info.name}:{remote_info.index}",
         remote_info_list=[remote_info],
     )
Пример #4
0
 def __init__(self, config):
     remote_name, remote_index = salobj.name_to_name_index(config.name)
     remote_info = base.RemoteInfo(
         name=remote_name,
         index=remote_index,
         callback_names=["evt_heartbeat"],
         poll_names=[],
     )
     super().__init__(
         config=config,
         name=f"Heartbeat.{remote_info.name}:{remote_info.index}",
         remote_info_list=[remote_info],
     )
     self.heartbeat_timer_task = salobj.make_done_future()
Пример #5
0
    async def configure(self, config):
        """Configure the script.

        Specify the CSCs to command, the command to run, the parameters for
        the command. Optionally, specify an event to wait and if the event
        should be flushed before sending the command.

        Parameters
        ----------
        config : `types.SimpleNamespace`

        Raises
        ------
        RuntimeError:
            If `config.command` is not a valid command from the CSC.

        """
        self.log.info("Configure started")

        self.config = config

        self.name, self.index = salobj.name_to_name_index(config.component)
        self.event = config.event if hasattr(config, "event") else None

        self.remote = salobj.Remote(
            domain=self.domain,
            name=self.name,
            index=self.index,
            include=[self.event] if self.event is not None else [],
        )

        if config.cmd in self.remote.salinfo.command_names:
            self.cmd = config.cmd
        else:
            raise RuntimeError(
                f"Command {config.cmd} not a valid command for {self.name}.")

        getattr(self.remote,
                f"cmd_{self.cmd}").set(**dict([(k, config.parameters[k])
                                               for k in config.parameters
                                               if k != "timeout"]))

        self.flush = config.flush if self.event is not None else False

        if self.event is not None and self.event not in self.remote.salinfo.event_names:
            raise RuntimeError(
                f"Event {self.event} not a valid event for {self.name}.")
Пример #6
0
 def __init__(self, config):
     remote_name, remote_index = salobj.name_to_name_index(config.name)
     remote_info = base.RemoteInfo(
         name=remote_name,
         index=remote_index,
         callback_names=["evt_heartbeat"],
         poll_names=[],
     )
     super().__init__(
         config=config,
         name=f"Clock.{remote_info.name}:{remote_info.index}",
         remote_info_list=[remote_info],
     )
     self.threshold = config.threshold
     # An array of up to `min_errors` recent measurements of clock error
     # (seconds), oldest first.
     self.clock_errors = np.zeros(self.min_errors, dtype=float)
     # The number of values in `clock_errors`; maxes out at `min_errors`
     self.n_clock_errors = 0
Пример #7
0
    async def do_requestAuthorization(self, data):
        """Implement the requestAuthorization command.

        Parameters
        ----------
        data : ``cmd_requestAuthorization.DataType``
            Command data.
        """

        self.assert_enabled()

        await self.cmd_requestAuthorization.ack_in_progress(
            data, timeout=self.config.timeout_request_authorization)

        cscs_to_command = await self.validate_request(data=data)

        cscs_failed_to_set_auth_list = set()
        for csc_name_index in cscs_to_command:
            csc_name, csc_index = salobj.name_to_name_index(csc_name_index)
            try:
                async with salobj.Remote(
                        domain=self.salinfo.domain,
                        name=csc_name,
                        index=csc_index,
                        include=[],
                ) as remote:
                    await remote.cmd_setAuthList.set_start(
                        authorizedUsers=data.authorizedUsers,
                        nonAuthorizedCSCs=data.nonAuthorizedCSCs,
                        timeout=TIMEOUT_SET_AUTH_LIST,
                    )
                self.log.info(f"Set authList for {csc_name_index}")
            except salobj.AckError as e:
                cscs_failed_to_set_auth_list.update({csc_name_index})
                self.log.warning(
                    f"Failed to set authList for {csc_name_index}: {e.args[0]}"
                )

        if len(cscs_failed_to_set_auth_list) > 0:
            raise RuntimeError(
                f"Failed to set authList for the following CSCs: {cscs_failed_to_set_auth_list}. "
                "The following CSCs were successfully updated: "
                f"{cscs_to_command-cscs_failed_to_set_auth_list}")
Пример #8
0
    async def make_model(self, names, enable, escalation=()):
        """Make a Model as self.model, with one or more Enabled rules.

        Parameters
        ----------
        names : `list` [`str`]
            Name and index of one or more CSCs.
            Each entry is of the form "name" or name:index".
            The associated alarm names have a prefix of "Enabled.".
        enable : `bool`
            Enable the model?
        escalation : `list` of `dict`, optional
            Escalation information.
            See `CONFIG_SCHEMA` for the format of entries.
        """
        if not names:
            raise ValueError("Must specify one or more CSCs")
        self.name_index_list = [
            salobj.name_to_name_index(name) for name in names
        ]

        configs = [dict(name=name_index) for name_index in names]
        watcher_config_dict = dict(
            disabled_sal_components=[],
            auto_acknowledge_delay=3600,
            auto_unacknowledge_delay=3600,
            rules=[dict(classname="Enabled", configs=configs)],
            escalation=escalation,
        )
        watcher_config = types.SimpleNamespace(**watcher_config_dict)

        self.read_severities = dict()
        self.read_max_severities = dict()

        self.controllers = []
        for name_index in names:
            name, index = salobj.name_to_name_index(name_index)
            self.controllers.append(salobj.Controller(name=name, index=index))
        self.model = watcher.Model(
            domain=self.controllers[0].domain,
            config=watcher_config,
            alarm_callback=self.alarm_callback,
        )

        for name in self.model.rules:
            self.read_severities[name] = []
            self.read_max_severities[name] = []

        controller_start_tasks = [
            controller.start_task for controller in self.controllers
        ]
        await asyncio.gather(self.model.start_task, *controller_start_tasks)
        if enable:
            self.model.enable()
            await self.model.enable_task

        for rule in self.model.rules.values():
            self.assertTrue(rule.alarm.nominal)
            self.assertFalse(rule.alarm.acknowledged)
            self.assertFalse(rule.alarm.muted)
            self.assertNotMuted(rule.alarm)

        try:
            yield
        finally:
            await self.model.close()
            controller_close_tasks = [
                asyncio.create_task(controller.close())
                for controller in self.controllers
            ]
            await asyncio.gather(*controller_close_tasks)
Пример #9
0
    def __init__(
        self,
        components: typing.List[str],
        domain: typing.Optional[salobj.Domain] = None,
        log: typing.Optional[logging.Logger] = None,
        intended_usage: typing.Optional[int] = None,
        concurrent_operation: bool = True,
    ) -> None:

        if log is None:
            self.log = logging.getLogger(type(self).__name__)
        else:
            self.log = log.getChild(type(self).__name__)

        self.fast_timeout = 5.0
        self.long_timeout = 30.0
        self.long_long_timeout = 120.0

        self._concurrent_operation = concurrent_operation

        self._components = dict([(component,
                                  component.lower().replace(":", "_"))
                                 for component in components])

        self.domain, self._close_domain = ((domain,
                                            False) if domain is not None else
                                           (salobj.Domain(), True))

        self._usages: typing.Union[None, typing.Dict[int,
                                                     UsagesResources]] = None

        self.rem = types.SimpleNamespace()

        for component in self._components:
            name, index = salobj.name_to_name_index(component)
            rname = self._components[component]
            resources = self.get_required_resources(rname, intended_usage)

            if resources.add_this:
                setattr(
                    self.rem,
                    rname,
                    salobj.Remote(
                        domain=self.domain,
                        name=name,
                        index=index,
                        readonly=resources.readonly,
                        include=resources.include,
                    ),
                )
            else:
                setattr(self.rem, rname, None)

        self.scheduled_coro: typing.List[asyncio.Task] = []

        # Dict of component attribute name: remote, if present, else None
        attr_remotes = {
            attr: getattr(self.rem, attr)
            for attr in self.components_attr
        }

        # Mark components that were excluded from the resources to not be
        # checked.
        self.check = types.SimpleNamespace(
            **{c: remote is not None
               for c, remote in attr_remotes.items()})

        start_task_list = [
            remote.start_task for remote in attr_remotes.values()
            if remote is not None
        ]

        self.start_task = (asyncio.gather(
            *start_task_list) if len(start_task_list) > 0 else None)
Пример #10
0
    def __init__(self, domain, config, alarm_callback=None):
        self.domain = domain
        self.alarm_callback = alarm_callback

        self._enabled = False
        self.enable_task = salobj.make_done_future()

        # Dict of (sal_component_name, sal_index): lsst.ts.salobj.Remote
        self.remotes = dict()

        # Dict of rule_name: Rule
        self.rules = dict()

        # Convert the name of each disabled sal component from a string
        # in the form ``name`` or ``name:index`` to a tuple ``(name, index)``.
        config.disabled_sal_components = [
            salobj.name_to_name_index(name)
            for name in config.disabled_sal_components
        ]
        self.config = config

        # Make the rules.
        for ruledata in self.config.rules:
            ruleclassname = ruledata["classname"]
            ruleclass = get_rule_class(ruleclassname)
            try:
                ruleschema = ruleclass.get_schema()
                if ruleschema is None:
                    validator = None
                else:
                    validator = salobj.DefaultingValidator(ruleschema)
            except Exception as e:
                raise ValueError(
                    f"Schema for rule class {ruleclassname} not valid") from e
            for i, ruleconfig_dict in enumerate(ruledata["configs"]):
                try:
                    if validator is None:
                        if ruleconfig_dict:
                            raise ValueError("Rule config dict must be empty")
                        full_ruleconfig_dict = dict()
                    else:
                        full_ruleconfig_dict = validator.validate(
                            ruleconfig_dict)
                    ruleconfig = types.SimpleNamespace(**full_ruleconfig_dict)
                except Exception as e:
                    raise ValueError(
                        f"Config {i+1} for rule class {ruleclassname} not valid: "
                        f"config={ruleconfig_dict}") from e
                rule = ruleclass(config=ruleconfig)
                if rule.is_usable(disabled_sal_components=config.
                                  disabled_sal_components):
                    self.add_rule(rule)

        # Accumulate a list of topics that have callback functions.
        self._topics_with_callbacks = list()
        for remote in self.remotes.values():
            for name in dir(remote):
                if name[0:4] in ("evt_", "tel_"):
                    topic = getattr(remote, name)
                    if topic.callback is not None:
                        self._topics_with_callbacks.append(topic)

        # Set escalation information in the alarms.
        remaining_names = set(self.rules)
        for escalation_item in config.escalation:
            for name_glob in escalation_item["alarms"]:
                name_regex = fnmatch.translate(name_glob)
                compiled_name_regex = re.compile(name_regex, re.IGNORECASE)
                matched_names = [
                    name for name in remaining_names
                    if compiled_name_regex.match(name)
                ]
                remaining_names = remaining_names.difference(matched_names)
                for name in matched_names:
                    alarm = self.rules[name].alarm
                    alarm.escalate_to = escalation_item["to"]
                    alarm.escalate_delay = escalation_item["delay"]

        self.start_task = asyncio.ensure_future(self.start())