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