Skip to content

Commit

Permalink
Add IDL format to topic entities (#245)
Browse files Browse the repository at this point in the history
* Refs #21458: Add type IDL to Topic info json [WORK IN PROGRESS]

Signed-off-by: Carlosespicur <[email protected]>

* Refs #21458: Add IDL getter for Monitor controller communication

Signed-off-by: Carlosespicur <[email protected]>

* Refs #21458: Delete Topic IDLs in topic info JSON

Signed-off-by: Carlosespicur <[email protected]>

* Refs #21458: Uncrustify

Signed-off-by: Carlosespicur <[email protected]>

* Refs #21458: Fix tests and idl serialization in callbacks

Signed-off-by: Carlosespicur <[email protected]>

* Refs #21458: Add review changes

Signed-off-by: Carlosespicur <[email protected]>

---------

Signed-off-by: Carlosespicur <[email protected]>
  • Loading branch information
Carlosespicur authored Sep 12, 2024
1 parent cdc0284 commit 2484640
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 6 deletions.
10 changes: 10 additions & 0 deletions include/fastdds_statistics_backend/StatisticsBackend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,16 @@ class StatisticsBackend
static Info get_info(
EntityId entity_id);

/**
* @brief Get the IDL representation of a data type in string format for a given topic entity
*
* @param entity_id The entity for which the data type IDL is retrieved.
* @return String object describing the entity's data type IDL.
*/
FASTDDS_STATISTICS_BACKEND_DllAPI
static std::string get_type_idl(
EntityId entity_id);

/**
* @brief Provides access to the data measured during the monitoring.
*
Expand Down
12 changes: 12 additions & 0 deletions src/cpp/StatisticsBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,18 @@ Info StatisticsBackend::get_info(
return StatisticsBackendData::get_instance()->database_->get_info(entity_id);
}

std::string StatisticsBackend::get_type_idl(
EntityId entity_id)
{
// Check if the entity is a topic
if (EntityKind::TOPIC != get_type(entity_id))
{
throw BadParameter("EntityId received does not match with a valid topic entity");
}
Info topic_info = StatisticsBackend::get_info(entity_id);
return StatisticsBackendData::get_instance()->database_->get_type_idl(topic_info[DATA_TYPE_TAG]);
}

std::vector<StatisticsData> StatisticsBackend::get_data(
DataKind data_type,
const std::vector<EntityId>& entity_ids_source,
Expand Down
40 changes: 38 additions & 2 deletions src/cpp/database/database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ EntityId Database::insert_new_topic(
std::shared_ptr<Domain> domain = std::const_pointer_cast<Domain>(
std::static_pointer_cast<const Domain>(get_entity_nts(domain_id)));

// Create the topic to insert in the database
auto topic = std::make_shared<Topic>(
name,
type_name,
Expand All @@ -279,6 +280,24 @@ EntityId Database::insert_new_topic(
return entity_id;
}

bool Database::is_type_in_database(
const std::string& type_name)
{
return (type_idls_.find(type_name) != type_idls_.end());
}

void Database::insert_new_type_idl(
const std::string& type_name,
const std::string& type_idl)
{
std::lock_guard<std::shared_timed_mutex> guard(mutex_);
if (type_name.empty())
{
throw BadParameter("Type name cannot be empty");
}
type_idls_[type_name] = type_idl;
}

EntityId Database::insert_new_endpoint(
const std::string& endpoint_guid,
const std::string& name,
Expand Down Expand Up @@ -656,7 +675,7 @@ void Database::insert_nts(
throw BadParameter("Topic data type cannot be empty");
}

/* Check that domain exits */
/* Check that domain exists */
bool domain_exists = false;
for (const auto& domain_it : domains_)
{
Expand Down Expand Up @@ -2103,6 +2122,24 @@ std::vector<std::pair<EntityId, EntityId>> Database::get_entities_by_name_nts(
return entities;
}

std::string Database::get_type_idl(
const std::string& type_name) const
{
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
return get_type_idl_nts(type_name);
}

std::string Database::get_type_idl_nts(
const std::string& type_name) const
{
auto it = type_idls_.find(type_name);
if (it != type_idls_.end())
{
return it->second;
}
throw BadParameter("Type " + type_name + " not found in the database");
}

void Database::erase(
EntityId& domain_id)
{
Expand Down Expand Up @@ -4624,7 +4661,6 @@ DatabaseDump Database::dump_entity_(
entity_info[ALIAS_TAG] = entity->alias;
entity_info[DATA_TYPE_TAG] = entity->data_type;
entity_info[STATUS_TAG] = entity->status;

entity_info[DOMAIN_ENTITY_TAG] = id_to_string(entity->domain->id);

// metatraffic and active attributes are stored but ignored when loading
Expand Down
46 changes: 46 additions & 0 deletions src/cpp/database/database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,24 @@ class Database
const std::string& alias,
const EntityId& domain_id);

/**
* @brief Check if a Topic data type is already in the database
* @param type_name The type name of the Topic.
*
* @return True if the Topic data type is in the database.
*/
bool is_type_in_database(
const std::string& type_name);

/**
* @brief Insert a new type IDL into the database.
* @param topic_type The type of the topic.
* @param topic_idl The IDL representation of the type
*/
void insert_new_type_idl(
const std::string& topic_type,
const std::string& topic_idl);

/**
* @brief Create new Endpoint and corresponding Locator, and insert them in database.
* @param endpoint_guid The GUID of the Endpoint.
Expand Down Expand Up @@ -444,6 +462,16 @@ class Database
EntityKind entity_kind,
const std::string& name) const;

/**
* @brief Get the type IDL of a given type name, if it exists.
*
* @param type_name The name of the data type for which to search.
* @throws eprosima::statistics_backend::BadParameter if \c type_name does not exist in the database.
* @return The IDL representation of the type in std::string format.
*/
std::string get_type_idl(
const std::string& type_name) const;

/**
* @brief Get the entity of a given EntityKind that matches with the requested GUID.
*
Expand Down Expand Up @@ -1167,6 +1195,16 @@ class Database
EntityKind entity_kind,
const std::string& name) const;

/**
* @brief Get the type IDL of a given type name, if it exists. This method is not thread safe.
*
* @param type_name The name of the data type for which to search.
* @throws eprosima::statistics_backend::BadParameter if \c type_name does not exist in the database.
* @return The IDL representation of the type in std::string format.
*/
std::string get_type_idl_nts(
const std::string& type_name) const;

/**
* @brief Get the entity of a given EntityKind that matches with the requested GUID. This method is not thread safe.
*
Expand Down Expand Up @@ -1446,6 +1484,14 @@ class Database
*/
std::map<EntityId, std::map<EntityId, std::shared_ptr<Topic>>> topics_;

/**
* Collection of topic IDLs sorted by topic data types, with which they are biunivocally identified.
* This is used to store the IDLs of the discovered topics
*
* Each value in the collection is in turn a map of the actual Topic IDLs sorted by data type
*/
std::map<std::string, std::string> type_idls_;

//! Graph map describing per domain complete topology of the entities.
std::map<EntityId, Graph> domain_view_graph;

Expand Down
6 changes: 6 additions & 0 deletions src/cpp/database/database_queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,12 @@ EntityId DatabaseEntityQueue::process_endpoint_discovery(
details::StatisticsBackendData::DiscoveryStatus::DISCOVERY);
}

// Store type IDL in the database if available and in case it is not already stored. Ignore metatraffic topics
if (!database_->is_type_in_database(info.type_name) && !info.is_virtual_metatraffic)
{
database_->insert_new_type_idl(info.type_name, info.type_idl);
}

// Create the endpoint
EntityId endpoint_id;
std::stringstream name;
Expand Down
4 changes: 2 additions & 2 deletions src/cpp/database/database_queue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#include <fastdds/rtps/common/SequenceNumber.hpp>
#include <fastdds/rtps/common/RemoteLocators.hpp>
#include <fastdds/dds/log/Log.hpp>

#include <fastdds_statistics_backend/types/JSONTags.h>

#include <database/database.hpp>
Expand Down Expand Up @@ -372,9 +371,10 @@ struct EntityDiscoveryInfo
std::string user;
std::string process;

// Enpoint data
// Endpoint data
std::string topic_name;
std::string type_name;
std::string type_idl;
fastdds::rtps::RemoteLocatorList locators;

// Alias
Expand Down
56 changes: 55 additions & 1 deletion src/cpp/subscriber/StatisticsParticipantListener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@

#include <fastdds/dds/core/status/StatusMask.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/domain/DomainParticipantListener.hpp>
#include <fastdds/dds/log/Log.hpp>
#include <fastdds/rtps/writer/WriterDiscoveryStatus.hpp>
#include <fastdds/dds/xtypes/dynamic_types/DynamicTypeBuilder.hpp>
#include <fastdds/dds/xtypes/dynamic_types/DynamicTypeBuilderFactory.hpp>
#include <fastdds/dds/xtypes/utils.hpp>
#include <fastdds/rtps/common/EntityId_t.hpp>
#include <fastdds/rtps/writer/WriterDiscoveryStatus.hpp>

#include "database/database_queue.hpp"
#include "subscriber/QosSerializer.hpp"
Expand Down Expand Up @@ -318,6 +322,31 @@ void StatisticsParticipantListener::on_data_reader_discovery(
}
}

// In case of a new data reader discovered, add type info if available
if (ReaderDiscoveryStatus::DISCOVERED_READER == reason && info.type_information.assigned() == true)
{
// Create IDL representation of the discovered type
// Get remote type information
xtypes::TypeObject remote_type_object;
if (RETCODE_OK != DomainParticipantFactory::get_instance()->type_object_registry().get_type_object(
info.type_information.type_information.complete().typeid_with_size().type_id(),
remote_type_object))
{
EPROSIMA_LOG_ERROR(STATISTICS_PARTICIPANT_LISTENER,
"Error getting type object for type " << info.type_name);
return;
}

// Build remotely discovered type
DynamicType::_ref_type remote_type = DynamicTypeBuilderFactory::get_instance()->create_type_w_type_object(
remote_type_object)->build();

// Serialize DynamicType into its IDL representation
std::stringstream idl;
idl_serialize(remote_type, idl);
discovery_info.type_idl = idl.str();
}

entity_queue_->push(timestamp, discovery_info);

// Wait until the entity queue is processed and restart the data queues
Expand Down Expand Up @@ -376,6 +405,31 @@ void StatisticsParticipantListener::on_data_writer_discovery(
}
}

