From 91741af6f4400471a6740f14ee6b4becbffa0da5 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:48:15 +0100 Subject: [PATCH 01/21] v5 renaming --- ecal/core/CMakeLists.txt | 8 +- ecal/core/include/ecal/ecal_server.h | 172 ------ ecal/core/include/ecal/ecal_server_v5.h | 175 ++++++ ecal/core/src/service/ecal_service_server.cpp | 209 -------- .../src/service/ecal_service_server_impl.cpp | 504 ----------------- .../src/service/ecal_service_server_impl.h | 131 ----- .../src/service/ecal_service_server_v5.cpp | 212 ++++++++ .../service/ecal_service_server_v5_impl.cpp | 507 ++++++++++++++++++ .../src/service/ecal_service_server_v5_impl.h | 134 +++++ 9 files changed, 1032 insertions(+), 1020 deletions(-) delete mode 100644 ecal/core/include/ecal/ecal_server.h create mode 100644 ecal/core/include/ecal/ecal_server_v5.h delete mode 100644 ecal/core/src/service/ecal_service_server.cpp delete mode 100644 ecal/core/src/service/ecal_service_server_impl.cpp delete mode 100644 ecal/core/src/service/ecal_service_server_impl.h create mode 100644 ecal/core/src/service/ecal_service_server_v5.cpp create mode 100644 ecal/core/src/service/ecal_service_server_v5_impl.cpp create mode 100644 ecal/core/src/service/ecal_service_server_v5_impl.h diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt index f6b7b313eb..ca8ead9bc9 100644 --- a/ecal/core/CMakeLists.txt +++ b/ecal/core/CMakeLists.txt @@ -385,9 +385,9 @@ if(ECAL_CORE_SERVICE) src/service/ecal_service_client_v5.cpp src/service/ecal_service_client_v5_impl.cpp src/service/ecal_service_client_v5_impl.h - 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 ) @@ -535,7 +535,7 @@ set(ecal_header_cmn include/ecal/ecal_process_severity.h 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/ecal_server.h b/ecal/core/include/ecal/ecal_server.h deleted file mode 100644 index d73f671aae..0000000000 --- a/ecal/core/include/ecal/ecal_server.h +++ /dev/null @@ -1,172 +0,0 @@ -/* ========================= 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.h - * @brief eCAL service interface -**/ - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include - -namespace eCAL -{ - 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; - bool m_created; - }; -} 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..759f40caee --- /dev/null +++ b/ecal/core/include/ecal/ecal_server_v5.h @@ -0,0 +1,175 @@ +/* ========================= 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; + bool m_created; + }; + } +} diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp deleted file mode 100644 index 0545deee11..0000000000 --- a/ecal/core/src/service/ecal_service_server.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL service server interface -**/ - -#include -#include - -#include "ecal_servicegate.h" -#include "ecal_global_accessors.h" -#include "ecal_service_server_impl.h" - -namespace eCAL -{ - /** - * @brief Service Server class. - **/ - - /** - * @brief Constructor. - **/ - CServiceServer::CServiceServer() : - m_service_server_impl(nullptr), - m_created(false) - { - } - - /** - * @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_); - } - - /** - * @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); - - // 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); - } - - /** - * @brief Destroys this object. - * - * @return True if successful. - **/ - bool CServiceServer::Destroy() - { - 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_) - { - 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_); - } - - /** - * @brief Remove client request callback. - * - * @return True if successful. - **/ - bool CServiceServer::RemMethodCallback(const std::string& method_) - { - if (!m_created) return false; - return m_service_server_impl->RemMethodCallback(method_); - } - - /** - * @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_) - { - if (!m_created) return false; - return m_service_server_impl->AddEventCallback(type_, callback_); - } - - /** - * @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_) - { - if (!m_created) return false; - return m_service_server_impl->RemEventCallback(type_); - } - - /** - * @brief Retrieve service name. - * - * @return The service name. - **/ - std::string CServiceServer::GetServiceName() - { - if (!m_created) return ""; - return m_service_server_impl->GetServiceName(); - } - - /** - * @brief Check connection state. - * - * @return True if connected, false if not. - **/ - bool CServiceServer::IsConnected() - { - if (!m_created) 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 deleted file mode 100644 index 3f9939d251..0000000000 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ /dev/null @@ -1,504 +0,0 @@ -/* ========================= 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 -**/ - -#include "registration/ecal_registration_provider.h" -#include "ecal_global_accessors.h" -#include "ecal_service_server_impl.h" -#include "ecal_service_singleton_manager.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_) - { - auto instance = std::shared_ptr (new CServiceServerImpl()); - instance->Start(service_name_); - return instance; - } - - CServiceServerImpl::CServiceServerImpl() : - m_created(false) - { - } - - CServiceServerImpl::~CServiceServerImpl() - { - Stop(); - } - - bool CServiceServerImpl::Start(const std::string& service_name_) - { - 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); - - // mark as created - m_created = true; - - return(true); - } - - bool CServiceServerImpl::Stop() - { - if (!m_created) return(false); - - // reset method callback map - { - std::lock_guard const lock(m_method_map_sync); - m_method_map.clear(); - } - - // reset event callback map - { - std::lock_guard const lock(m_event_callback_map_sync); - m_event_callback_map.clear(); - } - - if (m_tcp_server) - m_tcp_server->stop(); - - // mark as no more created - m_created = false; - - // and unregister this service - Unregister(); - - // reset internals - m_service_name.clear(); - m_service_id.clear(); - - { - const std::lock_guard connected_lock(m_connected_mutex); - m_connected = false; - } - - return(true); - } - - bool CServiceServerImpl::AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_) - { - { - 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; - } - } - - return true; - } - - // 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; - { - 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; - } - } - - return true; - } - - // remove callback function for server method calls - bool CServiceServerImpl::RemMethodCallback(const std::string& method_) - { - std::lock_guard const lock(m_method_map_sync); - - auto iter = m_method_map.find(method_); - if (iter != m_method_map.end()) - { - m_method_map.erase(iter); - return true; - } - return false; - } - - // add callback function for server events - bool CServiceServerImpl::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) - { - if (!m_created) return false; - - // store event callback - { - std::lock_guard const lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::AddEventCallback"); -#endif - m_event_callback_map[type_] = std::move(callback_); - } - - return true; - } - - // remove callback function for server events - bool CServiceServerImpl::RemEventCallback(eCAL_Server_Event type_) - { - if (!m_created) return false; - - // reset event callback - { - std::lock_guard const lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::RemEventCallback"); -#endif - m_event_callback_map[type_] = nullptr; - } - - return true; - } - - // check connection state - bool CServiceServerImpl::IsConnected() - { - if (!m_created) return false; - - return (m_tcp_server && m_tcp_server->is_connected()); - } - - // 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 - } - - // called by eCAL:CServiceGate every second to update registration layer - Registration::Sample CServiceServerImpl::GetRegistration() - { - return GetRegistrationSample(); - } - - 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; - - 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; - 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); - for (const auto& iter : m_method_map) - { - Service::Method method; - method.mname = iter.first; - method.req_type = iter.second.method.req_type; - method.req_desc = iter.second.method.req_desc; - method.resp_type = iter.second.method.resp_type; - method.resp_desc = iter.second.method.resp_desc; - method.call_count = iter.second.method.call_count; - service.methods.push_back(method); - } - } - - return ecal_reg_sample; - } - - Registration::Sample CServiceServerImpl::GetUnregistrationSample() - { - // create registration sample - Registration::Sample ecal_reg_sample; - ecal_reg_sample.cmd_type = bct_unreg_service; - - 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; - service.version = m_server_version; - service.pname = Process::GetProcessName(); - service.uname = Process::GetUnitName(); - service.sname = m_service_name; - - return ecal_reg_sample; - } - - void CServiceServerImpl::Register() - { -#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"); -#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; - response_header.hname = Process::GetHostName(); - response_header.sname = m_service_name; - response_header.sid = m_service_id; - - // try to parse request - 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"); - - response_header.state = Service::eMethodCallState::failed; - std::string const 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) - // serialize response and return "request message could not be parsed" - SerializeToBuffer(response, response_pb_); - - // Return Failed (error_code = -1), as parsing the request failed. The - // return value is not propagated to the remote caller. - return -1; - } - - // get method - SMethod method; - const auto& request_header = request.header; - response_header.mname = request_header.mname; - { - std::lock_guard const lock(m_method_map_sync); - - 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 + "'"; - 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) - // serialize response and return "method not found" - SerializeToBuffer(response, response_pb_); - - // Return Success (error_code = 0), as parsing the request worked. The - // return value is not propagated to the remote caller. - return 0; - } - 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 - method = requested_method_iterator->second; - } - } - - // 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); - - // set method call state 'executed' - response_header.state = Service::eMethodCallState::executed; - // set method response and return state - 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) - // serialize response and return "method not found" - SerializeToBuffer(response, response_pb_); - - // return success (error code 0) - return 0; - } - - void CServiceServerImpl::EventCallback(eCAL_Server_Event event_, 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"); - } - } - } - - if (mode_changed) - { - // 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); - } - } - } -} diff --git a/ecal/core/src/service/ecal_service_server_impl.h b/ecal/core/src/service/ecal_service_server_impl.h deleted file mode 100644 index 0a56ab6657..0000000000 --- a/ecal/core/src/service/ecal_service_server_impl.h +++ /dev/null @@ -1,131 +0,0 @@ -/* ========================= 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 -**/ - -#pragma once - -#include -#include -#include -#include - -#include "serialization/ecal_serialize_sample_registration.h" -#include "serialization/ecal_struct_service.h" - -#include -#include -#include -#include -#include - -namespace eCAL -{ - /** - * @brief Service server implementation class. - **/ - class CServiceServerImpl : public std::enable_shared_from_this - { - public: - static std::shared_ptr CreateInstance(); - static std::shared_ptr CreateInstance(const std::string& service_name_); - - private: - CServiceServerImpl(); - - 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_); - - // 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(); - - // called by the eCAL::CServiceGate to register a client - void RegisterClient(const std::string& key_, const SClientAttr& client_); - - // called by eCAL:CServiceGate every second to update registration layer - Registration::Sample GetRegistration(); - - std::string GetServiceName() { return m_service_name; }; - - protected: - void Register(); - void Unregister(); - - 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. - */ - int RequestCallback(const std::string& request_pb_, std::string& response_pb_); - void EventCallback(eCAL_Server_Event event_, const std::string& message_); - - std::shared_ptr m_tcp_server; - - static constexpr int m_server_version = 1; - - std::string m_service_name; - std::string m_service_id; - - struct SMethod - { - Service::Method method; - MethodCallbackT callback; - }; - - using MethodMapT = std::map; - std::mutex m_method_map_sync; - 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; - - std::atomic m_created; - }; -} 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..b665bdb12e --- /dev/null +++ b/ecal/core/src/service/ecal_service_server_v5.cpp @@ -0,0 +1,212 @@ +/* ========================= 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 +**/ + +#include +#include + +#include "ecal_servicegate.h" +#include "ecal_global_accessors.h" +#include "ecal_service_server_v5_impl.h" + +namespace eCAL +{ + namespace v5 + { + /** + * @brief Service Server class. + **/ + + /** + * @brief Constructor. + **/ + CServiceServer::CServiceServer() : + m_service_server_impl(nullptr), + m_created(false) + { + } + + /** + * @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_); + } + + /** + * @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); + + // 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); + } + + /** + * @brief Destroys this object. + * + * @return True if successful. + **/ + bool CServiceServer::Destroy() + { + 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_) + { + 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_); + } + + /** + * @brief Remove client request callback. + * + * @return True if successful. + **/ + bool CServiceServer::RemMethodCallback(const std::string& method_) + { + if (!m_created) return false; + return m_service_server_impl->RemMethodCallback(method_); + } + + /** + * @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_) + { + if (!m_created) return false; + return m_service_server_impl->AddEventCallback(type_, callback_); + } + + /** + * @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_) + { + if (!m_created) return false; + return m_service_server_impl->RemEventCallback(type_); + } + + /** + * @brief Retrieve service name. + * + * @return The service name. + **/ + std::string CServiceServer::GetServiceName() + { + if (!m_created) return ""; + return m_service_server_impl->GetServiceName(); + } + + /** + * @brief Check connection state. + * + * @return True if connected, false if not. + **/ + bool CServiceServer::IsConnected() + { + if (!m_created) 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..4770b5b425 --- /dev/null +++ b/ecal/core/src/service/ecal_service_server_v5_impl.cpp @@ -0,0 +1,507 @@ +/* ========================= 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 +**/ + +#include "registration/ecal_registration_provider.h" +#include "ecal_global_accessors.h" +#include "ecal_service_server_v5_impl.h" +#include "ecal_service_singleton_manager.h" +#include "serialization/ecal_serialize_service.h" + +#include +#include +#include +#include +#include +#include + +namespace eCAL +{ + namespace v5 + { + std::shared_ptr CServiceServerImpl::CreateInstance() + { + return std::shared_ptr(new CServiceServerImpl()); + } + + std::shared_ptr CServiceServerImpl::CreateInstance(const std::string& service_name_) + { + auto instance = std::shared_ptr (new CServiceServerImpl()); + instance->Start(service_name_); + return instance; + } + + CServiceServerImpl::CServiceServerImpl() : + m_created(false) + { + } + + CServiceServerImpl::~CServiceServerImpl() + { + Stop(); + } + + bool CServiceServerImpl::Start(const std::string& service_name_) + { + 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); + + // mark as created + m_created = true; + + return(true); + } + + bool CServiceServerImpl::Stop() + { + if (!m_created) return(false); + + // reset method callback map + { + std::lock_guard const lock(m_method_map_sync); + m_method_map.clear(); + } + + // reset event callback map + { + std::lock_guard const lock(m_event_callback_map_sync); + m_event_callback_map.clear(); + } + + if (m_tcp_server) + m_tcp_server->stop(); + + // mark as no more created + m_created = false; + + // and unregister this service + Unregister(); + + // reset internals + m_service_name.clear(); + m_service_id.clear(); + + { + const std::lock_guard connected_lock(m_connected_mutex); + m_connected = false; + } + + return(true); + } + + bool CServiceServerImpl::AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_) + { + { + 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; + } + } + + return true; + } + + // 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; + { + 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; + } + } + + return true; + } + + // remove callback function for server method calls + bool CServiceServerImpl::RemMethodCallback(const std::string& method_) + { + std::lock_guard const lock(m_method_map_sync); + + auto iter = m_method_map.find(method_); + if (iter != m_method_map.end()) + { + m_method_map.erase(iter); + return true; + } + return false; + } + + // add callback function for server events + bool CServiceServerImpl::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) + { + if (!m_created) return false; + + // store event callback + { + std::lock_guard const lock(m_event_callback_map_sync); + #ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::AddEventCallback"); + #endif + m_event_callback_map[type_] = std::move(callback_); + } + + return true; + } + + // remove callback function for server events + bool CServiceServerImpl::RemEventCallback(eCAL_Server_Event type_) + { + if (!m_created) return false; + + // reset event callback + { + std::lock_guard const lock(m_event_callback_map_sync); + #ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::RemEventCallback"); + #endif + m_event_callback_map[type_] = nullptr; + } + + return true; + } + + // check connection state + bool CServiceServerImpl::IsConnected() + { + if (!m_created) return false; + + return (m_tcp_server && m_tcp_server->is_connected()); + } + + // 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 + } + + // called by eCAL:CServiceGate every second to update registration layer + Registration::Sample CServiceServerImpl::GetRegistration() + { + return GetRegistrationSample(); + } + + 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; + + 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; + 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); + for (const auto& iter : m_method_map) + { + Service::Method method; + method.mname = iter.first; + method.req_type = iter.second.method.req_type; + method.req_desc = iter.second.method.req_desc; + method.resp_type = iter.second.method.resp_type; + method.resp_desc = iter.second.method.resp_desc; + method.call_count = iter.second.method.call_count; + service.methods.push_back(method); + } + } + + return ecal_reg_sample; + } + + Registration::Sample CServiceServerImpl::GetUnregistrationSample() + { + // create registration sample + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_unreg_service; + + 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; + service.version = m_server_version; + service.pname = Process::GetProcessName(); + service.uname = Process::GetUnitName(); + service.sname = m_service_name; + + return ecal_reg_sample; + } + + void CServiceServerImpl::Register() + { + #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"); + #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; + response_header.hname = Process::GetHostName(); + response_header.sname = m_service_name; + response_header.sid = m_service_id; + + // try to parse request + 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"); + + response_header.state = Service::eMethodCallState::failed; + std::string const 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) + // serialize response and return "request message could not be parsed" + SerializeToBuffer(response, response_pb_); + + // Return Failed (error_code = -1), as parsing the request failed. The + // return value is not propagated to the remote caller. + return -1; + } + + // get method + SMethod method; + const auto& request_header = request.header; + response_header.mname = request_header.mname; + { + std::lock_guard const lock(m_method_map_sync); + + 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 + "'"; + 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) + // serialize response and return "method not found" + SerializeToBuffer(response, response_pb_); + + // Return Success (error_code = 0), as parsing the request worked. The + // return value is not propagated to the remote caller. + return 0; + } + 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 + method = requested_method_iterator->second; + } + } + + // 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); + + // set method call state 'executed' + response_header.state = Service::eMethodCallState::executed; + // set method response and return state + 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) + // serialize response and return "method not found" + SerializeToBuffer(response, response_pb_); + + // return success (error code 0) + return 0; + } + + void CServiceServerImpl::EventCallback(eCAL_Server_Event event_, 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"); + } + } + } + + if (mode_changed) + { + // 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); + } + } + } + } +} 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..8d0ec183f4 --- /dev/null +++ b/ecal/core/src/service/ecal_service_server_v5_impl.h @@ -0,0 +1,134 @@ +/* ========================= 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 +**/ + +#pragma once + +#include +#include +#include +#include + +#include "serialization/ecal_serialize_sample_registration.h" +#include "serialization/ecal_struct_service.h" + +#include +#include +#include +#include +#include + +namespace eCAL +{ + namespace v5 + { + /** + * @brief Service server implementation class. + **/ + class CServiceServerImpl : public std::enable_shared_from_this + { + public: + static std::shared_ptr CreateInstance(); + static std::shared_ptr CreateInstance(const std::string& service_name_); + + private: + CServiceServerImpl(); + + 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_); + + // 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(); + + // called by the eCAL::CServiceGate to register a client + void RegisterClient(const std::string& key_, const SClientAttr& client_); + + // called by eCAL:CServiceGate every second to update registration layer + Registration::Sample GetRegistration(); + + std::string GetServiceName() { return m_service_name; }; + + protected: + void Register(); + void Unregister(); + + 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. + */ + int RequestCallback(const std::string& request_pb_, std::string& response_pb_); + void EventCallback(eCAL_Server_Event event_, const std::string& message_); + + std::shared_ptr m_tcp_server; + + static constexpr int m_server_version = 1; + + std::string m_service_name; + std::string m_service_id; + + struct SMethod + { + Service::Method method; + MethodCallbackT callback; + }; + + using MethodMapT = std::map; + std::mutex m_method_map_sync; + 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; + + std::atomic m_created; + }; + } +} From e79fa88eda740ab2faab48010b79e72f84add182 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:51:37 +0100 Subject: [PATCH 02/21] intermediate commit --- ecal/core/CMakeLists.txt | 4 + ecal/core/include/ecal/ecal_server.h | 110 ++++ ecal/core/src/service/ecal_service_server.cpp | 63 +++ .../src/service/ecal_service_server_impl.cpp | 504 ++++++++++++++++++ .../src/service/ecal_service_server_impl.h | 131 +++++ .../src/service/ecal_service_server_v5.cpp | 83 +-- 6 files changed, 815 insertions(+), 80 deletions(-) create mode 100644 ecal/core/include/ecal/ecal_server.h create mode 100644 ecal/core/src/service/ecal_service_server.cpp create mode 100644 ecal/core/src/service/ecal_service_server_impl.cpp create mode 100644 ecal/core/src/service/ecal_service_server_impl.h diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt index ca8ead9bc9..626d931bab 100644 --- a/ecal/core/CMakeLists.txt +++ b/ecal/core/CMakeLists.txt @@ -385,6 +385,9 @@ if(ECAL_CORE_SERVICE) src/service/ecal_service_client_v5.cpp src/service/ecal_service_client_v5_impl.cpp src/service/ecal_service_client_v5_impl.h + 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 @@ -535,6 +538,7 @@ set(ecal_header_cmn include/ecal/ecal_process_severity.h 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 diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h new file mode 100644 index 0000000000..5a2d56a4d1 --- /dev/null +++ b/ecal/core/include/ecal/ecal_server.h @@ -0,0 +1,110 @@ +/* ========================= 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 +{ + class CServiceServerImpl; + + /** + * @brief Service Server wrapper class. + **/ + class ECAL_API_CLASS CServiceServer + { + public: + /** + * @brief Constructor. + * + * @param service_name_ Unique service name. + **/ + ECAL_API_EXPORTED_MEMBER + explicit CServiceServer(const std::string& service_name_, const ServerEventCallbackT callback_ = ServerEventCallbackT()); + + /** + * @brief Destructor. + **/ + ECAL_API_EXPORTED_MEMBER + virtual ~CServiceServer(); + + // Deleted copy constructor and copy assignment operator + CServiceServer(const CServiceServer&) = delete; + CServiceServer& operator=(const CServiceServer&) = delete; + + // Move constructor and move assignment operator + ECAL_API_EXPORTED_MEMBER CServiceServer(CServiceServer&& rhs) noexcept; + ECAL_API_EXPORTED_MEMBER CServiceServer& operator=(CServiceServer&& rhs) noexcept; + + /** + * @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 SServiceMethodInformation& method_info_, 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 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/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp new file mode 100644 index 0000000000..16f9189666 --- /dev/null +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -0,0 +1,63 @@ +/* ========================= 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 +**/ + +#include +#include + +#include "ecal_servicegate.h" +#include "ecal_global_accessors.h" +#include "ecal_service_server_impl.h" + +namespace eCAL +{ + CServiceServer::CServiceServer(const std::string& service_name_, const ServerEventCallbackT callback_) : + m_service_server_impl(nullptr) + { + } + + CServiceServer::~CServiceServer() + { + } + + bool CServiceServer::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) + { + return false; + } + + bool CServiceServer::RemMethodCallback(const std::string& method_) + { + return false; + } + + std::string CServiceServer::GetServiceName() + { + return ""; + //return m_service_server_impl->GetServiceName(); + } + + bool CServiceServer::IsConnected() + { + 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 new file mode 100644 index 0000000000..b56b08f3a9 --- /dev/null +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -0,0 +1,504 @@ +/* ========================= 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 +**/ + +#include "registration/ecal_registration_provider.h" +#include "ecal_global_accessors.h" +#include "ecal_service_server_impl.h" +#include "ecal_service_singleton_manager.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_) + { + auto instance = std::shared_ptr(new CServiceServerImpl()); + instance->Start(service_name_); + return instance; + } + + CServiceServerImpl::CServiceServerImpl() : + m_created(false) + { + } + + CServiceServerImpl::~CServiceServerImpl() + { + Stop(); + } + + bool CServiceServerImpl::Start(const std::string& service_name_) + { + 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); + + // mark as created + m_created = true; + + return(true); + } + + bool CServiceServerImpl::Stop() + { + if (!m_created) return(false); + + // reset method callback map + { + std::lock_guard const lock(m_method_map_sync); + m_method_map.clear(); + } + + // reset event callback map + { + std::lock_guard const lock(m_event_callback_map_sync); + m_event_callback_map.clear(); + } + + if (m_tcp_server) + m_tcp_server->stop(); + + // mark as no more created + m_created = false; + + // and unregister this service + Unregister(); + + // reset internals + m_service_name.clear(); + m_service_id.clear(); + + { + const std::lock_guard connected_lock(m_connected_mutex); + m_connected = false; + } + + return(true); + } + + bool CServiceServerImpl::AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_) + { + { + 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; + } + } + + return true; + } + + // 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; + { + 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; + } + } + + return true; + } + + // remove callback function for server method calls + bool CServiceServerImpl::RemMethodCallback(const std::string& method_) + { + std::lock_guard const lock(m_method_map_sync); + + auto iter = m_method_map.find(method_); + if (iter != m_method_map.end()) + { + m_method_map.erase(iter); + return true; + } + return false; + } + + // add callback function for server events + bool CServiceServerImpl::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) + { + if (!m_created) return false; + + // store event callback + { + std::lock_guard const lock(m_event_callback_map_sync); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::AddEventCallback"); +#endif + m_event_callback_map[type_] = std::move(callback_); + } + + return true; + } + + // remove callback function for server events + bool CServiceServerImpl::RemEventCallback(eCAL_Server_Event type_) + { + if (!m_created) return false; + + // reset event callback + { + std::lock_guard const lock(m_event_callback_map_sync); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::RemEventCallback"); +#endif + m_event_callback_map[type_] = nullptr; + } + + return true; + } + + // check connection state + bool CServiceServerImpl::IsConnected() + { + if (!m_created) return false; + + return (m_tcp_server && m_tcp_server->is_connected()); + } + + // 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 + } + + // called by eCAL:CServiceGate every second to update registration layer + Registration::Sample CServiceServerImpl::GetRegistration() + { + return GetRegistrationSample(); + } + + 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; + + 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; + 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); + for (const auto& iter : m_method_map) + { + Service::Method method; + method.mname = iter.first; + method.req_type = iter.second.method.req_type; + method.req_desc = iter.second.method.req_desc; + method.resp_type = iter.second.method.resp_type; + method.resp_desc = iter.second.method.resp_desc; + method.call_count = iter.second.method.call_count; + service.methods.push_back(method); + } + } + + return ecal_reg_sample; + } + + Registration::Sample CServiceServerImpl::GetUnregistrationSample() + { + // create registration sample + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_unreg_service; + + 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; + service.version = m_server_version; + service.pname = Process::GetProcessName(); + service.uname = Process::GetUnitName(); + service.sname = m_service_name; + + return ecal_reg_sample; + } + + void CServiceServerImpl::Register() + { +#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"); +#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; + response_header.hname = Process::GetHostName(); + response_header.sname = m_service_name; + response_header.sid = m_service_id; + + // try to parse request + 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"); + + response_header.state = Service::eMethodCallState::failed; + std::string const 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) + // serialize response and return "request message could not be parsed" + SerializeToBuffer(response, response_pb_); + + // Return Failed (error_code = -1), as parsing the request failed. The + // return value is not propagated to the remote caller. + return -1; + } + + // get method + SMethod method; + const auto& request_header = request.header; + response_header.mname = request_header.mname; + { + std::lock_guard const lock(m_method_map_sync); + + 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 + "'"; + 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) + // serialize response and return "method not found" + SerializeToBuffer(response, response_pb_); + + // Return Success (error_code = 0), as parsing the request worked. The + // return value is not propagated to the remote caller. + return 0; + } + 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 + method = requested_method_iterator->second; + } + } + + // 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); + + // set method call state 'executed' + response_header.state = Service::eMethodCallState::executed; + // set method response and return state + 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) + // serialize response and return "method not found" + SerializeToBuffer(response, response_pb_); + + // return success (error code 0) + return 0; + } + + void CServiceServerImpl::EventCallback(eCAL_Server_Event event_, 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"); + } + } + } + + if (mode_changed) + { + // 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); + } + } + } +} diff --git a/ecal/core/src/service/ecal_service_server_impl.h b/ecal/core/src/service/ecal_service_server_impl.h new file mode 100644 index 0000000000..af2633da76 --- /dev/null +++ b/ecal/core/src/service/ecal_service_server_impl.h @@ -0,0 +1,131 @@ +/* ========================= 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 +**/ + +#pragma once + +#include +#include +#include +#include + +#include "serialization/ecal_serialize_sample_registration.h" +#include "serialization/ecal_struct_service.h" + +#include +#include +#include +#include +#include + +namespace eCAL +{ + /** + * @brief Service server implementation class. + **/ + class CServiceServerImpl : public std::enable_shared_from_this + { + public: + static std::shared_ptr CreateInstance(); + static std::shared_ptr CreateInstance(const std::string& service_name_); + + private: + CServiceServerImpl(); + + 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_); + + // 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(); + + // called by the eCAL::CServiceGate to register a client + void RegisterClient(const std::string& key_, const SClientAttr& client_); + + // called by eCAL:CServiceGate every second to update registration layer + Registration::Sample GetRegistration(); + + std::string GetServiceName() { return m_service_name; }; + + protected: + void Register(); + void Unregister(); + + 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. + */ + int RequestCallback(const std::string& request_pb_, std::string& response_pb_); + void EventCallback(eCAL_Server_Event event_, const std::string& message_); + + std::shared_ptr m_tcp_server; + + static constexpr int m_server_version = 1; + + std::string m_service_name; + std::string m_service_id; + + struct SMethod + { + Service::Method method; + MethodCallbackT callback; + }; + + using MethodMapT = std::map; + std::mutex m_method_map_sync; + 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; + + std::atomic m_created; + }; +} diff --git a/ecal/core/src/service/ecal_service_server_v5.cpp b/ecal/core/src/service/ecal_service_server_v5.cpp index b665bdb12e..2cadb309c5 100644 --- a/ecal/core/src/service/ecal_service_server_v5.cpp +++ b/ecal/core/src/service/ecal_service_server_v5.cpp @@ -22,6 +22,7 @@ **/ #include +#include #include #include "ecal_servicegate.h" @@ -32,24 +33,12 @@ namespace eCAL { namespace v5 { - /** - * @brief Service Server class. - **/ - - /** - * @brief Constructor. - **/ CServiceServer::CServiceServer() : m_service_server_impl(nullptr), m_created(false) { } - /** - * @brief Constructor. - * - * @param service_name_ Service name. - **/ CServiceServer::CServiceServer(const std::string& service_name_) : m_service_server_impl(nullptr), m_created(false) @@ -57,21 +46,11 @@ namespace eCAL Create(service_name_); } - /** - * @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); @@ -80,25 +59,20 @@ namespace eCAL m_service_server_impl = CServiceServerImpl::CreateInstance(service_name_); // register service - if (g_servicegate() != nullptr) g_servicegate()->Register(m_service_server_impl.get()); + //if (g_servicegate() != nullptr) g_servicegate()->Register(m_service_server_impl.get()); // we made it :-) m_created = true; return(m_created); } - /** - * @brief Destroys this object. - * - * @return True if successful. - **/ bool CServiceServer::Destroy() { if(!m_created) return(false); m_created = false; // unregister service - if (g_servicegate() != nullptr) g_servicegate()->Unregister(m_service_server_impl.get()); + //if (g_servicegate() != nullptr) g_servicegate()->Unregister(m_service_server_impl.get()); // stop & destroy service m_service_server_impl->Stop(); @@ -107,17 +81,6 @@ namespace eCAL 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_) { if (!m_created) return false; @@ -133,76 +96,36 @@ namespace eCAL 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_); } - /** - * @brief Remove client request callback. - * - * @return True if successful. - **/ bool CServiceServer::RemMethodCallback(const std::string& method_) { if (!m_created) return false; return m_service_server_impl->RemMethodCallback(method_); } - /** - * @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_) { if (!m_created) return false; return m_service_server_impl->AddEventCallback(type_, callback_); } - /** - * @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_) { if (!m_created) return false; return m_service_server_impl->RemEventCallback(type_); } - /** - * @brief Retrieve service name. - * - * @return The service name. - **/ std::string CServiceServer::GetServiceName() { if (!m_created) return ""; return m_service_server_impl->GetServiceName(); } - /** - * @brief Check connection state. - * - * @return True if connected, false if not. - **/ bool CServiceServer::IsConnected() { if (!m_created) return false; From a2ccd1085d9d9e34a1b88521ad6dd39435666cbc Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:20:11 +0100 Subject: [PATCH 03/21] intermediate commit (building, not working) --- ecal/core/include/ecal/ecal_server.h | 2 +- ecal/core/include/ecal/ecal_server_v5.h | 1 - ecal/core/include/ecal/msg/protobuf/server.h | 12 +- ecal/core/src/cimpl/ecal_server_cimpl.cpp | 17 +- ecal/core/src/service/ecal_service_server.cpp | 4 +- .../src/service/ecal_service_server_v5.cpp | 61 +-- .../service/ecal_service_server_v5_impl.cpp | 451 +----------------- .../src/service/ecal_service_server_v5_impl.h | 102 +--- .../latency_server/src/latency_server.cpp | 5 +- .../minimal_server/src/minimal_server.cpp | 5 +- .../src/clientserver_test.cpp | 53 +- .../src/clientserver_v5_test.cpp | 21 +- .../src/registration_getservices.cpp | 5 +- .../csharp/Continental.eCAL.Core/ecal_clr.cpp | 4 +- lang/csharp/Continental.eCAL.Core/ecal_clr.h | 3 +- lang/python/core/src/ecal_clang.cpp | 9 +- lang/python/core/src/ecal_wrap.cxx | 7 +- 17 files changed, 133 insertions(+), 629 deletions(-) diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h index 5a2d56a4d1..5cbb40a39b 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/ecal/core/include/ecal/ecal_server.h @@ -49,7 +49,7 @@ namespace eCAL * @param service_name_ Unique service name. **/ ECAL_API_EXPORTED_MEMBER - explicit CServiceServer(const std::string& service_name_, const ServerEventCallbackT callback_ = ServerEventCallbackT()); + explicit CServiceServer(const std::string& service_name_, const ServerEventIDCallbackT callback_ = ServerEventIDCallbackT()); /** * @brief Destructor. diff --git a/ecal/core/include/ecal/ecal_server_v5.h b/ecal/core/include/ecal/ecal_server_v5.h index 759f40caee..57a40094b7 100644 --- a/ecal/core/include/ecal/ecal_server_v5.h +++ b/ecal/core/include/ecal/ecal_server_v5.h @@ -169,7 +169,6 @@ namespace eCAL private: std::shared_ptr m_service_server_impl; - bool m_created; }; } } 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/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index 16f9189666..58618b845d 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -30,7 +30,7 @@ namespace eCAL { - CServiceServer::CServiceServer(const std::string& service_name_, const ServerEventCallbackT callback_) : + CServiceServer::CServiceServer(const std::string& service_name_, const ServerEventIDCallbackT callback_) : m_service_server_impl(nullptr) { } @@ -52,12 +52,10 @@ namespace eCAL std::string CServiceServer::GetServiceName() { return ""; - //return m_service_server_impl->GetServiceName(); } bool CServiceServer::IsConnected() { return false; - //return m_service_server_impl->IsConnected(); } } diff --git a/ecal/core/src/service/ecal_service_server_v5.cpp b/ecal/core/src/service/ecal_service_server_v5.cpp index 2cadb309c5..3a9b4e04f2 100644 --- a/ecal/core/src/service/ecal_service_server_v5.cpp +++ b/ecal/core/src/service/ecal_service_server_v5.cpp @@ -33,15 +33,13 @@ namespace eCAL { namespace v5 { - CServiceServer::CServiceServer() : - m_service_server_impl(nullptr), - m_created(false) + CServiceServer::CServiceServer() + : m_service_server_impl(nullptr) { } - CServiceServer::CServiceServer(const std::string& service_name_) : - m_service_server_impl(nullptr), - m_created(false) + CServiceServer::CServiceServer(const std::string& service_name_) + : m_service_server_impl(nullptr) { Create(service_name_); } @@ -53,82 +51,57 @@ namespace eCAL bool CServiceServer::Create(const std::string& service_name_) { - if(m_created) return(false); - - // 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); + 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_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(); + if (m_service_server_impl == nullptr) return false; m_service_server_impl.reset(); - - return(true); + 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_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); + 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_created) return false; + 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_created) return false; + 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_created) return false; + 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_created) return false; + if (m_service_server_impl == nullptr) return false; return m_service_server_impl->RemEventCallback(type_); } std::string CServiceServer::GetServiceName() { - if (!m_created) return ""; + if (m_service_server_impl == nullptr) return false; return m_service_server_impl->GetServiceName(); } 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_v5_impl.cpp b/ecal/core/src/service/ecal_service_server_v5_impl.cpp index 4770b5b425..5fc896abab 100644 --- a/ecal/core/src/service/ecal_service_server_v5_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_v5_impl.cpp @@ -21,487 +21,70 @@ * @brief eCAL service server implementation **/ -#include "registration/ecal_registration_provider.h" -#include "ecal_global_accessors.h" #include "ecal_service_server_v5_impl.h" -#include "ecal_service_singleton_manager.h" -#include "serialization/ecal_serialize_service.h" - -#include -#include -#include -#include -#include -#include namespace eCAL { namespace v5 { - std::shared_ptr CServiceServerImpl::CreateInstance() - { - return std::shared_ptr(new CServiceServerImpl()); - } - - std::shared_ptr CServiceServerImpl::CreateInstance(const std::string& service_name_) + CServiceServerImpl::CServiceServerImpl() + : m_service_server_impl(nullptr) { - auto instance = std::shared_ptr (new CServiceServerImpl()); - instance->Start(service_name_); - return instance; } - CServiceServerImpl::CServiceServerImpl() : - m_created(false) + CServiceServerImpl::CServiceServerImpl(const std::string& service_name_) + : m_service_server_impl(nullptr) { } CServiceServerImpl::~CServiceServerImpl() { - Stop(); } - bool CServiceServerImpl::Start(const std::string& service_name_) + bool CServiceServerImpl::Create(const std::string& service_name_) { - 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); - - // mark as created - m_created = true; - - return(true); + return false; } - bool CServiceServerImpl::Stop() + bool CServiceServerImpl::Destroy() { - if (!m_created) return(false); - - // reset method callback map - { - std::lock_guard const lock(m_method_map_sync); - m_method_map.clear(); - } - - // reset event callback map - { - std::lock_guard const lock(m_event_callback_map_sync); - m_event_callback_map.clear(); - } - - if (m_tcp_server) - m_tcp_server->stop(); - - // mark as no more created - m_created = false; - - // and unregister this service - Unregister(); - - // reset internals - m_service_name.clear(); - m_service_id.clear(); - - { - const std::lock_guard connected_lock(m_connected_mutex); - m_connected = false; - } - - return(true); + return false; } - bool CServiceServerImpl::AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_) + 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_) { - { - 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; - } - } - - return true; + return false; } - // 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; - { - 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; - } - } - - return true; + return false; } - // remove callback function for server method calls bool CServiceServerImpl::RemMethodCallback(const std::string& method_) { - std::lock_guard const lock(m_method_map_sync); - - auto iter = m_method_map.find(method_); - if (iter != m_method_map.end()) - { - m_method_map.erase(iter); - return true; - } return false; } - // add callback function for server events bool CServiceServerImpl::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) { - if (!m_created) return false; - - // store event callback - { - std::lock_guard const lock(m_event_callback_map_sync); - #ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::AddEventCallback"); - #endif - m_event_callback_map[type_] = std::move(callback_); - } - - return true; + return false; } - // remove callback function for server events bool CServiceServerImpl::RemEventCallback(eCAL_Server_Event type_) { - if (!m_created) return false; - - // reset event callback - { - std::lock_guard const lock(m_event_callback_map_sync); - #ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::RemEventCallback"); - #endif - m_event_callback_map[type_] = nullptr; - } - - return true; - } - - // check connection state - bool CServiceServerImpl::IsConnected() - { - if (!m_created) return false; - - return (m_tcp_server && m_tcp_server->is_connected()); - } - - // 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 - } - - // called by eCAL:CServiceGate every second to update registration layer - Registration::Sample CServiceServerImpl::GetRegistration() - { - return GetRegistrationSample(); - } - - 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; - - 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; - 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); - for (const auto& iter : m_method_map) - { - Service::Method method; - method.mname = iter.first; - method.req_type = iter.second.method.req_type; - method.req_desc = iter.second.method.req_desc; - method.resp_type = iter.second.method.resp_type; - method.resp_desc = iter.second.method.resp_desc; - method.call_count = iter.second.method.call_count; - service.methods.push_back(method); - } - } - - return ecal_reg_sample; - } - - Registration::Sample CServiceServerImpl::GetUnregistrationSample() - { - // create registration sample - Registration::Sample ecal_reg_sample; - ecal_reg_sample.cmd_type = bct_unreg_service; - - 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; - service.version = m_server_version; - service.pname = Process::GetProcessName(); - service.uname = Process::GetUnitName(); - service.sname = m_service_name; - - return ecal_reg_sample; + return false; } - void CServiceServerImpl::Register() - { - #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"); - #endif - #endif // ECAL_CORE_REGISTRATION - } - - void CServiceServerImpl::Unregister() + std::string CServiceServerImpl::GetServiceName() { - #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 + return ""; } - int CServiceServerImpl::RequestCallback(const std::string& request_pb_, std::string& response_pb_) - { - // prepare response - Service::Response response; - auto& response_header = response.header; - response_header.hname = Process::GetHostName(); - response_header.sname = m_service_name; - response_header.sid = m_service_id; - - // try to parse request - 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"); - - response_header.state = Service::eMethodCallState::failed; - std::string const 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) - // serialize response and return "request message could not be parsed" - SerializeToBuffer(response, response_pb_); - - // Return Failed (error_code = -1), as parsing the request failed. The - // return value is not propagated to the remote caller. - return -1; - } - - // get method - SMethod method; - const auto& request_header = request.header; - response_header.mname = request_header.mname; - { - std::lock_guard const lock(m_method_map_sync); - - 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 + "'"; - 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) - // serialize response and return "method not found" - SerializeToBuffer(response, response_pb_); - - // Return Success (error_code = 0), as parsing the request worked. The - // return value is not propagated to the remote caller. - return 0; - } - 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 - method = requested_method_iterator->second; - } - } - - // 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); - - // set method call state 'executed' - response_header.state = Service::eMethodCallState::executed; - // set method response and return state - 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) - // serialize response and return "method not found" - SerializeToBuffer(response, response_pb_); - - // return success (error code 0) - return 0; - } - - void CServiceServerImpl::EventCallback(eCAL_Server_Event event_, const std::string& /*message_*/) + bool CServiceServerImpl::IsConnected() { - 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"); - } - } - } - - if (mode_changed) - { - // 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); - } - } + return false; } } } diff --git a/ecal/core/src/service/ecal_service_server_v5_impl.h b/ecal/core/src/service/ecal_service_server_v5_impl.h index 8d0ec183f4..b574ed4caf 100644 --- a/ecal/core/src/service/ecal_service_server_v5_impl.h +++ b/ecal/core/src/service/ecal_service_server_v5_impl.h @@ -23,18 +23,10 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include -#include "serialization/ecal_serialize_sample_registration.h" -#include "serialization/ecal_struct_service.h" - -#include -#include -#include -#include #include namespace eCAL @@ -44,91 +36,33 @@ namespace eCAL /** * @brief Service server implementation class. **/ - class CServiceServerImpl : public std::enable_shared_from_this + class CServiceServerImpl { public: - static std::shared_ptr CreateInstance(); - static std::shared_ptr CreateInstance(const std::string& service_name_); - - private: CServiceServerImpl(); + explicit CServiceServerImpl(const std::string& service_name_); + virtual ~CServiceServerImpl(); - 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 Create(const std::string& service_name_); + bool Destroy(); - 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 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_); - - // 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(); - - // called by the eCAL::CServiceGate to register a client - void RegisterClient(const std::string& key_, const SClientAttr& client_); - - // called by eCAL:CServiceGate every second to update registration layer - Registration::Sample GetRegistration(); - std::string GetServiceName() { return m_service_name; }; - - protected: - void Register(); - void Unregister(); - - 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. - */ - int RequestCallback(const std::string& request_pb_, std::string& response_pb_); - void EventCallback(eCAL_Server_Event event_, const std::string& message_); - - std::shared_ptr m_tcp_server; - - static constexpr int m_server_version = 1; - - std::string m_service_name; - std::string m_service_id; - - struct SMethod - { - Service::Method method; - MethodCallbackT callback; - }; - - using MethodMapT = std::map; - std::mutex m_method_map_sync; - MethodMapT m_method_map; + std::string GetServiceName(); + bool IsConnected(); - 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; + // Prevent copy and move operations + CServiceServerImpl(const CServiceServerImpl&) = delete; + CServiceServerImpl& operator=(const CServiceServerImpl&) = delete; + CServiceServerImpl(CServiceServerImpl&&) = delete; + CServiceServerImpl& operator=(CServiceServerImpl&&) = delete; - std::atomic m_created; + private: + std::shared_ptr m_service_server_impl; }; } } 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..0509888ae6 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. @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -34,7 +35,7 @@ int main() eCAL::Initialize("latency server"); // create latency service - eCAL::CServiceServer latency_service("latency"); + eCAL::v5::CServiceServer latency_service("latency"); // add hello method callback latency_service.AddMethodCallback("hello", "", "", OnHello); 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..905e02d906 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. @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -39,7 +40,7 @@ int main() eCAL::Initialize("minimal server"); // create minimal service server - eCAL::CServiceServer minimal_server("service1"); + eCAL::v5::CServiceServer minimal_server("service1"); // add method callback minimal_server.AddMethodCallback("echo", "", "", OnMethodCallback); diff --git a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp index d63226a3a8..7d67461ba8 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,9 +200,9 @@ 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); @@ -281,8 +278,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 +383,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 @@ -535,8 +536,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 +601,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 +611,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 +722,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 @@ -810,8 +817,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"); 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..901fe9c062 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,13 +147,13 @@ 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)); 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)); EXPECT_EQ(2, event_connected_fired.get()); @@ -182,7 +183,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); @@ -261,7 +262,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 +371,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 +532,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 +605,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 +717,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 +812,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_getservices.cpp b/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp index f66b4447ca..53bfc70d1d 100644 --- a/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp +++ b/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -60,7 +61,7 @@ TEST_P(ServicesTestFixture, ServiceExpiration) // create simple service and let it expire { // create service - eCAL::CServiceServer service("foo::service"); + eCAL::v5::CServiceServer service("foo::service"); service.AddDescription("foo::method", "foo::req_type", "foo::req_desc", "foo::resp_type", "foo::resp_desc"); // let's register @@ -108,7 +109,7 @@ TEST_P(ServicesTestFixture, GetServiceIDs) // create simple server { // create server - eCAL::CServiceServer service("foo::service"); + eCAL::v5::CServiceServer service("foo::service"); // add description eCAL::SServiceMethodInformation service_method_info; diff --git a/lang/csharp/Continental.eCAL.Core/ecal_clr.cpp b/lang/csharp/Continental.eCAL.Core/ecal_clr.cpp index 80aeece79a..3c7521f680 100644 --- a/lang/csharp/Continental.eCAL.Core/ecal_clr.cpp +++ b/lang/csharp/Continental.eCAL.Core/ecal_clr.cpp @@ -398,13 +398,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 a272b65238..b4d629c00b 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; @@ -437,7 +438,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 a1e73e3a02..02294882fb 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" @@ -563,7 +564,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; @@ -577,7 +578,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; @@ -605,7 +606,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_); @@ -622,7 +623,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 56e00d5698..02b4021300 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" @@ -568,7 +569,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; @@ -611,7 +612,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")); @@ -665,7 +666,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")); From 9d2bf06020621b67f90c7ec8f5220ebc36d686ce Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:38:00 +0100 Subject: [PATCH 04/21] intermediate commit --- ecal/core/include/ecal/ecal_server.h | 2 +- ecal/core/src/service/ecal_service_server.cpp | 60 ++- .../src/service/ecal_service_server_impl.cpp | 460 +----------------- .../src/service/ecal_service_server_impl.h | 99 ++-- .../src/service/ecal_service_server_v5.cpp | 4 +- .../service/ecal_service_server_v5_impl.cpp | 2 +- .../src/service/ecal_service_server_v5_impl.h | 6 +- ecal/core/src/service/ecal_servicegate.cpp | 5 +- 8 files changed, 121 insertions(+), 517 deletions(-) diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h index 5cbb40a39b..a343a397ae 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/ecal/core/include/ecal/ecal_server.h @@ -49,7 +49,7 @@ namespace eCAL * @param service_name_ Unique service name. **/ ECAL_API_EXPORTED_MEMBER - explicit CServiceServer(const std::string& service_name_, const ServerEventIDCallbackT callback_ = ServerEventIDCallbackT()); + explicit CServiceServer(const std::string& service_name_, const ServerEventIDCallbackT event_callback_ = ServerEventIDCallbackT()); /** * @brief Destructor. diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index 58618b845d..e185757570 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -22,7 +22,6 @@ **/ #include -#include #include "ecal_servicegate.h" #include "ecal_global_accessors.h" @@ -30,32 +29,87 @@ namespace eCAL { - CServiceServer::CServiceServer(const std::string& service_name_, const ServerEventIDCallbackT callback_) : - m_service_server_impl(nullptr) + 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_); + + // Register server + if (g_servicegate() != nullptr) + { + //g_servicegate()->Register(m_service_server_impl); + } } CServiceServer::~CServiceServer() { + // Unregister server + if (g_servicegate() != nullptr) + { + //g_servicegate()->Unregister(m_service_server_impl->GetServiceName(), m_service_server_impl); + } + + // Reset server implementation + m_service_server_impl.reset(); + } + + CServiceServer::CServiceServer(CServiceServer&& rhs) noexcept + : m_service_server_impl(std::move(rhs.m_service_server_impl)) + { + } + + CServiceServer& CServiceServer::operator=(CServiceServer&& rhs) noexcept + { + if (this != &rhs) + { + // Unregister server + if (g_servicegate() != nullptr) + { + //g_servicegate()->Unregister(m_service_server_impl->GetServiceName(), m_service_server_impl); + } + + // Move data + m_service_server_impl = std::move(rhs.m_service_server_impl); + + rhs.m_service_server_impl = nullptr; + } + return *this; } bool CServiceServer::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { + if (m_service_server_impl) + { + return m_service_server_impl->AddMethodCallback(method_, method_info_, callback_); + } return false; } bool CServiceServer::RemMethodCallback(const std::string& method_) { + if (m_service_server_impl) + { + return m_service_server_impl->RemMethodCallback(method_); + } return false; } std::string CServiceServer::GetServiceName() { + if (m_service_server_impl) + { + return m_service_server_impl->GetServiceName(); + } return ""; } bool CServiceServer::IsConnected() { + if (m_service_server_impl) + { + return m_service_server_impl->IsConnected(); + } return false; } } diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index b56b08f3a9..9329751026 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -21,484 +21,78 @@ * @brief eCAL service server implementation **/ -#include "registration/ecal_registration_provider.h" #include "ecal_global_accessors.h" -#include "ecal_service_server_impl.h" -#include "ecal_service_singleton_manager.h" -#include "serialization/ecal_serialize_service.h" -#include -#include -#include -#include -#include -#include +#include "ecal_service_server_impl.h" +#include "registration/ecal_registration_provider.h" namespace eCAL { - std::shared_ptr CServiceServerImpl::CreateInstance() + // Factory method to create a new instance of CServiceClientImpl + std::shared_ptr CServiceServerImpl::CreateInstance( + const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) { - return std::shared_ptr(new CServiceServerImpl()); + return std::shared_ptr(new CServiceServerImpl(service_name_, event_callback_)); } - std::shared_ptr CServiceServerImpl::CreateInstance(const std::string& service_name_) - { - auto instance = std::shared_ptr(new CServiceServerImpl()); - instance->Start(service_name_); - return instance; - } - - CServiceServerImpl::CServiceServerImpl() : - m_created(false) + // Constructor: Initializes service ID and registers the service + CServiceServerImpl::CServiceServerImpl(const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) + : m_service_name(service_name_), m_event_callback(event_callback_) { } + // Destructor: Resets callbacks, unregisters the service, and clears data CServiceServerImpl::~CServiceServerImpl() { - Stop(); - } - - bool CServiceServerImpl::Start(const std::string& service_name_) - { - 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); - - // mark as created - m_created = true; - - return(true); - } - - bool CServiceServerImpl::Stop() - { - if (!m_created) return(false); - - // reset method callback map - { - std::lock_guard const lock(m_method_map_sync); - m_method_map.clear(); - } - - // reset event callback map - { - std::lock_guard const lock(m_event_callback_map_sync); - m_event_callback_map.clear(); - } - - if (m_tcp_server) - m_tcp_server->stop(); - - // mark as no more created - m_created = false; - - // and unregister this service - Unregister(); - - // reset internals - m_service_name.clear(); - m_service_id.clear(); - + // reset event callback { - const std::lock_guard connected_lock(m_connected_mutex); - m_connected = false; + const std::lock_guard lock(m_event_callback_mutex); + m_event_callback = nullptr; } - return(true); - } - - bool CServiceServerImpl::AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_) - { + // unregister client + if (g_registration_provider() != nullptr) { - 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; - } + g_registration_provider()->UnregisterSample(GetUnregistrationSample()); } - - return true; } - // 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_) + bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { - std::string req_desc; - std::string resp_desc; - { - 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; - } - } - - return true; + return false; } - // remove callback function for server method calls bool CServiceServerImpl::RemMethodCallback(const std::string& method_) { - std::lock_guard const lock(m_method_map_sync); - - auto iter = m_method_map.find(method_); - if (iter != m_method_map.end()) - { - m_method_map.erase(iter); - return true; - } return false; } - // add callback function for server events - bool CServiceServerImpl::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) - { - if (!m_created) return false; - - // store event callback - { - std::lock_guard const lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::AddEventCallback"); -#endif - m_event_callback_map[type_] = std::move(callback_); - } - - return true; - } - - // remove callback function for server events - bool CServiceServerImpl::RemEventCallback(eCAL_Server_Event type_) + bool CServiceServerImpl::IsConnected() const { - if (!m_created) return false; - - // reset event callback - { - std::lock_guard const lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::RemEventCallback"); -#endif - m_event_callback_map[type_] = nullptr; - } - - return true; + return false; } - // check connection state - bool CServiceServerImpl::IsConnected() + void CServiceServerImpl::RegisterClient(const std::string& key_, const SClientAttr& client_) { - if (!m_created) return false; - - return (m_tcp_server && m_tcp_server->is_connected()); } - // called by the eCAL::CServiceGate to register a client - void CServiceServerImpl::RegisterClient(const std::string& /*key_*/, const SClientAttr& /*client_*/) + Registration::Sample CServiceServerImpl::GetRegistration() { - // this function is just a placeholder to implement logic if a new client connects - // currently there is no need to do so + return Registration::Sample(); } - // called by eCAL:CServiceGate every second to update registration layer - Registration::Sample CServiceServerImpl::GetRegistration() + std::string CServiceServerImpl::GetServiceName() const { - return GetRegistrationSample(); + return m_service_name; } 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; - - 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; - 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); - for (const auto& iter : m_method_map) - { - Service::Method method; - method.mname = iter.first; - method.req_type = iter.second.method.req_type; - method.req_desc = iter.second.method.req_desc; - method.resp_type = iter.second.method.resp_type; - method.resp_desc = iter.second.method.resp_desc; - method.call_count = iter.second.method.call_count; - service.methods.push_back(method); - } - } - - return ecal_reg_sample; + return Registration::Sample(); } Registration::Sample CServiceServerImpl::GetUnregistrationSample() { - // create registration sample - Registration::Sample ecal_reg_sample; - ecal_reg_sample.cmd_type = bct_unreg_service; - - 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; - service.version = m_server_version; - service.pname = Process::GetProcessName(); - service.uname = Process::GetUnitName(); - service.sname = m_service_name; - - return ecal_reg_sample; - } - - void CServiceServerImpl::Register() - { -#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"); -#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; - response_header.hname = Process::GetHostName(); - response_header.sname = m_service_name; - response_header.sid = m_service_id; - - // try to parse request - 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"); - - response_header.state = Service::eMethodCallState::failed; - std::string const 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) - // serialize response and return "request message could not be parsed" - SerializeToBuffer(response, response_pb_); - - // Return Failed (error_code = -1), as parsing the request failed. The - // return value is not propagated to the remote caller. - return -1; - } - - // get method - SMethod method; - const auto& request_header = request.header; - response_header.mname = request_header.mname; - { - std::lock_guard const lock(m_method_map_sync); - - 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 + "'"; - 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) - // serialize response and return "method not found" - SerializeToBuffer(response, response_pb_); - - // Return Success (error_code = 0), as parsing the request worked. The - // return value is not propagated to the remote caller. - return 0; - } - 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 - method = requested_method_iterator->second; - } - } - - // 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); - - // set method call state 'executed' - response_header.state = Service::eMethodCallState::executed; - // set method response and return state - 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) - // serialize response and return "method not found" - SerializeToBuffer(response, response_pb_); - - // return success (error code 0) - return 0; - } - - void CServiceServerImpl::EventCallback(eCAL_Server_Event event_, 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"); - } - } - } - - if (mode_changed) - { - // 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); - } - } + return Registration::Sample(); } } diff --git a/ecal/core/src/service/ecal_service_server_impl.h b/ecal/core/src/service/ecal_service_server_impl.h index af2633da76..fa251d1a94 100644 --- a/ecal/core/src/service/ecal_service_server_impl.h +++ b/ecal/core/src/service/ecal_service_server_impl.h @@ -18,114 +18,73 @@ */ /** - * @brief eCAL service server interface + * @brief eCAL service server implementation **/ #pragma once -#include #include #include -#include #include "serialization/ecal_serialize_sample_registration.h" #include "serialization/ecal_struct_service.h" -#include -#include #include #include #include +#include +#include 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 AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_); bool RemMethodCallback(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 service 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: + // 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. - */ - int RequestCallback(const std::string& request_pb_, std::string& response_pb_); - void EventCallback(eCAL_Server_Event event_, const std::string& message_); - - std::shared_ptr m_tcp_server; - - static constexpr int m_server_version = 1; - - std::string m_service_name; - std::string m_service_id; - - struct SMethod - { - Service::Method method; - MethodCallbackT callback; - }; - - using MethodMapT = std::map; - std::mutex m_method_map_sync; - 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; + // Service attributes + std::string m_service_name; - std::atomic m_created; + // Event callback map and synchronization + mutable std::mutex m_event_callback_mutex; + ServerEventIDCallbackT m_event_callback; }; } diff --git a/ecal/core/src/service/ecal_service_server_v5.cpp b/ecal/core/src/service/ecal_service_server_v5.cpp index 3a9b4e04f2..705a0a09de 100644 --- a/ecal/core/src/service/ecal_service_server_v5.cpp +++ b/ecal/core/src/service/ecal_service_server_v5.cpp @@ -18,7 +18,7 @@ */ /** - * @brief eCAL service server interface + * @brief eCAL service server interface (deprecated eCAL5 version) **/ #include @@ -95,7 +95,7 @@ namespace eCAL std::string CServiceServer::GetServiceName() { - if (m_service_server_impl == nullptr) return false; + if (m_service_server_impl == nullptr) return ""; return m_service_server_impl->GetServiceName(); } diff --git a/ecal/core/src/service/ecal_service_server_v5_impl.cpp b/ecal/core/src/service/ecal_service_server_v5_impl.cpp index 5fc896abab..960f6c90ba 100644 --- a/ecal/core/src/service/ecal_service_server_v5_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_v5_impl.cpp @@ -18,7 +18,7 @@ */ /** - * @brief eCAL service server implementation + * @brief eCAL service server implementation (deprecated eCAL5 version) **/ #include "ecal_service_server_v5_impl.h" diff --git a/ecal/core/src/service/ecal_service_server_v5_impl.h b/ecal/core/src/service/ecal_service_server_v5_impl.h index b574ed4caf..3f19f920eb 100644 --- a/ecal/core/src/service/ecal_service_server_v5_impl.h +++ b/ecal/core/src/service/ecal_service_server_v5_impl.h @@ -18,14 +18,14 @@ */ /** - * @brief eCAL service server interface + * @brief eCAL service server implementation (deprecated eCAL5 version) **/ #pragma once #include #include -#include +#include #include @@ -62,7 +62,7 @@ namespace eCAL CServiceServerImpl& operator=(CServiceServerImpl&&) = delete; private: - std::shared_ptr m_service_server_impl; + std::shared_ptr m_service_server_impl; }; } } diff --git a/ecal/core/src/service/ecal_servicegate.cpp b/ecal/core/src/service/ecal_servicegate.cpp index 8967b68442..fb7858e7c2 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/ecal/core/src/service/ecal_servicegate.cpp @@ -52,10 +52,7 @@ namespace eCAL // destroy all remaining server const std::shared_lock lock(m_service_set_sync); - for (const auto& service : m_service_set) - { - service->Stop(); - } + m_service_set.clear(); m_created = false; } From d9d717715c9f0c0356d64f2162b499e99a5cdfac Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Mon, 9 Dec 2024 20:08:31 +0100 Subject: [PATCH 05/21] commenting m_created removed --- ecal/core/include/ecal/ecal_client_v5.h | 2 +- .../src/service/ecal_service_client_v5.cpp | 2 +- .../service/ecal_service_client_v5_impl.cpp | 38 +++++++++---------- .../src/service/ecal_service_client_v5_impl.h | 3 -- 4 files changed, 20 insertions(+), 25 deletions(-) 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/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..a4a960c406 100644 --- a/ecal/core/src/service/ecal_service_client_v5_impl.cpp +++ b/ecal/core/src/service/ecal_service_client_v5_impl.cpp @@ -18,7 +18,7 @@ */ /** - * @brief eCAL service client interface + * @brief eCAL service client implementation (deprecated eCAL5 version) **/ #include "ecal_service_client_v5_impl.h" @@ -29,20 +29,17 @@ namespace eCAL { CServiceClientImpl::CServiceClientImpl() : m_service_client_impl(nullptr) - , m_created(false) { } CServiceClientImpl::CServiceClientImpl(const std::string& service_name_) : m_service_client_impl(nullptr) - , m_created(false) { Create(service_name_); } CServiceClientImpl::CServiceClientImpl(const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_) : m_service_client_impl(nullptr) - , m_created(false) { Create(service_name_, method_information_map_); } @@ -59,7 +56,7 @@ 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) return false; // Define the event callback to pass to CServiceClientNew ClientEventIDCallbackT event_callback = [this](const Registration::SServiceMethodId& service_id_, const struct SClientEventCallbackData& data_) @@ -83,21 +80,19 @@ namespace eCAL event_callback ); - m_created = (m_service_client_impl != nullptr); - return m_created; + return true; } bool CServiceClientImpl::Destroy() { - if (!m_created) return false; + if (m_service_client_impl == nullptr) return false; // 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(); return true; @@ -105,7 +100,7 @@ namespace eCAL bool CServiceClientImpl::SetHostName(const std::string& host_name_) { - if (!m_created) return false; + if (m_service_client_impl == nullptr) return false; // Store the host name filter m_host_name = host_name_; @@ -115,7 +110,8 @@ 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) return false; + if (!m_response_callback) return false; // 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_) @@ -133,7 +129,8 @@ 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) return false; + if (!service_response_vec_) return false; // Call the method using the new API ServiceResponseVecT all_responses; @@ -160,7 +157,8 @@ namespace eCAL 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) return false; + if (!m_response_callback) return false; // 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_) @@ -178,7 +176,7 @@ namespace eCAL bool CServiceClientImpl::AddResponseCallback(const ResponseCallbackT& callback_) { - if (!m_created) return false; + if (m_service_client_impl == nullptr) return false; { const std::lock_guard lock(m_response_callback_mutex); @@ -190,7 +188,7 @@ namespace eCAL bool CServiceClientImpl::RemResponseCallback() { - if (!m_created) return false; + if (m_service_client_impl == nullptr) return false; { const std::lock_guard lock(m_response_callback_mutex); @@ -202,7 +200,7 @@ namespace eCAL bool CServiceClientImpl::AddEventCallback(eCAL_Client_Event type_, ClientEventCallbackT callback_) { - if (!m_created) return false; + if (m_service_client_impl == nullptr) return false; { const std::lock_guard lock(m_event_callback_map_mutex); @@ -214,7 +212,7 @@ namespace eCAL bool CServiceClientImpl::RemEventCallback(eCAL_Client_Event type_) { - if (!m_created) return false; + if (m_service_client_impl == nullptr) return false; { const std::lock_guard lock(m_event_callback_map_mutex); @@ -226,14 +224,14 @@ namespace eCAL std::string CServiceClientImpl::GetServiceName() { - if (!m_created) return ""; + if (m_service_client_impl == nullptr) return ""; return m_service_client_impl->GetServiceName(); } bool CServiceClientImpl::IsConnected() { - if (!m_created) return false; + if (m_service_client_impl == nullptr) return false; return m_service_client_impl->IsConnected(); } 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; From 3f9de032bde19ff5f88868b1d2fcd3630ee681e1 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:16:58 +0100 Subject: [PATCH 06/21] first server implementation service gate aligned with client gate service configuration removed --- ecal/core/CMakeLists.txt | 1 - ecal/core/include/ecal/config/configuration.h | 2 - ecal/core/include/ecal/config/service.h | 39 -- ecal/core/include/ecal/ecal_config.h | 7 - ecal/core/include/ecal/ecal_server.h | 7 +- ecal/core/src/config/configuration_reader.cpp | 1 - .../core/src/config/configuration_to_yaml.cpp | 26 -- ecal/core/src/config/configuration_to_yaml.h | 15 - .../core/src/config/default_configuration.cpp | 8 - ecal/core/src/config/ecal_config.cpp | 6 - .../src/config/ecal_config_initializer.cpp | 5 - ecal/core/src/service/ecal_service_server.cpp | 4 +- .../src/service/ecal_service_server_impl.cpp | 334 ++++++++++++++++-- .../src/service/ecal_service_server_impl.h | 48 ++- ecal/core/src/service/ecal_servicegate.cpp | 41 ++- ecal/core/src/service/ecal_servicegate.h | 12 +- .../src/clientserver_test.cpp | 24 +- 17 files changed, 392 insertions(+), 188 deletions(-) delete mode 100644 ecal/core/include/ecal/config/service.h diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt index 626d931bab..9fc282564b 100644 --- a/ecal/core/CMakeLists.txt +++ b/ecal/core/CMakeLists.txt @@ -513,7 +513,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 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_config.h b/ecal/core/include/ecal/ecal_config.h index 21096a53d8..802862a49d 100644 --- a/ecal/core/include/ecal/ecal_config.h +++ b/ecal/core/include/ecal/ecal_config.h @@ -38,7 +38,6 @@ namespace eCAL ECAL_API Subscriber::Configuration& GetSubscriberConfiguration (); ECAL_API Publisher::Configuration& GetPublisherConfiguration (); ECAL_API Time::Configuration& GetTimesyncConfiguration (); - ECAL_API Service::Configuration& GetServiceConfiguration (); ECAL_API Application::Configuration& GetApplicationConfiguration (); namespace Config @@ -115,12 +114,6 @@ namespace eCAL ECAL_API bool IsTopicTypeSharingEnabled (); ECAL_API bool IsTopicDescriptionSharingEnabled (); - ///////////////////////////////////// - // service - ///////////////////////////////////// - ECAL_API bool IsServiceProtocolV0Enabled (); - ECAL_API bool IsServiceProtocolV1Enabled (); - ///////////////////////////////////// // experimental ///////////////////////////////////// diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h index a343a397ae..d91371aa33 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/ecal/core/include/ecal/ecal_server.h @@ -68,10 +68,9 @@ namespace eCAL /** * @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. + * @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. **/ 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 e49f2d85ff..9b31966169 100644 --- a/ecal/core/src/config/configuration_to_yaml.cpp +++ b/ecal/core/src/config/configuration_to_yaml.cpp @@ -492,30 +492,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; - } - - /* ___ ___ __ _ / _ | ___ ___ / (_)______ _/ /_(_)__ ___ @@ -681,7 +657,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; @@ -695,7 +670,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 b983f350f8..16ae6eaf45 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 5ca0372257..0f3f617694 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 0095e08f99..f74930c9ad 100644 --- a/ecal/core/src/config/ecal_config.cpp +++ b/ecal/core/src/config/ecal_config.cpp @@ -99,12 +99,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; } - ///////////////////////////////////// // experimemtal ///////////////////////////////////// diff --git a/ecal/core/src/config/ecal_config_initializer.cpp b/ecal/core/src/config/ecal_config_initializer.cpp index 9d005ef577..b55e1cafca 100644 --- a/ecal/core/src/config/ecal_config_initializer.cpp +++ b/ecal/core/src/config/ecal_config_initializer.cpp @@ -280,11 +280,6 @@ namespace eCAL return GetConfiguration().timesync; } - Service::Configuration& GetServiceConfiguration() - { - return GetConfiguration().service; - } - Application::Configuration& GetApplicationConfiguration() { return GetConfiguration().application; diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index e185757570..60efd9ae15 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -38,7 +38,7 @@ namespace eCAL // Register server if (g_servicegate() != nullptr) { - //g_servicegate()->Register(m_service_server_impl); + g_servicegate()->Register(service_name_, m_service_server_impl); } } @@ -47,7 +47,7 @@ namespace eCAL // Unregister server if (g_servicegate() != nullptr) { - //g_servicegate()->Unregister(m_service_server_impl->GetServiceName(), m_service_server_impl); + g_servicegate()->Unregister(m_service_server_impl->GetServiceName(), m_service_server_impl); } // Reset server implementation diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index 9329751026..0ad8fce87f 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -21,10 +21,17 @@ * @brief eCAL service server implementation **/ +#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" namespace eCAL { @@ -32,53 +39,86 @@ namespace eCAL std::shared_ptr CServiceServerImpl::CreateInstance( const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) { - return std::shared_ptr(new CServiceServerImpl(service_name_, event_callback_)); + auto instance = std::shared_ptr(new CServiceServerImpl(service_name_, event_callback_)); + instance->Start(); + return instance; } // Constructor: Initializes service ID and registers the service CServiceServerImpl::CServiceServerImpl(const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) - : m_service_name(service_name_), m_event_callback(event_callback_) - { - } + : m_created(false), m_service_name(service_name_), m_event_callback(event_callback_) + { } // Destructor: Resets callbacks, unregisters the service, and clears data CServiceServerImpl::~CServiceServerImpl() { - // reset event callback - { - const std::lock_guard lock(m_event_callback_mutex); - m_event_callback = nullptr; - } - - // unregister client - if (g_registration_provider() != nullptr) - { - g_registration_provider()->UnregisterSample(GetUnregistrationSample()); - } + Stop(); } bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { - return false; + std::string req_desc; + std::string resp_desc; + { + 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 = 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; + + // set callback + iter->second.callback = callback_; + } + else + { + 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; } bool CServiceServerImpl::RemMethodCallback(const std::string& method_) { + std::lock_guard const lock(m_method_map_sync); + + auto iter = m_method_map.find(method_); + if (iter != m_method_map.end()) + { + m_method_map.erase(iter); + return true; + } return false; } bool CServiceServerImpl::IsConnected() const { - return false; + if (!m_created) return false; + return (m_tcp_server && m_tcp_server->is_connected()); } - void CServiceServerImpl::RegisterClient(const std::string& key_, const SClientAttr& 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 } Registration::Sample CServiceServerImpl::GetRegistration() { - return Registration::Sample(); + return GetRegistrationSample(); } std::string CServiceServerImpl::GetServiceName() const @@ -86,13 +126,265 @@ namespace eCAL return m_service_name; } + void CServiceServerImpl::Start() + { + // 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; + + // 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) + { + // we pass just service name and service id here + 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, 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 + m_tcp_server = server_manager->create_server(1, 0, service_callback, true, event_callback); + + // and register this service + if (g_registration_provider() != nullptr) + { + g_registration_provider()->RegisterSample(GetRegistrationSample()); + } + + // mark as created + m_created = true; + } + + void CServiceServerImpl::Stop() + { + if (!m_created) return; + + // reset method callback map + { + std::lock_guard const lock(m_method_map_sync); + m_method_map.clear(); + } + + // reset event callback + { + std::lock_guard const lock(m_event_callback_sync); + m_event_callback = nullptr; + } + + if (m_tcp_server) + m_tcp_server->stop(); + + // mark as no more created + m_created = false; + + // and unregister this service + if (g_registration_provider() != nullptr) + { + g_registration_provider()->UnregisterSample(GetUnregistrationSample()); + } + + // reset internals + m_service_name.clear(); + m_service_id.clear(); + + // reset connection state + { + const std::lock_guard connected_lock(m_connected_mutex); + m_connected = false; + } + } + Registration::Sample CServiceServerImpl::GetRegistrationSample() { - return Registration::Sample(); + // 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 (server_tcp_port == 0) return ecal_reg_sample; + + 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; + 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); + for (const auto& iter : m_method_map) + { + Service::Method method; + method.mname = iter.first; + method.req_type = iter.second.method.req_type; + method.req_desc = iter.second.method.req_desc; + method.resp_type = iter.second.method.resp_type; + method.resp_desc = iter.second.method.resp_desc; + method.call_count = iter.second.method.call_count; + service.methods.push_back(method); + } + } + + return ecal_reg_sample; } Registration::Sample CServiceServerImpl::GetUnregistrationSample() { - return Registration::Sample(); + // create registration sample + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_unreg_service; + + 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; + service.version = m_server_version; + service.pname = Process::GetProcessName(); + service.uname = Process::GetUnitName(); + service.sname = m_service_name; + + return ecal_reg_sample; + } + + int CServiceServerImpl::RequestCallback(const std::string& request_pb_, std::string& response_pb_) + { + // prepare response + Service::Response response; + auto& response_header = response.header; + response_header.hname = Process::GetHostName(); + response_header.sname = m_service_name; + response_header.sid = m_service_id; + + // try to parse request + 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"); + + response_header.state = Service::eMethodCallState::failed; + std::string const 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) + // serialize response and return "request message could not be parsed" + SerializeToBuffer(response, response_pb_); + + // Return Failed (error_code = -1), as parsing the request failed. The + // return value is not propagated to the remote caller. + return -1; + } + + // get method + SMethod method; + const auto& request_header = request.header; + response_header.mname = request_header.mname; + { + std::lock_guard const lock(m_method_map_sync); + + 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 + "'"; + 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) + // serialize response and return "method not found" + SerializeToBuffer(response, response_pb_); + + // Return Success (error_code = 0), as parsing the request worked. The + // return value is not propagated to the remote caller. + return 0; + } + 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 + method = requested_method_iterator->second; + } + } + + // 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); + + // set method call state 'executed' + response_header.state = Service::eMethodCallState::executed; + // set method response and return state + 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) + // serialize response and return "method not found" + SerializeToBuffer(response, response_pb_); + + // return success (error code 0) + return 0; + } + + void CServiceServerImpl::NotifyEventCallback(const Registration::SServiceMethodId& service_id_, eCAL_Server_Event event_type_, const std::string& /*message_*/) + { + // call event + std::lock_guard const lock_cb(m_event_callback_sync); + if (m_event_callback) + { + 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 fa251d1a94..af6b91c06a 100644 --- a/ecal/core/src/service/ecal_service_server_impl.h +++ b/ecal/core/src/service/ecal_service_server_impl.h @@ -25,15 +25,16 @@ #include #include +#include #include "serialization/ecal_serialize_sample_registration.h" #include "serialization/ecal_struct_service.h" +#include +#include #include #include #include -#include -#include namespace eCAL { @@ -60,7 +61,7 @@ namespace eCAL // Check connection state of a specific service bool IsConnected() const; - // Called by the registration receiver to process a service registration + // Called by the registration receiver to process a client registration void RegisterClient(const std::string& key_, const SClientAttr& client_); // Called by the registration provider to get a registration sample @@ -76,15 +77,46 @@ namespace eCAL 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(); - // Service attributes - std::string m_service_name; + // Request and event callback methods + int RequestCallback(const std::string& request_pb_, std::string& response_pb_); + void NotifyEventCallback(const Registration::SServiceMethodId& service_id_, eCAL_Server_Event event_type_, const std::string& message_); + + // 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; + + // 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; + MethodCallbackT callback; + }; + + using MethodMapT = std::map; + std::mutex m_method_map_sync; + MethodMapT m_method_map; + + // Event callback and synchronization + std::mutex m_event_callback_sync; + ServerEventIDCallbackT m_event_callback; - // Event callback map and synchronization - mutable std::mutex m_event_callback_mutex; - ServerEventIDCallbackT m_event_callback; + // Server interface + std::shared_ptr m_tcp_server; }; } diff --git a/ecal/core/src/service/ecal_servicegate.cpp b/ecal/core/src/service/ecal_servicegate.cpp index fb7858e7c2..44f94a400f 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/ecal/core/src/service/ecal_servicegate.cpp @@ -51,40 +51,37 @@ namespace eCAL if(!m_created) return; // destroy all remaining server - const std::shared_lock lock(m_service_set_sync); - m_service_set.clear(); + const std::shared_lock lock(m_service_server_map_sync); + 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_sync); + 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_sync); + 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; } } @@ -95,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_sync); + 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..020c6f47f1 100644 --- a/ecal/core/src/service/ecal_servicegate.h +++ b/ecal/core/src/service/ecal_servicegate.h @@ -43,16 +43,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_sync; + ServiceNameServiceImplMapT m_service_server_map; }; } diff --git a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp index 7d67461ba8..78dc3b7dbb 100644 --- a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp +++ b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp @@ -213,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(); From 63082f376478697679b4337cc3d6d47fb6e32295 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Wed, 11 Dec 2024 10:43:50 +0100 Subject: [PATCH 07/21] commenting, logging + minor fix --- ecal/core/include/ecal/ecal_server.h | 2 +- ecal/core/src/service/ecal_service_server.cpp | 4 +- .../src/service/ecal_service_server_impl.cpp | 233 ++++++++---------- .../src/service/ecal_service_server_impl.h | 2 +- ecal/core/src/service/ecal_servicegate.cpp | 2 +- .../src/clientserver_test.cpp | 8 +- 6 files changed, 117 insertions(+), 134 deletions(-) diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h index d91371aa33..c64610f3e0 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/ecal/core/include/ecal/ecal_server.h @@ -85,7 +85,7 @@ namespace eCAL * @return True if successful. **/ ECAL_API_EXPORTED_MEMBER - bool RemMethodCallback(const std::string& method_); + bool RemoveMethodCallback(const std::string& method_); /** * @brief Retrieve service name. diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index 60efd9ae15..90b1e52a30 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -86,11 +86,11 @@ namespace eCAL return false; } - bool CServiceServer::RemMethodCallback(const std::string& method_) + bool CServiceServer::RemoveMethodCallback(const std::string& method_) { if (m_service_server_impl) { - return m_service_server_impl->RemMethodCallback(method_); + return m_service_server_impl->RemoveMethodCallback(method_); } return false; } diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index 0ad8fce87f..4925415080 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -26,98 +26,106 @@ #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" namespace eCAL { - // Factory method to create a new instance of CServiceClientImpl + // Factory method to create a new instance of CServiceServerImpl std::shared_ptr CServiceServerImpl::CreateInstance( const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) { + Logging::Log(log_level_debug1, "Creating instance of CServiceServerImpl for service: " + service_name_); auto instance = std::shared_ptr(new CServiceServerImpl(service_name_, event_callback_)); instance->Start(); return instance; } - // Constructor: Initializes service ID and registers the service + // 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_) - { } + { + Logging::Log(log_level_debug1, "Initializing service server for: " + m_service_name); + } - // Destructor: Resets callbacks, unregisters the service, and clears data + // Destructor CServiceServerImpl::~CServiceServerImpl() { + Logging::Log(log_level_debug1, "Destroying service server for: " + m_service_name); Stop(); } bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { - std::string req_desc; - std::string resp_desc; - { - 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 = 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; + Logging::Log(log_level_debug1, "Adding method callback for method: " + method_); + std::lock_guard const lock(m_method_map_sync); - // set callback - iter->second.callback = callback_; - } - else - { - 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; - } + auto iter = m_method_map.find(method_); + if (iter != m_method_map.end()) + { + Logging::Log(log_level_warning, "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 + { + Logging::Log(log_level_debug1, "Registering new method: " + method_); + 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; } - bool CServiceServerImpl::RemMethodCallback(const std::string& method_) + bool CServiceServerImpl::RemoveMethodCallback(const std::string& method_) { + Logging::Log(log_level_debug1, "Removing method callback for method: " + method_); std::lock_guard const lock(m_method_map_sync); auto iter = m_method_map.find(method_); if (iter != m_method_map.end()) { m_method_map.erase(iter); + Logging::Log(log_level_debug1, "Successfully removed method callback: " + method_); return true; } + + Logging::Log(log_level_warning, "Attempt to remove non-existent method callback: " + method_); return false; } bool CServiceServerImpl::IsConnected() const { - if (!m_created) return false; - return (m_tcp_server && m_tcp_server->is_connected()); + if (!m_created) + { + Logging::Log(log_level_warning, "Service is not created; cannot check connection state for: " + m_service_name); + return false; + } + + bool connected = m_tcp_server && m_tcp_server->is_connected(); + Logging::Log(log_level_debug2, "Connection state for service " + m_service_name + ": " + (connected ? "connected" : "disconnected")); + return connected; } 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 + Logging::Log(log_level_debug2, "Client registration logic is not implemented for: " + m_service_name); } Registration::Sample CServiceServerImpl::GetRegistration() { + Logging::Log(log_level_debug2, "Generating registration sample for: " + m_service_name); return GetRegistrationSample(); } @@ -128,7 +136,15 @@ namespace eCAL void CServiceServerImpl::Start() { - // create service id + if (m_created) + { + Logging::Log(log_level_warning, "Service is already started: " + m_service_name); + return; + } + + Logging::Log(log_level_debug1, "Starting service server for: " + m_service_name); + + // Create service ID std::stringstream counter; counter << std::chrono::steady_clock::now().time_since_epoch().count(); m_service_id = counter.str(); @@ -136,109 +152,99 @@ namespace eCAL // Get global server manager auto server_manager = eCAL::service::ServiceManager::instance()->get_server_manager(); if (!server_manager || server_manager->is_stopped()) + { + Logging::Log(log_level_error, "Failed to start service: Global server manager is unavailable or stopped for: " + m_service_name); return; + } // 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) + 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) + if (auto me = weak_me.lock()) { - 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) - { - // we pass just service name and service id here 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, ecal_server_event, message); + 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); } }; - 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 + 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) + if (auto me = weak_me.lock()) return me->RequestCallback(*request, *response); - else - return -1; + return -1; }; - // start service + // Start service m_tcp_server = server_manager->create_server(1, 0, service_callback, true, event_callback); - // and register this service - if (g_registration_provider() != nullptr) + if (!m_tcp_server) { - g_registration_provider()->RegisterSample(GetRegistrationSample()); + Logging::Log(log_level_error, "Failed to create TCP server for service: " + m_service_name); + return; } - // mark as created + // Register service + if (g_registration_provider()) + g_registration_provider()->RegisterSample(GetRegistrationSample()); + m_created = true; + Logging::Log(log_level_debug1, "Service started successfully: " + m_service_name); } void CServiceServerImpl::Stop() { - if (!m_created) return; + if (!m_created) + { + Logging::Log(log_level_warning, "Service is not running; cannot stop: " + m_service_name); + return; + } - // reset method callback map + Logging::Log(log_level_debug1, "Stopping service server for: " + m_service_name); + + // Reset method callbacks { std::lock_guard const lock(m_method_map_sync); m_method_map.clear(); + Logging::Log(log_level_debug2, "Cleared all method callbacks for: " + m_service_name); } - // reset event callback + // Reset event callback { std::lock_guard const lock(m_event_callback_sync); m_event_callback = nullptr; + Logging::Log(log_level_debug2, "Cleared event callback for: " + m_service_name); } + // Stop TCP server if (m_tcp_server) - m_tcp_server->stop(); - - // mark as no more created - m_created = false; - - // and unregister this service - if (g_registration_provider() != nullptr) { - g_registration_provider()->UnregisterSample(GetUnregistrationSample()); + m_tcp_server->stop(); + Logging::Log(log_level_debug1, "TCP server stopped for: " + m_service_name); } - // reset internals - m_service_name.clear(); - m_service_id.clear(); + // Unregister service + if (g_registration_provider()) + g_registration_provider()->UnregisterSample(GetUnregistrationSample()); - // reset connection state - { - const std::lock_guard connected_lock(m_connected_mutex); - m_connected = false; - } + m_created = false; + Logging::Log(log_level_debug1, "Service stopped successfully: " + m_service_name); } Registration::Sample CServiceServerImpl::GetRegistrationSample() { - // create registration sample + Logging::Log(log_level_debug2, "Preparing registration sample for: " + m_service_name); + 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); + 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; @@ -254,7 +260,6 @@ namespace eCAL service.tcp_port_v0 = 0; service.tcp_port_v1 = server_tcp_port; - // add methods { std::lock_guard const lock(m_method_map_sync); for (const auto& iter : m_method_map) @@ -275,7 +280,8 @@ namespace eCAL Registration::Sample CServiceServerImpl::GetUnregistrationSample() { - // create registration sample + Logging::Log(log_level_debug2, "Preparing unregistration sample for: " + m_service_name); + Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_unreg_service; @@ -295,14 +301,14 @@ namespace eCAL int CServiceServerImpl::RequestCallback(const std::string& request_pb_, std::string& response_pb_) { - // prepare response + Logging::Log(log_level_debug2, "Processing request callback for: " + m_service_name); + Service::Response response; auto& response_header = response.header; response_header.hname = Process::GetHostName(); response_header.sname = m_service_name; response_header.sid = m_service_id; - // try to parse request Service::Request request; if (!DeserializeFromBuffer(request_pb_.c_str(), request_pb_.size(), request)) { @@ -312,72 +318,49 @@ namespace eCAL std::string const 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) - // serialize response and return "request message could not be parsed" SerializeToBuffer(response, response_pb_); - - // Return Failed (error_code = -1), as parsing the request failed. The - // return value is not propagated to the remote caller. return -1; } - // get method SMethod method; const auto& request_header = request.header; response_header.mname = request_header.mname; { std::lock_guard const lock(m_method_map_sync); - 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 + "'"; 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) - // serialize response and return "method not found" SerializeToBuffer(response, response_pb_); - - // Return Success (error_code = 0), as parsing the request worked. The - // return value is not propagated to the remote caller. return 0; } 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 method = requested_method_iterator->second; } } - // 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); - // set method call state 'executed' response_header.state = Service::eMethodCallState::executed; - // set method response and return state 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) - // serialize response and return "method not found" SerializeToBuffer(response, response_pb_); - - // return success (error code 0) return 0; } void CServiceServerImpl::NotifyEventCallback(const Registration::SServiceMethodId& service_id_, eCAL_Server_Event event_type_, const std::string& /*message_*/) { - // call event + Logging::Log(log_level_debug1, "Notifying event callback for: " + m_service_name + " Event Type: " + std::to_string(event_type_)); + std::lock_guard const lock_cb(m_event_callback_sync); if (m_event_callback) { diff --git a/ecal/core/src/service/ecal_service_server_impl.h b/ecal/core/src/service/ecal_service_server_impl.h index af6b91c06a..62f6b0ac06 100644 --- a/ecal/core/src/service/ecal_service_server_impl.h +++ b/ecal/core/src/service/ecal_service_server_impl.h @@ -56,7 +56,7 @@ namespace eCAL ~CServiceServerImpl(); bool AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_); - bool RemMethodCallback(const std::string& method_); + bool RemoveMethodCallback(const std::string& method_); // Check connection state of a specific service bool IsConnected() const; diff --git a/ecal/core/src/service/ecal_servicegate.cpp b/ecal/core/src/service/ecal_servicegate.cpp index 44f94a400f..275a22ab8b 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/ecal/core/src/service/ecal_servicegate.cpp @@ -51,7 +51,7 @@ namespace eCAL if(!m_created) return; // destroy all remaining server - const std::shared_lock lock(m_service_server_map_sync); + const std::unique_lock lock(m_service_server_map_sync); m_service_server_map.clear(); m_created = false; diff --git a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp index 78dc3b7dbb..e04c77449b 100644 --- a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp +++ b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp @@ -775,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 @@ -852,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(); From ffa87a528aa6b9ffafdecbb62f1d8cb045c5bd7c Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Wed, 11 Dec 2024 11:42:04 +0100 Subject: [PATCH 08/21] eCAL::v5::CServiceServerImpl implemented based on eCAL::CServiceServer tests adapted --- .../service/ecal_service_server_v5_impl.cpp | 125 ++++++++++++++++-- .../src/service/ecal_service_server_v5_impl.h | 11 +- .../src/clientserver_test.cpp | 4 +- .../src/clientserver_v5_test.cpp | 28 ++-- 4 files changed, 135 insertions(+), 33 deletions(-) diff --git a/ecal/core/src/service/ecal_service_server_v5_impl.cpp b/ecal/core/src/service/ecal_service_server_v5_impl.cpp index 960f6c90ba..a218a453ba 100644 --- a/ecal/core/src/service/ecal_service_server_v5_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_v5_impl.cpp @@ -21,6 +21,8 @@ * @brief eCAL service server implementation (deprecated eCAL5 version) **/ +#include + #include "ecal_service_server_v5_impl.h" namespace eCAL @@ -30,61 +32,166 @@ namespace eCAL CServiceServerImpl::CServiceServerImpl() : m_service_server_impl(nullptr) { + Logging::Log(log_level_debug2, "Initializing default service server implementation."); } CServiceServerImpl::CServiceServerImpl(const std::string& service_name_) : m_service_server_impl(nullptr) { + Logging::Log(log_level_debug2, "Initializing service server with name: " + service_name_); + Create(service_name_); } CServiceServerImpl::~CServiceServerImpl() { + Logging::Log(log_level_debug2, "Destroying service server implementation."); + Destroy(); } bool CServiceServerImpl::Create(const std::string& service_name_) { - return false; + if (m_service_server_impl) + { + Logging::Log(log_level_warning, "Service server already created: " + service_name_); + return false; + } + + // Define the event callback to pass to CServiceClientNew + 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, "Service server created with name: " + service_name_); + return true; } bool CServiceServerImpl::Destroy() { - return false; + if (!m_service_server_impl) + { + Logging::Log(log_level_warning, "Service server not initialized, cannot destroy."); + return false; + } + + m_service_server_impl.reset(); + Logging::Log(log_level_debug2, "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_) { - return false; + Logging::Log(log_level_debug1, "Adding description for method: " + method_); + + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "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_) { - return false; + Logging::Log(log_level_debug2, "Adding method callback for method: " + method_); + + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "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_) { - return false; + Logging::Log(log_level_debug2, "Removing method callback for method: " + method_); + + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "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_) { - return false; + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "Service server not initialized, cannot add event callback."); + return false; + } + Logging::Log(log_level_debug2, "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, "Service server not initialized, cannot remove event callback."); + return false; + } + Logging::Log(log_level_debug2, "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() { - return ""; + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "Service server not initialized, cannot get service name."); + return ""; + } + + return m_service_server_impl->GetServiceName(); } bool CServiceServerImpl::IsConnected() { - return false; + if (!m_service_server_impl) + { + Logging::Log(log_level_error, "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 index 3f19f920eb..fffc6af638 100644 --- a/ecal/core/src/service/ecal_service_server_v5_impl.h +++ b/ecal/core/src/service/ecal_service_server_v5_impl.h @@ -27,15 +27,13 @@ #include #include +#include #include namespace eCAL { namespace v5 { - /** - * @brief Service server implementation class. - **/ class CServiceServerImpl { public: @@ -47,8 +45,10 @@ namespace eCAL 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_); @@ -62,7 +62,12 @@ namespace eCAL 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/tests/cpp/clientserver_test/src/clientserver_test.cpp b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp index e04c77449b..1e18d876e2 100644 --- a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp +++ b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp @@ -205,7 +205,7 @@ TEST(core_cpp_clientserver, ServerConnectEvent) 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()); @@ -492,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); 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 901fe9c062..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 @@ -149,23 +149,18 @@ TEST(core_cpp_clientserver_v5, ClientConnectEvent) { 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::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()); @@ -221,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(); From 4181604d106eca70bf7bccabbcab3c6dd57f16ca Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:53:52 +0100 Subject: [PATCH 09/21] missing include --- ecal/core/src/service/ecal_clientgate.h | 8 +------- ecal/core/src/service/ecal_servicegate.h | 4 +++- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ecal/core/src/service/ecal_clientgate.h b/ecal/core/src/service/ecal_clientgate.h index fdc6b84f5a..3e7077eb66 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 diff --git a/ecal/core/src/service/ecal_servicegate.h b/ecal/core/src/service/ecal_servicegate.h index 020c6f47f1..cfebcfffde 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 { From 02e8ad1eadafaf68684ad99c0c178ef16b0563ca Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:00:51 +0100 Subject: [PATCH 10/21] logging improved --- .../src/service/ecal_service_server_impl.cpp | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index 4925415080..7fb88db186 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -37,7 +37,7 @@ namespace eCAL std::shared_ptr CServiceServerImpl::CreateInstance( const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) { - Logging::Log(log_level_debug1, "Creating instance of CServiceServerImpl for service: " + service_name_); + Logging::Log(log_level_debug1, "CServiceServerImpl::CreateInstance: Creating instance of CServiceServerImpl for service: " + service_name_); auto instance = std::shared_ptr(new CServiceServerImpl(service_name_, event_callback_)); instance->Start(); return instance; @@ -47,25 +47,25 @@ namespace eCAL CServiceServerImpl::CServiceServerImpl(const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) : m_created(false), m_service_name(service_name_), m_event_callback(event_callback_) { - Logging::Log(log_level_debug1, "Initializing service server for: " + m_service_name); + Logging::Log(log_level_debug1, "CServiceServerImpl: Initializing service server for: " + m_service_name); } // Destructor CServiceServerImpl::~CServiceServerImpl() { - Logging::Log(log_level_debug1, "Destroying service server for: " + m_service_name); + Logging::Log(log_level_debug1, "CServiceServerImpl: Destroying service server for: " + m_service_name); Stop(); } bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { - Logging::Log(log_level_debug1, "Adding method callback for method: " + method_); + Logging::Log(log_level_debug1, "CServiceServerImpl: Adding method callback for method: " + method_); std::lock_guard const lock(m_method_map_sync); auto iter = m_method_map.find(method_); if (iter != m_method_map.end()) { - Logging::Log(log_level_warning, "Method already exists, updating callback: " + method_); + Logging::Log(log_level_warning, "CServiceServerImpl: 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; @@ -74,7 +74,7 @@ namespace eCAL } else { - Logging::Log(log_level_debug1, "Registering new method: " + method_); + Logging::Log(log_level_debug1, "CServiceServerImpl: Registering new method: " + method_); SMethod method; method.method.mname = method_; method.method.req_type = method_info_.request_type.name; @@ -90,18 +90,18 @@ namespace eCAL bool CServiceServerImpl::RemoveMethodCallback(const std::string& method_) { - Logging::Log(log_level_debug1, "Removing method callback for method: " + method_); + Logging::Log(log_level_debug1, "CServiceServerImpl: Removing method callback for method: " + method_); std::lock_guard const lock(m_method_map_sync); auto iter = m_method_map.find(method_); if (iter != m_method_map.end()) { m_method_map.erase(iter); - Logging::Log(log_level_debug1, "Successfully removed method callback: " + method_); + Logging::Log(log_level_debug1, "CServiceServerImpl: Successfully removed method callback: " + method_); return true; } - Logging::Log(log_level_warning, "Attempt to remove non-existent method callback: " + method_); + Logging::Log(log_level_warning, "CServiceServerImpl: Attempt to remove non-existent method callback: " + method_); return false; } @@ -109,23 +109,23 @@ namespace eCAL { if (!m_created) { - Logging::Log(log_level_warning, "Service is not created; cannot check connection state for: " + m_service_name); + Logging::Log(log_level_warning, "CServiceServerImpl: Service is not created; cannot check connection state for: " + m_service_name); return false; } bool connected = m_tcp_server && m_tcp_server->is_connected(); - Logging::Log(log_level_debug2, "Connection state for service " + m_service_name + ": " + (connected ? "connected" : "disconnected")); + Logging::Log(log_level_debug2, "CServiceServerImpl: Connection state for service " + m_service_name + ": " + (connected ? "connected" : "disconnected")); return connected; } void CServiceServerImpl::RegisterClient(const std::string& /*key_*/, const SClientAttr& /*client_*/) { - Logging::Log(log_level_debug2, "Client registration logic is not implemented for: " + m_service_name); + Logging::Log(log_level_debug2, "CServiceServerImpl: Client registration logic is not implemented for: " + m_service_name); } Registration::Sample CServiceServerImpl::GetRegistration() { - Logging::Log(log_level_debug2, "Generating registration sample for: " + m_service_name); + Logging::Log(log_level_debug2, "CServiceServerImpl: Generating registration sample for: " + m_service_name); return GetRegistrationSample(); } @@ -138,11 +138,11 @@ namespace eCAL { if (m_created) { - Logging::Log(log_level_warning, "Service is already started: " + m_service_name); + Logging::Log(log_level_warning, "CServiceServerImpl: Service is already started: " + m_service_name); return; } - Logging::Log(log_level_debug1, "Starting service server for: " + m_service_name); + Logging::Log(log_level_debug1, "CServiceServerImpl: Starting service server for: " + m_service_name); // Create service ID std::stringstream counter; @@ -153,7 +153,7 @@ namespace eCAL auto server_manager = eCAL::service::ServiceManager::instance()->get_server_manager(); if (!server_manager || server_manager->is_stopped()) { - Logging::Log(log_level_error, "Failed to start service: Global server manager is unavailable or stopped for: " + m_service_name); + Logging::Log(log_level_error, "CServiceServerImpl: Failed to start service: Global server manager is unavailable or stopped for: " + m_service_name); return; } @@ -185,7 +185,7 @@ namespace eCAL if (!m_tcp_server) { - Logging::Log(log_level_error, "Failed to create TCP server for service: " + m_service_name); + Logging::Log(log_level_error, "CServiceServerImpl: Failed to create TCP server for service: " + m_service_name); return; } @@ -194,38 +194,38 @@ namespace eCAL g_registration_provider()->RegisterSample(GetRegistrationSample()); m_created = true; - Logging::Log(log_level_debug1, "Service started successfully: " + m_service_name); + Logging::Log(log_level_debug1, "CServiceServerImpl: Service started successfully: " + m_service_name); } void CServiceServerImpl::Stop() { if (!m_created) { - Logging::Log(log_level_warning, "Service is not running; cannot stop: " + m_service_name); + Logging::Log(log_level_warning, "CServiceServerImpl: Service is not running; cannot stop: " + m_service_name); return; } - Logging::Log(log_level_debug1, "Stopping service server for: " + m_service_name); + Logging::Log(log_level_debug1, "CServiceServerImpl: Stopping service server for: " + m_service_name); // Reset method callbacks { std::lock_guard const lock(m_method_map_sync); m_method_map.clear(); - Logging::Log(log_level_debug2, "Cleared all method callbacks for: " + m_service_name); + Logging::Log(log_level_debug2, "CServiceServerImpl: Cleared all method callbacks for: " + m_service_name); } // Reset event callback { std::lock_guard const lock(m_event_callback_sync); m_event_callback = nullptr; - Logging::Log(log_level_debug2, "Cleared event callback for: " + m_service_name); + Logging::Log(log_level_debug2, "CServiceServerImpl: Cleared event callback for: " + m_service_name); } // Stop TCP server if (m_tcp_server) { m_tcp_server->stop(); - Logging::Log(log_level_debug1, "TCP server stopped for: " + m_service_name); + Logging::Log(log_level_debug1, "CServiceServerImpl: TCP server stopped for: " + m_service_name); } // Unregister service @@ -233,12 +233,12 @@ namespace eCAL g_registration_provider()->UnregisterSample(GetUnregistrationSample()); m_created = false; - Logging::Log(log_level_debug1, "Service stopped successfully: " + m_service_name); + Logging::Log(log_level_debug1, "CServiceServerImpl: Service stopped successfully: " + m_service_name); } Registration::Sample CServiceServerImpl::GetRegistrationSample() { - Logging::Log(log_level_debug2, "Preparing registration sample for: " + m_service_name); + Logging::Log(log_level_debug2, "CServiceServerImpl: Preparing registration sample for: " + m_service_name); Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_reg_service; @@ -280,7 +280,7 @@ namespace eCAL Registration::Sample CServiceServerImpl::GetUnregistrationSample() { - Logging::Log(log_level_debug2, "Preparing unregistration sample for: " + m_service_name); + Logging::Log(log_level_debug2, "CServiceServerImpl: Preparing unregistration sample for: " + m_service_name); Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_unreg_service; @@ -301,7 +301,7 @@ namespace eCAL int CServiceServerImpl::RequestCallback(const std::string& request_pb_, std::string& response_pb_) { - Logging::Log(log_level_debug2, "Processing request callback for: " + m_service_name); + Logging::Log(log_level_debug2, "CServiceServerImpl: Processing request callback for: " + m_service_name); Service::Response response; auto& response_header = response.header; @@ -331,7 +331,7 @@ namespace eCAL if (requested_method_iterator == m_method_map.end()) { response_header.state = Service::eMethodCallState::failed; - std::string const emsg = "Service '" + m_service_name + "' has no method named '" + request_header.mname + "'"; + std::string const emsg = "CServiceServerImpl: Service '" + m_service_name + "' has no method named '" + request_header.mname + "'"; response_header.error = emsg; SerializeToBuffer(response, response_pb_); @@ -359,7 +359,7 @@ namespace eCAL void CServiceServerImpl::NotifyEventCallback(const Registration::SServiceMethodId& service_id_, eCAL_Server_Event event_type_, const std::string& /*message_*/) { - Logging::Log(log_level_debug1, "Notifying event callback for: " + m_service_name + " Event Type: " + std::to_string(event_type_)); + Logging::Log(log_level_debug1, "CServiceServerImpl: Notifying event callback for: " + m_service_name + " Event Type: " + std::to_string(event_type_)); std::lock_guard const lock_cb(m_event_callback_sync); if (m_event_callback) From f59b100b64655d0575bdddcab50091aff8442906 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:07:45 +0100 Subject: [PATCH 11/21] minor comment and include fixes --- ecal/core/include/ecal/ecal_server.h | 7 ++++--- ecal/core/include/ecal/ecal_server_v5.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h index c64610f3e0..f569e54b3c 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/ecal/core/include/ecal/ecal_server.h @@ -18,8 +18,8 @@ */ /** - * @file ecal_server_v5.h - * @brief eCAL server interface (deprecated eCAL5 version) + * @file ecal_server.h + * @brief eCAL server interface **/ #pragma once @@ -29,9 +29,9 @@ #include #include +#include #include #include -#include namespace eCAL { @@ -47,6 +47,7 @@ namespace eCAL * @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()); diff --git a/ecal/core/include/ecal/ecal_server_v5.h b/ecal/core/include/ecal/ecal_server_v5.h index 57a40094b7..31020f83a7 100644 --- a/ecal/core/include/ecal/ecal_server_v5.h +++ b/ecal/core/include/ecal/ecal_server_v5.h @@ -29,9 +29,9 @@ #include #include +#include #include #include -#include namespace eCAL { From a5fb463f7a07ccb11940467d0da5a02fd012329e Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:09:13 +0100 Subject: [PATCH 12/21] improved logging --- .../service/ecal_service_server_v5_impl.cpp | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/ecal/core/src/service/ecal_service_server_v5_impl.cpp b/ecal/core/src/service/ecal_service_server_v5_impl.cpp index a218a453ba..76bcaf6b6e 100644 --- a/ecal/core/src/service/ecal_service_server_v5_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_v5_impl.cpp @@ -32,19 +32,19 @@ namespace eCAL CServiceServerImpl::CServiceServerImpl() : m_service_server_impl(nullptr) { - Logging::Log(log_level_debug2, "Initializing default service server implementation."); + 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, "Initializing service server with name: " + service_name_); + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Initializing service server with name: " + service_name_); Create(service_name_); } CServiceServerImpl::~CServiceServerImpl() { - Logging::Log(log_level_debug2, "Destroying service server implementation."); + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Destroying service server implementation."); Destroy(); } @@ -52,7 +52,7 @@ namespace eCAL { if (m_service_server_impl) { - Logging::Log(log_level_warning, "Service server already created: " + service_name_); + Logging::Log(log_level_warning, "v5::CServiceServerImpl: Service server already created: " + service_name_); return false; } @@ -72,7 +72,7 @@ namespace eCAL }; m_service_server_impl = std::make_shared(service_name_, event_callback); - Logging::Log(log_level_debug1, "Service server created with name: " + service_name_); + Logging::Log(log_level_debug1, "v5::CServiceServerImpl: Service server created with name: " + service_name_); return true; } @@ -80,22 +80,22 @@ namespace eCAL { if (!m_service_server_impl) { - Logging::Log(log_level_warning, "Service server not initialized, cannot destroy."); + 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, "Service server destroyed."); + 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, "Adding description for method: " + method_); + Logging::Log(log_level_debug1, "v5::CServiceServerImpl: Adding description for method: " + method_); if (!m_service_server_impl) { - Logging::Log(log_level_error, "Service server not initialized, cannot add description."); + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot add description."); return false; } @@ -110,11 +110,11 @@ namespace eCAL 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, "Adding method callback for method: " + method_); + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Adding method callback for method: " + method_); if (!m_service_server_impl) { - Logging::Log(log_level_error, "Service server not initialized, cannot add method callback."); + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot add method callback."); return false; } @@ -127,11 +127,11 @@ namespace eCAL bool CServiceServerImpl::RemMethodCallback(const std::string& method_) { - Logging::Log(log_level_debug2, "Removing method callback for method: " + method_); + Logging::Log(log_level_debug2, "v5::CServiceServerImpl: Removing method callback for method: " + method_); if (!m_service_server_impl) { - Logging::Log(log_level_error, "Service server not initialized, cannot remove method callback."); + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot remove method callback."); return false; } @@ -142,10 +142,10 @@ namespace eCAL { if (!m_service_server_impl) { - Logging::Log(log_level_error, "Service server not initialized, cannot add event callback."); + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot add event callback."); return false; } - Logging::Log(log_level_debug2, "Adding event callback for event type: " + std::to_string(type_)); + 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); @@ -159,10 +159,10 @@ namespace eCAL { if (!m_service_server_impl) { - Logging::Log(log_level_error, "Service server not initialized, cannot remove event callback."); + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot remove event callback."); return false; } - Logging::Log(log_level_debug2, "Removing event callback for event type: " + std::to_string(type_)); + 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); @@ -176,7 +176,7 @@ namespace eCAL { if (!m_service_server_impl) { - Logging::Log(log_level_error, "Service server not initialized, cannot get service name."); + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot get service name."); return ""; } @@ -187,7 +187,7 @@ namespace eCAL { if (!m_service_server_impl) { - Logging::Log(log_level_error, "Service server not initialized, cannot check connection status."); + Logging::Log(log_level_error, "v5::CServiceServerImpl: Service server not initialized, cannot check connection status."); return false; } From 95187e90a9f45194b69791a54b2a6cc2d6789c10 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:34:35 +0100 Subject: [PATCH 13/21] formatting missing include --- ecal/core/src/service/ecal_clientgate.cpp | 10 +- ecal/core/src/service/ecal_clientgate.h | 4 +- .../src/service/ecal_service_client_impl.cpp | 22 +-- .../src/service/ecal_service_client_impl.h | 6 +- .../service/ecal_service_client_v5_impl.cpp | 142 +++++++++++++++--- .../src/service/ecal_service_server_impl.cpp | 14 +- .../src/service/ecal_service_server_impl.h | 4 +- .../service/ecal_service_server_v5_impl.cpp | 2 +- .../ecal_service_singleton_manager.cpp | 70 ++++----- .../service/ecal_service_singleton_manager.h | 76 +++++----- ecal/core/src/service/ecal_servicegate.cpp | 8 +- ecal/core/src/service/ecal_servicegate.h | 4 +- 12 files changed, 231 insertions(+), 131 deletions(-) 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 3e7077eb66..b4568b4842 100644 --- a/ecal/core/src/service/ecal_clientgate.h +++ b/ecal/core/src/service/ecal_clientgate.h @@ -29,8 +29,8 @@ #include #include -#include #include +#include #include #include @@ -58,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_impl.cpp b/ecal/core/src/service/ecal_service_client_impl.cpp index 5f7f6e9416..cd131ceafa 100644 --- a/ecal/core/src/service/ecal_service_client_impl.cpp +++ b/ecal/core/src/service/ecal_service_client_impl.cpp @@ -134,7 +134,7 @@ 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_; } @@ -150,13 +150,13 @@ namespace eCAL { // 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; } @@ -173,7 +173,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) { @@ -272,14 +272,14 @@ 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); } 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()) { @@ -340,7 +340,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 +386,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 +442,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 +475,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 +483,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_impl.cpp b/ecal/core/src/service/ecal_service_client_v5_impl.cpp index a4a960c406..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. @@ -22,6 +22,7 @@ **/ #include "ecal_service_client_v5_impl.h" +#include namespace eCAL { @@ -30,22 +31,26 @@ namespace eCAL CServiceClientImpl::CServiceClientImpl() : m_service_client_impl(nullptr) { + Logging::Log(log_level_debug2, "v5::CServiceClientImpl: Initializing default service client implementation."); } CServiceClientImpl::CServiceClientImpl(const std::string& service_name_) : m_service_client_impl(nullptr) { + 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) { + 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(); } @@ -56,11 +61,19 @@ namespace eCAL bool CServiceClientImpl::Create(const std::string& service_name_, const ServiceMethodInformationMapT& method_information_map_) { - if (m_service_client_impl != nullptr) 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); @@ -68,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_); } @@ -80,12 +94,19 @@ namespace eCAL event_callback ); + Logging::Log(log_level_debug1, "v5::CServiceClientImpl: Service client created successfully with name: " + service_name_); return true; } bool CServiceClientImpl::Destroy() { - if (m_service_client_impl == nullptr) 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(); @@ -95,12 +116,19 @@ namespace eCAL 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_service_client_impl == nullptr) 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_; @@ -110,14 +138,25 @@ namespace eCAL bool CServiceClientImpl::Call(const std::string& method_name_, const std::string& request_, int timeout_) { - if (m_service_client_impl == nullptr) return false; - if (!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_); } @@ -129,8 +168,18 @@ namespace eCAL bool CServiceClientImpl::Call(const std::string& method_name_, const std::string& request_, int timeout_, ServiceResponseVecT* service_response_vec_) { - if (m_service_client_impl == nullptr) return false; - if (!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; @@ -139,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) @@ -152,31 +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_service_client_impl == nullptr) return false; - if (!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_service_client_impl == nullptr) 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); @@ -188,7 +258,13 @@ namespace eCAL bool CServiceClientImpl::RemResponseCallback() { - if (m_service_client_impl == nullptr) 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); @@ -200,7 +276,13 @@ namespace eCAL bool CServiceClientImpl::AddEventCallback(eCAL_Client_Event type_, ClientEventCallbackT callback_) { - if (m_service_client_impl == nullptr) 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); @@ -212,7 +294,13 @@ namespace eCAL bool CServiceClientImpl::RemEventCallback(eCAL_Client_Event type_) { - if (m_service_client_impl == nullptr) 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); @@ -224,16 +312,28 @@ namespace eCAL std::string CServiceClientImpl::GetServiceName() { - if (m_service_client_impl == nullptr) 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_service_client_impl == nullptr) 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_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index 7fb88db186..4206195599 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -60,7 +60,7 @@ namespace eCAL bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { Logging::Log(log_level_debug1, "CServiceServerImpl: Adding method callback for method: " + method_); - std::lock_guard const lock(m_method_map_sync); + std::lock_guard const lock(m_method_map_mutex); auto iter = m_method_map.find(method_); if (iter != m_method_map.end()) @@ -91,7 +91,7 @@ namespace eCAL bool CServiceServerImpl::RemoveMethodCallback(const std::string& method_) { Logging::Log(log_level_debug1, "CServiceServerImpl: Removing method callback for method: " + method_); - std::lock_guard const lock(m_method_map_sync); + std::lock_guard const lock(m_method_map_mutex); auto iter = m_method_map.find(method_); if (iter != m_method_map.end()) @@ -209,14 +209,14 @@ namespace eCAL // Reset method callbacks { - std::lock_guard const lock(m_method_map_sync); + std::lock_guard const lock(m_method_map_mutex); m_method_map.clear(); Logging::Log(log_level_debug2, "CServiceServerImpl: Cleared all method callbacks for: " + m_service_name); } // Reset event callback { - std::lock_guard const lock(m_event_callback_sync); + std::lock_guard const lock(m_event_callback_mutex); m_event_callback = nullptr; Logging::Log(log_level_debug2, "CServiceServerImpl: Cleared event callback for: " + m_service_name); } @@ -261,7 +261,7 @@ namespace eCAL service.tcp_port_v1 = server_tcp_port; { - std::lock_guard const lock(m_method_map_sync); + std::lock_guard const lock(m_method_map_mutex); for (const auto& iter : m_method_map) { Service::Method method; @@ -326,7 +326,7 @@ namespace eCAL const auto& request_header = request.header; response_header.mname = request_header.mname; { - std::lock_guard const lock(m_method_map_sync); + std::lock_guard const lock(m_method_map_mutex); auto requested_method_iterator = m_method_map.find(request_header.mname); if (requested_method_iterator == m_method_map.end()) { @@ -361,7 +361,7 @@ namespace eCAL { Logging::Log(log_level_debug1, "CServiceServerImpl: Notifying event callback for: " + m_service_name + " Event Type: " + std::to_string(event_type_)); - std::lock_guard const lock_cb(m_event_callback_sync); + std::lock_guard const lock_cb(m_event_callback_mutex); if (m_event_callback) { SServerEventCallbackData 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 62f6b0ac06..be5ad0c44d 100644 --- a/ecal/core/src/service/ecal_service_server_impl.h +++ b/ecal/core/src/service/ecal_service_server_impl.h @@ -109,11 +109,11 @@ namespace eCAL }; using MethodMapT = std::map; - std::mutex m_method_map_sync; + std::mutex m_method_map_mutex; MethodMapT m_method_map; // Event callback and synchronization - std::mutex m_event_callback_sync; + std::mutex m_event_callback_mutex; ServerEventIDCallbackT m_event_callback; // Server interface diff --git a/ecal/core/src/service/ecal_service_server_v5_impl.cpp b/ecal/core/src/service/ecal_service_server_v5_impl.cpp index 76bcaf6b6e..715812eda4 100644 --- a/ecal/core/src/service/ecal_service_server_v5_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_v5_impl.cpp @@ -56,7 +56,7 @@ namespace eCAL return false; } - // Define the event callback to pass to CServiceClientNew + // 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 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 275a22ab8b..216f84eb8c 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/ecal/core/src/service/ecal_servicegate.cpp @@ -51,7 +51,7 @@ namespace eCAL if(!m_created) return; // destroy all remaining server - const std::unique_lock lock(m_service_server_map_sync); + const std::unique_lock lock(m_service_server_map_mutex); m_service_server_map.clear(); m_created = false; @@ -62,7 +62,7 @@ namespace eCAL if(!m_created) return(false); // register internal service - const std::unique_lock lock(m_service_server_map_sync); + const std::unique_lock lock(m_service_server_map_mutex); m_service_server_map.emplace(std::pair>(service_name_, server_)); return(true); @@ -73,7 +73,7 @@ namespace eCAL if (!m_created) return(false); bool ret_state = false; - const std::unique_lock lock(m_service_server_map_sync); + 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) { @@ -94,7 +94,7 @@ namespace eCAL // read server registrations { - const std::shared_lock lock(m_service_server_map_sync); + 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 cfebcfffde..8dc75429be 100644 --- a/ecal/core/src/service/ecal_servicegate.h +++ b/ecal/core/src/service/ecal_servicegate.h @@ -27,8 +27,8 @@ #include "serialization/ecal_struct_sample_registration.h" #include -#include #include +#include #include #include @@ -54,7 +54,7 @@ namespace eCAL static std::atomic m_created; using ServiceNameServiceImplMapT = std::multimap>; - std::shared_timed_mutex m_service_server_map_sync; + std::shared_timed_mutex m_service_server_map_mutex; ServiceNameServiceImplMapT m_service_server_map; }; } From f9d9a0a09d8395243b0090348a2be75745dd0f5f Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:26:49 +0100 Subject: [PATCH 14/21] minor review adaptions --- ecal/core/src/service/ecal_service_client.cpp | 10 ----- .../src/service/ecal_service_client_impl.cpp | 4 +- ecal/core/src/service/ecal_service_server.cpp | 9 ---- .../src/service/ecal_service_server_impl.cpp | 41 +++++++++++++++---- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/ecal/core/src/service/ecal_service_client.cpp b/ecal/core/src/service/ecal_service_client.cpp index a0d6e1e423..5398239533 100644 --- a/ecal/core/src/service/ecal_service_client.cpp +++ b/ecal/core/src/service/ecal_service_client.cpp @@ -58,23 +58,13 @@ namespace eCAL 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; } diff --git a/ecal/core/src/service/ecal_service_client_impl.cpp b/ecal/core/src/service/ecal_service_client_impl.cpp index cd131ceafa..76860b2002 100644 --- a/ecal/core/src/service/ecal_service_client_impl.cpp +++ b/ecal/core/src/service/ecal_service_client_impl.cpp @@ -138,7 +138,7 @@ namespace eCAL m_event_callback = event_callback_; } - // register client + // Send registration sample if (!m_service_name.empty() && g_registration_provider() != nullptr) { g_registration_provider()->RegisterSample(GetRegistrationSample()); @@ -160,7 +160,7 @@ namespace eCAL m_event_callback = nullptr; } - // unregister client + // Send unregistration sample if (g_registration_provider() != nullptr) { g_registration_provider()->UnregisterSample(GetUnregistrationSample()); diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index 90b1e52a30..ce8a30b0e6 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -63,16 +63,7 @@ namespace eCAL { if (this != &rhs) { - // Unregister server - if (g_servicegate() != nullptr) - { - //g_servicegate()->Unregister(m_service_server_impl->GetServiceName(), m_service_server_impl); - } - - // Move data m_service_server_impl = std::move(rhs.m_service_server_impl); - - rhs.m_service_server_impl = nullptr; } return *this; } diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index 4206195599..bcec71c805 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -189,7 +189,7 @@ namespace eCAL return; } - // Register service + // Send registration sample if (g_registration_provider()) g_registration_provider()->RegisterSample(GetRegistrationSample()); @@ -207,6 +207,14 @@ namespace eCAL Logging::Log(log_level_debug1, "CServiceServerImpl: Stopping service server for: " + m_service_name); + // Stop TCP server + if (m_tcp_server) + { + m_tcp_server->stop(); + Logging::Log(log_level_debug1, "CServiceServerImpl: TCP server stopped for: " + m_service_name); + } + m_tcp_server.reset(); + // Reset method callbacks { std::lock_guard const lock(m_method_map_mutex); @@ -221,14 +229,7 @@ namespace eCAL Logging::Log(log_level_debug2, "CServiceServerImpl: Cleared event callback for: " + m_service_name); } - // Stop TCP server - if (m_tcp_server) - { - m_tcp_server->stop(); - Logging::Log(log_level_debug1, "CServiceServerImpl: TCP server stopped for: " + m_service_name); - } - - // Unregister service + // Send unregistration sample if (g_registration_provider()) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); @@ -303,12 +304,14 @@ namespace eCAL { Logging::Log(log_level_debug2, "CServiceServerImpl: Processing request callback for: " + m_service_name); + // prepare response Service::Response response; auto& response_header = response.header; response_header.hname = Process::GetHostName(); response_header.sname = m_service_name; response_header.sid = m_service_id; + // try to parse request Service::Request request; if (!DeserializeFromBuffer(request_pb_.c_str(), request_pb_.size(), request)) { @@ -318,10 +321,16 @@ namespace eCAL std::string const 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) + // serialize response and return "request message could not be parsed" SerializeToBuffer(response, response_pb_); + + // Return Failed (error_code = -1), as parsing the request failed. The + // return value is not propagated to the remote caller. return -1; } + // get method SMethod method; const auto& request_header = request.header; response_header.mname = request_header.mname; @@ -330,11 +339,18 @@ namespace eCAL 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 = "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) + // serialize response and return "method not found" SerializeToBuffer(response, response_pb_); + + // Return Success (error_code = 0), as parsing the request worked. The + // return value is not propagated to the remote caller. return 0; } else @@ -345,15 +361,22 @@ 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); + // set method call state 'executed' response_header.state = Service::eMethodCallState::executed; + // set method response and return state 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) + // serialize response and return "method not found" SerializeToBuffer(response, response_pb_); + + // return success (error code 0) return 0; } From c327a908a877287889bdfd32fccbe7b9451855d3 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:05:22 +0100 Subject: [PATCH 15/21] shared_ptr handling, commenting --- ecal/core/include/ecal/ecal_client.h | 27 +++- ecal/core/include/ecal/ecal_server.h | 134 ++++++++++-------- ecal/core/src/service/ecal_service_client.cpp | 44 +++--- ecal/core/src/service/ecal_service_server.cpp | 43 ++---- 4 files changed, 124 insertions(+), 124 deletions(-) diff --git a/ecal/core/include/ecal/ecal_client.h b/ecal/core/include/ecal/ecal_client.h index dcbff48090..386f599ee9 100644 --- a/ecal/core/include/ecal/ecal_client.h +++ b/ecal/core/include/ecal/ecal_client.h @@ -26,9 +26,10 @@ #include #include + #include -#include #include +#include #include #include @@ -62,13 +63,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 @@ -136,4 +151,4 @@ namespace eCAL std::shared_ptr m_service_client_impl; }; } -} \ No newline at end of file +} diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h index f569e54b3c..f121721d53 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/ecal/core/include/ecal/ecal_server.h @@ -26,6 +26,7 @@ #include #include + #include #include @@ -37,74 +38,89 @@ namespace eCAL { class CServiceServerImpl; - /** - * @brief Service Server wrapper class. - **/ - class ECAL_API_CLASS CServiceServer + inline namespace v6 { - public: /** - * @brief Constructor. - * - * @param service_name_ Unique service name. - * @param event_callback_ Callback function for server events. + * @brief Service Server wrapper class. **/ - ECAL_API_EXPORTED_MEMBER - explicit CServiceServer(const std::string& service_name_, const ServerEventIDCallbackT event_callback_ = ServerEventIDCallbackT()); + 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 Destructor. + **/ + ECAL_API_EXPORTED_MEMBER + virtual ~CServiceServer(); - // Deleted copy constructor and copy assignment operator - CServiceServer(const CServiceServer&) = delete; - CServiceServer& operator=(const CServiceServer&) = delete; + /** + * @brief CServiceServer are non-copyable + **/ + CServiceServer(const CServiceServer&) = delete; - // Move constructor and move assignment operator - ECAL_API_EXPORTED_MEMBER CServiceServer(CServiceServer&& rhs) noexcept; - ECAL_API_EXPORTED_MEMBER CServiceServer& operator=(CServiceServer&& rhs) noexcept; + /** + * @brief CServiceServer are non-copyable + **/ + CServiceServer& operator=(const CServiceServer&) = delete; - /** - * @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. - **/ - ECAL_API_EXPORTED_MEMBER - bool AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_); + /** + * @brief CServiceServer are move-enabled + **/ + ECAL_API_EXPORTED_MEMBER CServiceServer(CServiceServer&& rhs) noexcept; - /** - * @brief Remove method callback. - * - * @param method_ Service method name. - * - * @return True if successful. - **/ - ECAL_API_EXPORTED_MEMBER - bool RemoveMethodCallback(const std::string& method_); + /** + * @brief CServiceServer are move-enabled + **/ + ECAL_API_EXPORTED_MEMBER CServiceServer& operator=(CServiceServer&& rhs) noexcept; - /** - * @brief Retrieve service name. - * - * @return The service name. - **/ - ECAL_API_EXPORTED_MEMBER - std::string GetServiceName(); + /** + * @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. + **/ + ECAL_API_EXPORTED_MEMBER + bool AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_); - /** - * @brief Check connection state. - * - * @return True if connected, false if not. - **/ - ECAL_API_EXPORTED_MEMBER - bool IsConnected(); + /** + * @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 Check connection state. + * + * @return True if connected, false if not. + **/ + ECAL_API_EXPORTED_MEMBER + bool IsConnected(); - private: - std::shared_ptr m_service_server_impl; - }; + private: + std::shared_ptr m_service_server_impl; + }; + } } diff --git a/ecal/core/src/service/ecal_service_client.cpp b/ecal/core/src/service/ecal_service_client.cpp index 5398239533..00df6e230e 100644 --- a/ecal/core/src/service/ecal_service_client.cpp +++ b/ecal/core/src/service/ecal_service_client.cpp @@ -33,26 +33,17 @@ 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); - } - - // 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 @@ -87,11 +78,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, @@ -102,32 +93,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; } } @@ -139,7 +133,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); @@ -161,7 +155,7 @@ namespace eCAL } catch (const std::exception& /*e*/) { - // Handle exceptions + // handle exceptions return_state = false; } } diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index ce8a30b0e6..1c5b784ca1 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -32,26 +32,17 @@ namespace eCAL CServiceServer::CServiceServer(const std::string& service_name_, const ServerEventIDCallbackT event_callback_) : m_service_server_impl(nullptr) { - // Create server implementation + // create server implementation m_service_server_impl = CServiceServerImpl::CreateInstance(service_name_, event_callback_); - // Register server - if (g_servicegate() != nullptr) - { - g_servicegate()->Register(service_name_, m_service_server_impl); - } + // register server + if (g_servicegate() != nullptr) g_servicegate()->Register(service_name_, m_service_server_impl); } CServiceServer::~CServiceServer() { - // Unregister server - if (g_servicegate() != nullptr) - { - g_servicegate()->Unregister(m_service_server_impl->GetServiceName(), m_service_server_impl); - } - - // Reset server implementation - m_service_server_impl.reset(); + // unregister server + if (g_servicegate() != nullptr) g_servicegate()->Unregister(m_service_server_impl->GetServiceName(), m_service_server_impl); } CServiceServer::CServiceServer(CServiceServer&& rhs) noexcept @@ -70,37 +61,21 @@ namespace eCAL bool CServiceServer::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { - if (m_service_server_impl) - { - return m_service_server_impl->AddMethodCallback(method_, method_info_, callback_); - } - return false; + return m_service_server_impl->AddMethodCallback(method_, method_info_, callback_); } bool CServiceServer::RemoveMethodCallback(const std::string& method_) { - if (m_service_server_impl) - { - return m_service_server_impl->RemoveMethodCallback(method_); - } - return false; + return m_service_server_impl->RemoveMethodCallback(method_); } std::string CServiceServer::GetServiceName() { - if (m_service_server_impl) - { - return m_service_server_impl->GetServiceName(); - } - return ""; + return m_service_server_impl->GetServiceName(); } bool CServiceServer::IsConnected() { - if (m_service_server_impl) - { - return m_service_server_impl->IsConnected(); - } - return false; + return m_service_server_impl->IsConnected(); } } From 2ccc9c13636ad353399d03adce1447fd87ad9ab6 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Fri, 13 Dec 2024 07:09:29 +0100 Subject: [PATCH 16/21] check for nullptr in destructor --- ecal/core/src/service/ecal_service_client.cpp | 3 +++ ecal/core/src/service/ecal_service_server.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ecal/core/src/service/ecal_service_client.cpp b/ecal/core/src/service/ecal_service_client.cpp index 00df6e230e..6bbc2cbdb5 100644 --- a/ecal/core/src/service/ecal_service_client.cpp +++ b/ecal/core/src/service/ecal_service_client.cpp @@ -42,6 +42,9 @@ namespace eCAL CServiceClient::~CServiceClient() { + // could be already destroyed by move + if (m_service_client_impl == nullptr) return; + // unregister client if (g_clientgate() != nullptr) g_clientgate()->Unregister(m_service_client_impl->GetServiceName(), m_service_client_impl); } diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index 1c5b784ca1..fee616f914 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -41,6 +41,9 @@ namespace eCAL CServiceServer::~CServiceServer() { + // could be already destroyed by move + if (m_service_server_impl == nullptr) return; + // unregister server if (g_servicegate() != nullptr) g_servicegate()->Unregister(m_service_server_impl->GetServiceName(), m_service_server_impl); } From 7bb8b57e743ac8f9440666bb77ceb87232d56788 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:49:44 +0100 Subject: [PATCH 17/21] logging added/improved for CServiceClientImpl and CServiceServerImpl --- .../src/service/ecal_service_client_impl.cpp | 53 ++++++++++++- .../src/service/ecal_service_server_impl.cpp | 78 +++++++++++++------ 2 files changed, 104 insertions(+), 27 deletions(-) diff --git a/ecal/core/src/service/ecal_service_client_impl.cpp b/ecal/core/src/service/ecal_service_client_impl.cpp index 76860b2002..622386a395 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) { @@ -142,12 +150,19 @@ namespace eCAL 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_mutex); @@ -164,6 +179,9 @@ namespace eCAL 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 } } @@ -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,22 +240,32 @@ 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()) { - ResponseError(entity_id_, m_service_name, method_name_, "Invalid service or method name.", response_callback_); + ResponseError(entity_id_, m_service_name, method_name_, "CServiceClientImpl::CallWithCallbackAsync: Invalid service or method name.", response_callback_); return false; } // 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_); } @@ -274,7 +316,11 @@ namespace eCAL { 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_) @@ -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(); } diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index bcec71c805..0349f6f011 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -37,7 +37,9 @@ namespace eCAL std::shared_ptr CServiceServerImpl::CreateInstance( const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) { - Logging::Log(log_level_debug1, "CServiceServerImpl::CreateInstance: Creating instance of CServiceServerImpl for service: " + 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_)); instance->Start(); return instance; @@ -47,25 +49,31 @@ namespace eCAL CServiceServerImpl::CServiceServerImpl(const std::string& service_name_, const ServerEventIDCallbackT& event_callback_) : m_created(false), m_service_name(service_name_), m_event_callback(event_callback_) { - Logging::Log(log_level_debug1, "CServiceServerImpl: Initializing service server for: " + m_service_name); +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceServerImpl::CServiceServerImpl: Initializing service server for: " + m_service_name); +#endif } // Destructor CServiceServerImpl::~CServiceServerImpl() { - Logging::Log(log_level_debug1, "CServiceServerImpl: Destroying service server for: " + m_service_name); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::~CServiceServerImpl: Destroying service server for: " + m_service_name); +#endif Stop(); } bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { - Logging::Log(log_level_debug1, "CServiceServerImpl: Adding method callback for method: " + method_); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::AddMethodCallback: Adding method callback for method: " + method_); +#endif std::lock_guard const lock(m_method_map_mutex); auto iter = m_method_map.find(method_); if (iter != m_method_map.end()) { - Logging::Log(log_level_warning, "CServiceServerImpl: Method already exists, updating callback: " + method_); + 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; @@ -74,7 +82,9 @@ namespace eCAL } else { - Logging::Log(log_level_debug1, "CServiceServerImpl: Registering new method: " + method_); +#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; @@ -90,18 +100,22 @@ namespace eCAL bool CServiceServerImpl::RemoveMethodCallback(const std::string& method_) { - Logging::Log(log_level_debug1, "CServiceServerImpl: Removing method callback for method: " + method_); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::RemoveMethodCallback: Removing method callback for method: " + method_); +#endif std::lock_guard const lock(m_method_map_mutex); auto iter = m_method_map.find(method_); if (iter != m_method_map.end()) { m_method_map.erase(iter); - Logging::Log(log_level_debug1, "CServiceServerImpl: Successfully removed method callback: " + method_); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::RemoveMethodCallback: Successfully removed method callback: " + method_); +#endif return true; } - Logging::Log(log_level_warning, "CServiceServerImpl: Attempt to remove non-existent method callback: " + method_); + Logging::Log(log_level_warning, "CServiceServerImpl::RemoveMethodCallback: Attempt to remove non-existent method callback: " + method_); return false; } @@ -109,23 +123,29 @@ namespace eCAL { if (!m_created) { - Logging::Log(log_level_warning, "CServiceServerImpl: Service is not created; cannot check connection state for: " + m_service_name); +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceServerImpl: Service is not created; cannot check connection state for: " + m_service_name); +#endif return false; } 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; } void CServiceServerImpl::RegisterClient(const std::string& /*key_*/, const SClientAttr& /*client_*/) { - Logging::Log(log_level_debug2, "CServiceServerImpl: Client registration logic is not implemented for: " + m_service_name); + // client registration logic is not implemented, as it is not required for service servers } Registration::Sample CServiceServerImpl::GetRegistration() { - Logging::Log(log_level_debug2, "CServiceServerImpl: Generating registration sample for: " + m_service_name); +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceServerImpl:::GetRegistration: Generating registration sample for: " + m_service_name); +#endif return GetRegistrationSample(); } @@ -142,7 +162,9 @@ namespace eCAL return; } +#ifndef NDEBUG Logging::Log(log_level_debug1, "CServiceServerImpl: Starting service server for: " + m_service_name); +#endif // Create service ID std::stringstream counter; @@ -194,24 +216,30 @@ namespace eCAL g_registration_provider()->RegisterSample(GetRegistrationSample()); m_created = true; +#ifndef NDEBUG Logging::Log(log_level_debug1, "CServiceServerImpl: Service started successfully: " + m_service_name); +#endif } void CServiceServerImpl::Stop() { if (!m_created) { - Logging::Log(log_level_warning, "CServiceServerImpl: Service is not running; cannot stop: " + m_service_name); + Logging::Log(log_level_warning, "CServiceServerImpl::Stop: Service is not running; cannot stop: " + m_service_name); return; } - Logging::Log(log_level_debug1, "CServiceServerImpl: Stopping service server for: " + m_service_name); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::Stop: Stopping service server for: " + m_service_name); +#endif // Stop TCP server if (m_tcp_server) { m_tcp_server->stop(); - Logging::Log(log_level_debug1, "CServiceServerImpl: TCP server stopped for: " + m_service_name); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::Stop: TCP server stopped for: " + m_service_name); +#endif } m_tcp_server.reset(); @@ -219,14 +247,12 @@ namespace eCAL { std::lock_guard const lock(m_method_map_mutex); m_method_map.clear(); - Logging::Log(log_level_debug2, "CServiceServerImpl: Cleared all method callbacks for: " + m_service_name); } // Reset event callback { std::lock_guard const lock(m_event_callback_mutex); m_event_callback = nullptr; - Logging::Log(log_level_debug2, "CServiceServerImpl: Cleared event callback for: " + m_service_name); } // Send unregistration sample @@ -234,13 +260,13 @@ namespace eCAL g_registration_provider()->UnregisterSample(GetUnregistrationSample()); m_created = false; - Logging::Log(log_level_debug1, "CServiceServerImpl: Service stopped successfully: " + m_service_name); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::Stop: Service stopped successfully: " + m_service_name); +#endif } Registration::Sample CServiceServerImpl::GetRegistrationSample() { - Logging::Log(log_level_debug2, "CServiceServerImpl: Preparing registration sample for: " + m_service_name); - Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_reg_service; @@ -281,8 +307,6 @@ namespace eCAL Registration::Sample CServiceServerImpl::GetUnregistrationSample() { - Logging::Log(log_level_debug2, "CServiceServerImpl: Preparing unregistration sample for: " + m_service_name); - Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_unreg_service; @@ -302,7 +326,9 @@ namespace eCAL int CServiceServerImpl::RequestCallback(const std::string& request_pb_, std::string& response_pb_) { - Logging::Log(log_level_debug2, "CServiceServerImpl: Processing request callback for: " + m_service_name); +#ifndef NDEBUG + Logging::Log(log_level_debug2, "CServiceServerImpl::RequestCallback: Processing request callback for: " + m_service_name); +#endif // prepare response Service::Response response; @@ -315,7 +341,7 @@ 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."; @@ -382,7 +408,9 @@ namespace eCAL void CServiceServerImpl::NotifyEventCallback(const Registration::SServiceMethodId& service_id_, eCAL_Server_Event event_type_, const std::string& /*message_*/) { - Logging::Log(log_level_debug1, "CServiceServerImpl: Notifying event callback for: " + m_service_name + " Event Type: " + std::to_string(event_type_)); +#ifndef NDEBUG + Logging::Log(log_level_debug1, "CServiceServerImpl::NotifyEventCallback: Notifying event callback for: " + m_service_name + " Event Type: " + std::to_string(event_type_)); +#endif std::lock_guard const lock_cb(m_event_callback_mutex); if (m_event_callback) From 995c66c24bc3f907304adf4f9fe54fa5678aff07 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:23:47 +0100 Subject: [PATCH 18/21] GetServiceID added to public API of CServerClient and CServiceServer (not implemented yet) --- ecal/core/include/ecal/ecal_client.h | 9 +++++++++ ecal/core/include/ecal/ecal_server.h | 9 +++++++++ ecal/core/src/service/ecal_service_client.cpp | 6 ++++++ ecal/core/src/service/ecal_service_server.cpp | 6 ++++++ 4 files changed, 30 insertions(+) diff --git a/ecal/core/include/ecal/ecal_client.h b/ecal/core/include/ecal/ecal_client.h index 386f599ee9..db6640a8a1 100644 --- a/ecal/core/include/ecal/ecal_client.h +++ b/ecal/core/include/ecal/ecal_client.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -139,6 +140,14 @@ namespace eCAL ECAL_API_EXPORTED_MEMBER std::string GetServiceName() const; + /** + * @brief Retrieve the service id. + * + * @return The service id. + **/ + ECAL_API_EXPORTED_MEMBER + Registration::SServiceMethodId GetServiceId() const; + /** * @brief Check connection to at least one service. * diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h index f121721d53..9b63239724 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/ecal/core/include/ecal/ecal_server.h @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -111,6 +112,14 @@ namespace eCAL ECAL_API_EXPORTED_MEMBER std::string GetServiceName(); + /** + * @brief Retrieve the service id. + * + * @return The service id. + **/ + ECAL_API_EXPORTED_MEMBER + Registration::SServiceMethodId GetServiceId() const; + /** * @brief Check connection state. * diff --git a/ecal/core/src/service/ecal_service_client.cpp b/ecal/core/src/service/ecal_service_client.cpp index 6bbc2cbdb5..528411999c 100644 --- a/ecal/core/src/service/ecal_service_client.cpp +++ b/ecal/core/src/service/ecal_service_client.cpp @@ -182,6 +182,12 @@ namespace eCAL 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_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index fee616f914..7892c8eb9d 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -77,6 +77,12 @@ namespace eCAL return m_service_server_impl->GetServiceName(); } + Registration::SServiceMethodId CServiceServer::GetServiceId() const + { + // TODO: Implement this + return Registration::SServiceMethodId(); + } + bool CServiceServer::IsConnected() { return m_service_server_impl->IsConnected(); From 238b326b810c14df65a6fe9c269675fbdcfd7366 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:35:36 +0100 Subject: [PATCH 19/21] review results --- ecal/core/src/service/ecal_service_client.cpp | 2 ++ ecal/core/src/service/ecal_service_client_impl.cpp | 2 +- ecal/core/src/service/ecal_service_server.cpp | 4 ++++ ecal/core/src/service/ecal_service_server_impl.cpp | 10 ++++++---- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ecal/core/src/service/ecal_service_client.cpp b/ecal/core/src/service/ecal_service_client.cpp index 528411999c..c29aba46f2 100644 --- a/ecal/core/src/service/ecal_service_client.cpp +++ b/ecal/core/src/service/ecal_service_client.cpp @@ -66,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()); @@ -179,6 +180,7 @@ namespace eCAL std::string CServiceClient::GetServiceName() const { + if (m_service_client_impl == nullptr) return ""; return m_service_client_impl->GetServiceName(); } diff --git a/ecal/core/src/service/ecal_service_client_impl.cpp b/ecal/core/src/service/ecal_service_client_impl.cpp index 622386a395..961a495e43 100644 --- a/ecal/core/src/service/ecal_service_client_impl.cpp +++ b/ecal/core/src/service/ecal_service_client_impl.cpp @@ -255,7 +255,7 @@ namespace eCAL // Validate service and method names if (m_service_name.empty() || method_name_.empty()) { - ResponseError(entity_id_, m_service_name, method_name_, "CServiceClientImpl::CallWithCallbackAsync: Invalid service or method name.", response_callback_); + ResponseError(entity_id_, m_service_name, method_name_, "Invalid service or method name.", response_callback_); return false; } diff --git a/ecal/core/src/service/ecal_service_server.cpp b/ecal/core/src/service/ecal_service_server.cpp index 7892c8eb9d..002b4e3fbd 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/ecal/core/src/service/ecal_service_server.cpp @@ -64,16 +64,19 @@ namespace eCAL bool CServiceServer::AddMethodCallback(const std::string& method_, const SServiceMethodInformation& method_info_, const MethodCallbackT& callback_) { + if (m_service_server_impl == nullptr) return false; return m_service_server_impl->AddMethodCallback(method_, method_info_, callback_); } bool CServiceServer::RemoveMethodCallback(const std::string& method_) { + if (m_service_server_impl == nullptr) return false; return m_service_server_impl->RemoveMethodCallback(method_); } std::string CServiceServer::GetServiceName() { + if (m_service_server_impl == nullptr) return ""; return m_service_server_impl->GetServiceName(); } @@ -85,6 +88,7 @@ namespace eCAL 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_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index 0349f6f011..ec7d9016fc 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -41,7 +41,10 @@ namespace eCAL 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_)); - instance->Start(); + if (instance != nullptr) + { + instance->Start(); + } return instance; } @@ -103,7 +106,7 @@ namespace eCAL #ifndef NDEBUG Logging::Log(log_level_debug1, "CServiceServerImpl::RemoveMethodCallback: Removing method callback for method: " + method_); #endif - std::lock_guard const lock(m_method_map_mutex); + const std::lock_guard lock(m_method_map_mutex); auto iter = m_method_map.find(method_); if (iter != m_method_map.end()) @@ -381,8 +384,7 @@ namespace eCAL } else { - auto call_count = requested_method_iterator->second.method.call_count; - requested_method_iterator->second.method.call_count = ++call_count; + requested_method_iterator->second.method.call_count++; method = requested_method_iterator->second; } } From 3f9a963978f8265b7f8df4bcc4a8af5ac129cd6d Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Fri, 13 Dec 2024 18:09:06 +0100 Subject: [PATCH 20/21] server/client samples updated to v6 minimal warning fix in CDataReader (lock name change) TODO's added for CServiceServer and CServiceClient --- ecal/core/include/ecal/ecal_client.h | 3 +++ ecal/core/include/ecal/ecal_server.h | 12 ++++++++++-- ecal/core/src/readwrite/ecal_reader.cpp | 2 +- .../src/service/ecal_service_server_impl.cpp | 18 +++++++++--------- .../latency_client/src/latency_client.cpp | 5 ++--- .../latency_server/src/latency_server.cpp | 5 ++--- .../minimal_server/src/minimal_server.cpp | 5 ++--- .../ping_client_dyn/src/ping_client_dyn.cpp | 5 ++--- .../src/registration_getclients.cpp | 5 ++--- .../src/registration_getservices.cpp | 16 +++++----------- 10 files changed, 38 insertions(+), 38 deletions(-) diff --git a/ecal/core/include/ecal/ecal_client.h b/ecal/core/include/ecal/ecal_client.h index db6640a8a1..d72c95c4b7 100644 --- a/ecal/core/include/ecal/ecal_client.h +++ b/ecal/core/include/ecal/ecal_client.h @@ -145,6 +145,9 @@ namespace eCAL * * @return The service id. **/ + + // TODO: Implement this + ECAL_API_EXPORTED_MEMBER Registration::SServiceMethodId GetServiceId() const; diff --git a/ecal/core/include/ecal/ecal_server.h b/ecal/core/include/ecal/ecal_server.h index 9b63239724..a86a83cad7 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/ecal/core/include/ecal/ecal_server.h @@ -75,12 +75,14 @@ namespace eCAL /** * @brief CServiceServer are move-enabled **/ - ECAL_API_EXPORTED_MEMBER CServiceServer(CServiceServer&& rhs) noexcept; + ECAL_API_EXPORTED_MEMBER + CServiceServer(CServiceServer&& rhs) noexcept; /** * @brief CServiceServer are move-enabled **/ - ECAL_API_EXPORTED_MEMBER CServiceServer& operator=(CServiceServer&& rhs) noexcept; + ECAL_API_EXPORTED_MEMBER + CServiceServer& operator=(CServiceServer&& rhs) noexcept; /** * @brief Add method callback. @@ -91,6 +93,9 @@ namespace eCAL * * @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_); @@ -117,6 +122,9 @@ namespace eCAL * * @return The service id. **/ + + // TODO: Implement this + ECAL_API_EXPORTED_MEMBER Registration::SServiceMethodId GetServiceId() const; diff --git a/ecal/core/src/readwrite/ecal_reader.cpp b/ecal/core/src/readwrite/ecal_reader.cpp index e947fc3704..f5d604a3b6 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); + 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_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index ec7d9016fc..6be955148f 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -71,7 +71,7 @@ namespace eCAL #ifndef NDEBUG Logging::Log(log_level_debug1, "CServiceServerImpl::AddMethodCallback: Adding method callback for method: " + method_); #endif - std::lock_guard const lock(m_method_map_mutex); + const std::lock_guard lock(m_method_map_mutex); auto iter = m_method_map.find(method_); if (iter != m_method_map.end()) @@ -248,13 +248,13 @@ namespace eCAL // Reset method callbacks { - std::lock_guard const lock(m_method_map_mutex); + const std::lock_guard lock(m_method_map_mutex); m_method_map.clear(); } // Reset event callback { - std::lock_guard const lock(m_event_callback_mutex); + const std::lock_guard lock(m_event_callback_mutex); m_event_callback = nullptr; } @@ -291,7 +291,7 @@ namespace eCAL service.tcp_port_v1 = server_tcp_port; { - std::lock_guard const lock(m_method_map_mutex); + const std::lock_guard lock(m_method_map_mutex); for (const auto& iter : m_method_map) { Service::Method method; @@ -347,7 +347,7 @@ namespace eCAL 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) @@ -364,14 +364,14 @@ namespace eCAL const auto& request_header = request.header; response_header.mname = request_header.mname; { - std::lock_guard const lock(m_method_map_mutex); + 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 = "CServiceServerImpl: 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) @@ -392,7 +392,7 @@ 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; @@ -414,7 +414,7 @@ namespace eCAL Logging::Log(log_level_debug1, "CServiceServerImpl::NotifyEventCallback: Notifying event callback for: " + m_service_name + " Event Type: " + std::to_string(event_type_)); #endif - std::lock_guard const lock_cb(m_event_callback_mutex); + const std::lock_guard lock_cb(m_event_callback_mutex); if (m_event_callback) { SServerEventCallbackData callback_data; 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 0509888ae6..213a5cd06a 100644 --- a/ecal/samples/cpp/services/latency_server/src/latency_server.cpp +++ b/ecal/samples/cpp/services/latency_server/src/latency_server.cpp @@ -18,7 +18,6 @@ */ #include -#include #include #include @@ -35,10 +34,10 @@ int main() eCAL::Initialize("latency server"); // create latency service - eCAL::v5::CServiceServer latency_service("latency"); + 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 905e02d906..42e23040b2 100644 --- a/ecal/samples/cpp/services/minimal_server/src/minimal_server.cpp +++ b/ecal/samples/cpp/services/minimal_server/src/minimal_server.cpp @@ -18,7 +18,6 @@ */ #include -#include #include #include @@ -40,10 +39,10 @@ int main() eCAL::Initialize("minimal server"); // create minimal service server - eCAL::v5::CServiceServer minimal_server("service1"); + 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/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 53bfc70d1d..1c7b17f059 100644 --- a/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp +++ b/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp @@ -18,7 +18,6 @@ */ #include -#include #include #include @@ -61,8 +60,8 @@ TEST_P(ServicesTestFixture, ServiceExpiration) // create simple service and let it expire { // create service - eCAL::v5::CServiceServer service("foo::service"); - service.AddDescription("foo::method", "foo::req_type", "foo::req_desc", "foo::resp_type", "foo::resp_desc"); + eCAL::CServiceServer service("foo::service"); + service.AddMethodCallback("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); @@ -109,20 +108,15 @@ TEST_P(ServicesTestFixture, GetServiceIDs) // create simple server { // create server - eCAL::v5::CServiceServer service("foo::service"); + 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); From d1cb8555b9e6c4c2b219044bc3bea24fe51b3cca Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Fri, 13 Dec 2024 18:35:48 +0100 Subject: [PATCH 21/21] minor test fix --- ecal/core/src/readwrite/ecal_reader.cpp | 2 +- .../registration_test_public/src/registration_getservices.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ecal/core/src/readwrite/ecal_reader.cpp b/ecal/core/src/readwrite/ecal_reader.cpp index f5d604a3b6..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 exec_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/tests/cpp/registration_test_public/src/registration_getservices.cpp b/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp index 1c7b17f059..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.AddMethodCallback("method", { { "foo::req_type", "foo::req_desc" }, { "foo::resp_type", "foo::resp_desc" } }, eCAL::MethodCallbackT()); + 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);