diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt index 3155494904..fa43f791df 100644 --- a/ecal/core/CMakeLists.txt +++ b/ecal/core/CMakeLists.txt @@ -389,6 +389,9 @@ if(ECAL_CORE_SERVICE) src/service/ecal_service_server.cpp src/service/ecal_service_server_impl.cpp src/service/ecal_service_server_impl.h + src/service/ecal_service_server_v5.cpp + src/service/ecal_service_server_v5_impl.cpp + src/service/ecal_service_server_v5_impl.h src/service/ecal_service_singleton_manager.cpp src/service/ecal_service_singleton_manager.h ) @@ -511,7 +514,6 @@ set(ecal_header_cmn include/ecal/config/monitoring.h include/ecal/config/publisher.h include/ecal/config/registration.h - include/ecal/config/service.h include/ecal/config/subscriber.h include/ecal/config/time.h include/ecal/config/transport_layer.h @@ -537,6 +539,7 @@ set(ecal_header_cmn include/ecal/ecal_registration.h include/ecal/ecal_publisher.h include/ecal/ecal_server.h + include/ecal/ecal_server_v5.h include/ecal/ecal_service_info.h include/ecal/ecal_subscriber.h include/ecal/ecal_time.h diff --git a/ecal/core/include/ecal/config/configuration.h b/ecal/core/include/ecal/config/configuration.h index a99f7832b3..ac2ac324cb 100644 --- a/ecal/core/include/ecal/config/configuration.h +++ b/ecal/core/include/ecal/config/configuration.h @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -54,7 +53,6 @@ namespace eCAL Subscriber::Configuration subscriber; Publisher::Configuration publisher; Time::Configuration timesync; - Service::Configuration service; Application::Configuration application; Logging::Configuration logging; Cli::Configuration command_line_arguments; diff --git a/ecal/core/include/ecal/config/service.h b/ecal/core/include/ecal/config/service.h deleted file mode 100644 index 3082a6c521..0000000000 --- a/ecal/core/include/ecal/config/service.h +++ /dev/null @@ -1,39 +0,0 @@ -/* =========================== LICENSE ================================= - * - * Copyright (C) 2016 - 2024 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. - * - * =========================== LICENSE ================================= - */ - -/** - * @file ecal_service_config.h - * @brief eCAL configuration for services -**/ - -#pragma once - -#include - -namespace eCAL -{ - namespace Service - { - struct Configuration - { - bool protocol_v0 { false }; //!< Support service protocol v0, eCAL 5.11 and older (Default: false) - bool protocol_v1 { true }; //!< Support service protocol v1, eCAL 5.12 and newer (Default: true) - }; - } -} \ No newline at end of file diff --git a/ecal/core/include/ecal/ecal_client.h b/ecal/core/include/ecal/ecal_client.h index dcbff48090..d72c95c4b7 100644 --- a/ecal/core/include/ecal/ecal_client.h +++ b/ecal/core/include/ecal/ecal_client.h @@ -26,9 +26,11 @@ #include #include + #include -#include #include +#include +#include #include #include @@ -62,13 +64,27 @@ namespace eCAL ECAL_API_EXPORTED_MEMBER virtual ~CServiceClient(); - // Deleted copy constructor and copy assignment operator + /** + * @brief CServiceClient are non-copyable + **/ CServiceClient(const CServiceClient&) = delete; + + /** + * @brief CServiceClient are non-copyable + **/ CServiceClient& operator=(const CServiceClient&) = delete; - // Move constructor and move assignment operator - ECAL_API_EXPORTED_MEMBER CServiceClient(CServiceClient&& rhs) noexcept; - ECAL_API_EXPORTED_MEMBER CServiceClient& operator=(CServiceClient&& rhs) noexcept; + /** + * @brief CServiceClient are move-enabled + **/ + ECAL_API_EXPORTED_MEMBER + CServiceClient(CServiceClient&& rhs) noexcept; + + /** + * @brief CServiceClient are move-enabled + **/ + ECAL_API_EXPORTED_MEMBER + CServiceClient& operator=(CServiceClient&& rhs) noexcept; /** * @brief Get the client instances for all matching services @@ -124,6 +140,17 @@ namespace eCAL ECAL_API_EXPORTED_MEMBER std::string GetServiceName() const; + /** + * @brief Retrieve the service id. + * + * @return The service id. + **/ + + // TODO: Implement this + + ECAL_API_EXPORTED_MEMBER + Registration::SServiceMethodId GetServiceId() const; + /** * @brief Check connection to at least one service. * @@ -136,4 +163,4 @@ namespace eCAL std::shared_ptr m_service_client_impl; }; } -} \ No newline at end of file +} diff --git a/ecal/core/include/ecal/ecal_client_v5.h b/ecal/core/include/ecal/ecal_client_v5.h index 4072efc72b..16be92c77c 100644 --- a/ecal/core/include/ecal/ecal_client_v5.h +++ b/ecal/core/include/ecal/ecal_client_v5.h @@ -214,7 +214,7 @@ namespace eCAL ECAL_API_EXPORTED_MEMBER bool IsConnected(); - protected: + private: std::shared_ptr m_service_client_impl; }; } diff --git a/ecal/core/include/ecal/ecal_config.h b/ecal/core/include/ecal/ecal_config.h index a0fd32b1a6..160cf878ac 100644 --- a/ecal/core/include/ecal/ecal_config.h +++ b/ecal/core/include/ecal/ecal_config.h @@ -33,7 +33,6 @@ namespace eCAL ECAL_API Configuration& GetConfiguration (); ECAL_API Subscriber::Configuration& GetSubscriberConfiguration (); ECAL_API Publisher::Configuration& GetPublisherConfiguration (); - ECAL_API Service::Configuration& GetServiceConfiguration (); namespace Config { @@ -103,18 +102,14 @@ namespace eCAL ///////////////////////////////////// // publisher ///////////////////////////////////// + ECAL_API bool IsTopicTypeSharingEnabled (); ECAL_API bool IsTopicDescriptionSharingEnabled (); - - ///////////////////////////////////// - // service - ///////////////////////////////////// - ECAL_API bool IsServiceProtocolV0Enabled (); - ECAL_API bool IsServiceProtocolV1Enabled (); ///////////////////////////////////// // subscriber ///////////////////////////////////// + ECAL_API bool GetDropOutOfOrderMessages (); ///////////////////////////////////// diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h index d73f671aae..a86a83cad7 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/ecal/core/include/ecal/ecal_server.h @@ -19,154 +19,125 @@ /** * @file ecal_server.h - * @brief eCAL service interface + * @brief eCAL server interface **/ #pragma once #include #include + #include #include +#include +#include #include #include -#include namespace eCAL { class CServiceServerImpl; - /** - * @brief Service Server wrapper class. - **/ - class ECAL_API_CLASS CServiceServer + inline namespace v6 { - public: - /** - * @brief Constructor. - **/ - ECAL_API_EXPORTED_MEMBER - CServiceServer(); - - /** - * @brief Constructor. - * - * @param service_name_ Unique service name. - **/ - ECAL_API_EXPORTED_MEMBER - explicit CServiceServer(const std::string& service_name_); - - /** - * @brief Destructor. - **/ - ECAL_API_EXPORTED_MEMBER - virtual ~CServiceServer(); - - /** - * @brief CServiceServers are non-copyable - **/ - CServiceServer(const CServiceServer&) = delete; - /** - * @brief CServiceServers are non-copyable + * @brief Service Server wrapper class. **/ - CServiceServer& operator=(const CServiceServer&) = delete; - - /** - * @brief Creates this object. - * - * @param service_name_ Unique service name. - * - * @return True if successful. - **/ - ECAL_API_EXPORTED_MEMBER - bool Create(const std::string& service_name_); - - /** - * @brief Destroys this object. - * - * @return True if successful. - **/ - ECAL_API_EXPORTED_MEMBER - bool Destroy(); - - /** - * @brief Add method type descriptions. - * - * @param method_ Service method name. - * @param req_type_ Service method request type. - * @param req_desc_ Service method request description. - * @param resp_type_ Service method response type. - * @param resp_desc_ Service method response description. - * - * @return True if successful. - **/ - ECAL_API_EXPORTED_MEMBER - bool AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_); - - /** - * @brief Add method callback. - * - * @param method_ Service method name. - * @param req_type_ Service method request type. - * @param resp_type_ Service method response type. - * @param callback_ Callback function for client request. - * - * @return True if successful. - **/ - ECAL_API_EXPORTED_MEMBER - bool AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_); - - /** - * @brief Remove method callback. - * - * @param method_ Service method name. - * - * @return True if successful. - **/ - ECAL_API_EXPORTED_MEMBER - bool RemMethodCallback(const std::string& method_); - - /** - * @brief Add server event callback function. - * - * @param type_ The event type to react on. - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - ECAL_API_EXPORTED_MEMBER - bool AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_); - - /** - * @brief Remove server event callback function. - * - * @param type_ The event type to remove. - * - * @return True if succeeded, false if not. - **/ - ECAL_API_EXPORTED_MEMBER - bool RemEventCallback(eCAL_Server_Event type_); - - /** - * @brief Retrieve service name. - * - * @return The service name. - **/ - ECAL_API_EXPORTED_MEMBER - std::string GetServiceName(); - - /** - * @brief Check connection state. - * - * @return True if connected, false if not. - **/ - ECAL_API_EXPORTED_MEMBER - bool IsConnected(); - - private: - std::shared_ptr m_service_server_impl; - bool m_created; - }; + class ECAL_API_CLASS CServiceServer + { + public: + /** + * @brief Constructor. + * + * @param service_name_ Unique service name. + * @param event_callback_ Callback function for server events. + **/ + ECAL_API_EXPORTED_MEMBER + explicit CServiceServer(const std::string& service_name_, const ServerEventIDCallbackT event_callback_ = ServerEventIDCallbackT()); + + /** + * @brief Destructor. + **/ + ECAL_API_EXPORTED_MEMBER + virtual ~CServiceServer(); + + /** + * @brief CServiceServer are non-copyable + **/ + CServiceServer(const CServiceServer&) = delete; + + /** + * @brief CServiceServer are non-copyable + **/ + CServiceServer& operator=(const CServiceServer&) = delete; + + /** + * @brief CServiceServer are move-enabled + **/ + ECAL_API_EXPORTED_MEMBER + CServiceServer(CServiceServer&& rhs) noexcept; + + /** + * @brief CServiceServer are move-enabled + **/ + ECAL_API_EXPORTED_MEMBER + CServiceServer& operator=(CServiceServer&& rhs) noexcept; + + /** + * @brief Add method callback. + * + * @param method_ Service method name. + * @param method_info_ Service method information (request & response types). + * @param callback_ Callback function for client request. + * + * @return True if successful. + **/ + + // TODO: Provide new MethodCallbackT type using SServiceMethodInformation instead "MethodCallbackT = std::function" + + ECAL_API_EXPORTED_MEMBER + bool AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_); + + /** + * @brief Remove method callback. + * + * @param method_ Service method name. + * + * @return True if successful. + **/ + ECAL_API_EXPORTED_MEMBER + bool RemoveMethodCallback(const std::string& method_); + + /** + * @brief Retrieve service name. + * + * @return The service name. + **/ + ECAL_API_EXPORTED_MEMBER + std::string GetServiceName(); + + /** + * @brief Retrieve the service id. + * + * @return The service id. + **/ + + // TODO: Implement this + + ECAL_API_EXPORTED_MEMBER + Registration::SServiceMethodId GetServiceId() const; + + /** + * @brief Check connection state. + * + * @return True if connected, false if not. + **/ + ECAL_API_EXPORTED_MEMBER + bool IsConnected(); + + private: + std::shared_ptr m_service_server_impl; + }; + } } diff --git a/ecal/core/include/ecal/ecal_server_v5.h b/ecal/core/include/ecal/ecal_server_v5.h new file mode 100644 index 0000000000..31020f83a7 --- /dev/null +++ b/ecal/core/include/ecal/ecal_server_v5.h @@ -0,0 +1,174 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 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 ================================= +*/ + +/** + * @file ecal_server_v5.h + * @brief eCAL server interface (deprecated eCAL5 version) +**/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace eCAL +{ + namespace v5 + { + class CServiceServerImpl; + + /** + * @brief Service Server wrapper class. + **/ + class ECAL_API_CLASS CServiceServer + { + public: + /** + * @brief Constructor. + **/ + ECAL_API_EXPORTED_MEMBER + CServiceServer(); + + /** + * @brief Constructor. + * + * @param service_name_ Unique service name. + **/ + ECAL_API_EXPORTED_MEMBER + explicit CServiceServer(const std::string& service_name_); + + /** + * @brief Destructor. + **/ + ECAL_API_EXPORTED_MEMBER + virtual ~CServiceServer(); + + /** + * @brief CServiceServers are non-copyable + **/ + CServiceServer(const CServiceServer&) = delete; + + /** + * @brief CServiceServers are non-copyable + **/ + CServiceServer& operator=(const CServiceServer&) = delete; + + /** + * @brief Creates this object. + * + * @param service_name_ Unique service name. + * + * @return True if successful. + **/ + ECAL_API_EXPORTED_MEMBER + bool Create(const std::string& service_name_); + + /** + * @brief Destroys this object. + * + * @return True if successful. + **/ + ECAL_API_EXPORTED_MEMBER + bool Destroy(); + + /** + * @brief Add method type descriptions. + * + * @param method_ Service method name. + * @param req_type_ Service method request type. + * @param req_desc_ Service method request description. + * @param resp_type_ Service method response type. + * @param resp_desc_ Service method response description. + * + * @return True if successful. + **/ + ECAL_API_EXPORTED_MEMBER + bool AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_); + + /** + * @brief Add method callback. + * + * @param method_ Service method name. + * @param req_type_ Service method request type. + * @param resp_type_ Service method response type. + * @param callback_ Callback function for client request. + * + * @return True if successful. + **/ + ECAL_API_EXPORTED_MEMBER + bool AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_); + + /** + * @brief Remove method callback. + * + * @param method_ Service method name. + * + * @return True if successful. + **/ + ECAL_API_EXPORTED_MEMBER + bool RemMethodCallback(const std::string& method_); + + /** + * @brief Add server event callback function. + * + * @param type_ The event type to react on. + * @param callback_ The callback function to add. + * + * @return True if succeeded, false if not. + **/ + ECAL_API_EXPORTED_MEMBER + bool AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_); + + /** + * @brief Remove server event callback function. + * + * @param type_ The event type to remove. + * + * @return True if succeeded, false if not. + **/ + ECAL_API_EXPORTED_MEMBER + bool RemEventCallback(eCAL_Server_Event type_); + + /** + * @brief Retrieve service name. + * + * @return The service name. + **/ + ECAL_API_EXPORTED_MEMBER + std::string GetServiceName(); + + /** + * @brief Check connection state. + * + * @return True if connected, false if not. + **/ + ECAL_API_EXPORTED_MEMBER + bool IsConnected(); + + private: + std::shared_ptr m_service_server_impl; + }; + } +} diff --git a/ecal/core/include/ecal/msg/protobuf/server.h b/ecal/core/include/ecal/msg/protobuf/server.h index 241bbe992c..ed411dc28b 100644 --- a/ecal/core/include/ecal/msg/protobuf/server.h +++ b/ecal/core/include/ecal/msg/protobuf/server.h @@ -24,7 +24,7 @@ #pragma once -#include +#include #include #include @@ -51,7 +51,7 @@ namespace eCAL * @brief Google Protobuf Server wrapper class. **/ template - class CServiceServer : public eCAL::CServiceServer + class CServiceServer : public eCAL::v5::CServiceServer { public: /** @@ -179,7 +179,7 @@ namespace eCAL return true; } - using eCAL::CServiceServer::Create; + using eCAL::v5::CServiceServer::Create; protected: int RequestCallback(const std::string& method_, const std::string& /*req_type_*/, const std::string& /*resp_type_*/, const std::string& request_, std::string& response_) @@ -217,9 +217,9 @@ namespace eCAL std::map m_methods; private: - using eCAL::CServiceServer::AddMethodCallback; - using eCAL::CServiceServer::RemMethodCallback; - using eCAL::CServiceServer::GetServiceName; + using eCAL::v5::CServiceServer::AddMethodCallback; + using eCAL::v5::CServiceServer::RemMethodCallback; + using eCAL::v5::CServiceServer::GetServiceName; }; } } diff --git a/ecal/core/src/cimpl/ecal_server_cimpl.cpp b/ecal/core/src/cimpl/ecal_server_cimpl.cpp index f3ed8ab0ee..82112e5ea0 100644 --- a/ecal/core/src/cimpl/ecal_server_cimpl.cpp +++ b/ecal/core/src/cimpl/ecal_server_cimpl.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ **/ #include +#include #include #include "ecal_common_cimpl.h" @@ -62,14 +63,14 @@ extern "C" ECALC_API ECAL_HANDLE eCAL_Server_Create(const char* service_name_) { if (service_name_ == nullptr) return(nullptr); - auto* server = new eCAL::CServiceServer(service_name_); + auto* server = new eCAL::v5::CServiceServer(service_name_); return(server); } ECALC_API int eCAL_Server_Destroy(ECAL_HANDLE handle_) { if (handle_ == nullptr) return(0); - auto* server = static_cast(handle_); + auto* server = static_cast(handle_); delete server; // NOLINT(*-owning-memory) return(1); } @@ -77,7 +78,7 @@ extern "C" ECALC_API int eCAL_Server_AddMethodCallback(ECAL_HANDLE handle_, const char* method_, const char* req_type_, const char* resp_type_, MethodCallbackCT callback_, void* par_) { if (handle_ == nullptr) return(0); - auto* server = static_cast(handle_); + auto* server = static_cast(handle_); auto callback = std::bind(g_method_callback, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, callback_, par_); return static_cast(server->AddMethodCallback(method_, req_type_, resp_type_, callback)); } @@ -85,14 +86,14 @@ extern "C" ECALC_API int eCAL_Server_RemMethodCallback(ECAL_HANDLE handle_, const char* method_) { if (handle_ == nullptr) return(0); - auto* server = static_cast(handle_); + auto* server = static_cast(handle_); return static_cast(server->RemMethodCallback(method_)); } ECALC_API int eCAL_Server_AddEventCallback(ECAL_HANDLE handle_, enum eCAL_Server_Event type_, ServerEventCallbackCT callback_, void* par_) { if (handle_ == nullptr) return(0); - auto* server = static_cast(handle_); + auto* server = static_cast(handle_); auto callback = std::bind(g_server_event_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); if (server->AddEventCallback(type_, callback)) return(1); return(0); @@ -101,7 +102,7 @@ extern "C" ECALC_API int eCAL_Server_RemEventCallback(ECAL_HANDLE handle_, enum eCAL_Server_Event type_) { if (handle_ == nullptr) return(0); - auto* server = static_cast(handle_); + auto* server = static_cast(handle_); if (server->RemEventCallback(type_)) return(1); return(0); } @@ -109,7 +110,7 @@ extern "C" ECALC_API int eCAL_Server_GetServiceName(ECAL_HANDLE handle_, void* buf_, int buf_len_) { if (handle_ == nullptr) return(0); - auto* server = static_cast(handle_); + auto* server = static_cast(handle_); const std::string service_name = server->GetServiceName(); const int buffer_len = CopyBuffer(buf_, buf_len_, service_name); if (buffer_len != static_cast(service_name.size())) diff --git a/ecal/core/src/config/configuration_reader.cpp b/ecal/core/src/config/configuration_reader.cpp index 1173c091ae..2fa730a061 100644 --- a/ecal/core/src/config/configuration_reader.cpp +++ b/ecal/core/src/config/configuration_reader.cpp @@ -10,7 +10,6 @@ namespace YAML::AssignValue(config_.registration, node_, "registration"); YAML::AssignValue(config_.monitoring, node_, "monitoring"); YAML::AssignValue(config_.timesync, node_, "time"); - YAML::AssignValue(config_.service, node_, "service"); YAML::AssignValue(config_.application, node_, "application"); YAML::AssignValue(config_.logging, node_, "logging"); } diff --git a/ecal/core/src/config/configuration_to_yaml.cpp b/ecal/core/src/config/configuration_to_yaml.cpp index 15eb57f61c..44252203f5 100644 --- a/ecal/core/src/config/configuration_to_yaml.cpp +++ b/ecal/core/src/config/configuration_to_yaml.cpp @@ -494,30 +494,6 @@ namespace YAML } - /* - ____ _ - / __/__ _____ __(_)______ - _\ \/ -_) __/ |/ / / __/ -_) - /___/\__/_/ |___/_/\__/\__/ - */ - - Node convert::encode(const eCAL::Service::Configuration& config_) - { - Node node; - node["protocol_v0"] = config_.protocol_v0; - node["protocol_v1"] = config_.protocol_v1; - - return node; - } - - bool convert::decode(const Node& node_, eCAL::Service::Configuration& config_) - { - AssignValue(config_.protocol_v0, node_, "protocol_v0"); - AssignValue(config_.protocol_v1, node_, "protocol_v1"); - return true; - } - - /* ___ ___ __ _ / _ | ___ ___ / (_)______ _/ /_(_)__ ___ @@ -703,7 +679,6 @@ namespace YAML node["registration"] = config_.registration; node["monitoring"] = config_.monitoring; node["time"] = config_.timesync; - node["service"] = config_.service; node["application"] = config_.application; node["logging"] = config_.logging; return node; @@ -717,7 +692,6 @@ namespace YAML AssignValue(config_.registration, node_, "registration"); AssignValue(config_.monitoring, node_, "monitoring"); AssignValue(config_.timesync, node_, "time"); - AssignValue(config_.service, node_, "service"); AssignValue(config_.application, node_, "application"); AssignValue(config_.logging, node_, "logging"); return true; diff --git a/ecal/core/src/config/configuration_to_yaml.h b/ecal/core/src/config/configuration_to_yaml.h index 2122b649c7..fa56381f59 100644 --- a/ecal/core/src/config/configuration_to_yaml.h +++ b/ecal/core/src/config/configuration_to_yaml.h @@ -243,21 +243,6 @@ namespace YAML }; - /* - ____ _ - / __/__ _____ __(_)______ - _\ \/ -_) __/ |/ / / __/ -_) - /___/\__/_/ |___/_/\__/\__/ - */ - template<> - struct convert - { - static Node encode(const eCAL::Service::Configuration& config_); - - static bool decode(const Node& node_, eCAL::Service::Configuration& config_); - }; - - /* ___ ___ __ _ / _ | ___ ___ / (_)______ _/ /_(_)__ ___ diff --git a/ecal/core/src/config/default_configuration.cpp b/ecal/core/src/config/default_configuration.cpp index 070b9d848c..fc30fcd04d 100644 --- a/ecal/core/src/config/default_configuration.cpp +++ b/ecal/core/src/config/default_configuration.cpp @@ -289,14 +289,6 @@ namespace eCAL ss << R"( replay: )" << quoteString(config_.timesync.timesync_module_replay) << "\n"; ss << R"()" << "\n"; ss << R"()" << "\n"; - ss << R"(# Service configuration)" << "\n"; - ss << R"(service:)" << "\n"; - ss << R"( # Support service protocol v0, eCAL 5.11 and older)" << "\n"; - ss << R"( protocol_v0: )" << config_.service.protocol_v0 << "\n"; - ss << R"( # Support service protocol v1, eCAL 5.12 and newer)" << "\n"; - ss << R"( protocol_v1: )" << config_.service.protocol_v1 << "\n"; - ss << R"()" << "\n"; - ss << R"()" << "\n"; ss << R"(# eCAL Application Configuration)" << "\n"; ss << R"(application:)" << "\n"; ss << R"( # Configuration for eCAL Sys)" << "\n"; diff --git a/ecal/core/src/config/ecal_config.cpp b/ecal/core/src/config/ecal_config.cpp index 4273107289..4c944d191c 100644 --- a/ecal/core/src/config/ecal_config.cpp +++ b/ecal/core/src/config/ecal_config.cpp @@ -97,13 +97,6 @@ namespace eCAL bool IsTopicTypeSharingEnabled () { return GetConfiguration().publisher.share_topic_type; } bool IsTopicDescriptionSharingEnabled () { return GetConfiguration().publisher.share_topic_description; } - ///////////////////////////////////// - // service - ///////////////////////////////////// - - bool IsServiceProtocolV0Enabled () { return GetConfiguration().service.protocol_v0; } - bool IsServiceProtocolV1Enabled () { return GetConfiguration().service.protocol_v1; } - ///////////////////////////////////// // subscriber ///////////////////////////////////// diff --git a/ecal/core/src/config/ecal_config_initializer.cpp b/ecal/core/src/config/ecal_config_initializer.cpp index 52fe02151a..28ccea1f68 100644 --- a/ecal/core/src/config/ecal_config_initializer.cpp +++ b/ecal/core/src/config/ecal_config_initializer.cpp @@ -281,11 +281,6 @@ namespace eCAL return GetConfiguration().timesync; } - Service::Configuration& GetServiceConfiguration() - { - return GetConfiguration().service; - } - Application::Configuration& GetApplicationConfiguration() { return GetConfiguration().application; diff --git a/ecal/core/src/readwrite/ecal_reader.cpp b/ecal/core/src/readwrite/ecal_reader.cpp index e947fc3704..60661eab65 100644 --- a/ecal/core/src/readwrite/ecal_reader.cpp +++ b/ecal/core/src/readwrite/ecal_reader.cpp @@ -534,7 +534,7 @@ namespace eCAL pub_info.process_id = topic_info_.pid; // execute it - std::lock_guard lock(m_connection_map_mtx); + const std::lock_guard exec_lock(m_connection_map_mtx); (m_receive_callback)(topic_id, m_connection_map[pub_info].data_type_info, cb_data); processed = true; } diff --git a/ecal/core/src/service/ecal_clientgate.cpp b/ecal/core/src/service/ecal_clientgate.cpp index 0ec03bcbfe..c132985157 100644 --- a/ecal/core/src/service/ecal_clientgate.cpp +++ b/ecal/core/src/service/ecal_clientgate.cpp @@ -54,7 +54,7 @@ namespace eCAL if (!m_created) return; // destroy all remaining clients - const std::unique_lock lock(m_service_client_map_sync); + const std::unique_lock lock(m_service_client_map_mutex); m_service_client_map.clear(); m_created = false; @@ -65,7 +65,7 @@ namespace eCAL if (!m_created) return(false); // register internal client - const std::unique_lock lock(m_service_client_map_sync); + const std::unique_lock lock(m_service_client_map_mutex); m_service_client_map.emplace(std::pair>(service_name_, client_)); return(true); @@ -76,7 +76,7 @@ namespace eCAL if (!m_created) return(false); bool ret_state = false; - const std::unique_lock lock(m_service_client_map_sync); + const std::unique_lock lock(m_service_client_map_mutex); auto res = m_service_client_map.equal_range(service_name_); for (auto iter = res.first; iter != res.second; ++iter) { @@ -110,7 +110,7 @@ namespace eCAL // inform matching clients { - const std::shared_lock lock(m_service_client_map_sync); + const std::shared_lock lock(m_service_client_map_mutex); auto res = m_service_client_map.equal_range(service.sname); for (ServiceNameClientIDImplMapT::const_iterator iter = res.first; iter != res.second; ++iter) { @@ -129,7 +129,7 @@ namespace eCAL // read client registrations { - const std::shared_lock lock(m_service_client_map_sync); + const std::shared_lock lock(m_service_client_map_mutex); for (const auto& iter : m_service_client_map) { reg_sample_list_.push_back(iter.second->GetRegistration()); diff --git a/ecal/core/src/service/ecal_clientgate.h b/ecal/core/src/service/ecal_clientgate.h index fdc6b84f5a..b4568b4842 100644 --- a/ecal/core/src/service/ecal_clientgate.h +++ b/ecal/core/src/service/ecal_clientgate.h @@ -25,23 +25,17 @@ #include "ecal_def.h" #include "serialization/ecal_struct_sample_registration.h" -#include "util/ecal_expmap.h" #include #include #include -#include +#include #include #include -#include namespace eCAL { - // deprecated >>> - class CServiceClientImpl; - // deprecated <<< - class CServiceClientImpl; class CClientGate @@ -64,7 +58,7 @@ namespace eCAL static std::atomic m_created; using ServiceNameClientIDImplMapT = std::multimap>; - std::shared_timed_mutex m_service_client_map_sync; + std::shared_timed_mutex m_service_client_map_mutex; ServiceNameClientIDImplMapT m_service_client_map; }; } diff --git a/ecal/core/src/service/ecal_service_client.cpp b/ecal/core/src/service/ecal_service_client.cpp index a0d6e1e423..c29aba46f2 100644 --- a/ecal/core/src/service/ecal_service_client.cpp +++ b/ecal/core/src/service/ecal_service_client.cpp @@ -33,48 +33,32 @@ namespace eCAL { CServiceClient::CServiceClient(const std::string& service_name_, const ServiceMethodInformationMapT method_information_map_, const ClientEventIDCallbackT event_callback_) { - // Create client implementation + // create client implementation m_service_client_impl = CServiceClientImpl::CreateInstance(service_name_, method_information_map_, event_callback_); - // Register client - if (g_clientgate() != nullptr) - { - g_clientgate()->Register(service_name_, m_service_client_impl); - } + // register client + if (g_clientgate() != nullptr) g_clientgate()->Register(service_name_, m_service_client_impl); } CServiceClient::~CServiceClient() { - // Unregister client - if (g_clientgate() != nullptr) - { - g_clientgate()->Unregister(m_service_client_impl->GetServiceName(), m_service_client_impl); - } + // could be already destroyed by move + if (m_service_client_impl == nullptr) return; - // Reset client implementation - m_service_client_impl.reset(); + // unregister client + if (g_clientgate() != nullptr) g_clientgate()->Unregister(m_service_client_impl->GetServiceName(), m_service_client_impl); } CServiceClient::CServiceClient(CServiceClient&& rhs) noexcept : m_service_client_impl(std::move(rhs.m_service_client_impl)) { - rhs.m_service_client_impl = nullptr; } CServiceClient& CServiceClient::operator=(CServiceClient&& rhs) noexcept { if (this != &rhs) { - // Unregister current client - if (g_clientgate()) - { - g_clientgate()->Unregister(m_service_client_impl->GetServiceName(), m_service_client_impl); - } - - // Move data m_service_client_impl = std::move(rhs.m_service_client_impl); - - rhs.m_service_client_impl = nullptr; } return *this; } @@ -82,6 +66,7 @@ namespace eCAL std::vector CServiceClient::GetClientInstances() const { std::vector instances; + if (m_service_client_impl == nullptr) return instances; auto entity_ids = m_service_client_impl->GetServiceIDs(); instances.reserve(entity_ids.size()); @@ -97,11 +82,11 @@ namespace eCAL auto instances = GetClientInstances(); size_t num_instances = instances.size(); - // Vector to hold futures for the return values and responses + // vector to hold futures for the return values and responses std::vector>> futures; futures.reserve(num_instances); - // Launch asynchronous calls for each instance + // launch asynchronous calls for each instance for (auto& instance : instances) { futures.emplace_back(std::async(std::launch::async, @@ -112,32 +97,35 @@ namespace eCAL } bool overall_success = true; - service_response_vec_.clear(); // Ensure the response vector is empty before populating it + // ensure the response vector is empty before populating it + service_response_vec_.clear(); - // Collect responses + // collect responses for (auto& future : futures) { try { - // Explicitly unpack the pair + // explicitly unpack the pair std::pair result = future.get(); bool success = result.first; SServiceResponse response = result.second; - // Add response to the vector + // add response to the vector service_response_vec_.emplace_back(response); - // Aggregate success states + // aggregate success states overall_success &= success; } catch (const std::exception& e) { - // Handle exceptions and add an error response + // handle exceptions and add an error response SServiceResponse error_response; error_response.error_msg = e.what(); error_response.call_state = call_state_failed; service_response_vec_.emplace_back(error_response); - overall_success = false; // Mark overall success as false if any call fails + + // mark overall success as false if any call fails + overall_success = false; } } @@ -149,7 +137,7 @@ namespace eCAL auto instances = GetClientInstances(); size_t num_instances = instances.size(); - // Vector to hold futures for the return values + // vector to hold futures for the return values std::vector> futures; futures.reserve(num_instances); @@ -171,7 +159,7 @@ namespace eCAL } catch (const std::exception& /*e*/) { - // Handle exceptions + // handle exceptions return_state = false; } } @@ -192,9 +180,16 @@ namespace eCAL std::string CServiceClient::GetServiceName() const { + if (m_service_client_impl == nullptr) return ""; return m_service_client_impl->GetServiceName(); } + Registration::SServiceMethodId CServiceClient::GetServiceId() const + { + // TODO: Implement this + return Registration::SServiceMethodId(); + } + bool CServiceClient::IsConnected() const { const auto instances = GetClientInstances(); diff --git a/ecal/core/src/service/ecal_service_client_impl.cpp b/ecal/core/src/service/ecal_service_client_impl.cpp index 5f7f6e9416..961a495e43 100644 --- a/ecal/core/src/service/ecal_service_client_impl.cpp +++ b/ecal/core/src/service/ecal_service_client_impl.cpp @@ -103,6 +103,7 @@ namespace void ResponseError(const eCAL::Registration::SEntityId& entity_id_, const std::string& service_name_, const std::string& method_name_, const std::string& error_message_, const eCAL::ResponseIDCallbackT& response_callback_) { + eCAL::Logging::Log(log_level_error, "CServiceClientImpl: Response error for service: " + service_name_ + ", method: " + method_name_ + ", error: " + error_message_); response_callback_(entity_id_, CreateErrorResponse(entity_id_, service_name_, method_name_, error_message_)); } } @@ -113,6 +114,9 @@ namespace eCAL std::shared_ptr CServiceClientImpl::CreateInstance( const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_, const ClientEventIDCallbackT& event_callback_) { +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug2, "CServiceClientImpl::CreateInstance: Creating instance of CServiceClientImpl for service: " + service_name_); +#endif return std::shared_ptr(new CServiceClientImpl(service_name_, method_information_map_, event_callback_)); } @@ -121,6 +125,10 @@ namespace eCAL const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_, const ClientEventIDCallbackT& event_callback_) : m_service_name(service_name_), m_method_information_map(method_information_map_) { +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug2, "CServiceClientImpl::CServiceClientImpl: Initializing service client for: " + service_name_); +#endif + // initialize method call counts for (const auto& method_information_pair : m_method_information_map) { @@ -134,36 +142,46 @@ namespace eCAL // add event callback { - const std::lock_guard lock(m_event_callback_sync); + const std::lock_guard lock(m_event_callback_mutex); m_event_callback = event_callback_; } - // register client + // Send registration sample if (!m_service_name.empty() && g_registration_provider() != nullptr) { g_registration_provider()->RegisterSample(GetRegistrationSample()); +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug2, "CServiceClientImpl::CServiceClientImpl: Registered client with service name: " + m_service_name); +#endif } } // Destructor: Resets callbacks, unregisters the client, and clears data CServiceClientImpl::~CServiceClientImpl() { +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug2, "CServiceClientImpl::~CServiceClientImpl: Destroying service client for: " + m_service_name); +#endif + // reset client map { - const std::lock_guard lock(m_client_session_map_sync); + const std::lock_guard lock(m_client_session_map_mutex); m_client_session_map.clear(); } // reset event callback { - const std::lock_guard lock(m_event_callback_sync); + const std::lock_guard lock(m_event_callback_mutex); m_event_callback = nullptr; } - // unregister client + // Send unregistration sample if (g_registration_provider() != nullptr) { g_registration_provider()->UnregisterSample(GetUnregistrationSample()); +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug2, "CServiceClientImpl::~CServiceClientImpl: Unregistered client for service name: " + m_service_name); +#endif } } @@ -173,7 +191,7 @@ namespace eCAL std::vector entity_vector; // lock client map - const std::lock_guard lock(m_client_session_map_sync); + const std::lock_guard lock(m_client_session_map_mutex); // copy session entities into return vector for (const auto& client_session : m_client_session_map) { @@ -188,9 +206,16 @@ namespace eCAL const Registration::SEntityId& entity_id_, const std::string& method_name_, const std::string& request_, int timeout_ms_, const ResponseIDCallbackT& response_callback_) { +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug1, "CServiceClientImpl::CallWithCallback: Performing synchronous call for service: " + m_service_name + ", method: " + method_name_); +#endif + SClient client; if (!GetClientByEntity(entity_id_, client)) + { + eCAL::Logging::Log(log_level_warning, "CServiceClientImpl::CallWithCallback: Failed to find client for entity ID: " + entity_id_.entity_id); return { false, SServiceResponse() }; + } auto response = CallMethodWithTimeout(entity_id_, client, method_name_, request_, std::chrono::milliseconds(timeout_ms_)); @@ -204,6 +229,9 @@ namespace eCAL if (!response.first && response.second.call_state == eCallState::call_state_timeouted) { NotifyEventCallback(entity_id_, eCAL_Client_Event::client_event_timeout, client.service_attr); +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug1, "CServiceClientImpl::CallWithCallback: Synchronous call for service: " + m_service_name + ", method: " + method_name_ + " timed out."); +#endif } return response; @@ -212,10 +240,17 @@ namespace eCAL // Asynchronous call to a service with a specified timeout bool CServiceClientImpl::CallWithCallbackAsync(const Registration::SEntityId& entity_id_, const std::string& method_name_, const std::string& request_, const ResponseIDCallbackT& response_callback_) { +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug2, "CServiceClientImpl::CallWithCallbackAsync: Performing asynchronous call for service: " + m_service_name + ", method: " + method_name_); +#endif + // Retrieve the client SClient client; if (!GetClientByEntity(entity_id_, client)) + { + eCAL::Logging::Log(log_level_warning, "CServiceClientImpl::CallWithCallbackAsync: Failed to find client for entity ID: " + entity_id_.entity_id); return false; + } // Validate service and method names if (m_service_name.empty() || method_name_.empty()) @@ -227,7 +262,10 @@ namespace eCAL // Serialize the request auto request_shared_ptr = SerializeRequest(method_name_, request_); if (!request_shared_ptr) + { + eCAL::Logging::Log(log_level_error, "CServiceClientImpl::CallWithCallbackAsync: Request serialization failed."); return false; + } // Prepare response data auto response_data = PrepareInitialResponse(client, method_name_); @@ -240,6 +278,7 @@ namespace eCAL { if (error) { + eCAL::Logging::Log(log_level_error, "CServiceClientImpl::CallWithCallbackAsync: Asynchronous call returned an error: " + error.ToString()); response_data->response->first = false; response_data->response->second.error_msg = error.ToString(); response_data->response->second.call_state = eCallState::call_state_failed; @@ -247,6 +286,9 @@ namespace eCAL } else { +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug1, "CServiceClientImpl::CallWithCallbackAsync: Asynchronous call succeded"); +#endif response_data->response->first = true; response_data->response->second = DeserializedResponse(*response_); } @@ -272,14 +314,18 @@ namespace eCAL // Check if a specific service is connected bool CServiceClientImpl::IsConnected(const Registration::SEntityId& entity_id_) { - const std::lock_guard lock(m_client_session_map_sync); + const std::lock_guard lock(m_client_session_map_mutex); auto iter = m_client_session_map.find(entity_id_); - return (iter != m_client_session_map.end() && iter->second.connected); + bool state((iter != m_client_session_map.end() && iter->second.connected)); +#ifndef NDEBUG + eCAL::Logging::Log(log_level_debug2, "CServiceClientImpl::IsConnected: Returned: " + std::to_string(state)); +#endif + return state; } void CServiceClientImpl::RegisterService(const Registration::SEntityId& entity_id_, const SServiceAttr& service_) { - const std::lock_guard lock(m_client_session_map_sync); + const std::lock_guard lock(m_client_session_map_mutex); if (m_client_session_map.find(entity_id_) == m_client_session_map.end()) { @@ -315,6 +361,9 @@ namespace eCAL // Retrieves registration information for the client Registration::Sample CServiceClientImpl::GetRegistration() { +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceClientImpl:::GetRegistration: Generating registration sample for: " + m_service_name); +#endif UpdateConnectionStates(); return GetRegistrationSample(); } @@ -340,7 +389,7 @@ namespace eCAL service_client.uname = Process::GetUnitName(); service_client.sname = m_service_name; - const std::lock_guard lock(m_method_information_map_sync); + const std::lock_guard lock(m_method_information_map_mutex); for (const auto& method_information_pair : m_method_information_map) { const auto& method_name = method_information_pair.first; @@ -386,7 +435,7 @@ namespace eCAL // Attempts to retrieve a client session for a given entity ID bool CServiceClientImpl::GetClientByEntity(const Registration::SEntityId& entity_id_, SClient& client_) { - const std::lock_guard lock(m_client_session_map_sync); + const std::lock_guard lock(m_client_session_map_mutex); auto iter = m_client_session_map.find(entity_id_); if (iter == m_client_session_map.end()) return false; @@ -442,7 +491,7 @@ namespace eCAL // Updates the connection states for the client sessions void CServiceClientImpl::UpdateConnectionStates() { - const std::lock_guard lock(m_client_session_map_sync); + const std::lock_guard lock(m_client_session_map_mutex); for (auto it = m_client_session_map.begin(); it != m_client_session_map.end(); ) { @@ -475,7 +524,7 @@ namespace eCAL void CServiceClientImpl::IncrementMethodCallCount(const std::string& method_name_) { - const std::lock_guard lock(m_method_information_map_sync); + const std::lock_guard lock(m_method_information_map_mutex); m_method_call_count_map[method_name_]++; } @@ -483,7 +532,7 @@ namespace eCAL // Helper function to notify event callback void CServiceClientImpl::NotifyEventCallback(const Registration::SEntityId& entity_id_, eCAL_Client_Event event_type_, const SServiceAttr& service_attr_) { - const std::lock_guard lock(m_event_callback_sync); + const std::lock_guard lock(m_event_callback_mutex); if (m_event_callback == nullptr) return; SClientEventCallbackData callback_data; diff --git a/ecal/core/src/service/ecal_service_client_impl.h b/ecal/core/src/service/ecal_service_client_impl.h index 04d13e4181..f0bffdc0ce 100644 --- a/ecal/core/src/service/ecal_service_client_impl.h +++ b/ecal/core/src/service/ecal_service_client_impl.h @@ -146,11 +146,11 @@ namespace eCAL // Client session map and synchronization using ClientSessionsMapT = std::map; - std::mutex m_client_session_map_sync; + std::mutex m_client_session_map_mutex; ClientSessionsMapT m_client_session_map; // Method information map (tracks method attributes like data type and description) - std::mutex m_method_information_map_sync; + std::mutex m_method_information_map_mutex; ServiceMethodInformationMapT m_method_information_map; // Method call count map (tracks number of calls for each method) @@ -158,7 +158,7 @@ namespace eCAL MethodCallCountMapT m_method_call_count_map; // Event callback map and synchronization - std::mutex m_event_callback_sync; + std::mutex m_event_callback_mutex; ClientEventIDCallbackT m_event_callback; }; } diff --git a/ecal/core/src/service/ecal_service_client_v5.cpp b/ecal/core/src/service/ecal_service_client_v5.cpp index e4fc0745ce..dd8021bedb 100644 --- a/ecal/core/src/service/ecal_service_client_v5.cpp +++ b/ecal/core/src/service/ecal_service_client_v5.cpp @@ -18,7 +18,7 @@ */ /** - * @brief eCAL service client interface + * @brief eCAL service client interface (deprecated eCAL5 version) **/ #include diff --git a/ecal/core/src/service/ecal_service_client_v5_impl.cpp b/ecal/core/src/service/ecal_service_client_v5_impl.cpp index 787079137e..e4e4b05b01 100644 --- a/ecal/core/src/service/ecal_service_client_v5_impl.cpp +++ b/ecal/core/src/service/ecal_service_client_v5_impl.cpp @@ -5,9 +5,9 @@ * 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. @@ -18,10 +18,11 @@ */ /** - * @brief eCAL service client interface + * @brief eCAL service client implementation (deprecated eCAL5 version) **/ #include "ecal_service_client_v5_impl.h" +#include namespace eCAL { @@ -29,26 +30,27 @@ namespace eCAL { CServiceClientImpl::CServiceClientImpl() : m_service_client_impl(nullptr) - , m_created(false) { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Initializing default service client implementation."); } CServiceClientImpl::CServiceClientImpl(const std::string& service_name_) : m_service_client_impl(nullptr) - , m_created(false) { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Initializing service client with name: " + service_name_); Create(service_name_); } CServiceClientImpl::CServiceClientImpl(const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_) : m_service_client_impl(nullptr) - , m_created(false) { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Initializing service client with name: " + service_name_); Create(service_name_, method_information_map_); } CServiceClientImpl::~CServiceClientImpl() { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Destroying service client implementation."); Destroy(); } @@ -59,11 +61,19 @@ namespace eCAL bool CServiceClientImpl::Create(const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_) { - if (m_created) return false; + if (m_service_client_impl != nullptr) + { + Logging::Log(log_level_warning, "v5::CServiceClientImpl: Service client already created: " + service_name_); + return false; + } + + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Creating service client with name: " + service_name_); - // Define the event callback to pass to CServiceClientNew + // Define the event callback to pass to CServiceClient ClientEventIDCallbackT event_callback = [this](const Registration::SServiceMethodId& service_id_, const struct SClientEventCallbackData& data_) { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Event callback triggered for event type: " + std::to_string(data_.type)); + // Lock the mutex to safely access m_event_callbacks std::lock_guard lock(m_event_callback_map_mutex); @@ -71,6 +81,7 @@ namespace eCAL const auto& callback = m_event_callback_map.find(data_.type); if (callback != m_event_callback_map.end()) { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Executing event callback for event type: " + std::to_string(data_.type)); // Call the user's callback callback->second(service_id_.service_name.c_str(), &data_); } @@ -83,29 +94,41 @@ namespace eCAL event_callback ); - m_created = (m_service_client_impl != nullptr); - return m_created; + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Service client created successfully with name: " + service_name_); + return true; } bool CServiceClientImpl::Destroy() { - if (!m_created) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_warning, "v5::CServiceClientImpl: Service client not initialized, cannot destroy."); + return false; + } + + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Destroying service client implementation."); // Reset the service client implementation m_service_client_impl.reset(); - m_created = false; // Clear stored callbacks m_response_callback = nullptr; - //m_event_callback = nullptr; + m_event_callback_map.clear(); m_host_name.clear(); + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Service client destroyed successfully."); return true; } bool CServiceClientImpl::SetHostName(const std::string& host_name_) { - if (!m_created) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot set host name."); + return false; + } + + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Setting host name to: " + host_name_); // Store the host name filter m_host_name = host_name_; @@ -115,13 +138,25 @@ namespace eCAL bool CServiceClientImpl::Call(const std::string& method_name_, const std::string& request_, int timeout_) { - if (!m_created || !m_response_callback) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot make a call."); + return false; + } + if (!m_response_callback) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Response callback not set, cannot make a call."); + return false; + } + + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Making a synchronous call to method: " + method_name_); // Wrap the response callback to filter by host name if necessary ResponseIDCallbackT wrapped_callback = [this](const Registration::SEntityId& /*entity_id_*/, const struct SServiceResponse& service_response_) { if (m_host_name.empty() || service_response_.host_name == m_host_name) { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Response received for method call."); // Call the stored response callback m_response_callback(service_response_); } @@ -133,7 +168,18 @@ namespace eCAL bool CServiceClientImpl::Call(const std::string& method_name_, const std::string& request_, int timeout_, ServiceResponseVecT* service_response_vec_) { - if (!m_created || !service_response_vec_) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot make a call."); + return false; + } + if (!service_response_vec_) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Response vector is null, cannot make a call."); + return false; + } + + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Making a synchronous call with response collection to method: " + method_name_); // Call the method using the new API ServiceResponseVecT all_responses; @@ -142,6 +188,7 @@ namespace eCAL // Filter responses based on host name if necessary if (!m_host_name.empty()) { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Filtering responses based on host name: " + m_host_name); for (const auto& response : all_responses) { if (response.host_name == m_host_name) @@ -155,30 +202,51 @@ namespace eCAL *service_response_vec_ = std::move(all_responses); } + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Call completed with success: " + std::to_string(success)); return success; } bool CServiceClientImpl::CallAsync(const std::string& method_name_, const std::string& request_, int /*timeout_*/) { - if (!m_created || !m_response_callback) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot make an async call."); + return false; + } + if (!m_response_callback) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Response callback not set, cannot make an async call."); + return false; + } + + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Making an asynchronous call to method: " + method_name_); // Wrap the response callback to filter by host name if necessary ResponseIDCallbackT wrapped_callback = [this](const Registration::SEntityId& /*entity_id_*/, const struct SServiceResponse& service_response_) { if (m_host_name.empty() || service_response_.host_name == m_host_name) { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Response received for async method call."); // Call the stored response callback m_response_callback(service_response_); } }; // Call the method asynchronously using the new API - return m_service_client_impl->CallWithCallbackAsync(method_name_, request_, wrapped_callback); + bool success = m_service_client_impl->CallWithCallbackAsync(method_name_, request_, wrapped_callback); + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Async call to method: " + method_name_ + " completed with success: " + std::to_string(success)); + return success; } bool CServiceClientImpl::AddResponseCallback(const ResponseCallbackT& callback_) { - if (!m_created) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot add response callback."); + return false; + } + + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Adding response callback."); { const std::lock_guard lock(m_response_callback_mutex); @@ -190,7 +258,13 @@ namespace eCAL bool CServiceClientImpl::RemResponseCallback() { - if (!m_created) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot remove response callback."); + return false; + } + + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Removing response callback."); { const std::lock_guard lock(m_response_callback_mutex); @@ -202,7 +276,13 @@ namespace eCAL bool CServiceClientImpl::AddEventCallback(eCAL_Client_Event type_, ClientEventCallbackT callback_) { - if (!m_created) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot add event callback."); + return false; + } + + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Adding event callback for event type: " + std::to_string(type_)); { const std::lock_guard lock(m_event_callback_map_mutex); @@ -214,7 +294,13 @@ namespace eCAL bool CServiceClientImpl::RemEventCallback(eCAL_Client_Event type_) { - if (!m_created) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot remove event callback."); + return false; + } + + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Removing event callback for event type: " + std::to_string(type_)); { const std::lock_guard lock(m_event_callback_map_mutex); @@ -226,16 +312,28 @@ namespace eCAL std::string CServiceClientImpl::GetServiceName() { - if (!m_created) return ""; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot get service name."); + return ""; + } - return m_service_client_impl->GetServiceName(); + std::string service_name = m_service_client_impl->GetServiceName(); + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Retrieved service name: " + service_name); + return service_name; } bool CServiceClientImpl::IsConnected() { - if (!m_created) return false; + if (m_service_client_impl == nullptr) + { + Logging::Log(log_level_error, "v5::CServiceClientImpl: Service client not initialized, cannot check connection status."); + return false; + } - return m_service_client_impl->IsConnected(); + bool connected = m_service_client_impl->IsConnected(); + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Connection status: " + std::string(connected ? "connected" : "disconnected")); + return connected; } } } diff --git a/ecal/core/src/service/ecal_service_client_v5_impl.h b/ecal/core/src/service/ecal_service_client_v5_impl.h index 3827a95289..1ba2939bf4 100644 --- a/ecal/core/src/service/ecal_service_client_v5_impl.h +++ b/ecal/core/src/service/ecal_service_client_v5_impl.h @@ -103,9 +103,6 @@ namespace eCAL // Pointer to the underlying service client implementation std::shared_ptr m_service_client_impl; - // Indicates whether the client has been successfully created - bool m_created; - // Host name filter for the service client std::string m_host_name; diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index 0545deee11..002b4e3fbd 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ **/ #include -#include #include "ecal_servicegate.h" #include "ecal_global_accessors.h" @@ -30,180 +29,66 @@ namespace eCAL { - /** - * @brief Service Server class. - **/ - - /** - * @brief Constructor. - **/ - CServiceServer::CServiceServer() : - m_service_server_impl(nullptr), - m_created(false) + CServiceServer::CServiceServer(const std::string& service_name_, const ServerEventIDCallbackT event_callback_) + : m_service_server_impl(nullptr) { - } + // create server implementation + m_service_server_impl = CServiceServerImpl::CreateInstance(service_name_, event_callback_); - /** - * @brief Constructor. - * - * @param service_name_ Service name. - **/ - CServiceServer::CServiceServer(const std::string& service_name_) : - m_service_server_impl(nullptr), - m_created(false) - { - Create(service_name_); + // register server + if (g_servicegate() != nullptr) g_servicegate()->Register(service_name_, m_service_server_impl); } - /** - * @brief Destructor. - **/ CServiceServer::~CServiceServer() { - Destroy(); - } - - /** - * @brief Creates this object. - * - * @param service_name_ Service name. - * - * @return True if successful. - **/ - bool CServiceServer::Create(const std::string& service_name_) - { - if(m_created) return(false); + // could be already destroyed by move + if (m_service_server_impl == nullptr) return; - // create service - m_service_server_impl = CServiceServerImpl::CreateInstance(service_name_); - - // register service - if (g_servicegate() != nullptr) g_servicegate()->Register(m_service_server_impl.get()); - - // we made it :-) - m_created = true; - return(m_created); + // unregister server + if (g_servicegate() != nullptr) g_servicegate()->Unregister(m_service_server_impl->GetServiceName(), m_service_server_impl); } - /** - * @brief Destroys this object. - * - * @return True if successful. - **/ - bool CServiceServer::Destroy() + CServiceServer::CServiceServer(CServiceServer&& rhs) noexcept + : m_service_server_impl(std::move(rhs.m_service_server_impl)) { - if(!m_created) return(false); - m_created = false; - - // unregister service - if (g_servicegate() != nullptr) g_servicegate()->Unregister(m_service_server_impl.get()); - - // stop & destroy service - m_service_server_impl->Stop(); - m_service_server_impl.reset(); - - return(true); } - /** - * @brief Add method type descriptions. - * - * @param method_ Service method name. - * @param req_type_ Service method request type. - * @param req_desc_ Service method request description. - * @param resp_type_ Service method response type. - * @param resp_desc_ Service method response description. - * - * @return True if successful. - **/ - bool CServiceServer::AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_) + CServiceServer& CServiceServer::operator=(CServiceServer&& rhs) noexcept { - if (!m_created) return false; - - SDataTypeInformation request_type_information; - request_type_information.name = req_type_; - request_type_information.descriptor = req_desc_; - - SDataTypeInformation response_type_information; - response_type_information.name = resp_type_; - response_type_information.descriptor = resp_desc_; - - return m_service_server_impl->AddDescription(method_, request_type_information, response_type_information); - } - - /** - * @brief Add client request callback. - * - * @param method_ Service method name. - * @param req_type_ Service method request type. - * @param resp_type_ Service method response type. - * @param callback_ Callback function for client request. - * - * @return True if successful. - **/ - bool CServiceServer::AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_) - { - if (!m_created) return false; - return m_service_server_impl->AddMethodCallback(method_, req_type_, resp_type_, callback_); + if (this != &rhs) + { + m_service_server_impl = std::move(rhs.m_service_server_impl); + } + return *this; } - /** - * @brief Remove client request callback. - * - * @return True if successful. - **/ - bool CServiceServer::RemMethodCallback(const std::string& method_) + bool CServiceServer::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { - if (!m_created) return false; - return m_service_server_impl->RemMethodCallback(method_); + if (m_service_server_impl == nullptr) return false; + return m_service_server_impl->AddMethodCallback(method_, method_info_, callback_); } - /** - * @brief Add callback function for server events. - * - * @param type_ The event type to react on. - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - bool CServiceServer::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) + bool CServiceServer::RemoveMethodCallback(const std::string& method_) { - if (!m_created) return false; - return m_service_server_impl->AddEventCallback(type_, callback_); + if (m_service_server_impl == nullptr) return false; + return m_service_server_impl->RemoveMethodCallback(method_); } - /** - * @brief Remove callback function for server events. - * - * @param type_ The event type to remove. - * - * @return True if succeeded, false if not. - **/ - bool CServiceServer::RemEventCallback(eCAL_Server_Event type_) + std::string CServiceServer::GetServiceName() { - if (!m_created) return false; - return m_service_server_impl->RemEventCallback(type_); + if (m_service_server_impl == nullptr) return ""; + return m_service_server_impl->GetServiceName(); } - /** - * @brief Retrieve service name. - * - * @return The service name. - **/ - std::string CServiceServer::GetServiceName() + Registration::SServiceMethodId CServiceServer::GetServiceId() const { - if (!m_created) return ""; - return m_service_server_impl->GetServiceName(); + // TODO: Implement this + return Registration::SServiceMethodId(); } - /** - * @brief Check connection state. - * - * @return True if connected, false if not. - **/ bool CServiceServer::IsConnected() { - if (!m_created) return false; + if (m_service_server_impl == nullptr) return false; return m_service_server_impl->IsConnected(); } } diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index 3f9939d251..6be955148f 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -21,303 +21,277 @@ * @brief eCAL service server implementation **/ -#include "registration/ecal_registration_provider.h" +#include +#include +#include + #include "ecal_global_accessors.h" #include "ecal_service_server_impl.h" #include "ecal_service_singleton_manager.h" +#include "registration/ecal_registration_provider.h" #include "serialization/ecal_serialize_service.h" -#include -#include -#include -#include -#include -#include - namespace eCAL { - std::shared_ptr CServiceServerImpl::CreateInstance() - { - return std::shared_ptr(new CServiceServerImpl()); - } - - std::shared_ptr CServiceServerImpl::CreateInstance(const std::string& service_name_) + // Factory method to create a new instance of CServiceServerImpl + std::shared_ptr CServiceServerImpl::CreateInstance( + const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) { - auto instance = std::shared_ptr (new CServiceServerImpl()); - instance->Start(service_name_); +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceServerImpl::CreateInstance: Creating instance of CServiceServerImpl for service: " + service_name_); +#endif + auto instance = std::shared_ptr(new CServiceServerImpl(service_name_, event_callback_)); + if (instance != nullptr) + { + instance->Start(); + } return instance; } - CServiceServerImpl::CServiceServerImpl() : - m_created(false) + // Constructor + CServiceServerImpl::CServiceServerImpl(const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) + : m_created(false), m_service_name(service_name_), m_event_callback(event_callback_) { +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceServerImpl::CServiceServerImpl: Initializing service server for: " + m_service_name); +#endif } + // Destructor CServiceServerImpl::~CServiceServerImpl() { +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::~CServiceServerImpl: Destroying service server for: " + m_service_name); +#endif Stop(); } - bool CServiceServerImpl::Start(const std::string& service_name_) + bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { - if (m_created) return(false); - - // set service name - m_service_name = service_name_; - - // create service id - std::stringstream counter; - counter << std::chrono::steady_clock::now().time_since_epoch().count(); - m_service_id = counter.str(); - - // Get global server manager - auto server_manager = eCAL::service::ServiceManager::instance()->get_server_manager(); - - if (!server_manager || server_manager->is_stopped()) - return false; - - // Create callback functions - const eCAL::service::Server::EventCallbackT event_callback - = [weak_me = std::weak_ptr(shared_from_this())] - (eCAL::service::ServerEventType event, const std::string& message) - { - auto me = weak_me.lock(); - - eCAL_Server_Event ecal_server_event = eCAL_Server_Event::server_event_none; - switch (event) - { - case eCAL::service::ServerEventType::Connected: - ecal_server_event = eCAL_Server_Event::server_event_connected; - break; - case eCAL::service::ServerEventType::Disconnected: - ecal_server_event = eCAL_Server_Event::server_event_disconnected; - break; - default: - break; - } - - if (me) - me->EventCallback(ecal_server_event, message); - }; - - const eCAL::service::Server::ServiceCallbackT service_callback - = [weak_me = std::weak_ptr(shared_from_this())] - (const std::shared_ptr& request, const std::shared_ptr& response) -> int - { - auto me = weak_me.lock(); - if (me) - return me->RequestCallback(*request, *response); - else - return -1; - }; - - // start service protocol version 1 - m_tcp_server = server_manager->create_server(1, 0, service_callback, true, event_callback); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::AddMethodCallback: Adding method callback for method: " + method_); +#endif + const std::lock_guard lock(m_method_map_mutex); - // mark as created - m_created = true; + auto iter = m_method_map.find(method_); + if (iter != m_method_map.end()) + { + Logging::Log(log_level_warning, "CServiceServerImpl::AddMethodCallback: Method already exists, updating callback: " + method_); + iter->second.method.req_type = method_info_.request_type.name; + iter->second.method.req_desc = method_info_.request_type.descriptor; + iter->second.method.resp_type = method_info_.response_type.name; + iter->second.method.resp_desc = method_info_.response_type.descriptor; + iter->second.callback = callback_; + } + else + { +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::AddMethodCallback: Registering new method: " + method_); +#endif + SMethod method; + method.method.mname = method_; + method.method.req_type = method_info_.request_type.name; + method.method.req_desc = method_info_.request_type.descriptor; + method.method.resp_type = method_info_.response_type.name; + method.method.resp_desc = method_info_.response_type.descriptor; + method.callback = callback_; + m_method_map[method_] = method; + } - return(true); + return true; } - bool CServiceServerImpl::Stop() + bool CServiceServerImpl::RemoveMethodCallback(const std::string& method_) { - if (!m_created) return(false); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::RemoveMethodCallback: Removing method callback for method: " + method_); +#endif + const std::lock_guard lock(m_method_map_mutex); - // reset method callback map + auto iter = m_method_map.find(method_); + if (iter != m_method_map.end()) { - std::lock_guard const lock(m_method_map_sync); - m_method_map.clear(); + m_method_map.erase(iter); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::RemoveMethodCallback: Successfully removed method callback: " + method_); +#endif + return true; } - // reset event callback map + Logging::Log(log_level_warning, "CServiceServerImpl::RemoveMethodCallback: Attempt to remove non-existent method callback: " + method_); + return false; + } + + bool CServiceServerImpl::IsConnected() const + { + if (!m_created) { - std::lock_guard const lock(m_event_callback_map_sync); - m_event_callback_map.clear(); +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceServerImpl: Service is not created; cannot check connection state for: " + m_service_name); +#endif + return false; } - if (m_tcp_server) - m_tcp_server->stop(); - - // mark as no more created - m_created = false; - - // and unregister this service - Unregister(); + bool connected = m_tcp_server && m_tcp_server->is_connected(); +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceServerImpl: Connection state for service " + m_service_name + ": " + (connected ? "connected" : "disconnected")); +#endif + return connected; + } - // reset internals - m_service_name.clear(); - m_service_id.clear(); + void CServiceServerImpl::RegisterClient(const std::string& /*key_*/, const SClientAttr& /*client_*/) + { + // client registration logic is not implemented, as it is not required for service servers + } - { - const std::lock_guard connected_lock(m_connected_mutex); - m_connected = false; - } + Registration::Sample CServiceServerImpl::GetRegistration() + { +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceServerImpl:::GetRegistration: Generating registration sample for: " + m_service_name); +#endif + return GetRegistrationSample(); + } - return(true); + std::string CServiceServerImpl::GetServiceName() const + { + return m_service_name; } - bool CServiceServerImpl::AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_) + void CServiceServerImpl::Start() { + if (m_created) { - std::lock_guard const lock(m_method_map_sync); - auto iter = m_method_map.find(method_); - if (iter != m_method_map.end()) - { - iter->second.method.mname = method_; - iter->second.method.req_type = request_type_information_.name; - iter->second.method.req_desc = request_type_information_.descriptor; - iter->second.method.resp_type = response_type_information_.name; - iter->second.method.resp_desc = response_type_information_.descriptor; - } - else - { - SMethod method; - method.method.mname = method_; - method.method.req_type = request_type_information_.name; - method.method.req_desc = request_type_information_.descriptor; - method.method.resp_type = response_type_information_.name; - method.method.resp_desc = response_type_information_.descriptor; - m_method_map[method_] = method; - } + Logging::Log(log_level_warning, "CServiceServerImpl: Service is already started: " + m_service_name); + return; } - return true; - } +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl: Starting service server for: " + m_service_name); +#endif - // add callback function for server method calls - bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_) - { - std::string req_desc; - std::string resp_desc; + // Create service ID + std::stringstream counter; + counter << std::chrono::steady_clock::now().time_since_epoch().count(); + m_service_id = counter.str(); + + // Get global server manager + auto server_manager = eCAL::service::ServiceManager::instance()->get_server_manager(); + if (!server_manager || server_manager->is_stopped()) { - std::lock_guard const lock(m_method_map_sync); - auto iter = m_method_map.find(method_); - if (iter != m_method_map.end()) - { - // should we overwrite this ? - iter->second.method.mname = method_; - iter->second.method.req_type = req_type_; - iter->second.method.resp_type = resp_type_; - // set callback - iter->second.callback = callback_; - - // read descriptors back from existing service method - req_desc = iter->second.method.req_desc; - resp_desc = iter->second.method.resp_desc; - } - else - { - SMethod method; - method.method.mname = method_; - method.method.req_type = req_type_; - method.method.resp_type = resp_type_; - method.callback = callback_; - m_method_map[method_] = method; - } + Logging::Log(log_level_error, "CServiceServerImpl: Failed to start service: Global server manager is unavailable or stopped for: " + m_service_name); + return; } - return true; - } + // Create callback functions + const eCAL::service::Server::EventCallbackT event_callback = + [weak_me = std::weak_ptr(shared_from_this())](eCAL::service::ServerEventType event, const std::string& message) + { + if (auto me = weak_me.lock()) + { + Registration::SServiceMethodId service_id; + service_id.service_name = me->m_service_name; + service_id.service_id.entity_id = me->m_service_id; + me->NotifyEventCallback(service_id, event == eCAL::service::ServerEventType::Connected + ? eCAL_Server_Event::server_event_connected + : eCAL_Server_Event::server_event_disconnected, message); + } + }; - // remove callback function for server method calls - bool CServiceServerImpl::RemMethodCallback(const std::string& method_) - { - std::lock_guard const lock(m_method_map_sync); + const eCAL::service::Server::ServiceCallbackT service_callback = + [weak_me = std::weak_ptr(shared_from_this())](const std::shared_ptr& request, const std::shared_ptr& response) -> int + { + if (auto me = weak_me.lock()) + return me->RequestCallback(*request, *response); + return -1; + }; - auto iter = m_method_map.find(method_); - if (iter != m_method_map.end()) + // Start service + m_tcp_server = server_manager->create_server(1, 0, service_callback, true, event_callback); + + if (!m_tcp_server) { - m_method_map.erase(iter); - return true; + Logging::Log(log_level_error, "CServiceServerImpl: Failed to create TCP server for service: " + m_service_name); + return; } - return false; - } - // add callback function for server events - bool CServiceServerImpl::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) - { - if (!m_created) return false; + // Send registration sample + if (g_registration_provider()) + g_registration_provider()->RegisterSample(GetRegistrationSample()); - // store event callback - { - std::lock_guard const lock(m_event_callback_map_sync); + m_created = true; #ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::AddEventCallback"); + Logging::Log(log_level_debug1, "CServiceServerImpl: Service started successfully: " + m_service_name); #endif - m_event_callback_map[type_] = std::move(callback_); - } - - return true; } - // remove callback function for server events - bool CServiceServerImpl::RemEventCallback(eCAL_Server_Event type_) + void CServiceServerImpl::Stop() { - if (!m_created) return false; + if (!m_created) + { + Logging::Log(log_level_warning, "CServiceServerImpl::Stop: Service is not running; cannot stop: " + m_service_name); + return; + } + +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::Stop: Stopping service server for: " + m_service_name); +#endif - // reset event callback + // Stop TCP server + if (m_tcp_server) { - std::lock_guard const lock(m_event_callback_map_sync); + m_tcp_server->stop(); #ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::RemEventCallback"); + Logging::Log(log_level_debug1, "CServiceServerImpl::Stop: TCP server stopped for: " + m_service_name); #endif - m_event_callback_map[type_] = nullptr; } + m_tcp_server.reset(); - return true; - } - - // check connection state - bool CServiceServerImpl::IsConnected() - { - if (!m_created) return false; + // Reset method callbacks + { + const std::lock_guard lock(m_method_map_mutex); + m_method_map.clear(); + } - return (m_tcp_server && m_tcp_server->is_connected()); - } + // Reset event callback + { + const std::lock_guard lock(m_event_callback_mutex); + m_event_callback = nullptr; + } - // called by the eCAL::CServiceGate to register a client - void CServiceServerImpl::RegisterClient(const std::string& /*key_*/, const SClientAttr& /*client_*/) - { - // this function is just a placeholder to implement logic if a new client connects - // currently there is no need to do so - } + // Send unregistration sample + if (g_registration_provider()) + g_registration_provider()->UnregisterSample(GetUnregistrationSample()); - // called by eCAL:CServiceGate every second to update registration layer - Registration::Sample CServiceServerImpl::GetRegistration() - { - return GetRegistrationSample(); + m_created = false; +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::Stop: Service stopped successfully: " + m_service_name); +#endif } Registration::Sample CServiceServerImpl::GetRegistrationSample() { - // create registration sample Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_reg_service; // might be zero in contruction phase - unsigned short const server_tcp_port(m_tcp_server ? m_tcp_server->get_port() : 0); - if ((Config::IsServiceProtocolV1Enabled()) && (server_tcp_port == 0)) return ecal_reg_sample; + const unsigned short server_tcp_port(m_tcp_server ? m_tcp_server->get_port() : 0); + if (server_tcp_port == 0) return ecal_reg_sample; - auto& identifier = ecal_reg_sample.identifier; + auto& identifier = ecal_reg_sample.identifier; identifier.entity_id = m_service_id; identifier.process_id = Process::GetProcessID(); identifier.host_name = Process::GetHostName(); - auto& service = ecal_reg_sample.service; + auto& service = ecal_reg_sample.service; service.version = m_server_version; service.pname = Process::GetProcessName(); service.uname = Process::GetUnitName(); service.sname = m_service_name; - service.tcp_port_v0 = 0; service.tcp_port_v1 = server_tcp_port; - // add methods { - std::lock_guard const lock(m_method_map_sync); + const std::lock_guard lock(m_method_map_mutex); for (const auto& iter : m_method_map) { Service::Method method; @@ -336,7 +310,6 @@ namespace eCAL Registration::Sample CServiceServerImpl::GetUnregistrationSample() { - // create registration sample Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_unreg_service; @@ -354,32 +327,12 @@ namespace eCAL return ecal_reg_sample; } - void CServiceServerImpl::Register() + int CServiceServerImpl::RequestCallback(const std::string& request_pb_, std::string& response_pb_) { -#if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(GetRegistrationSample()); - #ifndef NDEBUG - // log it - Logging::Log(log_level_debug4, m_service_name + "::CServiceServerImpl::Register"); + Logging::Log(log_level_debug2, "CServiceServerImpl::RequestCallback: Processing request callback for: " + m_service_name); #endif -#endif // ECAL_CORE_REGISTRATION - } - void CServiceServerImpl::Unregister() - { -#if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug4, m_service_name + "::CServiceServerImpl::Unregister"); -#endif -#endif // ECAL_CORE_REGISTRATION - } - - int CServiceServerImpl::RequestCallback(const std::string& request_pb_, std::string& response_pb_) - { // prepare response Service::Response response; auto& response_header = response.header; @@ -391,10 +344,10 @@ namespace eCAL Service::Request request; if (!DeserializeFromBuffer(request_pb_.c_str(), request_pb_.size(), request)) { - Logging::Log(log_level_error, m_service_name + "::CServiceServerImpl::RequestCallback failed to parse request message"); + Logging::Log(log_level_error, m_service_name + "::CServiceServerImpl::RequestCallback: Failed to parse request message"); response_header.state = Service::eMethodCallState::failed; - std::string const emsg = "Service '" + m_service_name + "' request message could not be parsed."; + const std::string emsg = "Service '" + m_service_name + "' request message could not be parsed."; response_header.error = emsg; // TODO: The next version of the service protocol should omit the double-serialization (i.e. copying the binary data in a protocol buffer and then serializing that again) @@ -411,15 +364,14 @@ namespace eCAL const auto& request_header = request.header; response_header.mname = request_header.mname; { - std::lock_guard const lock(m_method_map_sync); - + const std::lock_guard lock(m_method_map_mutex); auto requested_method_iterator = m_method_map.find(request_header.mname); if (requested_method_iterator == m_method_map.end()) { // set method call state 'failed' response_header.state = Service::eMethodCallState::failed; // set error message - std::string const emsg = "Service '" + m_service_name + "' has no method named '" + request_header.mname + "'"; + const std::string emsg = "CServiceServerImpl: Service '" + m_service_name + "' has no method named '" + request_header.mname + "'"; response_header.error = emsg; // TODO: The next version of the service protocol should omit the double-serialization (i.e. copying the binary data in a protocol buffer and then serializing that again) @@ -432,11 +384,7 @@ namespace eCAL } else { - // increase call count - auto call_count = requested_method_iterator->second.method.call_count; - requested_method_iterator->second.method.call_count = ++call_count; - - // store (copy) the method object, so we can release the mutex before calling the function + requested_method_iterator->second.method.call_count++; method = requested_method_iterator->second; } } @@ -444,12 +392,12 @@ namespace eCAL // execute method (outside lock guard) const std::string& request_s = request.request; std::string response_s; - int const service_return_state = method.callback(method.method.mname, method.method.req_type, method.method.resp_type, request_s, response_s); + const int service_return_state = method.callback(method.method.mname, method.method.req_type, method.method.resp_type, request_s, response_s); // set method call state 'executed' response_header.state = Service::eMethodCallState::executed; // set method response and return state - response.response = response_s; + response.response = response_s; response.ret_state = service_return_state; // TODO: The next version of the service protocol should omit the double-serialization (i.e. copying the binary data in a protocol buffer and then serializing that again) @@ -460,45 +408,19 @@ namespace eCAL return 0; } - void CServiceServerImpl::EventCallback(eCAL_Server_Event event_, const std::string& /*message_*/) + void CServiceServerImpl::NotifyEventCallback(const Registration::SServiceMethodId& service_id_, eCAL_Server_Event event_type_, const std::string& /*message_*/) { - bool mode_changed(false); - - { - const std::lock_guard connected_lock(m_connected_mutex); - - if (m_connected) - { - if (m_tcp_server && !m_tcp_server->is_connected()) - { - mode_changed = true; - m_connected = false; - Logging::Log(log_level_debug2, m_service_name + ": " + "client with protocol version 1 disconnected"); - } - } - else - { - if (m_tcp_server && m_tcp_server->is_connected()) - { - mode_changed = true; - m_connected = true; - Logging::Log(log_level_debug2, m_service_name + ": " + "client with protocol version 1 connected"); - } - } - } +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::NotifyEventCallback: Notifying event callback for: " + m_service_name + " Event Type: " + std::to_string(event_type_)); +#endif - if (mode_changed) + const std::lock_guard lock_cb(m_event_callback_mutex); + if (m_event_callback) { - // call event - std::lock_guard const lock_cb(m_event_callback_map_sync); - auto e_iter = m_event_callback_map.find(event_); - if (e_iter != m_event_callback_map.end()) - { - SServerEventCallbackData sdata; - sdata.type = event_; - sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - (e_iter->second)(m_service_name.c_str(), &sdata); - } + SServerEventCallbackData callback_data; + callback_data.type = event_type_; + callback_data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + m_event_callback(service_id_, callback_data); } } } diff --git a/ecal/core/src/service/ecal_service_server_impl.h b/ecal/core/src/service/ecal_service_server_impl.h index 0a56ab6657..be5ad0c44d 100644 --- a/ecal/core/src/service/ecal_service_server_impl.h +++ b/ecal/core/src/service/ecal_service_server_impl.h @@ -18,12 +18,11 @@ */ /** - * @brief eCAL service server interface + * @brief eCAL service server implementation **/ #pragma once -#include #include #include #include @@ -31,7 +30,7 @@ #include "serialization/ecal_serialize_sample_registration.h" #include "serialization/ecal_struct_service.h" -#include +#include #include #include #include @@ -40,75 +39,69 @@ namespace eCAL { /** - * @brief Service server implementation class. - **/ + * @brief Implementation class for eCAL service server. + */ class CServiceServerImpl : public std::enable_shared_from_this { public: - static std::shared_ptr CreateInstance(); - static std::shared_ptr CreateInstance(const std::string& service_name_); + // Factory method to create an instance of the client implementation + static std::shared_ptr CreateInstance( + const std::string& service_name_, const ServerEventIDCallbackT& event_callback_); private: - CServiceServerImpl(); + // Private constructor to enforce creation through factory method + CServiceServerImpl(const std::string& service_name_, const ServerEventIDCallbackT& event_callback_); public: - // Delete copy and move constructors and assign operators. Necessary, as the class uses the this pointer, that would be dangling / pointing to a wrong object otherwise. - CServiceServerImpl(const CServiceServerImpl&) = delete; // Copy construct - CServiceServerImpl(CServiceServerImpl&&) = delete; // Move construct - CServiceServerImpl& operator=(const CServiceServerImpl&) = delete; // Copy assign - CServiceServerImpl& operator=(CServiceServerImpl&&) = delete; // Move assign - ~CServiceServerImpl(); - bool Start(const std::string& service_name_); - bool Stop(); - - bool AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_); - - // add and remove callback function for server method calls - bool AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_); - bool RemMethodCallback(const std::string& method_); + bool AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_); + bool RemoveMethodCallback(const std::string& method_); - // add and remove callback function for server events - bool AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_); - bool RemEventCallback(eCAL_Server_Event type_); - - // check connection state - bool IsConnected(); + // Check connection state of a specific service + bool IsConnected() const; - // called by the eCAL::CServiceGate to register a client + // Called by the registration receiver to process a client registration void RegisterClient(const std::string& key_, const SClientAttr& client_); - // called by eCAL:CServiceGate every second to update registration layer + // Called by the registration provider to get a registration sample Registration::Sample GetRegistration(); - std::string GetServiceName() { return m_service_name; }; + // Retrieves the service name + std::string GetServiceName() const; - protected: - void Register(); - void Unregister(); + // Prevent copy and move operations + CServiceServerImpl(const CServiceServerImpl&) = delete; + CServiceServerImpl& operator=(const CServiceServerImpl&) = delete; + CServiceServerImpl(CServiceServerImpl&&) = delete; + CServiceServerImpl& operator=(CServiceServerImpl&&) = delete; + + private: + // Start/Stop server + void Start(); + void Stop(); + // Prepare and retrieve registration and unregistration samples Registration::Sample GetRegistrationSample(); Registration::Sample GetUnregistrationSample(); - /** - * @brief Calls the request callback based on the request and fills the response - * - * @param[in] request_ The service request in serialized protobuf form - * @param[out] response_ A serialized protobuf response. My not be set at all. - * - * @return 0 if succeeded, -1 if not. - */ + // Request and event callback methods int RequestCallback(const std::string& request_pb_, std::string& response_pb_); - void EventCallback(eCAL_Server_Event event_, const std::string& message_); + void NotifyEventCallback(const Registration::SServiceMethodId& service_id_, eCAL_Server_Event event_type_, const std::string& message_); - std::shared_ptr m_tcp_server; + // Server version (incremented for protocol or functionality changes) + static constexpr int m_server_version = 1; + + // Server attributes + std::string m_service_name; + std::string m_service_id; - static constexpr int m_server_version = 1; - - std::string m_service_name; - std::string m_service_id; + // Server connection state and synchronization + mutable std::mutex m_connected_mutex; // mutex protecting m_connected (modified by the event callbacks in another thread) + bool m_connected = false; + std::atomic m_created; + // Server method map and synchronization struct SMethod { Service::Method method; @@ -116,16 +109,14 @@ namespace eCAL }; using MethodMapT = std::map; - std::mutex m_method_map_sync; - MethodMapT m_method_map; + std::mutex m_method_map_mutex; + MethodMapT m_method_map; - using EventCallbackMapT = std::map; - std::mutex m_event_callback_map_sync; - EventCallbackMapT m_event_callback_map; - - mutable std::mutex m_connected_mutex; //!< mutex protecting the m_connected_v0 and m_connected_v1 variable, as those are modified by the event callbacks in another thread. - bool m_connected = false; + // Event callback and synchronization + std::mutex m_event_callback_mutex; + ServerEventIDCallbackT m_event_callback; - std::atomic m_created; + // Server interface + std::shared_ptr m_tcp_server; }; } diff --git a/ecal/core/src/service/ecal_service_server_v5.cpp b/ecal/core/src/service/ecal_service_server_v5.cpp new file mode 100644 index 0000000000..705a0a09de --- /dev/null +++ b/ecal/core/src/service/ecal_service_server_v5.cpp @@ -0,0 +1,108 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 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 ================================= +*/ + +/** + * @brief eCAL service server interface (deprecated eCAL5 version) +**/ + +#include +#include +#include + +#include "ecal_servicegate.h" +#include "ecal_global_accessors.h" +#include "ecal_service_server_v5_impl.h" + +namespace eCAL +{ + namespace v5 + { + CServiceServer::CServiceServer() + : m_service_server_impl(nullptr) + { + } + + CServiceServer::CServiceServer(const std::string& service_name_) + : m_service_server_impl(nullptr) + { + Create(service_name_); + } + + CServiceServer::~CServiceServer() + { + Destroy(); + } + + bool CServiceServer::Create(const std::string& service_name_) + { + if (m_service_server_impl != nullptr) return false; + m_service_server_impl = std::make_shared(service_name_); + return m_service_server_impl != nullptr; + } + + bool CServiceServer::Destroy() + { + if (m_service_server_impl == nullptr) return false; + m_service_server_impl.reset(); + return true; + } + + bool CServiceServer::AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_) + { + if (m_service_server_impl == nullptr) return false; + return m_service_server_impl->AddDescription(method_, req_type_, req_desc_, resp_type_, resp_desc_); + } + + bool CServiceServer::AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_) + { + if (m_service_server_impl == nullptr) return false; + return m_service_server_impl->AddMethodCallback(method_, req_type_, resp_type_, callback_); + } + + bool CServiceServer::RemMethodCallback(const std::string& method_) + { + if (m_service_server_impl == nullptr) return false; + return m_service_server_impl->RemMethodCallback(method_); + } + + bool CServiceServer::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) + { + if (m_service_server_impl == nullptr) return false; + return m_service_server_impl->AddEventCallback(type_, callback_); + } + + bool CServiceServer::RemEventCallback(eCAL_Server_Event type_) + { + if (m_service_server_impl == nullptr) return false; + return m_service_server_impl->RemEventCallback(type_); + } + + std::string CServiceServer::GetServiceName() + { + if (m_service_server_impl == nullptr) return ""; + return m_service_server_impl->GetServiceName(); + } + + bool CServiceServer::IsConnected() + { + if (m_service_server_impl == nullptr) return false; + return m_service_server_impl->IsConnected(); + } + } +} diff --git a/ecal/core/src/service/ecal_service_server_v5_impl.cpp b/ecal/core/src/service/ecal_service_server_v5_impl.cpp new file mode 100644 index 0000000000..715812eda4 --- /dev/null +++ b/ecal/core/src/service/ecal_service_server_v5_impl.cpp @@ -0,0 +1,197 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 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 ================================= +*/ + +/** + * @brief eCAL service server implementation (deprecated eCAL5 version) +**/ + +#include + +#include "ecal_service_server_v5_impl.h" + +namespace eCAL +{ + namespace v5 + { + CServiceServerImpl::CServiceServerImpl() + : m_service_server_impl(nullptr) + { + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Initializing default service server implementation."); + } + + CServiceServerImpl::CServiceServerImpl(const std::string& service_name_) + : m_service_server_impl(nullptr) + { + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Initializing service server with name: " + service_name_); + Create(service_name_); + } + + CServiceServerImpl::~CServiceServerImpl() + { + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Destroying service server implementation."); + Destroy(); + } + + bool CServiceServerImpl::Create(const std::string& service_name_) + { + if (m_service_server_impl) + { + Logging::Log(log_level_warning, "v5::CServiceServerImpl: Service server already created: " + service_name_); + return false; + } + + // Define the event callback to pass to CServiceClient + ServerEventIDCallbackT event_callback = [this](const Registration::SServiceMethodId& service_id_, const struct SServerEventCallbackData& data_) + { + // Lock the mutex to safely access m_event_callbacks + std::lock_guard lock(m_event_callback_map_mutex); + + // Check if there's a callback registered for the event type + const auto& callback = m_event_callback_map.find(data_.type); + if (callback != m_event_callback_map.end()) + { + // Call the user's callback + callback->second(service_id_.service_name.c_str(), &data_); + } + }; + + m_service_server_impl = std::make_shared(service_name_, event_callback); + Logging::Log(log_level_debug1, "v5::CServiceServerImpl: Service server created with name: " + service_name_); + return true; + } + + bool CServiceServerImpl::Destroy() + { + if (!m_service_server_impl) + { + Logging::Log(log_level_warning, "v5::CServiceServerImpl: Service server not initialized, cannot destroy."); + return false; + } + + m_service_server_impl.reset(); + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Service server destroyed."); + return true; + } + + bool CServiceServerImpl::AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_) + { + Logging::Log(log_level_debug1, "v5::CServiceServerImpl: Adding description for method: " + method_); + + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot add description."); + return false; + } + + SServiceMethodInformation method_info; + method_info.request_type.name = req_type_; + method_info.request_type.descriptor = req_desc_; + method_info.response_type.name = resp_type_; + method_info.response_type.descriptor = resp_desc_; + + return m_service_server_impl->AddMethodCallback(method_, method_info, nullptr); + } + + bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_) + { + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Adding method callback for method: " + method_); + + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot add method callback."); + return false; + } + + SServiceMethodInformation method_info; + method_info.request_type.name = req_type_; + method_info.response_type.name = resp_type_; + + return m_service_server_impl->AddMethodCallback(method_, method_info, callback_); + } + + bool CServiceServerImpl::RemMethodCallback(const std::string& method_) + { + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Removing method callback for method: " + method_); + + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot remove method callback."); + return false; + } + + return m_service_server_impl->RemoveMethodCallback(method_); + } + + bool CServiceServerImpl::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) + { + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot add event callback."); + return false; + } + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Adding event callback for event type: " + std::to_string(type_)); + + { + const std::lock_guard lock(m_event_callback_map_mutex); + m_event_callback_map[type_] = callback_; + } + + return true; + } + + bool CServiceServerImpl::RemEventCallback(eCAL_Server_Event type_) + { + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot remove event callback."); + return false; + } + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Removing event callback for event type: " + std::to_string(type_)); + + { + const std::lock_guard lock(m_event_callback_map_mutex); + m_event_callback_map.erase(type_); + } + + return false; + } + + std::string CServiceServerImpl::GetServiceName() + { + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot get service name."); + return ""; + } + + return m_service_server_impl->GetServiceName(); + } + + bool CServiceServerImpl::IsConnected() + { + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot check connection status."); + return false; + } + + return m_service_server_impl->IsConnected(); + } + } +} diff --git a/ecal/core/src/service/ecal_service_server_v5_impl.h b/ecal/core/src/service/ecal_service_server_v5_impl.h new file mode 100644 index 0000000000..fffc6af638 --- /dev/null +++ b/ecal/core/src/service/ecal_service_server_v5_impl.h @@ -0,0 +1,73 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 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 ================================= +*/ + +/** + * @brief eCAL service server implementation (deprecated eCAL5 version) +**/ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace eCAL +{ + namespace v5 + { + class CServiceServerImpl + { + public: + CServiceServerImpl(); + explicit CServiceServerImpl(const std::string& service_name_); + virtual ~CServiceServerImpl(); + + bool Create(const std::string& service_name_); + bool Destroy(); + + bool AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_); + + bool AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_); + bool RemMethodCallback(const std::string& method_); + + bool AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_); + bool RemEventCallback(eCAL_Server_Event type_); + + std::string GetServiceName(); + bool IsConnected(); + + // Prevent copy and move operations + CServiceServerImpl(const CServiceServerImpl&) = delete; + CServiceServerImpl& operator=(const CServiceServerImpl&) = delete; + CServiceServerImpl(CServiceServerImpl&&) = delete; + CServiceServerImpl& operator=(CServiceServerImpl&&) = delete; + + private: + // Pointer to the underlying service server implementation + std::shared_ptr m_service_server_impl; + + // Mutex and map for managing event callbacks + std::mutex m_event_callback_map_mutex; + std::map m_event_callback_map; + }; + } +} diff --git a/ecal/core/src/service/ecal_service_singleton_manager.cpp b/ecal/core/src/service/ecal_service_singleton_manager.cpp index fcaf6a99f5..fa8a3b7fd9 100644 --- a/ecal/core/src/service/ecal_service_singleton_manager.cpp +++ b/ecal/core/src/service/ecal_service_singleton_manager.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,7 +73,7 @@ namespace eCAL } ServiceManager::ServiceManager() - : stopped(false) + : m_stopped(false) {} ServiceManager::~ServiceManager() @@ -90,34 +90,34 @@ namespace eCAL // Quickly check the atomic stopped boolean before actually locking the // mutex. It can theoretically change before we got mutex access, so we // will have to check it again. - if (stopped) + if (m_stopped) return nullptr; // Lock the mutex to actually make it thread safe - const std::lock_guard singleton_lock(singleton_mutex); - if (!stopped) + const std::lock_guard singleton_lock(m_singleton_mutex); + if (!m_stopped) { // Create io_context, if it didn't exist, yet - if (!io_context) - io_context = std::make_unique(); + if (!m_io_context) + m_io_context = std::make_unique(); // Create the client manager, if it didn't exist, yet - if (!client_manager) - client_manager = eCAL::service::ClientManager::create(io_context, ecal_logger("Service Client")); + if (!m_client_manager) + m_client_manager = eCAL::service::ClientManager::create(m_io_context, ecal_logger("Service Client")); // Start io threads, if necessary - if (io_threads.empty()) + if (m_io_threads.empty()) { for (size_t i = 0; i < num_io_threads; i++) { - io_threads.emplace_back(std::make_unique([this]() { io_context->run(); })); + m_io_threads.emplace_back(std::make_unique([this]() { m_io_context->run(); })); } } // Return the client manager. The client manager has its own dummy work // object, so it will keep the io_context alive, until the // client_manager is stopped. - return client_manager; + return m_client_manager; } return nullptr; } @@ -127,63 +127,63 @@ namespace eCAL // Quickly check the atomic stopped boolean before actually locking the // mutex. It can theoretically change before we got mutex access, so we // will have to check it again. - if (stopped) + if (m_stopped) return nullptr; // Lock the mutex to actually make it thread safe - const std::lock_guard singleton_lock(singleton_mutex); - if (!stopped) + const std::lock_guard singleton_lock(m_singleton_mutex); + if (!m_stopped) { // Create io_context, if it didn't exist, yet - if (!io_context) - io_context = std::make_unique(); + if (!m_io_context) + m_io_context = std::make_unique(); // Create the server manager, if it didn't exit, yet - if (!server_manager) - server_manager = eCAL::service::ServerManager::create(io_context, ecal_logger("Service Server")); + if (!m_server_manager) + m_server_manager = eCAL::service::ServerManager::create(m_io_context, ecal_logger("Service Server")); // Start io threads, if necessary - if (io_threads.empty()) + if (m_io_threads.empty()) { for (size_t i = 0; i < num_io_threads; i++) { - io_threads.emplace_back(std::make_unique([this]() { io_context->run(); })); + m_io_threads.emplace_back(std::make_unique([this]() { m_io_context->run(); })); } } // Return the server manager. The server manager has its own dummy work // object, so it will keep the io_context alive, until the // client_manager is stopped. - return server_manager; + return m_server_manager; } return nullptr; } void ServiceManager::stop() { - const std::lock_guard singleton_lock(singleton_mutex); + const std::lock_guard singleton_lock(m_singleton_mutex); - stopped = true; + m_stopped = true; - if (server_manager) - server_manager->stop(); + if (m_server_manager) + m_server_manager->stop(); - if (client_manager) - client_manager->stop(); + if (m_client_manager) + m_client_manager->stop(); - for (const auto& thread : io_threads) + for (const auto& thread : m_io_threads) thread->join(); - server_manager.reset(); - client_manager.reset(); - io_threads.clear(); - io_context.reset(); + m_server_manager.reset(); + m_client_manager.reset(); + m_io_threads.clear(); + m_io_context.reset(); } void ServiceManager::reset() { - const std::lock_guard singleton_lock(singleton_mutex); - stopped = false; + const std::lock_guard singleton_lock(m_singleton_mutex); + m_stopped = false; } } // namespace service diff --git a/ecal/core/src/service/ecal_service_singleton_manager.h b/ecal/core/src/service/ecal_service_singleton_manager.h index a2aa2d5993..926587a611 100644 --- a/ecal/core/src/service/ecal_service_singleton_manager.h +++ b/ecal/core/src/service/ecal_service_singleton_manager.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,53 +33,53 @@ namespace eCAL { namespace service { - class ServiceManager - { - //////////////////////////////////////////////////////////// - // Singleton interface, Constructor, destructor - //////////////////////////////////////////////////////////// - public: - static ServiceManager* instance(); + class ServiceManager + { + //////////////////////////////////////////////////////////// + // Singleton interface, Constructor, destructor + //////////////////////////////////////////////////////////// + public: + static ServiceManager* instance(); - private: - ServiceManager(); + private: + ServiceManager(); - public: - ~ServiceManager(); + public: + ~ServiceManager(); - // Delete copy constructor and assignment operator - ServiceManager(const ServiceManager&) = delete; - ServiceManager& operator=(const ServiceManager&) = delete; + // Delete copy constructor and assignment operator + ServiceManager(const ServiceManager&) = delete; + ServiceManager& operator=(const ServiceManager&) = delete; - // Delete move constructor and assignment operator - ServiceManager(ServiceManager&&) = delete; - ServiceManager& operator=(ServiceManager&&) = delete; + // Delete move constructor and assignment operator + ServiceManager(ServiceManager&&) = delete; + ServiceManager& operator=(ServiceManager&&) = delete; - //////////////////////////////////////////////////////////// - // Public API - //////////////////////////////////////////////////////////// - public: - std::shared_ptr get_client_manager(); - std::shared_ptr get_server_manager(); + //////////////////////////////////////////////////////////// + // Public API + //////////////////////////////////////////////////////////// + public: + std::shared_ptr get_client_manager(); + std::shared_ptr get_server_manager(); - void stop(); - void reset(); + void stop(); + void reset(); - //////////////////////////////////////////////////////////// - // Member variables - //////////////////////////////////////////////////////////// - private: - static constexpr size_t num_io_threads = 4; + //////////////////////////////////////////////////////////// + // Member variables + //////////////////////////////////////////////////////////// + private: + static constexpr size_t num_io_threads = 4; - std::mutex singleton_mutex; + std::mutex m_singleton_mutex; - std::atomic stopped; - std::shared_ptr io_context; - std::vector> io_threads; + std::atomic m_stopped; + std::shared_ptr m_io_context; + std::vector> m_io_threads; - std::shared_ptr client_manager; - std::shared_ptr server_manager; - }; + std::shared_ptr m_client_manager; + std::shared_ptr m_server_manager; + }; } } diff --git a/ecal/core/src/service/ecal_servicegate.cpp b/ecal/core/src/service/ecal_servicegate.cpp index 8967b68442..216f84eb8c 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/ecal/core/src/service/ecal_servicegate.cpp @@ -51,43 +51,37 @@ namespace eCAL if(!m_created) return; // destroy all remaining server - const std::shared_lock lock(m_service_set_sync); - for (const auto& service : m_service_set) - { - service->Stop(); - } + const std::unique_lock lock(m_service_server_map_mutex); + m_service_server_map.clear(); m_created = false; } - bool CServiceGate::Register(CServiceServerImpl* service_) + bool CServiceGate::Register(const std::string& service_name_, const std::shared_ptr& server_) { if(!m_created) return(false); // register internal service - const std::unique_lock lock(m_service_set_sync); - m_service_set.insert(service_); + const std::unique_lock lock(m_service_server_map_mutex); + m_service_server_map.emplace(std::pair>(service_name_, server_)); return(true); } - bool CServiceGate::Unregister(CServiceServerImpl* service_) + bool CServiceGate::Unregister(const std::string& service_name_, const std::shared_ptr& server_) { - if(!m_created) return(false); - bool ret_state(false); + if (!m_created) return(false); + bool ret_state = false; - // unregister internal service - const std::unique_lock lock(m_service_set_sync); - for (auto iter = m_service_set.begin(); iter != m_service_set.end();) + const std::unique_lock lock(m_service_server_map_mutex); + auto res = m_service_server_map.equal_range(service_name_); + for (auto iter = res.first; iter != res.second; ++iter) { - if (*iter == service_) + if (iter->second == server_) { - iter = m_service_set.erase(iter); + m_service_server_map.erase(iter); ret_state = true; - } - else - { - iter++; + break; } } @@ -98,11 +92,13 @@ namespace eCAL { if (!m_created) return; - // read service registrations - std::shared_lock const lock(m_service_set_sync); - for (const auto& service_server_impl : m_service_set) + // read server registrations { - reg_sample_list_.push_back(service_server_impl->GetRegistration()); + const std::shared_lock lock(m_service_server_map_mutex); + for (const auto& iter : m_service_server_map) + { + reg_sample_list_.push_back(iter.second->GetRegistration()); + } } } } diff --git a/ecal/core/src/service/ecal_servicegate.h b/ecal/core/src/service/ecal_servicegate.h index 687c527e3a..8dc75429be 100644 --- a/ecal/core/src/service/ecal_servicegate.h +++ b/ecal/core/src/service/ecal_servicegate.h @@ -27,8 +27,10 @@ #include "serialization/ecal_struct_sample_registration.h" #include +#include +#include #include -#include +#include namespace eCAL { @@ -43,16 +45,16 @@ namespace eCAL void Start(); void Stop(); - bool Register (CServiceServerImpl* service_); - bool Unregister(CServiceServerImpl* service_); + bool Register (const std::string& service_name_, const std::shared_ptr& server_); + bool Unregister(const std::string& service_name_, const std::shared_ptr& server_); void GetRegistrations(Registration::SampleList& reg_sample_list_); protected: - static std::atomic m_created; + static std::atomic m_created; - using ServiceNameServiceImplSetT = std::set; - std::shared_timed_mutex m_service_set_sync; - ServiceNameServiceImplSetT m_service_set; + using ServiceNameServiceImplMapT = std::multimap>; + std::shared_timed_mutex m_service_server_map_mutex; + ServiceNameServiceImplMapT m_service_server_map; }; } diff --git a/ecal/samples/cpp/services/latency_client/src/latency_client.cpp b/ecal/samples/cpp/services/latency_client/src/latency_client.cpp index 6b007d3369..b0baa97f69 100644 --- a/ecal/samples/cpp/services/latency_client/src/latency_client.cpp +++ b/ecal/samples/cpp/services/latency_client/src/latency_client.cpp @@ -18,7 +18,6 @@ */ #include -#include #include #include @@ -34,7 +33,7 @@ int main() eCAL::Initialize("latency client"); // create latency client - eCAL::v5::CServiceClient latency_client("latency"); + eCAL::CServiceClient latency_client("latency"); // waiting for service while (eCAL::Ok() && !latency_client.IsConnected()) @@ -60,7 +59,7 @@ int main() // call service method "hello" subcalls times (for better time accuracy) for (auto i = 0; i < subcalls; ++i) { - latency_client.Call("hello", ""); + latency_client.CallWithCallback("hello", "", -1, eCAL::ResponseIDCallbackT()); } // take return time and store it into the latency array diff --git a/ecal/samples/cpp/services/latency_server/src/latency_server.cpp b/ecal/samples/cpp/services/latency_server/src/latency_server.cpp index 26c4e4a6ae..213a5cd06a 100644 --- a/ecal/samples/cpp/services/latency_server/src/latency_server.cpp +++ b/ecal/samples/cpp/services/latency_server/src/latency_server.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ int main() eCAL::CServiceServer latency_service("latency"); // add hello method callback - latency_service.AddMethodCallback("hello", "", "", OnHello); + latency_service.AddMethodCallback("hello", eCAL::SServiceMethodInformation(), OnHello); // idle main thread while (eCAL::Ok()) std::this_thread::sleep_for(std::chrono::milliseconds(100)); diff --git a/ecal/samples/cpp/services/minimal_server/src/minimal_server.cpp b/ecal/samples/cpp/services/minimal_server/src/minimal_server.cpp index 31c2f4ee92..42e23040b2 100644 --- a/ecal/samples/cpp/services/minimal_server/src/minimal_server.cpp +++ b/ecal/samples/cpp/services/minimal_server/src/minimal_server.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ int main() eCAL::CServiceServer minimal_server("service1"); // add method callback - minimal_server.AddMethodCallback("echo", "", "", OnMethodCallback); + minimal_server.AddMethodCallback("echo", eCAL::SServiceMethodInformation(), OnMethodCallback); // idle while(eCAL::Ok()) diff --git a/ecal/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp b/ecal/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp index 12dbdb0cd8..fac1277602 100644 --- a/ecal/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp +++ b/ecal/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp @@ -18,7 +18,6 @@ */ #include -#include #include #include @@ -38,7 +37,7 @@ int main() // create ping service client const std::string service_name("ping service"); const std::string method_name ("Ping"); - eCAL::v5::CServiceClient ping_client(service_name); + eCAL::CServiceClient ping_client(service_name); // waiting for service while (eCAL::Ok() && !ping_client.IsConnected()) @@ -97,7 +96,7 @@ int main() { // call Ping service method eCAL::ServiceResponseVecT service_response_vec; - if (ping_client.Call("Ping", ping_request, -1, &service_response_vec)) + if (ping_client.CallWithResponse("Ping", ping_request, -1, service_response_vec)) { std::cout << '\n' << "PingService::Ping method called with message (JSON) : " << req_json << '\n'; diff --git a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp index d63226a3a8..1e18d876e2 100644 --- a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp +++ b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp @@ -52,7 +52,7 @@ enum { namespace { - typedef std::vector> ServiceVecT; + typedef std::vector> ServiceVecT; typedef std::vector> ClientVecT; #if DO_LOGGING @@ -177,15 +177,12 @@ TEST(core_cpp_clientserver, ServerConnectEvent) // initialize eCAL API eCAL::Initialize("clientserver base connect event callback"); - // create server - eCAL::CServiceServer server("service"); - // server event callback for connect events atomic_signalable event_connected_fired (0); atomic_signalable event_disconnected_fired(0); - auto event_callback = [&](const struct eCAL::SServerEventCallbackData* data_) -> void + auto event_callback = [&](const eCAL::Registration::SServiceMethodId& /*service_id_*/, const struct eCAL::SServerEventCallbackData& data_) -> void { - switch (data_->type) + switch (data_.type) { case server_event_connected: #if DO_LOGGING @@ -203,12 +200,12 @@ TEST(core_cpp_clientserver, ServerConnectEvent) break; } }; - // attach event - server.AddEventCallback(server_event_connected, std::bind(event_callback, std::placeholders::_2)); - server.AddEventCallback(server_event_disconnected, std::bind(event_callback, std::placeholders::_2)); + + // create server + eCAL::CServiceServer server("service", event_callback); // check events - eCAL::Process::SleepMS(1000); + eCAL::Process::SleepMS(CMN_REGISTRATION_REFRESH_MS); EXPECT_EQ(0, event_connected_fired.get()); EXPECT_EQ(0, event_disconnected_fired.get()); @@ -216,31 +213,23 @@ TEST(core_cpp_clientserver, ServerConnectEvent) { eCAL::CServiceClient client1("service"); + // one client connected, no client disconnected event_connected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); EXPECT_EQ(1, event_connected_fired.get()); EXPECT_EQ(0, event_disconnected_fired.get()); eCAL::CServiceClient client2("service"); - // TODO: Service API should trigger connect event with every new connection (client side is acting this way!) - //event_disconnected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); - //EXPECT_EQ(2, event_connected_fired.get()); - //EXPECT_EQ(0, event_disconnected_fired.get()); - - eCAL::Process::SleepMS(2000); - EXPECT_EQ(1, event_connected_fired.get()); + // two clients connected, no client disconnected + event_disconnected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); + EXPECT_EQ(2, event_connected_fired.get()); EXPECT_EQ(0, event_disconnected_fired.get()); } - // TODO: Service API should trigger disconnect event with every single disconnection (client side is acting this way!) - //event_disconnected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); - //EXPECT_EQ(2, event_connected_fired.get()); - //EXPECT_EQ(2, event_disconnected_fired.get()); - - // wait for disconnection - event_disconnected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); - EXPECT_EQ(1, event_connected_fired.get()); - EXPECT_EQ(1, event_disconnected_fired.get()); + // two clients connected, two clients disconnected + event_disconnected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); + EXPECT_EQ(2, event_connected_fired.get()); + EXPECT_EQ(2, event_disconnected_fired.get()); // finalize eCAL API eCAL::Finalize(); @@ -281,8 +270,10 @@ TEST(core_cpp_clientserver, ClientServerBaseCallback) // add method callbacks for (const auto& service : service_vec) { - service->AddMethodCallback("foo::method1", "foo::req_type1", "foo::resp_type1", method_callback); - service->AddMethodCallback("foo::method2", "foo::req_type2", "foo::resp_type2", method_callback); + eCAL::SServiceMethodInformation method1_info{ {"foo::req_type1", "", ""}, {"foo::resp_type1", "", ""}}; + eCAL::SServiceMethodInformation method2_info{ {"foo::req_type2", "", ""}, {"foo::resp_type2", "", ""} }; + service->AddMethodCallback("foo::method1", method1_info, method_callback); + service->AddMethodCallback("foo::method2", method2_info, method_callback); } // create service clients @@ -384,8 +375,10 @@ TEST(core_cpp_clientserver, ClientServerBaseCallbackTimeout) // add method callbacks for (const auto& service : service_vec) { - service->AddMethodCallback("foo::method1", "foo::req_type1", "foo::resp_type1", method_callback); - service->AddMethodCallback("foo::method2", "foo::req_type2", "foo::resp_type2", method_callback); + eCAL::SServiceMethodInformation method1_info{ {"foo::req_type1", "", ""}, {"foo::resp_type1", "", ""} }; + eCAL::SServiceMethodInformation method2_info{ {"foo::req_type2", "", ""}, {"foo::resp_type2", "", ""} }; + service->AddMethodCallback("foo::method1", method1_info, method_callback); + service->AddMethodCallback("foo::method2", method2_info, method_callback); } // event callback for timeout event @@ -499,7 +492,7 @@ TEST(core_cpp_clientserver, ClientServerBaseCallbackTimeout) methods_called++; } } - eCAL::Process::SleepMS(1000); + eCAL::Process::SleepMS(CMN_REGISTRATION_REFRESH_MS); EXPECT_EQ(false, success); EXPECT_EQ(0, responses_executed); @@ -535,8 +528,10 @@ TEST(core_cpp_clientserver, ClientServerBaseAsyncCallback) }; // add callback for client request - server.AddMethodCallback("foo::method1", "foo::req_type1", "foo::resp_type1", method_callback); - server.AddMethodCallback("foo::method2", "foo::req_type2", "foo::resp_type2", method_callback); + eCAL::SServiceMethodInformation method1_info{ {"foo::req_type1", "", ""}, {"foo::resp_type1", "", ""} }; + eCAL::SServiceMethodInformation method2_info{ {"foo::req_type2", "", ""}, {"foo::resp_type2", "", ""} }; + server.AddMethodCallback("foo::method1", method1_info, method_callback); + server.AddMethodCallback("foo::method2", method2_info, method_callback); // create service client eCAL::CServiceClient client("service"); @@ -598,7 +593,7 @@ TEST(core_cpp_clientserver, ClientServerBaseAsync) atomic_signalable num_service_callbacks_finished(0); int service_callback_time_ms(0); - auto service_callback = [&](const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_) -> int + auto method_callback = [&](const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_) -> int { eCAL::Process::SleepMS(service_callback_time_ms); PrintRequest(method_, req_type_, resp_type_, request_); @@ -608,8 +603,10 @@ TEST(core_cpp_clientserver, ClientServerBaseAsync) }; // add callback for client request - server.AddMethodCallback("foo::method1", "foo::req_type1", "foo::resp_type1", service_callback); - server.AddMethodCallback("foo::method2", "foo::req_type2", "foo::resp_type2", service_callback); + eCAL::SServiceMethodInformation method1_info{ {"foo::req_type1", "", ""}, {"foo::resp_type1", "", ""} }; + eCAL::SServiceMethodInformation method2_info{ {"foo::req_type2", "", ""}, {"foo::resp_type2", "", ""} }; + server.AddMethodCallback("foo::method1", method1_info, method_callback); + server.AddMethodCallback("foo::method2", method2_info, method_callback); // create service client eCAL::CServiceClient client("service"); @@ -717,8 +714,10 @@ TEST(core_cpp_clientserver, ClientServerBaseBlocking) // add method callback for (const auto& service : service_vec) { - service->AddMethodCallback("foo::method1", "foo::req_type1", "foo::resp_type1", method_callback); - service->AddMethodCallback("foo::method2", "foo::req_type2", "foo::resp_type2", method_callback); + eCAL::SServiceMethodInformation method1_info{ {"foo::req_type1", "", ""}, {"foo::resp_type1", "", ""} }; + eCAL::SServiceMethodInformation method2_info{ {"foo::req_type2", "", ""}, {"foo::resp_type2", "", ""} }; + service->AddMethodCallback("foo::method1", method1_info, method_callback); + service->AddMethodCallback("foo::method2", method2_info, method_callback); } // create service clients @@ -776,8 +775,8 @@ TEST(core_cpp_clientserver, ClientServerBaseBlocking) // remove method callback for (const auto& service : service_vec) { - service->RemMethodCallback("foo::method1"); - service->RemMethodCallback("foo::method2"); + service->RemoveMethodCallback("foo::method1"); + service->RemoveMethodCallback("foo::method2"); } // finalize eCAL API @@ -810,8 +809,10 @@ TEST(core_cpp_clientserver, NestedRPCCall) }; // add callback for client request - server.AddMethodCallback("foo::method1", "foo::req_type1", "foo::resp_type1", method_callback); - server.AddMethodCallback("foo::method2", "foo::req_type2", "foo::resp_type2", method_callback); + eCAL::SServiceMethodInformation method1_info{ {"foo::req_type1", "", ""}, {"foo::resp_type1", "", ""} }; + eCAL::SServiceMethodInformation method2_info{ {"foo::req_type2", "", ""}, {"foo::resp_type2", "", ""} }; + server.AddMethodCallback("foo::method1", method1_info, method_callback); + server.AddMethodCallback("foo::method2", method2_info, method_callback); // create service client eCAL::CServiceClient client1("service"); @@ -851,8 +852,8 @@ TEST(core_cpp_clientserver, NestedRPCCall) EXPECT_EQ(methods_called, responses_executed); // remove method callback - server.RemMethodCallback("foo::method1"); - server.RemMethodCallback("foo::method2"); + server.RemoveMethodCallback("foo::method1"); + server.RemoveMethodCallback("foo::method2"); // finalize eCAL API eCAL::Finalize(); diff --git a/ecal/tests/cpp/clientserver_v5_test/src/clientserver_v5_test.cpp b/ecal/tests/cpp/clientserver_v5_test/src/clientserver_v5_test.cpp index 3ef4f43d26..79e0942db0 100644 --- a/ecal/tests/cpp/clientserver_v5_test/src/clientserver_v5_test.cpp +++ b/ecal/tests/cpp/clientserver_v5_test/src/clientserver_v5_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -48,7 +49,7 @@ enum { namespace { - typedef std::vector> ServiceVecT; + typedef std::vector> ServiceVecT; typedef std::vector> ClientVecT; #if DO_LOGGING @@ -146,25 +147,20 @@ TEST(core_cpp_clientserver_v5, ClientConnectEvent) // create server { - eCAL::CServiceServer server1("service"); + eCAL::v5::CServiceServer server1("service"); - event_connected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::seconds(5)); + event_connected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); EXPECT_EQ(1, event_connected_fired.get()); EXPECT_EQ(0, event_disconnected_fired.get()); - eCAL::CServiceServer server2("service"); + eCAL::v5::CServiceServer server2("service"); - event_connected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::seconds(5)); + event_connected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); EXPECT_EQ(2, event_connected_fired.get()); EXPECT_EQ(0, event_disconnected_fired.get()); } - // eCAL doesn't use the service callback, which would detect the disconnection - // instantly. Instead, eCAL waits for an entire monitoring loop, then tries - // to reconnect and then fires the disconnect callback itself, once that - // reconnection failes. That takes a lot of time, so we need to wait for a - // long time here. - event_disconnected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::seconds(20)); + event_disconnected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); EXPECT_EQ(2, event_connected_fired.get()); EXPECT_EQ(2, event_disconnected_fired.get()); @@ -182,7 +178,7 @@ TEST(core_cpp_clientserver_v5, ServerConnectEvent) eCAL::Initialize("clientserver base connect event callback"); // create server - eCAL::CServiceServer server("service"); + eCAL::v5::CServiceServer server("service"); // add server event callback for connect event atomic_signalable event_connected_fired (0); @@ -220,25 +216,20 @@ TEST(core_cpp_clientserver_v5, ServerConnectEvent) { eCAL::v5::CServiceClient client1("service"); - event_connected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::seconds(5)); + event_connected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); EXPECT_EQ(1, event_connected_fired.get()); EXPECT_EQ(0, event_disconnected_fired.get()); eCAL::v5::CServiceClient client2("service"); - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); - EXPECT_EQ(1, event_connected_fired.get()); + event_disconnected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); + EXPECT_EQ(2, event_connected_fired.get()); EXPECT_EQ(0, event_disconnected_fired.get()); } - // eCAL doesn't use the service callback, which would detect the disconnection - // instantly. Instead, eCAL waits for an entire monitoring loop, then tries - // to reconnect and then fires the disconnect callback itself, once that - // reconnection failes. That takes a lot of time, so we need to wait for a - // long time here. - event_disconnected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::seconds(10)); - EXPECT_EQ(1, event_connected_fired.get()); - EXPECT_EQ(1, event_disconnected_fired.get()); + event_disconnected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(3 * CMN_REGISTRATION_REFRESH_MS)); + EXPECT_EQ(2, event_connected_fired.get()); + EXPECT_EQ(2, event_disconnected_fired.get()); // finalize eCAL API eCAL::Finalize(); @@ -261,7 +252,7 @@ TEST(core_cpp_clientserver_v5, ClientServerBaseCallback) ServiceVecT service_vec; for (auto s = 0; s < num_services; ++s) { - service_vec.push_back(std::make_shared("service")); + service_vec.push_back(std::make_shared("service")); } // method callback function @@ -370,7 +361,7 @@ TEST(core_cpp_clientserver_v5, ClientServerBaseCallbackTimeout) ServiceVecT service_vec; for (auto s = 0; s < num_services; ++s) { - service_vec.push_back(std::make_shared("service")); + service_vec.push_back(std::make_shared("service")); } // method callback function @@ -531,7 +522,7 @@ TEST(core_cpp_clientserver_v5, ClientServerBaseAsyncCallback) eCAL::Initialize("clientserver base async callback test"); // create service server - eCAL::CServiceServer server("service"); + eCAL::v5::CServiceServer server("service"); // method callback function std::atomic methods_executed(0); @@ -604,7 +595,7 @@ TEST(core_cpp_clientserver_v5, ClientServerBaseAsync) eCAL::Initialize("clientserver base async callback test with timeout"); // create service server - eCAL::CServiceServer server("service"); + eCAL::v5::CServiceServer server("service"); // method callback function atomic_signalable num_service_callbacks_finished(0); @@ -716,7 +707,7 @@ TEST(core_cpp_clientserver_v5, ClientServerBaseBlocking) ServiceVecT service_vec; for (auto s = 0; s < num_services; ++s) { - service_vec.push_back(std::make_shared("service")); + service_vec.push_back(std::make_shared("service")); } // method callback function @@ -811,7 +802,7 @@ TEST(core_cpp_clientserver_v5, NestedRPCCall) eCAL::Initialize("nested rpc call test"); // create service server - eCAL::CServiceServer server("service"); + eCAL::v5::CServiceServer server("service"); // request callback function std::atomic methods_executed(0); diff --git a/ecal/tests/cpp/registration_test_public/src/registration_getclients.cpp b/ecal/tests/cpp/registration_test_public/src/registration_getclients.cpp index 83e0174efb..091b8127e4 100644 --- a/ecal/tests/cpp/registration_test_public/src/registration_getclients.cpp +++ b/ecal/tests/cpp/registration_test_public/src/registration_getclients.cpp @@ -18,7 +18,6 @@ */ #include -#include #include #include @@ -65,7 +64,7 @@ TEST_P(ClientsTestFixture, ClientExpiration) service_method_info.request_type.descriptor = "foo::req_desc"; service_method_info.response_type.name = "foo::resp_type"; service_method_info.response_type.descriptor = "foo::resp_desc"; - const eCAL::v5::CServiceClient client("foo::service", { {"foo::method", service_method_info} }); + const eCAL::CServiceClient client("foo::service", { {"foo::method", service_method_info} }); // let's register eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); @@ -117,7 +116,7 @@ TEST_P(ClientsTestFixture, GetClientIDs) service_method_info.request_type.descriptor = "foo::req_desc"; service_method_info.response_type.name = "foo::resp_type"; service_method_info.response_type.descriptor = "foo::resp_desc"; - const eCAL::v5::CServiceClient client("foo::service", { {"foo::method", service_method_info} }); + const eCAL::CServiceClient client("foo::service", { {"foo::method", service_method_info} }); // let's register eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); diff --git a/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp b/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp index f66b4447ca..acd25ac68f 100644 --- a/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp +++ b/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp @@ -61,7 +61,7 @@ TEST_P(ServicesTestFixture, ServiceExpiration) { // create service eCAL::CServiceServer service("foo::service"); - service.AddDescription("foo::method", "foo::req_type", "foo::req_desc", "foo::resp_type", "foo::resp_desc"); + service.AddMethodCallback("foo::method", { { "foo::req_type", "foo::req_desc" }, { "foo::resp_type", "foo::resp_desc" } }, eCAL::MethodCallbackT()); // let's register eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); @@ -110,18 +110,13 @@ TEST_P(ServicesTestFixture, GetServiceIDs) // create server eCAL::CServiceServer service("foo::service"); - // add description + // add method eCAL::SServiceMethodInformation service_method_info; service_method_info.request_type.name = "foo::req_type"; service_method_info.request_type.descriptor = "foo::req_desc"; service_method_info.response_type.name = "foo::resp_type"; service_method_info.response_type.descriptor = "foo::resp_desc"; - - service.AddDescription("foo::method", - service_method_info.request_type.name, - service_method_info.request_type.descriptor, - service_method_info.response_type.name, - service_method_info.response_type.descriptor); + service.AddMethodCallback("method", service_method_info, eCAL::MethodCallbackT()); // let's register eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); diff --git a/lang/csharp/Continental.eCAL.Core/ecal_clr.cpp b/lang/csharp/Continental.eCAL.Core/ecal_clr.cpp index 544cf4bf1a..6e520b3df1 100644 --- a/lang/csharp/Continental.eCAL.Core/ecal_clr.cpp +++ b/lang/csharp/Continental.eCAL.Core/ecal_clr.cpp @@ -380,13 +380,13 @@ void Subscriber::OnReceiveUnsafe(const char* topic_name_, const ::eCAL::SReceive ///////////////////////////////////////////////////////////////////////////// // ServiceServer ///////////////////////////////////////////////////////////////////////////// -ServiceServer::ServiceServer() : m_serv(new ::eCAL::CServiceServer()) +ServiceServer::ServiceServer() : m_serv(new ::eCAL::v5::CServiceServer()) { } ServiceServer::ServiceServer(System::String^ server_name_) { - m_serv = new ::eCAL::CServiceServer(StringToStlString(server_name_)); + m_serv = new ::eCAL::v5::CServiceServer(StringToStlString(server_name_)); } ServiceServer::~ServiceServer() diff --git a/lang/csharp/Continental.eCAL.Core/ecal_clr.h b/lang/csharp/Continental.eCAL.Core/ecal_clr.h index d48a4f7de2..b1f97ab2b5 100644 --- a/lang/csharp/Continental.eCAL.Core/ecal_clr.h +++ b/lang/csharp/Continental.eCAL.Core/ecal_clr.h @@ -25,6 +25,7 @@ #pragma once #include #include +#include using namespace System; using namespace System::Collections::Generic; @@ -430,7 +431,7 @@ namespace Continental bool RemMethodCallback(String^ methodName, MethodCallback^ callback_); private: - ::eCAL::CServiceServer* m_serv; + ::eCAL::v5::CServiceServer* m_serv; /** * @brief managed callbacks that will get executed during the eCAL method callback **/ diff --git a/lang/python/core/src/ecal_clang.cpp b/lang/python/core/src/ecal_clang.cpp index 214f7db3b3..10b4d014dc 100644 --- a/lang/python/core/src/ecal_clang.cpp +++ b/lang/python/core/src/ecal_clang.cpp @@ -23,6 +23,7 @@ #include #include +#include #include "ecal_clang.h" @@ -555,7 +556,7 @@ bool sub_rem_event_callback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type /****************************************/ ECAL_HANDLE server_create(const char* service_name_) { - auto* server = new eCAL::CServiceServer; + auto* server = new eCAL::v5::CServiceServer; if (!server->Create(service_name_)) { delete server; @@ -569,7 +570,7 @@ ECAL_HANDLE server_create(const char* service_name_) /****************************************/ bool server_destroy(ECAL_HANDLE handle_) { - auto* server = static_cast(handle_); + auto* server = static_cast(handle_); if (server != nullptr) { delete server; @@ -597,7 +598,7 @@ static int g_server_method_callback(const std::string& method_, const std::strin bool server_add_method_callback(ECAL_HANDLE handle_, const char* method_name_, const char* req_type_, const char* resp_type_, const MethodCallbackCT callback_, void* par_) { - auto* server = static_cast(handle_); + auto* server = static_cast(handle_); if (server != nullptr) { auto callback = std::bind(g_server_method_callback, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, callback_, par_); @@ -614,7 +615,7 @@ bool server_add_method_callback(ECAL_HANDLE handle_, const char* method_name_, /****************************************/ bool server_rem_method_callback(ECAL_HANDLE handle_, const char* method_name_) { - auto* server = static_cast(handle_); + auto* server = static_cast(handle_); if (server != nullptr) { return(server->RemMethodCallback(method_name_)); diff --git a/lang/python/core/src/ecal_wrap.cxx b/lang/python/core/src/ecal_wrap.cxx index 16fbb4699f..03763cc011 100644 --- a/lang/python/core/src/ecal_wrap.cxx +++ b/lang/python/core/src/ecal_wrap.cxx @@ -26,6 +26,7 @@ #include #include +#include #include "ecal_clang.h" @@ -569,7 +570,7 @@ static int c_server_method_callback(const std::string& method_name_, const std:: PyTuple_SetItem(args, 2, resp_type); PyTuple_SetItem(args, 3, request); - eCAL::CServiceServer* server = (eCAL::CServiceServer*)handle_; + eCAL::v5::CServiceServer* server = (eCAL::v5::CServiceServer*)handle_; std::string server_method = server->GetServiceName(); server_method = method_name_ + "@" + server_method; @@ -612,7 +613,7 @@ PyObject* server_add_method_callback(PyObject* /*self*/, PyObject* args) // (s if (!PyArg_ParseTuple(args, "nsssO", &server_handle, &method_name, &req_type, &resp_type, &cb_func)) return nullptr; - eCAL::CServiceServer* server = (eCAL::CServiceServer*)server_handle; + eCAL::v5::CServiceServer* server = (eCAL::v5::CServiceServer*)server_handle; if (!server) { return(Py_BuildValue("is", -1, "server invalid")); @@ -666,7 +667,7 @@ PyObject* server_rem_method_callback(PyObject* /*self*/, PyObject* args) if (!PyArg_ParseTuple(args, "ns", &server_handle, &method_name)) return nullptr; - eCAL::CServiceServer* server = (eCAL::CServiceServer*)server_handle; + eCAL::v5::CServiceServer* server = (eCAL::v5::CServiceServer*)server_handle; if (!server) { return(Py_BuildValue("is", -1, "server invalid"));