Example #1
0
 def createWriter(self, topic):
     global mutex
     guard = OpenRTM_aist.ScopedLock(mutex)
     if self._qosProfile:
         return self._publisher.create_datawriter(
             topic, self._qosProfile.get_writer_qos())
     else:
         writer_qos = dds.Qos([
             dds.DurabilityQosPolicy(dds.DDSDurabilityKind.TRANSIENT),
             dds.DeadlineQosPolicy(dds.DDSDuration(500)),
             dds.LatencyBudgetQosPolicy(dds.DDSDuration(3000)),
             dds.LivelinessQosPolicy(
                 dds.DDSLivelinessKind.MANUAL_BY_PARTICIPANT),
             dds.ReliabilityQosPolicy(dds.DDSReliabilityKind.RELIABLE,
                                      dds.DDSDuration.infinity()),
             dds.DestinationOrderQosPolicy(
                 dds.DDSDestinationOrderKind.BY_SOURCE_TIMESTAMP),
             dds.HistoryQosPolicy(dds.DDSHistoryKind.KEEP_ALL),
             dds.ResourceLimitsQosPolicy(10, 10, 10),
             dds.TransportPriorityQosPolicy(700),
             dds.LifespanQosPolicy(dds.DDSDuration(10, 500)),
             dds.OwnershipQosPolicy(dds.DDSOwnershipKind.EXCLUSIVE),
             dds.OwnershipStrengthQosPolicy(100),
             dds.WriterDataLifecycleQosPolicy(False)
         ])
         return self._publisher.create_datawriter(topic, writer_qos)
Example #2
0
 def createReader(self, topic, listener):
     global mutex
     guard = OpenRTM_aist.ScopedLock(mutex)
     if self._qosProfile:
         return self._subscriber.create_datareader(
             topic, self._qosProfile.get_reader_qos(), listener)
     else:
         reader_qos = dds.Qos([
             dds.DurabilityQosPolicy(dds.DDSDurabilityKind.TRANSIENT),
             dds.DeadlineQosPolicy(dds.DDSDuration(500)),
             dds.LatencyBudgetQosPolicy(dds.DDSDuration(3000)),
             dds.LivelinessQosPolicy(
                 dds.DDSLivelinessKind.MANUAL_BY_PARTICIPANT),
             dds.ReliabilityQosPolicy(dds.DDSReliabilityKind.RELIABLE,
                                      dds.DDSDuration.infinity()),
             dds.DestinationOrderQosPolicy(
                 dds.DDSDestinationOrderKind.BY_SOURCE_TIMESTAMP),
             dds.HistoryQosPolicy(dds.DDSHistoryKind.KEEP_ALL),
             dds.ResourceLimitsQosPolicy(10, 10, 10),
             dds.OwnershipQosPolicy(dds.DDSOwnershipKind.EXCLUSIVE),
             dds.TimeBasedFilterQosPolicy(dds.DDSDuration(2, 500)),
             dds.ReaderDataLifecycleQosPolicy(dds.DDSDuration(3),
                                              dds.DDSDuration(5))
         ])
         return self._subscriber.create_datareader(topic, reader_qos,
                                                   listener)
Example #3
0
    def _wait_history(self):
        """Wait for historical data to be available for all topics.

        Blocks, so intended to be run in a background thread.

        Returns
        -------
        iosk : `bool`
            True if we got historical data or none was wanted
        """
        wait_timeout = dds.DDSDuration(sec=HISTORY_TIMEOUT)
        num_ok = 0
        num_checked = 0
        for reader in list(self.readers.values()):
            if not self.isopen:  # shutting down
                return False
            if not reader.isopen:
                continue
            if reader.volatile:
                self.log.debug(
                    f"not calling wait_for_historical_data for {reader.name}; it is volatile"
                )
                # volatile; should not get late joiner data
                continue
            num_checked += 1
            isok = reader.reader.wait_for_historical_data(wait_timeout)
            if isok:
                num_ok += 1
                wait_timeout = dds.DDSDuration(sec=0.1)
        return num_ok > 0 or num_checked == 0
 def createTopic(self, datatype, topicname):
     global mutex
     guard = OpenRTM_aist.ScopedLock(mutex)
     if topicname in self._topic:
         return self._topic[topicname]
     else:
         geninfo = self.genInfo(datatype)
         if geninfo:
             if self._qosProfile:
                 self._topic[topicname] = geninfo.register_topic(
                     self._domainParticipant, topicname, self._qosProfile.get_topic_qos())
             else:
                 topic_qos = dds.Qos([dds.DurabilityQosPolicy(dds.DDSDurabilityKind.TRANSIENT),
                                      dds.DurabilityServiceQosPolicy(dds.DDSDuration(
                                          2, 500), dds.DDSHistoryKind.KEEP_ALL, 2, 100, 100, 100),
                                      dds.DeadlineQosPolicy(
                                          dds.DDSDuration(500)),
                                      dds.LatencyBudgetQosPolicy(
                                          dds.DDSDuration(3000)),
                                      dds.LivelinessQosPolicy(
                     dds.DDSLivelinessKind.MANUAL_BY_PARTICIPANT),
                     dds.ReliabilityQosPolicy(
                     dds.DDSReliabilityKind.RELIABLE, dds.DDSDuration.infinity()),
                     dds.DestinationOrderQosPolicy(
                     dds.DDSDestinationOrderKind.BY_SOURCE_TIMESTAMP),
                     dds.HistoryQosPolicy(
                                          dds.DDSHistoryKind.KEEP_ALL),
                     dds.ResourceLimitsQosPolicy(
                                          10, 10, 10),
                     dds.TransportPriorityQosPolicy(700),
                     dds.LifespanQosPolicy(
                                          dds.DDSDuration(10, 500)),
                     dds.OwnershipQosPolicy(
                                          dds.DDSOwnershipKind.EXCLUSIVE)
                 ])
                 self._topic[topicname] = geninfo.register_topic(
                     self._domainParticipant, topicname, topic_qos)
             return self._topic[topicname]
         return None