// In case of a new data writer discovered, add type info if available
if (WriterDiscoveryStatus::DISCOVERED_WRITER == reason && info.type_information.assigned() == true)
{
// Create IDL representation of the discovered type
// Get remote type information
xtypes::TypeObject remote_type_object;
if (RETCODE_OK != DomainParticipantFactory::get_instance()->type_object_registry().get_type_object(
info.type_information.type_information.complete().typeid_with_size().type_id(),
remote_type_object))
{
EPROSIMA_LOG_ERROR(STATISTICS_PARTICIPANT_LISTENER,
"Error getting type object for type " << info.type_name);
return;
}

// Build remotely discovered type
DynamicType::_ref_type remote_type = DynamicTypeBuilderFactory::get_instance()->create_type_w_type_object(
remote_type_object)->build();

// Serialize DynamicType into its IDL representation
std::stringstream idl;
idl_serialize(remote_type, idl);
discovery_info.type_idl = idl.str();
}

entity_queue_->push(timestamp, discovery_info);

// Wait until the entity queue is processed and restart the data queues
Expand Down
7 changes: 7 additions & 0 deletions test/mock/database/database/database/database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ class Database
const std::string& topic_type,
const EntityId& topic_id));

MOCK_METHOD1(is_type_in_database, bool(
const std::string& type_name));

