File type_lookup_service.hpp
File List > astutedds > xtypes > type_lookup_service.hpp
Go to the documentation of this file
//
// Copyright (c) 2026, Astute Systems PTY LTD
//
// This file is part of the Astute DDS developed by Astute Systems.
//
// See the commercial LICENSE file in the project root for full license details.
//
// @file type_lookup_service.hpp
// @brief DDS X-Types TypeLookup Service
//
// Implements remote type discovery protocol per DDS-XTypes 1.3 specification.
// Reference: DDS-XTypes 1.3 Section 7.6.3
//
#ifndef ASTUTEDDS_XTYPES_TYPE_LOOKUP_SERVICE_HPP
#define ASTUTEDDS_XTYPES_TYPE_LOOKUP_SERVICE_HPP
#include <astutedds/rtps/rtps_types.hpp>
#include <astutedds/xtypes/type_object.hpp>
#include <chrono>
#include <functional>
#include <future>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
namespace astutedds::xtypes
{
// ============================================================================
// TypeLookup Request/Reply Types (7.6.3.3)
// ============================================================================
using RequestSequenceNumber = int64_t;
using TypeIdentifierKey = std::string;
inline TypeIdentifierKey type_id_to_key(const TypeIdentifier& id)
{
// Create a key based on discriminator and value index
return std::to_string(static_cast<int>(id.discriminator)) + "_" + std::to_string(id.value.index());
}
using TypeIdentifierSeq = std::vector<TypeIdentifier>;
struct TypeIdentifierTypeObjectPair
{
TypeIdentifier type_identifier;
TypeObject type_object;
};
struct TypeIdentifierWithDependencies
{
TypeIdentifier type_identifier;
TypeIdentifierSeq dependent_types;
};
enum class TypeLookupRequestKind : uint8_t
{
GET_TYPES = 0,
GET_TYPE_DEPENDENCIES
};
enum class TypeLookupReplyStatus : uint8_t
{
OK = 0,
ERROR = 1,
UNKNOWN_TYPE = 2,
UNSUPPORTED_OPERATION = 3
};
struct GetTypesRequest
{
TypeIdentifierSeq type_identifiers;
};
struct GetTypesReply
{
TypeLookupReplyStatus status{TypeLookupReplyStatus::OK};
std::vector<TypeIdentifierTypeObjectPair> types;
};
struct GetTypeDependenciesRequest
{
TypeIdentifierSeq type_identifiers;
TypeIdentifierSeq known_types;
int32_t continuation_point{0};
};
struct GetTypeDependenciesReply
{
TypeLookupReplyStatus status{TypeLookupReplyStatus::OK};
std::vector<TypeIdentifierWithDependencies> dependencies;
int32_t continuation_point{-1};
};
struct TypeLookupRequest
{
rtps::GUID_t requestor_guid;
RequestSequenceNumber sequence_number;
TypeLookupRequestKind kind;
std::variant<GetTypesRequest, GetTypeDependenciesRequest> data;
};
struct TypeLookupReply
{
rtps::GUID_t replier_guid;
RequestSequenceNumber sequence_number;
std::variant<GetTypesReply, GetTypeDependenciesReply> data;
};
// ============================================================================
// TypeLookup Service Interface
// ============================================================================
using TypeLookupCallback = std::function<void(const TypeLookupReply&)>;
using TypeLookupRequestHandler = std::function<TypeLookupReply(const TypeLookupRequest&)>;
class TypeLookupService
{
public:
explicit TypeLookupService(const rtps::GUID_t& participant_guid);
~TypeLookupService();
// Non-copyable
TypeLookupService(const TypeLookupService&) = delete;
TypeLookupService& operator=(const TypeLookupService&) = delete;
// ========================================================================
// Client Operations
// ========================================================================
std::future<std::optional<GetTypesReply>> get_types(const rtps::GUID_t& remote_guid,
const TypeIdentifierSeq& type_ids,
std::chrono::milliseconds timeout = std::chrono::seconds(5));
std::future<std::optional<GetTypeDependenciesReply>> get_type_dependencies(
const rtps::GUID_t& remote_guid, const TypeIdentifierSeq& type_ids, const TypeIdentifierSeq& known_types = {},
std::chrono::milliseconds timeout = std::chrono::seconds(5));
void get_types_async(const rtps::GUID_t& remote_guid, const TypeIdentifierSeq& type_ids,
TypeLookupCallback callback);
// ========================================================================
// Server Operations
// ========================================================================
void register_type(const TypeIdentifier& type_id, const TypeObject& type_object);
void unregister_type(const TypeIdentifier& type_id);
void set_request_handler(TypeLookupRequestHandler handler);
void handle_request(const TypeLookupRequest& request);
void handle_reply(const TypeLookupReply& reply);
// ========================================================================
// Local Registry Operations
// ========================================================================
std::optional<TypeObject> lookup_local(const TypeIdentifier& type_id) const;
TypeIdentifierSeq get_dependencies(const TypeIdentifier& type_id) const;
bool has_type(const TypeIdentifier& type_id) const;
TypeIdentifierSeq get_all_types() const;
void clear();
private:
void send_request(const rtps::GUID_t& remote_guid, TypeLookupRequest& request);
void send_reply(const rtps::GUID_t& remote_guid, const TypeLookupReply& reply);
GetTypesReply handle_get_types(const GetTypesRequest& request);
GetTypeDependenciesReply handle_get_dependencies(const GetTypeDependenciesRequest& request);
RequestSequenceNumber next_sequence_number();
void calculate_dependencies(const TypeIdentifier& type_id, const TypeIdentifierSeq& deps,
const TypeIdentifierSeq& known) const;
private:
rtps::GUID_t participant_guid_;
// Local type registry (using string keys for compatibility)
mutable std::mutex registry_mutex_;
std::unordered_map<TypeIdentifierKey, std::pair<TypeIdentifier, TypeObject>> type_registry_;
std::unordered_map<TypeIdentifierKey, TypeIdentifierSeq> dependency_cache_;
// Pending requests
mutable std::mutex requests_mutex_;
std::atomic<RequestSequenceNumber> sequence_counter_{0};
std::map<RequestSequenceNumber, std::promise<TypeLookupReply>> pending_requests_;
std::map<RequestSequenceNumber, TypeLookupCallback> async_callbacks_;
// Request handler (server mode)
TypeLookupRequestHandler request_handler_;
};
// ============================================================================
// TypeLookup Manager (Participant-level)
// ============================================================================
class TypeLookupManager
{
public:
explicit TypeLookupManager(const rtps::GUID_t& participant_guid);
TypeLookupService& service() { return service_; }
const TypeLookupService& service() const { return service_; }
void register_type(const std::string& type_name, const TypeObject& type_object);
std::optional<TypeObject> lookup_type(const std::string& type_name, const rtps::GUID_t& remote_guid,
std::chrono::milliseconds timeout = std::chrono::seconds(5));
std::vector<TypeIdentifierTypeObjectPair> resolve_type_with_dependencies(const TypeIdentifier& type_id,
const rtps::GUID_t& remote_guid);
bool check_type_compatibility(const TypeIdentifier& local_type, const TypeIdentifier& remote_type,
const rtps::GUID_t& remote_guid);
private:
TypeLookupService service_;
// Type name to identifier mapping
mutable std::mutex name_map_mutex_;
std::map<std::string, TypeIdentifier> name_to_id_;
};
// ============================================================================
// Built-in TypeLookup Topics
// ============================================================================
namespace TypeLookupTopics
{
constexpr const char* REQUEST_TOPIC = "dds/builtin/TypeLookup/Request";
constexpr const char* REPLY_TOPIC = "dds/builtin/TypeLookup/Reply";
} // namespace TypeLookupTopics
namespace TypeLookupSerialization
{
std::vector<uint8_t> serialize_request(const TypeLookupRequest& request);
TypeLookupRequest deserialize_request(const std::vector<uint8_t>& data);
std::vector<uint8_t> serialize_reply(const TypeLookupReply& reply);
TypeLookupReply deserialize_reply(const std::vector<uint8_t>& data);
} // namespace TypeLookupSerialization
} // namespace astutedds::xtypes
#endif // ASTUTEDDS_XTYPES_TYPE_LOOKUP_SERVICE_HPP