Example #5
0
    def _wait_history(self) -> bool:
        """Wait for historical data to be available for all topics.

        Blocks, so intended to be run in a background thread.

        Returns
        -------
        iosk : `bool`
            True if we got historical data or none was wanted
        """
        time_limit_str = os.environ.get("LSST_DDS_HISTORYSYNC")
        if time_limit_str is None:
            time_limit: float = DEFAULT_LSST_DDS_HISTORYSYNC
        else:
            time_limit = float(time_limit_str)
        if time_limit < 0:
            self.log.info(
                f"Time limit {time_limit} < 0; not waiting for historical data"
            )
            return True
        wait_timeout = dds.DDSDuration(sec=time_limit)
        num_ok = 0
        num_checked = 0
        t0 = time.monotonic()
        for reader in self._reader_dict.values():
            if not self.isopen:  # shutting down
                return False
            if reader.volatile or not reader.isopen:
                continue
            num_checked += 1
            isok = reader._reader.wait_for_historical_data(wait_timeout)
            self.wait_history_isok[reader.sal_name] = isok
            if isok:
                num_ok += 1
            elapsed_time = time.monotonic() - t0
            rem_time = max(0.01, time_limit - elapsed_time)
            wait_timeout = dds.DDSDuration(sec=rem_time)
        return num_ok > 0 or num_checked == 0
Example #6
0
    def __init__(self, domain, idl_path, index=0, partitions=("ddsexample", )):
        self.isopen = True
        self.domain = domain
        self.index = index
        self.idl_path = pathlib.Path(idl_path).resolve()
        if not self.idl_path.is_file():
            raise ValueError(f"idl_path={idl_path} is not a file")
        self.name = self.idl_path.stem

        self.log = logging.getLogger(self.name)

        self.partitions = partitions

        # Create the publisher and subscriber. Both depend on the DDS
        # partitions, and so are created here instead of in Domain,
        # where most similar objects are created.
        partition_qos_policy = dds.PartitionQosPolicy(self.partitions)

        publisher_qos = domain.qos_provider.get_publisher_qos()
        publisher_qos.set_policies([partition_qos_policy])
        self.publisher = domain.participant.create_publisher(publisher_qos)

        subscriber_qos = domain.qos_provider.get_subscriber_qos()
        subscriber_qos.set_policies([partition_qos_policy])
        self.subscriber = domain.participant.create_subscriber(subscriber_qos)

        # A task that is set done when Component.start is done
        # (or to the exception if that fails).
        self.start_task = asyncio.Future()

        # dict of dds.ReadCondition: Topic
        self.readers = dict()
        # wait_timeout is a failsafe for shutdown; normally all you have to do
        # is call `close` to trigger the guard condition and stop the wait
        self._wait_timeout = dds.DDSDuration(sec=10)
        self._guardcond = dds.GuardCondition()
        self._waitset = dds.WaitSet()
        self._waitset.attach(self._guardcond)
        self._read_loop_task = make_done_future()
        self._start_begtime = None
        self._wait_history_begtime = None
        self._read_history_begtime = None
        self._start_endtime = None
        self._wait_history_duration = None

        domain.add_component(self)
