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)
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)
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
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
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)
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)