Discovery ========= This section discusses the implementation of discovery plugins in |rti_me|. For a general overview of discovery in |rti_me|, see :ref:`what_is_discovery`. |me| discovery traffic is conducted through transports. Please see the :doc:`transports/index` section for more information about registering and configuring transports. .. _what_is_discovery: What is Discovery? ------------------ Discovery is the behind-the-scenes way in which |rti_me| objects (*DomainParticipants*, *DataWriters*, and *DataReaders*) on different nodes find out about each other. Each *DomainParticipant* maintains a database of information about all the active *DataReaders* and *DataWriters* that are in the same DDS domain. This database is what makes it possible for *DataWriters* and *DataReaders* to communicate. To create and refresh the database, each application follows a common discovery process. This section describes the default discovery mechanism known as the Simple Discovery Protocol, which includes two phases: `Simple Participant Discovery`_ and `Simple Endpoint Discovery`_. The goal of these two phases is to build, for each *DomainParticipant*, a complete picture of all the entities that belong to the remote participants that are in its peers list. The peers list is the list of nodes with which a participant may communicate. It starts out the same as the *initial_peers* list that you configure in the DISCOVERY_ QosPolicy. If the accept_unknown_peers_ flag in that same QosPolicy is TRUE, then other nodes may also be added as they are discovered; if it is FALSE, then the peers list will match the initial_peers_ list, plus any peers added using the *DomainParticipant’s* **add_peer()** operation. The following section discusses how |me| objects on different nodes find out about each other using the default Simple Discovery Protocol (SDP). It describes the sequence of messages that are passed between |me| on the sending and receiving sides. The discovery process occurs automatically, so you do not have to implement any special code. For more information about advanced topics related to Discovery, please refer to |rti_core_um_discovery_verbose| in the |rti_core_um| (available |rti_core_um_discovery|_ if you have Internet access). Simple Participant Discovery ............................ This phase of the Simple Discovery Protocol is performed by the Simple Participant Discovery Protocol (SPDP). During the Participant Discovery phase, *DomainParticipants* learn about each other. The *DomainParticipant*’s details are communicated to all other *DomainParticipants* in the same DDS domain by sending participant declaration messages, also known as participant *DATA* submessages. The details include the *DomainParticipant*’s unique identifying key (GUID or Globally Unique ID described below), transport locators (addresses and port numbers), and QoS. These messages are sent on a periodic basis using best-effort communication. *Participant DATAs* are sent periodically to maintain the liveliness of the *DomainParticipant*. They are also used to communicate changes in the *DomainParticipant*’s QoS. Only changes to QosPolicies that are part of the *DomainParticipant*’s built-in data need to be propagated. When receiving remote participant discovery information, |rti_me| determines if the local participant matches the remote one. A ‘match’ between the local and remote participant occurs only if the local and remote participant have the same Domain ID and Domain Tag. This matching process occurs as soon as the local participant receives discovery information from the remote one. If there is no match, the discovery DATA is ignored, resulting in the remote participant (and all its associated entities) not being discovered. When a *DomainParticipant* is deleted, a participant *DATA (delete)* submessage with the *DomainParticipant*'s identifying GUID is sent. The GUID is a unique reference to an entity. It is composed of a GUID prefix and an Entity ID. By default, the GUID prefix is calculated from the IP address and the process ID. The entityID is set by |me| (you may be able to change it in a future version). Once a pair of remote participants have discovered each other, they can move on to the Endpoint Discovery phase, which is how *DataWriters* and *DataReaders* find each other. Simple Endpoint Discovery ......................... This phase of the Simple Discovery Protocol is performed by the Simple Endpoint Discovery Protocol (SEDP). During the Endpoint Discovery phase, |rti_me| matches *DataWriters* and *DataReaders*. Information (GUID, QoS, etc.) about your application’s *DataReaders* and *DataWriters* is exchanged by sending publication/subscription declarations in DATA messages that we will refer to as *publication DATAs* and *subscription DATAs*. The Endpoint Discovery phase uses reliable communication. These declaration or DATA messages are exchanged until each *DomainParticipant* has a complete database of information about the participants in its peers list and their entities. Then the discovery process is complete and the system switches to a steady state. During steady state, *participant DATAs* are still sent periodically to maintain the liveliness status of participants. They may also be sent to communicate QoS changes or the deletion of a *DomainParticipant*. When a remote *DataWriter/DataReader* is discovered, |me| determines if the local application has a matching *DataReader/DataWriter*. A ‘match’ between the local and remote entities occurs only if the *DataReader* and *DataWriter* have the same *Topic*, same data type, and compatible QosPolicies. Furthermore, if the *DomainParticipant* has been set up to ignore certain *DataWriters/DataReaders*, those entities will not be considered during the matching process. This ‘matching’ process occurs as soon as a remote entity is discovered, even if the entire database is not yet complete: that is, the application may still be discovering other remote entities. A *DataReader* and *DataWriter* can only communicate with each other if each one’s application has hooked up its local entity with the matching remote entity. That is, both sides must agree to the connection. Please refer to |rti_core_um_discoveryimplementation_verbose| in the |rti_core_um| for more details about the discovery process (available |rti_core_um_discoveryimplementation|_ if you have Internet access). Configuring Participant Discovery Peers --------------------------------------- An |rti_me| *DomainParticipant* must be able to send participant discovery announcement messages for other *DomainParticipants* to discover itself, and it must receive announcements from other *DomainParticipants* to discover them. To do so, each *DomainParticipant* will send its discovery announcements to a set of locators known as its peer list, where a peer is the transport locator of one or more potential other *DomainParticipants* to discover. peer_desc_string ................ A peer descriptor string of the initial_peers_ string sequence conveys the interface and address of the locator to which to send, as well as the indices of participants to which to send. For example:: DDS_StringSeq_set_maximum(&dp_qos.discovery.initial_peers, 3); DDS_StringSeq_set_length(&dp_qos.discovery.initial_peers, 3); *DDS_StringSeq_get_reference(&dp_qos.discovery.initial_peers, 0) = DDS_String_dup("_udp://239.255.0.1"); *DDS_StringSeq_get_reference(&dp_qos.discovery.initial_peers, 1) = DDS_String_dup("[1-4]@_udp://10.10.30.101"); *DDS_StringSeq_get_reference(&dp_qos.discovery.initial_peers, 2) = DDS_String_dup("[2]@_udp://10.10.30.102"); The peer descriptor format is: :: [index@][interface://]address Remember that every *DomainParticipant* has a participant index that is unique within a DDS domain. The participant index (also referred to as the participant ID), together with the DDS domain ID, is used to calculate the network port on which *DataReaders* of that participant will receive messages. Thus, by specifying the participant index, or a range of indices, for a peer locator, that locator becomes a port to which messages will be sent only if addressed to the entities of a particular *DomainParticipant*. Specifying indices restricts the number of participant announcements sent to a locator where other *DomainParticipants* exist and, thus, should be considered to minimize network bandwidth usage. In the above example, the first peer, "_udp://239.255.0.1," has the default UDPv4 multicast peer locator. Note that there is no [index@] associated with a multicast locator. The second peer, "[1-4]@_udp://10.10.30.101," has a unicast address. It also has indices in brackets, [1-4]. These represent a range of participant indices, 1 through 4, to which participant discovery messages will be sent. Lastly, the third peer, "[2]@_udp://10.10.30.102," is a unicast locator to a single participant with index 2. Configuring Initial Peers and Adding Peers ------------------------------------------ .. _DiscoveryQosPolicy_initial_peers: initial_peers_ DiscoveryQosPolicy_initial_peers_ is the list of peers a *DomainParticipant* sends its participant announcement messages, when it is enabled, as part of the discovery process. DiscoveryQosPolicy_initial_peers_ is an empty sequence by default, so while DiscoveryQosPolicy_enabled_transports_ by default includes the DDS default loopback and multicast (239.255.0.1) addresses, initial_peers_ must be configured to include them. Peers can also be added to the list, before and after a *DomainParticipant* has been enabled, by using DomainParticipant_add_peer_. The *DomainParticipant* will start sending participant announcement messages to the new peer as soon as it is enabled. Discovery Plugins ----------------- When a *DomainParticipant* receives a participant discovery message from another *DomainParticipant*, it will engage in the process of exchanging information of user-created *DataWriter* and *DataReader* endpoints. |rti_me| provides two ways of determinig endpoint information of other *DomainParticipants*: :ref:`dynamic_endpoint_disc` and :ref:`static_endpoint_disc`. .. _dynamic_endpoint_disc: Dynamic Discovery Plugin ........................ Dynamic endpoint discovery uses builtin discovery *DataWriters* and *DataReader* to exchange messages about user created *DataWriter* and *DataReaders*. A *DomainParticipant* using dynamic participant, dynamic endpoint (DPDE_) discovery will have a pair of builtin *DataWriters* for sending messages about its own user created *DataWriters* and *DataReaders*, and a pair of builtin *DataReaders* for receiving messages from other *DomainParticipants* about their user created *DataWriters* and *DataReaders*. Given a *DomainParticipant* with a user *DataWriter*, receiving an endpoint discovery message for a user *DataReader* allows the *DomainParticipant* to get the type, topic, and QoS of the *DataReader* that determine whether the *DataReader* is a match. When a matching *DataReader* is discovered, the *DataWriter* will include that *DataReader* and its locators as destinations for its subsequent writes. .. _static_endpoint_disc: Static Discovery Plugin ....................... Static endpoint discovery uses function calls to statically assert information about remote endpoints belonging to remote *DomainParticipants*. An application with a *DomainParticipant* using dynamic participant, static endpoint (DPSE_) discovery has control over which endpoints belonging to particular remote *DomainParticipants* are discoverable. Whereas dynamic endpoint-discovery can establish matches for all endpoint-discovery messages it receives, static endpoint-discovery establishes matches only for the endpoint that have been asserted programmatically. With DPSE_, a user needs to know *a priori* the configuration of the entities that will need to be discovered by its application. The user must know the names of all *DomainParticipants* within the DDS domain and the exact QoS of the remote *DataWriters* and *DataReaders*. Please refer to the |api_ref_c|_ and |api_ref_cpp|_ for the following remote entity assertion APIs: - DPSE_RemoteParticipant_assert_ - DPSE_RemotePublication_assert_ - DPSE_RemoteSubscription_assert_ Remote Participant Assertion '''''''''''''''''''''''''''' Given a local *DomainParticipant*, static discovery requires first the names of remote *DomainParticipants* to be asserted, in order for endpoints on them to match. This is done by calling DPSE_RemoteParticipant_assert_ with the name of a remote *DomainParticipant*. The name must match the name contained in the participant discovery announcement produced by that *DomainParticipant*. This has to be done reciprocally between two *DomainParticipants* so that they may discover one another. For example, a *DomainParticipant* has entity name "participant_1", while another *DomainParticipant* has name "participant_2." participant_1 should call DPSE_RemoteParticipant_assert("participant_2") in order to discover participant_2. Similarly, participant_2 must also assert participant_1 for discovery between the two to succeed. :: /* participant_1 is asserting (remote) participant_2 */ retcode = DPSE_RemoteParticipant_assert(participant_1, "participant_2"); if (retcode != DDS_RETCODE_OK) { printf("participant_1 failed to assert participant_2\n"); goto done; } Remote Publication and Subscription Assertion ''''''''''''''''''''''''''''''''''''''''''''' Next, a *DomainParticipant* needs to assert the remote endpoints it wants to match that belong to an already asserted remote *DomainParticipant*. The endpoint assertion function is used, specifying an argument which contains all the QoS and configuration of the remote endpoint. Where DPDE_ gets remote endpoint QoS information from received endpoint-discovery messages, in DPSE_, the remote endpoint's QoS must be configured locally. With remote endpoints asserted, the *DomainParticipant* then waits until it receives a participant discovery announcement from an asserted remote *DomainParticipant*. Once received that, all endpoints that have been asserted for that remote *DomainParticipant* are considered discovered and ready to be matched with local endpoints. Assume participant_1 contains a *DataWriter*, and participant_2 has a *DataReader*, both communicating on topic HelloWorld. participant_1 needs to assert the *DataReader* in participant_2 as a remote subscription. The remote subscription data passed to the operation must match exactly the QoS actually used by the remote *DataReader*:: /* Set participant_2's reader's QoS in remote subscription data */ rem_subscription_data.key.value[DDS_BUILTIN_TOPIC_KEY_OBJECT_ID] = 200; rem_subscription_data.topic_name = DDS_String_dup("Example HelloWorld"); rem_subscription_data.type_name = DDS_String_dup("HelloWorld"); rem_subscription_data.reliability.kind = DDS_RELIABLE_RELIABILITY_QOS; /* Assert reader as a remote subscription belonging to (remote) participant_2 */ retcode = DPSE_RemoteSubscription_assert(participant_1, "participant_2", &rem_subscription_data, HelloWorld_get_key_kind(HelloWorldTypePlugin_get(), NULL)); if (retcode != DDS_RETCODE_OK) { printf("failed to assert remote subscription\n"); goto done; } Reciprocally, participant_2 must assert participant_1's *DataWriter* as a remote publication, also specifying matching QoS parameters:: /* Set participant_1's writer's QoS in remote publication data */ rem_publication_data.key.value[DDS_BUILTIN_TOPIC_KEY_OBJECT_ID] = 100; rem_publication_data.key.value.topic_name = DDS_String_dup("Example HelloWorld"); rem_publication_data.key.value.type_name = DDS_String_dup("HelloWorld"); rem_publication_data.key.value.reliability.kind = DDS_RELIABLE_RELIABILITY_QOS; /* Assert writer as a remote publication belonging to (remote) participant_1 */ retcode = DPSE_RemotePublication_assert(participant_2, "participant_1", &rem_publication_data, HelloWorld_get_key_kind(HelloWorldTypePlugin_get(), NULL)); if (retcode != DDS_RETCODE_OK) { printf("failed to assert remote publication\n"); goto done; } When participant_1 receives a participant discovery message from participant_2, it is aware of participant_2, based on its previous assertion, and it knows participant_2 has a matching *DataReader*, also based on the previous assertion of the remote endpoint. It therefore establishes a match between its *DataWriter* and participant_2's *DataReader*. Likewise, participant_2 will match participant_1's *DataWriter* with its local *DataRead*, upon receiving one of participant_1's participant discovery messages. Note, with DPSE_, there is no runtime check of QoS consistency between *DataWriters* and *DataReaders*, because no endpoint discovery messages are exchanged. This makes it extremely important that users of DPSE_ ensure that the QoS set for a local *DataWriter* and *DataReader* is the same QoS being used by another *DomainParticipant* to assert it as a remote *DataWriter* or *DataReader*.