MOCK_METHOD2(insert_new_type_idl, void(
const std::string& type_name,
const std::string& type_idl));

MOCK_METHOD4(insert_new_topic, EntityId(
const std::string& name,
const std::string& type_name,
Expand Down
17 changes: 17 additions & 0 deletions test/unittest/DatabaseQueue/DatabaseQueueTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,8 @@ TEST_F(database_queue_tests, push_datawriter)
std::make_pair(EntityId(0), EntityId(2)))));
EXPECT_CALL(database, is_topic_in_database(_, EntityId(2))).Times(AnyNumber())
.WillRepeatedly(Return(true));
EXPECT_CALL(database, is_type_in_database(type_name)).Times(AnyNumber())
.WillRepeatedly(Return(true));

// Datawriter undiscovery: FAILURE
{
Expand Down Expand Up @@ -1625,6 +1627,9 @@ TEST_F(database_queue_tests, push_datawriter_topic_does_not_exist)
EXPECT_CALL(database, get_entities_by_name(EntityKind::TOPIC, topic_name)).Times(AnyNumber())
.WillOnce(Return(std::vector<std::pair<EntityId, EntityId>>()));

EXPECT_CALL(database, is_type_in_database(type_name)).Times(AnyNumber())
.WillRepeatedly(Return(false));

// Datawriter discovery: SUCCESS
{
// Precondition: The writer does not exist
Expand Down Expand Up @@ -1681,6 +1686,10 @@ TEST_F(database_queue_tests, push_datawriter_topic_does_not_exist)
EXPECT_CALL(database, insert_new_endpoint(_, _, _, _, _, _, _, _, _, _)).Times(1)
.WillOnce(Invoke(&insert_datawriter_args, &InsertEndpointArgs::insert));

// Expectation: Add the type to the database
EXPECT_CALL(database, is_type_in_database(type_name)).Times(1).WillOnce(Return(false));
EXPECT_CALL(database, insert_new_type_idl(type_name, "")).Times(1);

// Expectation: Modify graph and notify user
EXPECT_CALL(database, update_endpoint_in_graph(_, _, _, _)).Times(1).WillOnce(Return(true));
EXPECT_CALL(*details::StatisticsBackendData::get_instance(), on_domain_view_graph_update(_)).Times(1);
Expand Down Expand Up @@ -1746,6 +1755,8 @@ TEST_F(database_queue_tests, push_datareader)
std::make_pair(EntityId(0), EntityId(2)))));
EXPECT_CALL(database, is_topic_in_database(_, EntityId(2))).Times(AnyNumber())
.WillRepeatedly(Return(true));
EXPECT_CALL(database, is_type_in_database(type_name)).Times(AnyNumber())
.WillRepeatedly(Return(true));