Example #7
0
    def __init__(
        self,
        domain: Domain,
        name: str,
        index: typing.Optional[int] = 0,
        write_only: bool = False,
    ) -> None:
        if not isinstance(domain, Domain):
            raise TypeError(
                f"domain {domain!r} must be an lsst.ts.salobj.Domain")
        if index is not None:
            if not (isinstance(index, int) or isinstance(index, enum.IntEnum)):
                raise TypeError(
                    f"index {index!r} must be an integer, enum.IntEnum, or None"
                )
        self.domain = domain
        self.name = name
        self.index = 0 if index is None else index
        self.write_only = write_only
        self.identity = domain.default_identity

        self.log = logging.getLogger(self.name)
        if self.log.getEffectiveLevel() > MAX_LOG_LEVEL:
            self.log.setLevel(MAX_LOG_LEVEL)

        # Dict of SAL topic name: wait_for_historical_data succeeded
        # for each topic for which wait_for_historical_data was called.
        # This is primarily intended for unit tests.
        self.wait_history_isok: typing.Dict[str, bool] = dict()

        self.partition_prefix = os.environ.get("LSST_DDS_PARTITION_PREFIX")
        if self.partition_prefix is None:
            raise RuntimeError(
                "Environment variable $LSST_DDS_PARTITION_PREFIX not defined.")

        self.isopen = True
        self.start_called = False
        self.done_task: asyncio.Future = asyncio.Future()
        self.start_task: asyncio.Future = asyncio.Future()

        # Parse environment variable LSST_DDS_ENABLE_AUTHLIST
        # to determine whether to implement command authorization.
        # TODO DM-32379: remove this code block, including the
        # default_authorize attribute.
        authorize_str = os.environ.get("LSST_DDS_ENABLE_AUTHLIST", "0")
        if authorize_str not in ("0", "1"):
            self.log.warning(
                f"Invalid value $LSST_DDS_ENABLE_AUTHLIST={authorize_str!r}. "
                "Specify '1' to enable, '0' or undefined to disable "
                "authlist-based command authorization. Disabling.")
        self.default_authorize = authorize_str == "1"
        if self.default_authorize:
            self.log.info("Enabling authlist-based command authorization")
        else:
            self.log.info("Disabling authlist-based command authorization")

        self.authorized_users: typing.Set[str] = set()
        self.non_authorized_cscs: typing.Set[str] = set()

        # Publishers and subscribers.
        # Create at need to avoid unnecessary instances.
        # Controller needs a _cmd_publisher and _data_subscriber.
        # Remote needs a _cmd_subscriber and _data_publisher.
        self._cmd_publisher = None
        self._cmd_subscriber = None
        self._data_publisher = None
        self._data_subscriber = None

        # dict of private_seqNum: salobj.topics.CommandInfo
        self._running_cmds: typing.Dict[int, topics.CommandInfo] = dict()
        # dict of dds.ReadCondition: salobj topics.ReadTopic
        # This is needed because read conditions don't store the associated
        # data reader. When a wait on a dds.WaitSet returns a read condition
        # we use this dict to figure out which topic to read.
        self._reader_dict: typing.Dict[dds.ReadCondition,
                                       topics.ReadTopic] = dict()
        # list of salobj topics.WriteTopic
        self._writer_list: typing.List[topics.WriteTopic] = list()
        # the first RemoteCommand created should set this to
        # an lsst.ts.salobj.topics.AckCmdReader
        # and set its callback to self._ackcmd_callback
        self._ackcmd_reader: typing.Optional[topics.AckCmdReader] = None
        # the first ControllerCommand created should set this to
        # an lsst.ts.salobj.topics.AckCmdWriter
        self._ackcmd_writer: typing.Optional[topics.AckCmdWriter] = None
        # wait_timeout is a failsafe for shutdown; normally all you have to do
        # is call `close` to trigger the guard condition and stop the wait
        self._wait_timeout = dds.DDSDuration(sec=10)
        self._guardcond = dds.GuardCondition()
        self._waitset = dds.WaitSet()
        self._waitset.attach(self._guardcond)
        self._read_loop_task = utils.make_done_future()

        idl_path = domain.idl_dir / f"sal_revCoded_{self.name}.idl"
        if not idl_path.is_file():
            raise RuntimeError(
                f"Cannot find IDL file {idl_path} for name={self.name!r}")
        self.parsed_idl = ddsutil.parse_idl_file(idl_path)
        self.metadata = idl_metadata.parse_idl(name=self.name,
                                               idl_path=idl_path)
        self.parse_metadata()  # Adds self.indexed, self.revnames, etc.
        if self.index != 0 and not self.indexed:
            raise ValueError(
                f"Index={index!r} must be 0 or None; {name} is not an indexed SAL component"
            )
        if len(self.command_names) > 0:
            ackcmd_revname = self.revnames.get("ackcmd")
            if ackcmd_revname is None:
                raise RuntimeError(
                    f"Could not find {self.name} topic 'ackcmd'")
            self._ackcmd_type: type_hints.AckCmdDataType = ddsutil.make_dds_topic_class(
                parsed_idl=self.parsed_idl, revname=ackcmd_revname)

        domain.add_salinfo(self)

        # Make sure the background thread terminates.
        atexit.register(self.basic_close)