Skip to content

Commit

Permalink
get topic/service types and descriptions (#990)
Browse files Browse the repository at this point in the history
new functions eCAL::Util::GetTopics and eCAL::Util::GetServices introduced for efficiently getting types and descriptions for massive amount of entity scenarios
  • Loading branch information
rex-schilasky authored Mar 1, 2023
1 parent 9a6374d commit 4cbd401
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 53 deletions.
30 changes: 30 additions & 0 deletions ecal/core/include/ecal/ecal_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#pragma once

#include <ecal/ecal_os.h>

#include <map>
#include <string>

namespace eCAL
Expand Down Expand Up @@ -135,6 +137,19 @@ namespace eCAL
**/
ECAL_API void PubShareDescription(bool state_);

struct STopicInfo
{
std::string type_name; //!< Type name of the current topic
std::string type_description; //!< Descriptor string of the current topic.
};
/**
* @brief Get complete topic map (including types and descriptions).
*
* @param topic_info_map_ Map to store the topic informations.
* Map containing { TopicName -> (Type, Description) } mapping of all topics that are currently known.
**/
ECAL_API void GetTopics(std::map<std::string, STopicInfo>& topic_info_map_);

/**
* @brief Gets type name of the specified topic.
*
Expand Down Expand Up @@ -173,6 +188,21 @@ namespace eCAL
**/
ECAL_API std::string GetTopicDescription(const std::string& topic_name_);

struct SServiceMethodInfo
{
std::string request_type_name; //!< Type name of the request message
std::string request_type_description; //!< Descriptor string of the request description
std::string response_type_name; //!< Type name of the response message
std::string response_type_description; //!< Descriptor string of the response message
};
/**
* @brief Get complete service map (including request and response types and descriptions).
*
* @param service_info_map_ Map to store the topic informations.
* Map { (ServiceName, MethodName) -> ( (ReqType, ReqDescription), (RespType, RespDescription) ) } mapping of all currently known services.
**/
ECAL_API void GetServices(std::map<std::tuple<std::string, std::string>, Util::SServiceMethodInfo>& service_info_map_);

/**
* @brief Gets service method request and response type names.
*
Expand Down
78 changes: 49 additions & 29 deletions ecal/core/src/ecal_descgate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ namespace eCAL
if(topic_info_it == m_topic_info_map.end())
{
// TODO: Maybe we should copy the description from another topic with the same type, if it is empty?
STopicInfo topic_info;
topic_info.type_name = topic_type_;
topic_info.type_description = topic_desc_;
topic_info.description_quality = description_quality_;
STopicInfoQuality topic_info;
topic_info.info.type_name = topic_type_;
topic_info.info.type_description = topic_desc_;
topic_info.description_quality = description_quality_;
m_topic_info_map.emplace(topic_name_, std::move(topic_info));
}
else
Expand All @@ -61,11 +61,11 @@ namespace eCAL
// we log the error only one time
if( !topic_info_it->second.type_missmatch_logged
&& !topic_type_.empty()
&& !topic_info_it->second.type_name.empty()
&& (topic_info_it->second.type_name != topic_type_)
&& !topic_info_it->second.info.type_name.empty()
&& (topic_info_it->second.info.type_name != topic_type_)
)
{
std::string ttype1 = topic_info_it->second.type_name;
std::string ttype1 = topic_info_it->second.info.type_name;
std::string ttype2 = topic_type_;
std::replace(ttype1.begin(), ttype1.end(), '\0', '?');
std::replace(ttype1.begin(), ttype1.end(), '\t', '?');
Expand All @@ -87,8 +87,8 @@ namespace eCAL
// we log the warning only one time
if ( !topic_info_it->second.type_missmatch_logged
&& !topic_desc_.empty()
&& !topic_info_it->second.type_description.empty()
&& (topic_info_it->second.type_description != topic_desc_)
&& !topic_info_it->second.info.type_description.empty()
&& (topic_info_it->second.info.type_description != topic_desc_)
)
{
std::string msg = "eCAL Pub/Sub description mismatch for topic ";
Expand All @@ -102,13 +102,23 @@ namespace eCAL
// If it has a higher quality, we overwrite it.
if (description_quality_ > topic_info_it->second.description_quality)
{
topic_info_it->second.type_name = topic_type_;
topic_info_it->second.type_description = topic_desc_;
topic_info_it->second.description_quality = description_quality_;
topic_info_it->second.info.type_name = topic_type_;
topic_info_it->second.info.type_description = topic_desc_;
topic_info_it->second.description_quality = description_quality_;
}
}
}

void CDescGate::GetTopics(std::map<std::string, Util::STopicInfo>& topic_info_map_)
{
topic_info_map_.clear();
std::shared_lock<std::shared_timed_mutex> lock(m_topic_info_map_mutex);
for (auto& topic_info : m_topic_info_map)
{
topic_info_map_[topic_info.first] = topic_info.second.info;
}
}

bool CDescGate::GetTopicTypeName(const std::string& topic_name_, std::string& topic_type_)
{
if(topic_name_.empty()) return(false);
Expand All @@ -117,7 +127,7 @@ namespace eCAL
const auto topic_info_it = m_topic_info_map.find(topic_name_);

if(topic_info_it == m_topic_info_map.end()) return(false);
topic_type_ = topic_info_it->second.type_name;
topic_type_ = topic_info_it->second.info.type_name;
return(true);
}

Expand All @@ -129,7 +139,7 @@ namespace eCAL
const auto topic_info_it = m_topic_info_map.find(topic_name_);

if(topic_info_it == m_topic_info_map.end()) return(false);
topic_desc_ = topic_info_it->second.type_description;
topic_desc_ = topic_info_it->second.info.type_description;
return(true);
}

Expand All @@ -147,12 +157,12 @@ namespace eCAL
auto service_info_map_it = m_service_info_map.find(service_method_tuple);
if (service_info_map_it == m_service_info_map.end())
{
SServiceMethodInfo service_info;
service_info.request_type_name = req_type_name_;
service_info.request_type_description = req_type_desc_;
service_info.response_type_name = resp_type_name_;
service_info.response_type_description = resp_type_desc_;
service_info.info_quality = info_quality_;
SServiceMethodInfoQuality service_info;
service_info.info.request_type_name = req_type_name_;
service_info.info.request_type_description = req_type_desc_;
service_info.info.response_type_name = resp_type_name_;
service_info.info.response_type_description = resp_type_desc_;
service_info.info_quality = info_quality_;

m_service_info_map[service_method_tuple] = std::move(service_info);
}
Expand All @@ -161,15 +171,25 @@ namespace eCAL
// do we need to check consistency ?
if (info_quality_ > service_info_map_it->second.info_quality)
{
service_info_map_it->second.request_type_name = req_type_name_;
service_info_map_it->second.request_type_description = req_type_desc_;
service_info_map_it->second.response_type_name = resp_type_name_;
service_info_map_it->second.response_type_description = resp_type_desc_;
service_info_map_it->second.info_quality = info_quality_;
service_info_map_it->second.info.request_type_name = req_type_name_;
service_info_map_it->second.info.request_type_description = req_type_desc_;
service_info_map_it->second.info.response_type_name = resp_type_name_;
service_info_map_it->second.info.response_type_description = resp_type_desc_;
service_info_map_it->second.info_quality = info_quality_;
}
}
}

void CDescGate::GetServices(std::map<std::tuple<std::string, std::string>, Util::SServiceMethodInfo>& service_info_map_)
{
service_info_map_.clear();
std::shared_lock<std::shared_timed_mutex> lock(m_service_info_map_mutex);
for (auto& service_info : m_service_info_map)
{
service_info_map_[service_info.first] = service_info.second.info;
}
}

bool CDescGate::GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_name_, std::string& resp_type_name_)
{
std::tuple<std::string, std::string> service_method_tuple = std::make_tuple(service_name_, method_name_);
Expand All @@ -179,8 +199,8 @@ namespace eCAL

if (service_info_map_it == m_service_info_map.end()) return false;

req_type_name_ = service_info_map_it->second.request_type_name;
resp_type_name_ = service_info_map_it->second.response_type_name;
req_type_name_ = service_info_map_it->second.info.request_type_name;
resp_type_name_ = service_info_map_it->second.info.response_type_name;

return true;
}
Expand All @@ -194,8 +214,8 @@ namespace eCAL

if (service_info_map_it == m_service_info_map.end()) return false;

req_type_desc_ = service_info_map_it->second.request_type_description;
resp_type_desc_ = service_info_map_it->second.response_type_description;
req_type_desc_ = service_info_map_it->second.info.request_type_description;
resp_type_desc_ = service_info_map_it->second.info.response_type_description;

return true;
}
Expand Down
41 changes: 19 additions & 22 deletions ecal/core/src/ecal_descgate.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

#pragma once

#include <ecal/ecal_util.h>

#include "ecal_global_accessors.h"
#include "ecal_def.h"

Expand All @@ -38,14 +40,14 @@ namespace eCAL
class CDescGate
{
public:
// Enumeration of qulity bits used for detecting how good a topic information is.
// Enumeration of quality bits used for detecting how good a topic information is.
enum class QualityFlags : int
{
NO_QUALITY = 0, //!< Special value for initialization

DESCRIPTION_AVAILABLE = 0x1 << 4, //!< Having a descriptor at all is the most important thing
INFO_COMES_FROM_CORRECT_TOPIC = 0x1 << 3, //!< The information comes from the current topic (and has not been borrowed from another topic)
INFO_COMES_FROM_PUBLISHER = 0x1 << 2, //!< A descriptor coming from the publisher is better than one from a subsriber, as we assume that the publisher knows best what he is publishing
INFO_COMES_FROM_PUBLISHER = 0x1 << 2, //!< A descriptor coming from the publisher is better than one from a subscriber, as we assume that the publisher knows best what he is publishing
INFO_COMES_FROM_THIS_PROCESS = 0x1 << 1, //!< We prefer descriptors from the current process
TYPE_AVAILABLE = 0x1 << 0, //!< Having information about the type's name available is nice but not that important to us.
};
Expand All @@ -62,6 +64,7 @@ namespace eCAL
const std::string& topic_desc_,
const QualityFlags description_quality_);

void GetTopics(std::map<std::string, Util::STopicInfo>& topic_info_map_);
bool GetTopicTypeName(const std::string& topic_name_, std::string& topic_type_);
bool GetTopicDescription(const std::string& topic_name_, std::string& topic_desc_);

Expand All @@ -73,40 +76,34 @@ namespace eCAL
const std::string& resp_type_desc_,
const QualityFlags info_quality_);

void GetServices(std::map<std::tuple<std::string, std::string>, Util::SServiceMethodInfo>& service_info_map_);
bool GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_name_, std::string& resp_type_name_);
bool GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_type_desc_, std::string& resp_type_desc_);


protected:
struct STopicInfo
struct STopicInfoQuality
{
std::string type_name; //!< Type name of the current topic
std::string type_description; //!< Descriptor String of the current topic. Used e.g. for dynamic deserialization

QualityFlags description_quality = QualityFlags::NO_QUALITY; //!< QualityFlags to determine whether we may overwrite the current data with better one. E.g. we prefer the description sent by a publisher over one sent by a subscriber.
bool type_missmatch_logged = false; //!< Whether we have already logged a type-missmatch
Util::STopicInfo info; //!< Topic info struct with type name and descriptor.
QualityFlags description_quality = QualityFlags::NO_QUALITY; //!< QualityFlags to determine whether we may overwrite the current data with better one. E.g. we prefer the description sent by a publisher over one sent by a subscriber.
bool type_missmatch_logged = false; //!< Whether we have already logged a type-missmatch
};

struct SServiceMethodInfo
struct SServiceMethodInfoQuality
{
std::string request_type_name; //!< Type name of the request message
std::string request_type_description; //!< Descriptor String of the request description
std::string response_type_name; //!< Type name of the response message
std::string response_type_description; //!< Descriptor String of the response message

QualityFlags info_quality = QualityFlags::NO_QUALITY; //!< The Quality of the Info
Util::SServiceMethodInfo info; //!< Service info struct with type names and descriptors for request and response.
QualityFlags info_quality = QualityFlags::NO_QUALITY; //!< The Quality of the Info
};

// key: topic name | value: topic(type/desc)
using TopicInfoMap = std::map<std::string, STopicInfo>; //!< Map containing { TopicName -> (Type, Description) } mapping of all topics that are currently known
mutable std::shared_timed_mutex m_topic_info_map_mutex; //!< Mutex protecting the m_topic_info_map
TopicInfoMap m_topic_info_map; //!< Map containing information about each known topic
using TopicInfoMap = std::map<std::string, STopicInfoQuality>; //!< Map containing { TopicName -> (Type, Description) } mapping of all topics that are currently known
mutable std::shared_timed_mutex m_topic_info_map_mutex; //!< Mutex protecting the m_topic_info_map
TopicInfoMap m_topic_info_map; //!< Map containing information about each known topic

// key: tup<service name, method name> | value: tup<request (type/desc), response (type/desc)>
using ServiceMethodInfoMap
= std::map<std::tuple<std::string, std::string>, SServiceMethodInfo>; //! Map { (ServiceName, MethodName) -> ( (ReqType, ReqDescription), (RespType, RespDescription) ) } mapping of all currently known services
mutable std::shared_timed_mutex m_service_info_map_mutex; //!< Mutex protecting the m_service_info_map
ServiceMethodInfoMap m_service_info_map; //!< Map containing information about each known service method
= std::map<std::tuple<std::string, std::string>, SServiceMethodInfoQuality>; //!< Map { (ServiceName, MethodName) -> ( (ReqType, ReqDescription), (RespType, RespDescription) ) } mapping of all currently known services
mutable std::shared_timed_mutex m_service_info_map_mutex; //!< Mutex protecting the m_service_info_map
ServiceMethodInfoMap m_service_info_map; //!< Map containing information about each known service method
};

constexpr inline CDescGate::QualityFlags operator~ (CDescGate::QualityFlags a) { return static_cast<CDescGate::QualityFlags>( ~static_cast<std::underlying_type<CDescGate::QualityFlags>::type>(a) ); }
Expand Down
26 changes: 25 additions & 1 deletion ecal/core/src/ecal_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ namespace eCAL
{
const eCAL::pb::Process& process = monitoring.processes(i);
std::string uname = process.uname();
if ((uname != "eCALMon")
if ((uname != "eCALMon")
&& (uname != "eCALRPCService")
&& (uname != "eCALParam")
&& (uname != "eCALPlay")
Expand Down Expand Up @@ -223,6 +223,18 @@ namespace eCAL
if (g_pubgate()) g_pubgate()->ShareDescription(state_);
}

/**
* @brief Get complete topic map (including types and descriptions).
*
* @param topic_info_map_ Map to store the topic informations.
* Map containing { TopicName -> (Type, Description) } mapping of all topics that are currently known.
**/
void GetTopics(std::map<std::string, STopicInfo>& topic_info_map_)
{
if (!g_descgate()) return;
g_descgate()->GetTopics(topic_info_map_);
}

/**
* @brief Gets type name of the specified topic.
*
Expand Down Expand Up @@ -309,6 +321,18 @@ namespace eCAL
return GetTopicDescription(topic_name_);
}

/**
* @brief Get complete service map (including request and response types and descriptions).
*
* @param service_info_map_ Map to store the topic informations.
* Map { (ServiceName, MethodName) -> ( (ReqType, ReqDescription), (RespType, RespDescription) ) } mapping of all currently known services.
**/
void GetServices(std::map<std::tuple<std::string, std::string>, Util::SServiceMethodInfo>& service_info_map_)
{
if (!g_descgate()) return;
g_descgate()->GetServices(service_info_map_);
}

/**
* @brief Gets service method request and response type names.
*
Expand Down
1 change: 1 addition & 0 deletions samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ add_subdirectory(cpp/misc/proto_dyn)
add_subdirectory(cpp/misc/proto_dyn_json)
add_subdirectory(cpp/misc/time)
add_subdirectory(cpp/misc/timer)
add_subdirectory(cpp/monitoring/monitoring_get_topics)
add_subdirectory(cpp/monitoring/monitoring_rec)
add_subdirectory(cpp/monitoring/monitoring_reg)
add_subdirectory(cpp/multiple/multiple_rec_cb)
Expand Down
41 changes: 41 additions & 0 deletions samples/cpp/monitoring/monitoring_get_topics/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# ========================= eCAL LICENSE =================================
#
# Copyright (C) 2016 - 2019 Continental Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ========================= eCAL LICENSE =================================

cmake_minimum_required(VERSION 3.10)

set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)

project(monitoring_get_topics)

find_package(eCAL REQUIRED)

set(monitoring_get_topics_src
src/monitoring_get_topics.cpp
)

ecal_add_sample(${PROJECT_NAME} ${monitoring_get_topics_src})

target_link_libraries(${PROJECT_NAME} eCAL::core)

target_link_libraries(${PROJECT_NAME} eCAL::core_pb)

target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14)

ecal_install_sample(${PROJECT_NAME})

set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/monitoring)
Loading

1 comment on commit 4cbd401

@FlorianReimold
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core: Added API Functions GetServices and GetTopics that return the according monitoring information without having to query the monitoring Layer.

  • There is no need to have the Monitoring Flag enabled
  • The data is directly taken from the descriptor gate

Please sign in to comment.