// Datareader undiscovery: FAILURE
{
Expand Down Expand Up @@ -1979,6 +1990,8 @@ TEST_F(database_queue_tests, push_datareader_topic_does_not_exist)
// Precondition: The topic does not exist
EXPECT_CALL(database, get_entities_by_name(EntityKind::TOPIC, topic_name)).Times(AnyNumber())
.WillOnce(Return(std::vector<std::pair<EntityId, EntityId>>()));
EXPECT_CALL(database, is_type_in_database(type_name)).Times(AnyNumber())
.WillOnce(Return(false));

// Datareader discovery: SUCCESS
{
Expand Down Expand Up @@ -2036,6 +2049,10 @@ TEST_F(database_queue_tests, push_datareader_topic_does_not_exist)
EXPECT_CALL(database, insert_new_endpoint(_, _, _, _, _, _, _, _, _, _)).Times(1)
.WillOnce(Invoke(&insert_datareader_args, &InsertEndpointArgs::insert));

// Expectation: Add the type to the database
EXPECT_CALL(database, is_type_in_database(type_name)).Times(1).WillOnce(Return(false));
EXPECT_CALL(database, insert_new_type_idl(type_name, "")).Times(1);

// Expectation: Modify graph and notify user
EXPECT_CALL(database, update_endpoint_in_graph(_, _, _, _)).Times(1).WillOnce(Return(true));
EXPECT_CALL(*details::StatisticsBackendData::get_instance(), on_domain_view_graph_update(_)).Times(1);
Expand Down
1 change: 0 additions & 1 deletion test/unittest/StatisticsBackend/IsActiveTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,6 @@ TEST_F(is_active_tests, discover_datawriter_on_inactive_domain)
// The discovered reader is in the topic
data.topic_name = topic->name;
data.type_name = topic->data_type;

// The discovered reader contains the locator
eprosima::fastdds::rtps::Locator_t dds_existing_unicast_locator(LOCATOR_KIND_UDPv4, 1024);
dds_existing_unicast_locator.address[12] = 127;
Expand Down
Loading

0 comments on commit 2484640

Please sign in to comment.