/******************************************************************************** * Copyright (c) 2025-2026 ZF Friedrichshafen AG * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at * https://www.apache.org/licenses/LICENSE-2.0 * * SPDX-License-Identifier: Apache-2.0 * * Contributors: * Erik Verhoeven - initial API and implementation ********************************************************************************/ #include "client.h" #include #include #include #include #include CRepositoryProxy::CRepositoryProxy(CClient& rClient, sdv::com::TConnectionID tConnection, sdv::IInterfaceAccess* pRepositoryProxy) : m_rClient(rClient), m_tConnection(tConnection), m_ptrRepositoryProxy(pRepositoryProxy) {} void CRepositoryProxy::DestroyObject() { // Call the client to disconnect the connection and destroy the object. m_rClient.Disconnect(m_tConnection); } bool CClient::OnInitialize() { return true; } void CClient::OnShutdown() { sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); if (!pConnectionControl) SDV_LOG_ERROR("Failed to get communication control!"); // Disconnect from all repositories std::unique_lock lock(m_mtxRepositoryProxies); auto mapRepositoryProxiesCopy = std::move(m_mapRepositoryProxies); lock.unlock(); if (pConnectionControl) { for (const auto& rvtRepository : mapRepositoryProxiesCopy) pConnectionControl->RemoveConnection(rvtRepository.first); } } sdv::IInterfaceAccess* CClient::Connect(const sdv::u8string& ssConnectString) { const sdv::app::IAppContext* pContext = sdv::core::GetCore(); if (!pContext) { SDV_LOG_ERROR("Failed to get application context!"); return nullptr; } sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); if (!pConnectionControl) { SDV_LOG_ERROR("Failed to get communication control!"); return nullptr; } sdv::ipc::IChannelAccess* pChannelAccess = nullptr; std::string ssConfig; try { // Determine whether the service is running as server or as client. sdv::toml::CTOMLParser config(ssConnectString); std::string ssType = config.GetDirect("Client.Type").GetValue(); if (ssType.empty()) ssType = "Local"; if (ssType == "Local") { uint32_t uiInstanceID = config.GetDirect("Client.Instance").GetValue(); pChannelAccess = sdv::core::GetObject("LocalChannelControl"); if (!pChannelAccess) { SDV_LOG_ERROR("No local channel control or channel control not configured as client!"); return nullptr; } ssConfig = std::string(R"code([IpcChannel] Name = "LISTENER_)code") + std::to_string(uiInstanceID ? uiInstanceID : pContext->GetInstanceID()) + R"code(" )code"; } else if (ssType == "Remote") { std::string ssInterface = config.GetDirect("Client.Interface").GetValue(); uint32_t uiPort = config.GetDirect("Client.Interface").GetValue(); if (ssInterface.empty() || !uiPort) { SDV_LOG_ERROR("Missing interface or port number to initialize a remote client!"); return nullptr; } pChannelAccess = sdv::core::GetObject("RemoteChannelControl"); if (!pChannelAccess) { SDV_LOG_ERROR("No remote channel control or channel control not configured as client!"); return nullptr; } ssConfig = R"code([IpcChannel] Interface = ")code" + ssInterface + R"code( Port = ")code" + std::to_string(uiPort) + R"code( )code"; } else { SDV_LOG_ERROR("Invalid or missing listener configuration for listener service!"); SetObjectIntoConfigErrorState(); return nullptr; } } catch (const sdv::toml::XTOMLParseException& rexcept) { SDV_LOG_ERROR("Invalid service configuration for listener service: ", rexcept.what(), "!"); SetObjectIntoConfigErrorState(); return nullptr; } // First access the listener channel. This allows us to access the channel creation interface. // TODO: Use named mutex to prevent multiple connections at the same time. // Connect to the channel. sdv::TObjectPtr ptrListenerEndpoint = pChannelAccess->Access(ssConfig); // Assign the endpoint to the communication service. sdv::IInterfaceAccess* pListenerProxy = nullptr; sdv::com::TConnectionID tListenerConnection = pConnectionControl->AssignClientEndpoint(ptrListenerEndpoint, 5000, pListenerProxy); ptrListenerEndpoint.Clear(); // Lifetime has been taken over by communication control. if (!tListenerConnection || !pListenerProxy) { SDV_LOG_ERROR("Could not assign the client endpoint!"); if (tListenerConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tListenerConnection); return nullptr; } sdv::TInterfaceAccessPtr ptrListenerProxy(pListenerProxy); // Request for a private channel sdv::com::IRequestChannel* pRequestChannel = ptrListenerProxy.GetInterface(); if (!pRequestChannel) { SDV_LOG_ERROR("Could not get the channel creation interface!"); if (tListenerConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tListenerConnection); return nullptr; } sdv::u8string ssConnectionString = pRequestChannel->RequestChannel(""); // Disconnect from the listener if (tListenerConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tListenerConnection); if (ssConnectionString.empty()) { SDV_LOG_ERROR("Could not get the private channel connection information!"); return nullptr; } // TODO: Use named mutex to prevent multiple connections at the same time. // Connect to the privatechannel. sdv::TObjectPtr ptrPrivateEndpoint = pChannelAccess->Access(ssConnectionString); // Get and return the proxy sdv::IInterfaceAccess* pPrivateProxy = nullptr; sdv::com::TConnectionID tPrivateConnection = pConnectionControl->AssignClientEndpoint(ptrPrivateEndpoint, 5000, pPrivateProxy); ptrPrivateEndpoint.Clear(); // Lifetime has been taken over by communication control. if (!tPrivateConnection || !pPrivateProxy) { SDV_LOG_ERROR("Could not assign the client endpoint to the private channel!"); if (tPrivateConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tPrivateConnection); return nullptr; } // Create a remote repository object std::unique_lock lock(m_mtxRepositoryProxies); m_mapRepositoryProxies.try_emplace(tPrivateConnection, *this, tPrivateConnection, pPrivateProxy); return pPrivateProxy; } void CClient::Disconnect(sdv::com::TConnectionID tConnectionID) { // Find the connection, disconnect and remove the connection from the repository list. std::unique_lock lock(m_mtxRepositoryProxies); auto itRepository = m_mapRepositoryProxies.find(tConnectionID); if (itRepository == m_mapRepositoryProxies.end()) return; // Disconnect sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); if (!pConnectionControl) SDV_LOG_ERROR("Failed to get communication control!"); else pConnectionControl->RemoveConnection(itRepository->first); // Remove entry m_mapRepositoryProxies.erase(itRepository); }