mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-04-21 03:38:15 +00:00
33
sdv_services/ipc_com/CMakeLists.txt
Normal file
33
sdv_services/ipc_com/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
# Include cross-compilation toolchain file
|
||||
include(../../cross-compile-tools.cmake)
|
||||
|
||||
# Define project
|
||||
project(service_component_isolation VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
# Define target
|
||||
add_library(ipc_com SHARED
|
||||
"com_ctrl.h"
|
||||
"com_ctrl.cpp"
|
||||
"com_channel.h"
|
||||
"com_channel.cpp"
|
||||
"marshall_object.h"
|
||||
"marshall_object.cpp"
|
||||
|
||||
#"scheduler.cpp"
|
||||
)
|
||||
if(UNIX)
|
||||
target_link_libraries(ipc_com rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||
else()
|
||||
target_link_libraries(ipc_com ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_options(ipc_com PRIVATE)
|
||||
endif()
|
||||
target_include_directories(ipc_com PRIVATE ./include)
|
||||
set_target_properties(ipc_com PROPERTIES PREFIX "")
|
||||
set_target_properties(ipc_com PROPERTIES SUFFIX ".sdv")
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(ipc_com CompileCoreIDL)
|
||||
add_dependencies(ipc_com core_ps)
|
||||
|
||||
# Appending the service in the service list
|
||||
set(SDV_Service_List ${SDV_Service_List} ipc_com PARENT_SCOPE)
|
||||
321
sdv_services/ipc_com/com_channel.cpp
Normal file
321
sdv_services/ipc_com/com_channel.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
#include "com_channel.h"
|
||||
#include "com_ctrl.h"
|
||||
#include "marshall_object.h"
|
||||
#include <support/pssup.h>
|
||||
#include <support/serdes.h>
|
||||
#include <support/local_service_access.h>
|
||||
#include <interfaces/serdes/core_ps_serdes.h>
|
||||
#include "../../global/scheduler/scheduler.cpp"
|
||||
|
||||
CChannelConnector::CChannelConnector(CCommunicationControl& rcontrol, uint32_t uiIndex, sdv::IInterfaceAccess* pChannelEndpoint) :
|
||||
m_rcontrol(rcontrol), m_ptrChannelEndpoint(pChannelEndpoint),
|
||||
m_pDataSend(m_ptrChannelEndpoint.GetInterface<sdv::ipc::IDataSend>())
|
||||
{
|
||||
m_tConnectionID.uiIdent = uiIndex;
|
||||
m_tConnectionID.uiControl = static_cast<uint32_t>(rand());
|
||||
}
|
||||
|
||||
CChannelConnector::~CChannelConnector()
|
||||
{
|
||||
// Finalize the scheduled calls.
|
||||
m_scheduler.WaitForExecution();
|
||||
|
||||
// Remove all calls from the queue
|
||||
std::unique_lock<std::mutex> lock(m_mtxCalls);
|
||||
while (m_mapCalls.size())
|
||||
{
|
||||
SCallEntry& rsEntry = m_mapCalls.begin()->second;
|
||||
m_mapCalls.erase(m_mapCalls.begin());
|
||||
lock.unlock();
|
||||
|
||||
// Cancel the processing
|
||||
rsEntry.bCancel = true;
|
||||
rsEntry.cvWaitForResult.notify_all();
|
||||
|
||||
// Handle next call.
|
||||
lock.lock();
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
// Disconnect
|
||||
sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface<sdv::ipc::IConnect>();
|
||||
if (pConnection)
|
||||
{
|
||||
if (m_uiConnectionStatusCookie) pConnection->UnregisterStatusEventCallback(m_uiConnectionStatusCookie);
|
||||
pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
bool CChannelConnector::ServerConnect(sdv::IInterfaceAccess* pObject, bool bAllowReconnect)
|
||||
{
|
||||
if (!m_ptrChannelEndpoint || !m_pDataSend) return false; // No channel
|
||||
if (!pObject) return false; // No object
|
||||
if (m_ptrInitialMarshallObject) return false; // Has already a marshall object.
|
||||
m_bAllowReconnect = bAllowReconnect;
|
||||
m_eEndpointType = EEndpointType::server;
|
||||
|
||||
m_ptrInitialMarshallObject = m_rcontrol.GetOrCreateStub(pObject);
|
||||
if (!m_ptrInitialMarshallObject) return false;
|
||||
|
||||
// Establish connection...
|
||||
sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface<sdv::ipc::IConnect>();
|
||||
if (!pConnection) return false;
|
||||
m_uiConnectionStatusCookie = pConnection->RegisterStatusEventCallback(this);
|
||||
return m_uiConnectionStatusCookie != 0 && pConnection->AsyncConnect(this);
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CChannelConnector::ClientConnect(uint32_t uiTimeoutMs)
|
||||
{
|
||||
if (!m_ptrChannelEndpoint || !m_pDataSend) return nullptr; // No channel
|
||||
if (!uiTimeoutMs) return nullptr; // No timeout
|
||||
if (m_ptrInitialMarshallObject) return nullptr; // Has already a marshall object.
|
||||
m_eEndpointType = EEndpointType::client;
|
||||
|
||||
// Get or create the proxy
|
||||
m_ptrInitialMarshallObject = GetOrCreateProxy(sdv::GetInterfaceId<sdv::IInterfaceAccess>(), sdv::ps::TMarshallID{});
|
||||
if (!m_ptrInitialMarshallObject)
|
||||
{
|
||||
SDV_LOG_ERROR("Could not get or create proxy object!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Establish connection...
|
||||
sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface<sdv::ipc::IConnect>();
|
||||
if (pConnection) m_uiConnectionStatusCookie = pConnection->RegisterStatusEventCallback(this);
|
||||
if (!pConnection || m_uiConnectionStatusCookie == 0 || !pConnection->AsyncConnect(this) ||
|
||||
!pConnection->WaitForConnection(uiTimeoutMs))
|
||||
{
|
||||
SDV_LOG_ERROR("Could not establish a connection!");
|
||||
m_ptrInitialMarshallObject.reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_ptrInitialMarshallObject->GetProxy().get<sdv::IInterfaceAccess>();
|
||||
}
|
||||
|
||||
bool CChannelConnector::IsConnected() const
|
||||
{
|
||||
return m_eConnectStatus == sdv::ipc::EConnectStatus::connected;
|
||||
}
|
||||
|
||||
void CChannelConnector::SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus)
|
||||
{
|
||||
auto eConnectStatusTemp = m_eConnectStatus;
|
||||
m_eConnectStatus = eConnectStatus;
|
||||
switch (m_eConnectStatus)
|
||||
{
|
||||
case sdv::ipc::EConnectStatus::disconnected:
|
||||
case sdv::ipc::EConnectStatus::disconnected_forced:
|
||||
// Invalidate the proxy objects.
|
||||
for (auto& rvtProxyObject : m_mapProxyObjects)
|
||||
rvtProxyObject.second.reset();
|
||||
|
||||
if (m_eEndpointType == EEndpointType::server)
|
||||
{
|
||||
// Report information (but only once)
|
||||
if (sdv::app::ConsoleIsVerbose() && eConnectStatusTemp != sdv::ipc::EConnectStatus::disconnected)
|
||||
std::cout << "Client disconnected (ID#" << m_tConnectionID.uiIdent << ")" << std::endl;
|
||||
|
||||
// Remove the connection if reconnection is not enabled (normal case).
|
||||
if (!m_bAllowReconnect)
|
||||
m_rcontrol.RemoveConnection(m_tConnectionID);
|
||||
}
|
||||
break;
|
||||
case sdv::ipc::EConnectStatus::connected:
|
||||
if (m_eEndpointType == EEndpointType::server)
|
||||
{
|
||||
// Report information
|
||||
if (sdv::app::ConsoleIsVerbose())
|
||||
std::cout << "Client connected (ID#" << m_tConnectionID.uiIdent << ")" << std::endl;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CChannelConnector::ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
{
|
||||
// Schedule the data reception
|
||||
std::mutex mtxSyncData;
|
||||
std::condition_variable cvSyncData;
|
||||
bool bSyncData = false;
|
||||
bool bResult = m_scheduler.Schedule([&]()
|
||||
{
|
||||
// Copy the data to keep validity
|
||||
std::unique_lock<std::mutex> lock(mtxSyncData);
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqDataCopy = std::move(seqData);
|
||||
lock.unlock();
|
||||
bSyncData = true;
|
||||
cvSyncData.notify_all();
|
||||
|
||||
// Call the receive function
|
||||
DecoupledReceiveData(seqDataCopy);
|
||||
});
|
||||
std::unique_lock<std::mutex> lockSyncData(mtxSyncData);
|
||||
while (bResult && !bSyncData) cvSyncData.wait_for(lockSyncData, std::chrono::milliseconds(10));
|
||||
lockSyncData.unlock();
|
||||
|
||||
// TODO: Handle a schedule failure - send back a resource depletion.
|
||||
}
|
||||
|
||||
void CChannelConnector::DecoupledReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
{
|
||||
// The first data pointer in the sequence contains the address header
|
||||
if (seqData.size() < 1) return;
|
||||
sdv::pointer<uint8_t> rptrAddress = std::move(seqData.front());
|
||||
seqData.erase(seqData.begin());
|
||||
|
||||
// The data should be at least the size of the header.
|
||||
if (rptrAddress.size() < sizeof(sdv::ps::SMarshallAddress)) return; // Invalid size
|
||||
|
||||
// Deserialize the address portion of the header
|
||||
// The first byte in the data pointer determines the endianness
|
||||
sdv::EEndian eSourceEndianess = static_cast<sdv::EEndian>(rptrAddress[0]);
|
||||
|
||||
// Deserialize the address structure
|
||||
sdv::ps::SMarshallAddress sAddress{};
|
||||
if (eSourceEndianess == sdv::EEndian::big_endian)
|
||||
{
|
||||
sdv::deserializer<sdv::EEndian::big_endian> desInput;
|
||||
desInput.attach(rptrAddress, 0); // Do not check checksum...
|
||||
serdes::CSerdes<sdv::ps::SMarshallAddress>::Deserialize(desInput, sAddress);
|
||||
} else
|
||||
{
|
||||
sdv::deserializer<sdv::EEndian::little_endian> desInput;
|
||||
desInput.attach(rptrAddress, 0); // Do not check checksum...
|
||||
serdes::CSerdes<sdv::ps::SMarshallAddress>::Deserialize(desInput, sAddress);
|
||||
}
|
||||
|
||||
// If the data should be interprated as input data, the data should go into a stub object.
|
||||
// If the data should be interprated as output data, the data is returning from the call and should go into the proxy object.
|
||||
if (sAddress.eInterpret == sdv::ps::EMarshallDataInterpret::input_data)
|
||||
{
|
||||
// A proxy ID should be present in any case - if not, this is a serious error, since it is not possible to inform the
|
||||
// caller.
|
||||
if (!sAddress.tProxyID) return;
|
||||
|
||||
// In case the stub ID is not provided, the stub ID is the ID of the initial marshall object
|
||||
sdv::ps::TMarshallID tStubID = sAddress.tStubID;
|
||||
if (!tStubID) tStubID = m_ptrInitialMarshallObject->GetMarshallID();
|
||||
|
||||
// Store the channel context (used to marshall interfaces over the same connector)
|
||||
m_rcontrol.SetConnectorContext(this);
|
||||
|
||||
// Call the stub function
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqResult;
|
||||
try
|
||||
{
|
||||
seqResult = m_rcontrol.CallStub(tStubID, seqData);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Should not occur.
|
||||
std::cout << "Exception occurred..." << std::endl;
|
||||
}
|
||||
|
||||
// Store the address struct into the result sequence
|
||||
sAddress.eInterpret = sdv::ps::EMarshallDataInterpret::output_data;
|
||||
if (eSourceEndianess == sdv::EEndian::big_endian)
|
||||
{
|
||||
sdv::serializer<sdv::EEndian::big_endian> serOutput;
|
||||
serdes::CSerdes<sdv::ps::SMarshallAddress>::Serialize(serOutput, sAddress);
|
||||
seqResult.insert(seqResult.begin(), serOutput.buffer());
|
||||
} else
|
||||
{
|
||||
sdv::serializer<sdv::EEndian::little_endian> serOutput;
|
||||
serdes::CSerdes<sdv::ps::SMarshallAddress>::Serialize(serOutput, sAddress);
|
||||
seqResult.insert(seqResult.begin(), serOutput.buffer());
|
||||
}
|
||||
|
||||
// Send the result back
|
||||
m_pDataSend->SendData(seqResult);
|
||||
} else
|
||||
{
|
||||
// Look for the call entry
|
||||
std::unique_lock<std::mutex> lockCallMap(m_mtxCalls);
|
||||
auto itCall = m_mapCalls.find(sAddress.uiCallIndex);
|
||||
if (itCall == m_mapCalls.end()) return;
|
||||
SCallEntry& rsCallEntry = itCall->second;
|
||||
m_mapCalls.erase(itCall);
|
||||
lockCallMap.unlock();
|
||||
|
||||
// Update the result
|
||||
std::unique_lock<std::mutex> lockCall(rsCallEntry.mtxWaitForResult);
|
||||
rsCallEntry.seqResult = seqData;
|
||||
lockCall.unlock();
|
||||
rsCallEntry.cvWaitForResult.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> CChannelConnector::MakeCall(sdv::ps::TMarshallID tProxyID, sdv::ps::TMarshallID tStubID,
|
||||
sdv::sequence<sdv::pointer<uint8_t>>& rseqInputData)
|
||||
{
|
||||
if (!m_pDataSend) throw sdv::ps::XMarshallNotInitialized();
|
||||
|
||||
// Create an address structure
|
||||
sdv::ps::SMarshallAddress sAddress{};
|
||||
sAddress.eEndian = sdv::GetPlatformEndianess();
|
||||
sAddress.uiVersion = SDVFrameworkInterfaceVersion;
|
||||
sAddress.tProxyID = tProxyID;
|
||||
sAddress.tStubID = tStubID;
|
||||
sAddress.uiCallIndex = m_rcontrol.CreateUniqueCallIndex();
|
||||
sAddress.eInterpret = sdv::ps::EMarshallDataInterpret::input_data;
|
||||
|
||||
// Create an additional stream for the address struct.
|
||||
sdv::serializer serAddress;
|
||||
serdes::CSerdes<sdv::ps::SMarshallAddress>::Serialize(serAddress, sAddress);
|
||||
rseqInputData.insert(rseqInputData.begin(), serAddress.buffer());
|
||||
|
||||
// Add a call entry to be able to receive the result.
|
||||
std::unique_lock<std::mutex> lock(m_mtxCalls);
|
||||
SCallEntry sResult;
|
||||
m_mapCalls.try_emplace(sAddress.uiCallIndex, sResult);
|
||||
lock.unlock();
|
||||
|
||||
// Store the channel context (used to marshall interfaces over the same connector)
|
||||
m_rcontrol.SetConnectorContext(this);
|
||||
|
||||
// Send the data
|
||||
try
|
||||
{
|
||||
if (!m_pDataSend->SendData(rseqInputData)) throw sdv::ps::XMarshallExcept();
|
||||
}
|
||||
catch (const sdv::ps::XMarshallExcept& /*rexcept*/)
|
||||
{
|
||||
// Exception occurred. Remove the call und rethrow.
|
||||
lock.lock();
|
||||
m_mapCalls.erase(sAddress.uiCallIndex);
|
||||
lock.unlock();
|
||||
throw;
|
||||
}
|
||||
|
||||
// Wait for the result
|
||||
if (sResult.bCancel) throw sdv::ps::XMarshallTimeout();
|
||||
std::unique_lock<std::mutex> lockResult(sResult.mtxWaitForResult);
|
||||
sResult.cvWaitForResult.wait(lockResult);
|
||||
if (sResult.bCancel) throw sdv::ps::XMarshallTimeout();
|
||||
|
||||
return sResult.seqResult;
|
||||
}
|
||||
|
||||
std::shared_ptr<CMarshallObject> CChannelConnector::GetOrCreateProxy(sdv::interface_id id, sdv::ps::TMarshallID tStubID)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mtxMarshallObjects);
|
||||
|
||||
// Check for an existing proxy.
|
||||
auto itMarshallObject = m_mapProxyObjects.find(tStubID);
|
||||
if (itMarshallObject != m_mapProxyObjects.end())
|
||||
return itMarshallObject->second;
|
||||
|
||||
// Proxy doesn't exist; create a new proxy.
|
||||
auto ptrMarshallObject = m_rcontrol.CreateProxy(id, tStubID, *this);
|
||||
if (!ptrMarshallObject)return {};
|
||||
m_mapProxyObjects[tStubID] = ptrMarshallObject;
|
||||
return ptrMarshallObject;
|
||||
}
|
||||
|
||||
sdv::com::TConnectionID CChannelConnector::GetConnectionID() const
|
||||
{
|
||||
return m_tConnectionID;
|
||||
}
|
||||
139
sdv_services/ipc_com/com_channel.h
Normal file
139
sdv_services/ipc_com/com_channel.h
Normal file
@@ -0,0 +1,139 @@
|
||||
#ifndef COM_CHANNEL_H
|
||||
#define COM_CHANNEL_H
|
||||
|
||||
#include <support/pssup.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include "../../global/scheduler/scheduler.h"
|
||||
|
||||
// Forward declaration
|
||||
class CCommunicationControl;
|
||||
class CMarshallObject;
|
||||
|
||||
/**
|
||||
* @brief Communication channel connector (endpoint).
|
||||
*/
|
||||
class CChannelConnector : public sdv::IInterfaceAccess, public sdv::ipc::IConnectEventCallback,
|
||||
public sdv::ipc::IDataReceiveCallback
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor for establishing the server connection.
|
||||
* @param[in] rcontrol Reference to the communication control class.
|
||||
* @param[in] uiIndex The current index of this connection (used to create the connection ID).
|
||||
* @param[in] pChannelEndpoint Interface pointer to the channel.
|
||||
*/
|
||||
CChannelConnector(CCommunicationControl& rcontrol, uint32_t uiIndex, sdv::IInterfaceAccess* pChannelEndpoint);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CChannelConnector();
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Connect as server (attaching the channel to the target object using a stub).
|
||||
* @param[in] pObject Pointer to the object to attach.
|
||||
* @param[in] bAllowReconnect Allow a disconnect and re-connect (disconnect doesn't trigger a channel removal).
|
||||
* @return Returns 'true' when the attachment succeeds; 'false' when not.
|
||||
*/
|
||||
bool ServerConnect(sdv::IInterfaceAccess* pObject, bool bAllowReconnect);
|
||||
|
||||
/**
|
||||
* @brief Connect as client (connecting to an existing server and creating a proxy).
|
||||
* @param[in] uiTimeoutMs The timeout time trying to connect.
|
||||
* @return Returns a pointer to the proxy object representing the object at the other end of the channel. Or NULL when a
|
||||
* timeout occurred.
|
||||
*/
|
||||
sdv::IInterfaceAccess* ClientConnect(uint32_t uiTimeoutMs);
|
||||
|
||||
/**
|
||||
* @brief Is the communication channel currently connected?
|
||||
* @return Returns whether the connector has an active connection.
|
||||
*/
|
||||
bool IsConnected() const;
|
||||
|
||||
/**
|
||||
* @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus.
|
||||
* @param[in] eConnectStatus The connection status.
|
||||
*/
|
||||
virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override;
|
||||
|
||||
/**
|
||||
* @brief Callback to be called by the IPC connection when receiving a data packet. Overload of
|
||||
* sdv::ipc::IDataReceiveCallback::ReceiveData.
|
||||
* @param[inout] seqData Sequence of data buffers to received. The sequence might be changed to optimize the communication
|
||||
* without having to copy the data.
|
||||
*/
|
||||
virtual void ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) override;
|
||||
|
||||
/**
|
||||
* @brief Decoupled receive callback to be called by the scheduler when receiving a data packet.
|
||||
* @param[inout] seqData Sequence of data buffers to received. The sequence might be changed to optimize the communication
|
||||
* without having to copy the data.
|
||||
*/
|
||||
void DecoupledReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData);
|
||||
|
||||
/**
|
||||
* @brief Sends data consisting of multiple data chunks via the IPC connection.
|
||||
* @param[in] tProxyID Marshall ID of the proxy (source).
|
||||
* @param[in] tStubID Marshall ID of the stub (target).
|
||||
* @param[in] rseqInputData Sequence of data buffers to be sent. May be altered during processing to add/change the sequence content
|
||||
* without having to copy the data.
|
||||
* @return Returns the results of the call or throws a marshall exception.
|
||||
*/
|
||||
sdv::sequence<sdv::pointer<uint8_t>> MakeCall(sdv::ps::TMarshallID tProxyID, sdv::ps::TMarshallID tStubID,
|
||||
sdv::sequence<sdv::pointer<uint8_t>>& rseqInputData);
|
||||
|
||||
/**
|
||||
* @brief Get a proxy for the interface connection to the stub.
|
||||
* @param[in] id The ID of the interface this object marshalls the calls for.
|
||||
* @param[in] tStubID The stub ID this proxy is communicating to.
|
||||
* @return Returns a shared pointer to the proxy object.
|
||||
*/
|
||||
std::shared_ptr<CMarshallObject> GetOrCreateProxy(sdv::interface_id id, sdv::ps::TMarshallID tStubID);
|
||||
|
||||
/**
|
||||
* @brief Get the connection ID of this connector.
|
||||
* @return The connection ID.
|
||||
*/
|
||||
sdv::com::TConnectionID GetConnectionID() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Endpoint type the channel connector
|
||||
*/
|
||||
enum class EEndpointType {server, client};
|
||||
|
||||
/**
|
||||
* @brief Call entry structure that is defined for a call to wait for the result.
|
||||
*/
|
||||
struct SCallEntry
|
||||
{
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqResult; ///< The result data.
|
||||
std::mutex mtxWaitForResult; ///< Mutex to protect result processing.
|
||||
std::condition_variable cvWaitForResult; ///< Condition variable to trigger result processing.
|
||||
bool bCancel = false; ///< Cancel processing when set.
|
||||
};
|
||||
|
||||
CCommunicationControl& m_rcontrol; ///< Reference to the communication control class.
|
||||
sdv::TObjectPtr m_ptrChannelEndpoint; ///< Managed pointer to the channel endpoint.
|
||||
uint64_t m_uiConnectionStatusCookie = 0; ///< Connection status cookie (received after registration).
|
||||
std::shared_ptr<CMarshallObject> m_ptrInitialMarshallObject; ///< Initial marshall object used after a connect event.
|
||||
sdv::ipc::EConnectStatus m_eConnectStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current connection status.
|
||||
bool m_bAllowReconnect = false; ///< When set, allow reconnection of the server.
|
||||
EEndpointType m_eEndpointType = EEndpointType::client; ///< Endpoint type of this connector.
|
||||
std::recursive_mutex m_mtxMarshallObjects; ///< Synchronize access to the marshall object vector.
|
||||
std::map<sdv::ps::TMarshallID, std::shared_ptr<CMarshallObject>> m_mapProxyObjects; ///< Map of stub IDs to proxy objects
|
||||
sdv::com::TConnectionID m_tConnectionID{}; ///< Connection ID for this connector.
|
||||
sdv::ipc::IDataSend* m_pDataSend = nullptr; ///< Pointer to the send interface.
|
||||
std::mutex m_mtxCalls; ///< Call map protection.
|
||||
std::map<uint64_t, SCallEntry&> m_mapCalls; ///< call map.
|
||||
CTaskScheduler m_scheduler; ///< Scheduler to process incoming calls.
|
||||
};
|
||||
|
||||
#endif // !defined COM_CHANNEL_H
|
||||
322
sdv_services/ipc_com/com_ctrl.cpp
Normal file
322
sdv_services/ipc_com/com_ctrl.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
#include "com_ctrl.h"
|
||||
#include <interfaces/ipc.h>
|
||||
#include <support/toml.h>
|
||||
#include "com_channel.h"
|
||||
#include "marshall_object.h"
|
||||
|
||||
thread_local CChannelConnector* CCommunicationControl::m_pConnectorContext = nullptr;
|
||||
|
||||
CCommunicationControl::CCommunicationControl()
|
||||
{
|
||||
std::srand(static_cast<unsigned int>(std::time(nullptr)));
|
||||
}
|
||||
|
||||
CCommunicationControl::~CCommunicationControl()
|
||||
{}
|
||||
|
||||
void CCommunicationControl::Initialize(const sdv::u8string& /*ssObjectConfig*/)
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialized;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CCommunicationControl::GetStatus() const
|
||||
{
|
||||
return m_eObjectStatus;
|
||||
}
|
||||
|
||||
void CCommunicationControl::SetOperationMode(sdv::EOperationMode eMode)
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::configuring;
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::configuring || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::running;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CCommunicationControl::Shutdown()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
|
||||
// Wait for threads to terminate... in case one is still running
|
||||
// (use a copy to prevent circular access)
|
||||
std::unique_lock<std::mutex> lock(m_mtxChannels);
|
||||
auto vecInitialConnectMon = std::move(m_vecInitialConnectMon);
|
||||
lock.unlock();
|
||||
for (std::thread& rthread : vecInitialConnectMon)
|
||||
{
|
||||
if (rthread.joinable())
|
||||
rthread.join();
|
||||
}
|
||||
vecInitialConnectMon.clear();
|
||||
|
||||
// Clear the channels and remove the stub objects (use a copy to prevent circular access)
|
||||
lock.lock();
|
||||
auto vecChannels = std::move(m_vecChannels);
|
||||
auto mapStubObjects = std::move(m_mapStubObjects);
|
||||
lock.unlock();
|
||||
vecChannels.clear();
|
||||
mapStubObjects.clear();
|
||||
|
||||
m_eObjectStatus = sdv::EObjectStatus::destruction_pending;
|
||||
}
|
||||
|
||||
sdv::com::TConnectionID CCommunicationControl::CreateServerConnection(/*in*/ sdv::com::EChannelType eChannelType,
|
||||
/*in*/ sdv::IInterfaceAccess* pObject, /*in*/ uint32_t uiTimeoutMs, /*out*/ sdv::u8string& ssConnectionString)
|
||||
{
|
||||
std::string ssChannelServer;
|
||||
switch (eChannelType)
|
||||
{
|
||||
case sdv::com::EChannelType::local_channel:
|
||||
ssChannelServer = "LocalChannelControl";
|
||||
break;
|
||||
case sdv::com::EChannelType::remote_channel:
|
||||
ssChannelServer = "RemoteChannelControl";
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create the channel endpoint
|
||||
sdv::ipc::ICreateEndpoint* pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>(ssChannelServer);
|
||||
if (!pEndpoint)
|
||||
{
|
||||
SDV_LOG_ERROR("No channel control!");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create a standard endpoint
|
||||
sdv::ipc::SChannelEndpoint sEndpoint = pEndpoint->CreateEndpoint("");
|
||||
if (!sEndpoint.pConnection)
|
||||
{
|
||||
SDV_LOG_ERROR("Could not create the endpoint!");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Manage the channel endpoint object
|
||||
sdv::TObjectPtr ptrChannelEndpoint(sEndpoint.pConnection);
|
||||
|
||||
// Manage the endpoint connection object.
|
||||
sdv::com::TConnectionID tConnectionID = AssignServerEndpoint(ptrChannelEndpoint, pObject, uiTimeoutMs, false);
|
||||
if (!tConnectionID.uiControl) return {};
|
||||
|
||||
ssConnectionString = sEndpoint.ssConnectString;
|
||||
return tConnectionID;
|
||||
}
|
||||
|
||||
sdv::com::TConnectionID CCommunicationControl::CreateClientConnection(/*in*/ const sdv::u8string& ssConnectionString,
|
||||
/*in*/ uint32_t uiTimeoutMs, /*out*/ sdv::IInterfaceAccess*& pProxy)
|
||||
{
|
||||
pProxy = nullptr;
|
||||
|
||||
// The channel connection string contains the nme of the provider
|
||||
sdv::toml::CTOMLParser parser(ssConnectionString);
|
||||
if (!parser.IsValid()) return {};
|
||||
std::string ssProvider = parser.GetDirect("Provider.Name").GetValue();
|
||||
|
||||
// Get the channel access interface
|
||||
sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject<sdv::ipc::IChannelAccess>(ssProvider);
|
||||
if (!pChannelAccess)
|
||||
{
|
||||
SDV_LOG_ERROR("No channel control!");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create the endpoint and establish the channel
|
||||
sdv::TObjectPtr ptrChannelEndpoint(pChannelAccess->Access(ssConnectionString));
|
||||
|
||||
return AssignClientEndpoint(ptrChannelEndpoint, uiTimeoutMs, pProxy);
|
||||
}
|
||||
|
||||
sdv::com::TConnectionID CCommunicationControl::AssignServerEndpoint(/*in*/ sdv::IInterfaceAccess* pChannelEndpoint,
|
||||
/*in*/ sdv::IInterfaceAccess* pObject, /*in*/ uint32_t uiTimeoutMs, /*in*/ bool bAllowReconnect)
|
||||
{
|
||||
if (!pChannelEndpoint || !pObject || !(uiTimeoutMs || bAllowReconnect)) return {};
|
||||
|
||||
// Create a communication channel object
|
||||
std::unique_lock<std::mutex> lock(m_mtxChannels);
|
||||
auto ptrCommunication = std::make_shared<CChannelConnector>(*this, static_cast<uint32_t>(m_vecChannels.size()), pChannelEndpoint);
|
||||
// Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an
|
||||
// exception was triggered).
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!ptrCommunication)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to allocate SDV communication channel!");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Start the connection
|
||||
if (!ptrCommunication->ServerConnect(pObject, bAllowReconnect)) return {};
|
||||
|
||||
// Add the channel to the vector
|
||||
m_vecChannels.push_back(ptrCommunication);
|
||||
|
||||
//// If reconnect is not allowed and if not already connected, start the monitor...
|
||||
//if (!bAllowReconnect && !ptrCommunication->IsConnected())
|
||||
// m_vecInitialConnectMon.emplace_back([&]()
|
||||
// {
|
||||
// auto ptrLocalChannel = ptrCommunication;
|
||||
// auto tpStart = std::chrono::high_resolution_clock::now();
|
||||
// while (!ptrLocalChannel->IsConnected())
|
||||
// {
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
// if (std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
// std::chrono::high_resolution_clock::now() - tpStart).count() >
|
||||
// uiTimeoutMs) break;
|
||||
// }
|
||||
|
||||
// if (!ptrLocalChannel->IsConnected())
|
||||
// RemoveChannelConnector(ptrLocalChannel.get());
|
||||
// });
|
||||
|
||||
// Done!
|
||||
return ptrCommunication->GetConnectionID();
|
||||
}
|
||||
|
||||
sdv::com::TConnectionID CCommunicationControl::AssignClientEndpoint(/*in*/ sdv::IInterfaceAccess* pChannelEndpoint,
|
||||
/*in*/ uint32_t uiTimeoutMs, /*out*/ sdv::IInterfaceAccess*& pProxy)
|
||||
{
|
||||
pProxy = nullptr;
|
||||
|
||||
// Create a communication channel object
|
||||
std::unique_lock<std::mutex> lock(m_mtxChannels);
|
||||
auto ptrCommunication =
|
||||
std::make_shared<CChannelConnector>(*this, static_cast<uint32_t>(m_vecChannels.size()), pChannelEndpoint);
|
||||
// Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an
|
||||
// exception was triggered).
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!ptrCommunication)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to allocate SDV communication channel!");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Start the connection
|
||||
pProxy = ptrCommunication->ClientConnect(uiTimeoutMs);
|
||||
if (!pProxy)
|
||||
{
|
||||
SDV_LOG_ERROR("Could not connect client!");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Add the channel to the vector
|
||||
m_vecChannels.push_back(ptrCommunication);
|
||||
|
||||
return ptrCommunication->GetConnectionID();
|
||||
}
|
||||
|
||||
void CCommunicationControl::RemoveConnection(/*in*/ const sdv::com::TConnectionID& tConnectionID)
|
||||
{
|
||||
// Clear the vector entry.
|
||||
std::unique_lock<std::mutex> lock(m_mtxChannels);
|
||||
std::shared_ptr<CChannelConnector> ptrChannelsCopy;
|
||||
if (tConnectionID.uiIdent < m_vecChannels.size())
|
||||
{
|
||||
ptrChannelsCopy = m_vecChannels[tConnectionID.uiIdent]; // Keep the channel alive while clearing the vector entry.
|
||||
m_vecChannels[tConnectionID.uiIdent].reset(); // Only clear the pointer; do not remove the entry (this would mix up IDs).
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
// Clear the channel.
|
||||
ptrChannelsCopy.reset();
|
||||
}
|
||||
|
||||
sdv::interface_t CCommunicationControl::GetProxy(/*in*/ const sdv::ps::TMarshallID& tStubID, /*in*/ sdv::interface_id id)
|
||||
{
|
||||
if (!m_pConnectorContext) return {};
|
||||
|
||||
// Get the proxy
|
||||
std::shared_ptr<CMarshallObject> ptrProxy = m_pConnectorContext->GetOrCreateProxy(id, tStubID);
|
||||
if (!ptrProxy) return {};
|
||||
|
||||
return ptrProxy->GetProxy();
|
||||
}
|
||||
|
||||
sdv::ps::TMarshallID CCommunicationControl::GetStub(/*in*/ sdv::interface_t ifc)
|
||||
{
|
||||
// Get the stub
|
||||
auto ptrStub = GetOrCreateStub(ifc);
|
||||
if (!ptrStub) return {};
|
||||
|
||||
return ptrStub->GetMarshallID();
|
||||
}
|
||||
|
||||
std::shared_ptr<CMarshallObject> CCommunicationControl::CreateProxy(sdv::interface_id id, sdv::ps::TMarshallID tStubID,
|
||||
CChannelConnector& rConnector)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mtxObjects);
|
||||
|
||||
// Create the marshall object.
|
||||
size_t nIndex = m_vecMarshallObjects.size();
|
||||
auto ptrMarshallObject = std::make_shared<CMarshallObject>(*this);
|
||||
// Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an
|
||||
// exception was triggered).
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!ptrMarshallObject)
|
||||
return {};
|
||||
m_vecMarshallObjects.push_back(ptrMarshallObject);
|
||||
if (!ptrMarshallObject->InitializeAsProxy(static_cast<uint32_t>(nIndex), id, tStubID, rConnector))
|
||||
ptrMarshallObject.reset();
|
||||
if (!ptrMarshallObject) return {};
|
||||
return ptrMarshallObject;
|
||||
}
|
||||
|
||||
std::shared_ptr<CMarshallObject> CCommunicationControl::GetOrCreateStub(sdv::interface_t ifc)
|
||||
{
|
||||
if (!ifc) return {};
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mtxObjects);
|
||||
|
||||
// TODO: Check the proxy list if the interface is a proxy object. If so, get the corresponding stub ID instead of creating
|
||||
// another stub object.
|
||||
|
||||
// If not existing, add an empty object in the map.
|
||||
auto itMarshallObject = m_mapStubObjects.find(ifc);
|
||||
if (itMarshallObject == m_mapStubObjects.end())
|
||||
{
|
||||
auto prMarshallObject = m_mapStubObjects.try_emplace(ifc, std::make_shared<CMarshallObject>(*this));
|
||||
if (!prMarshallObject.second) return {};
|
||||
itMarshallObject = prMarshallObject.first;
|
||||
size_t nIndex = m_vecMarshallObjects.size();
|
||||
m_vecMarshallObjects.push_back(itMarshallObject->second);
|
||||
if (!itMarshallObject->second->InitializeAsStub(static_cast<uint32_t>(nIndex), ifc))
|
||||
itMarshallObject->second.reset();
|
||||
}
|
||||
|
||||
return itMarshallObject->second;
|
||||
}
|
||||
|
||||
uint64_t CCommunicationControl::CreateUniqueCallIndex()
|
||||
{
|
||||
// Return the next call count.
|
||||
return m_uiCurrentCallCnt++;
|
||||
}
|
||||
|
||||
void CCommunicationControl::SetConnectorContext(CChannelConnector* pConnectorContext)
|
||||
{
|
||||
// Store the current channel for this thread (needed during the proxy creation).
|
||||
m_pConnectorContext = pConnectorContext;
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> CCommunicationControl::CallStub(sdv::ps::TMarshallID tStubID,
|
||||
sdv::sequence<sdv::pointer<uint8_t>>& seqInputData)
|
||||
{
|
||||
// Find stub and call the function
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mtxObjects);
|
||||
if (tStubID.uiIdent >= m_vecMarshallObjects.size()) throw sdv::ps::XMarshallIntegrity();
|
||||
auto ptrMarshallObject = m_vecMarshallObjects[tStubID.uiIdent].lock();
|
||||
lock.unlock();
|
||||
|
||||
// Check for a valid object
|
||||
if (!ptrMarshallObject) throw sdv::ps::XMarshallIntegrity();
|
||||
if (ptrMarshallObject->GetMarshallID() != tStubID) throw sdv::ps::XMarshallIntegrity();
|
||||
|
||||
// Make the call
|
||||
return ptrMarshallObject->Call(seqInputData);
|
||||
}
|
||||
209
sdv_services/ipc_com/com_ctrl.h
Normal file
209
sdv_services/ipc_com/com_ctrl.h
Normal file
@@ -0,0 +1,209 @@
|
||||
#ifndef COM_CTRL_H
|
||||
#define COM_CTRL_H
|
||||
|
||||
#include <support/pssup.h>
|
||||
#include <support/component_impl.h>
|
||||
#include <interfaces/com.h>
|
||||
|
||||
// Forward declaration
|
||||
class CChannelConnector;
|
||||
class CMarshallObject;
|
||||
|
||||
/**
|
||||
* @brief Test object simulating an component isolation service implementation for channel implementation testing
|
||||
*/
|
||||
class CCommunicationControl : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::com::IConnectionControl,
|
||||
public sdv::ps::IMarshallAccess
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
*/
|
||||
CCommunicationControl();
|
||||
|
||||
/**
|
||||
* @brief Destructor added to cleanly stop service in case process is stopped (without shutdown via repository service)
|
||||
*/
|
||||
virtual ~CCommunicationControl() override;
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ps::IMarshallAccess)
|
||||
SDV_INTERFACE_ENTRY(sdv::com::IConnectionControl)
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Component declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
DECLARE_OBJECT_CLASS_NAME("CommunicationControl")
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
*/
|
||||
virtual void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
*/
|
||||
virtual sdv::EObjectStatus GetStatus() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
*/
|
||||
virtual void Shutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Create an IPC channel endpoint and use it for SDV communication. Overload of
|
||||
* sdv::com::IConnectionControl::CreateServerConnection.
|
||||
* @remarks The channel will be destroyed automatically when a timeout occurs (no initial connection took place within the
|
||||
* specified time).
|
||||
* @remarks The function doesn't wait until a connection has been made, but returns straight after the channel creation.
|
||||
* @param[in] eChannelType Type of channel to create. Must be local or remote.
|
||||
* @param[in] pObject Initial object to start the communication with.
|
||||
* @param[in] uiTimeoutMs Timeout for waiting for a connection.
|
||||
* @param[out] ssConnectionString String describing the connection details to connect to this channel.
|
||||
* @return Channel connection ID if successful or 0 if not.
|
||||
*/
|
||||
virtual sdv::com::TConnectionID CreateServerConnection(/*in*/ sdv::com::EChannelType eChannelType,
|
||||
/*in*/ sdv::IInterfaceAccess* pObject, /*in*/ uint32_t uiTimeoutMs, /*out*/ sdv::u8string& ssConnectionString) override;
|
||||
|
||||
/**
|
||||
* @brief Connect to an SDV channel (an IPC channel managed by the channel control and waiting for an initial connection.
|
||||
* Overload of sdv::com::IConnectionControl::CreateClientConnection.
|
||||
* @remarks The connection will be destroyed automatically when a timeout occurs (no initial connection took place within
|
||||
* the specified time).
|
||||
* @param[in] ssConnectionString The string describing the connection details.
|
||||
* @param[in] uiTimeoutMs Timeout for waiting for a connection.
|
||||
* @param[out] pProxy Pointer to the object representing the remote object (a proxy to the remote object). Or nullptr
|
||||
* when a timeout occurred.
|
||||
* @return Channel connection ID if successful or 0 if not.
|
||||
*/
|
||||
virtual sdv::com::TConnectionID CreateClientConnection(/*in*/ const sdv::u8string& ssConnectionString,
|
||||
/*in*/ uint32_t uiTimeoutMs, /*out*/ sdv::IInterfaceAccess*& pProxy) override;
|
||||
|
||||
/**
|
||||
* @brief Assign and take over an already initialized IPC channel server endpoint for use with SDV communication. Overload of
|
||||
* sdv::com::IConnectionControl::AssignServerEndpoint.
|
||||
* @remarks The channel will be destroyed automatically when a timeout occurs (no initial connection took place within the
|
||||
* specified time) unless the flag bAllowReconnect has been set.
|
||||
* @remarks The channel uses the interface sdv::IObjectLifetime to control the lifetime of the channel. If IObjectLifetime is
|
||||
* not available, IObjectDestroy will be used.
|
||||
* @remarks The function doesn't wait until a connection has been made, but returns straight after the assignment.
|
||||
* @param[in] pChannelEndpoint Pointer to the channel endpoint to be assigned.
|
||||
* @param[in] pObject Initial object to start the communication with.
|
||||
* @param[in] uiTimeoutMs Timeout for waiting for an initial connection. Not used when the bAllowReconnect flag has been set.
|
||||
* @param[in] bAllowReconnect When set, the channel is allowed to be disconnected and reconnected again.
|
||||
* @return Channel connection ID if successful or 0 if not.
|
||||
*/
|
||||
virtual sdv::com::TConnectionID AssignServerEndpoint(/*in*/ sdv::IInterfaceAccess* pChannelEndpoint,
|
||||
/*in*/ sdv::IInterfaceAccess* pObject, /*in*/ uint32_t uiTimeoutMs, /*in*/ bool bAllowReconnect) override;
|
||||
|
||||
/**
|
||||
* @brief Assign and take over an already initialized IPC channel client endpoint for use with SDV communication. Overload of
|
||||
* sdv::com::IConnectionControl::AssignClientEndpoint.
|
||||
* @remarks The connection will be destroyed automatically when a timeout occurs (no initial connection took place
|
||||
* within the specified time).
|
||||
* @remarks The channel uses the interface sdv::IObjectLifetime to control the lifetime of the channel. If
|
||||
* IObjectLifetime is not available, IObjectDestroy will be used.
|
||||
* @param[in] pChannelEndpoint Pointer to the channel endpoint to be assigned.
|
||||
* @param[in] uiTimeoutMs Timeout for waiting for an initial connection. Not used when the bAllowReconnect flag has
|
||||
* been set.
|
||||
* @param[out] pProxy Pointer to the object representing the remote object (a proxy to the remote object). Or nullptr
|
||||
* when a timeout occurred.
|
||||
* @return Channel connection ID if successful or 0 if not.
|
||||
*/
|
||||
virtual sdv::com::TConnectionID AssignClientEndpoint(/*in*/ sdv::IInterfaceAccess* pChannelEndpoint,
|
||||
/*in*/ uint32_t uiTimeoutMs, /*out*/ sdv::IInterfaceAccess*& pProxy) override;
|
||||
|
||||
/**
|
||||
* @brief Remove a connection with the provided connection ID. Overload of sdv::com::IConnectionControl::RemoveConnection.
|
||||
* @param[in] tConnectionID The connection ID of the connection to remove.
|
||||
*/
|
||||
virtual void RemoveConnection(/*in*/ const sdv::com::TConnectionID& tConnectionID) override;
|
||||
|
||||
/**
|
||||
* @brief Get a proxy for the interface connection to the stub. Overload of sdv::ps::IMarshallAcess::GetProxy.
|
||||
* @param[in] tStubID Reference to the ID of the stub to connect to.
|
||||
* @param[in] id The interface ID to get the proxy for.
|
||||
* @return Returns the interface to the proxy object.
|
||||
*/
|
||||
virtual sdv::interface_t GetProxy(/*in*/ const sdv::ps::TMarshallID& tStubID, /*in*/ sdv::interface_id id) override;
|
||||
|
||||
/**
|
||||
* @brief Get a stub for the interface with the supplied ID. Overload of sdv::ps::IMarshallAcess::GetStub.
|
||||
* @param[in] ifc The interface to get the stub for..
|
||||
* @return Returns the Stub ID that is assigned to the interface. Or an empty ID when no stub could be found.
|
||||
*/
|
||||
virtual sdv::ps::TMarshallID GetStub(/*in*/ sdv::interface_t ifc) override;
|
||||
|
||||
/**
|
||||
* @brief Create a proxy for the interface connection to the stub.
|
||||
* @remarks Unlike stubs, which are unique for the process they run in, proxy objects are unique within the channel they are
|
||||
* used - using the identification of the stub ID of the process the call. The proxy object is not stored here; just created.
|
||||
* @param[in] id The ID of the interface this object marshalls the calls for.
|
||||
* @param[in] tStubID The stub ID this proxy is communicating to.
|
||||
* @param[in] rConnector Reference to channel connector.
|
||||
* @return Returns a shared pointer to the proxy object.
|
||||
*/
|
||||
std::shared_ptr<CMarshallObject> CreateProxy(sdv::interface_id id, sdv::ps::TMarshallID tStubID,
|
||||
CChannelConnector& rConnector);
|
||||
|
||||
/**
|
||||
* @brief Get a stub for the interface with the supplied ID. Overload of sdv::ps::IMarshallAcess::GetStub.
|
||||
* @param[in] ifc The interface to get the stub for..
|
||||
* @return Returns the Stub ID that is assigned to the interface. Or an empty ID when no stub could be found.
|
||||
*/
|
||||
std::shared_ptr<CMarshallObject> GetOrCreateStub(sdv::interface_t ifc);
|
||||
|
||||
/**
|
||||
* @brief To identify the send and receive packets belonging to one call, the call is identified with a unique index, which
|
||||
* is created here.
|
||||
* @return The unique call index.
|
||||
*/
|
||||
uint64_t CreateUniqueCallIndex();
|
||||
|
||||
/**
|
||||
* @brief Set the channel connector context for the current thread. This is used to marshall interfaces over the same connector.
|
||||
* @param[in] pConnectorContext Pointer to the connector currently being used.
|
||||
*/
|
||||
void SetConnectorContext(CChannelConnector* pConnectorContext);
|
||||
|
||||
/**
|
||||
* @brief Call the stub function.
|
||||
* @remarks This function call is synchronous and does not return until the call has been finalized or a timeout
|
||||
* exception has occurred.
|
||||
* @remarks The sequence contains all data to make the call. It is important that the data in the sequence is
|
||||
* complete and in the correct order.
|
||||
* @param[in] tStubID ID of the stub to call.
|
||||
* @param[inout] seqInputData Reference to sequence of input data pointers. The first data pointer contains the
|
||||
* marshalling header. The second contains the parameters (if available) and the others contain raw data pointers
|
||||
* (if available). The call is allowed to change the sequence to be able to add additional information during the
|
||||
* communication without having to copy the existing data.
|
||||
* @return Sequence of output data pointers. The first data pointer contains the marshalling header. The second
|
||||
* contains the return value and parameters (if available) and the others contain raw data pointers (if available).
|
||||
*/
|
||||
sdv::sequence<sdv::pointer<uint8_t>> CallStub(sdv::ps::TMarshallID tStubID, sdv::sequence<sdv::pointer<uint8_t>>& seqInputData);
|
||||
|
||||
private:
|
||||
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
|
||||
std::mutex m_mtxChannels; ///< Protect the channel map.
|
||||
std::vector<std::shared_ptr<CChannelConnector>> m_vecChannels; ///< Channel vector.
|
||||
std::vector<std::thread> m_vecInitialConnectMon; ///< Initial connection monitor.
|
||||
std::recursive_mutex m_mtxObjects; ///< Protect object vectors.
|
||||
std::vector<std::weak_ptr<CMarshallObject>> m_vecMarshallObjects; ///< Vector with marshall objects; lifetime is handled by channel.
|
||||
std::map<sdv::interface_t, std::shared_ptr<CMarshallObject>> m_mapStubObjects; ///< Map of interfaces to stub objects
|
||||
std::atomic_uint64_t m_uiCurrentCallCnt = 0; ///< The current call count.
|
||||
thread_local static CChannelConnector* m_pConnectorContext; ///< The current connector; variable local to each thread.
|
||||
};
|
||||
DEFINE_SDV_OBJECT(CCommunicationControl)
|
||||
|
||||
#endif // !defined COM_CTRL_H
|
||||
176
sdv_services/ipc_com/marshall_object.cpp
Normal file
176
sdv_services/ipc_com/marshall_object.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "marshall_object.h"
|
||||
#include "com_ctrl.h"
|
||||
#include <support/serdes.h>
|
||||
#include "com_channel.h"
|
||||
|
||||
CMarshallObject::CMarshallObject(CCommunicationControl& rcontrol) : m_rcontrol(rcontrol)
|
||||
{}
|
||||
|
||||
CMarshallObject::~CMarshallObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CMarshallObject::IsValid() const
|
||||
{
|
||||
return m_tMarshallID.uiControl ? true : false;
|
||||
}
|
||||
|
||||
void CMarshallObject::Reset()
|
||||
{
|
||||
m_eType = EType::unknown;
|
||||
m_tMarshallID = { 0, 0, 0, 0 };
|
||||
m_pMarshall = nullptr;
|
||||
m_tStubID = {};
|
||||
m_ifcProxy = {};
|
||||
m_pConnector = nullptr;
|
||||
}
|
||||
|
||||
sdv::interface_t CMarshallObject::InitializeAsProxy(uint32_t uiProxyIndex, sdv::interface_id id,
|
||||
sdv::ps::TMarshallID tStubID, CChannelConnector& rConnector)
|
||||
{
|
||||
m_eType = EType::proxy;
|
||||
|
||||
// Create marshall ID from index and a random number.
|
||||
sdv::ps::TMarshallID tMarshallID = { 0, GetProcessID(), uiProxyIndex, static_cast<uint32_t>(rand()) };
|
||||
|
||||
// Get the stub creation interface from the repository
|
||||
sdv::core::IRepositoryMarshallCreate* pMarshallCreate =
|
||||
sdv::core::GetObject<sdv::core::IRepositoryMarshallCreate>("RepositoryService");
|
||||
if (!pMarshallCreate)
|
||||
{
|
||||
Reset();
|
||||
return {};
|
||||
}
|
||||
m_ptrMarshallObject = pMarshallCreate->CreateProxyObject(id);
|
||||
if (!m_ptrMarshallObject)
|
||||
{
|
||||
Reset();
|
||||
return {};
|
||||
}
|
||||
|
||||
// Set the proxy ID and control value
|
||||
sdv::ps::IMarshallObjectIdent* pObjectIdent = m_ptrMarshallObject.GetInterface<sdv::ps::IMarshallObjectIdent>();
|
||||
if (!pObjectIdent)
|
||||
{
|
||||
Reset();
|
||||
return {};
|
||||
}
|
||||
pObjectIdent->SetIdentification(tMarshallID);
|
||||
|
||||
// Set the connecting stub ID for this proxy and get the target interface that the proxy provides.
|
||||
sdv::ps::IProxyControl* pProxyControl = m_ptrMarshallObject.GetInterface<sdv::ps::IProxyControl>();
|
||||
if (!pProxyControl)
|
||||
{
|
||||
Reset();
|
||||
return {};
|
||||
}
|
||||
m_tStubID = tStubID;
|
||||
m_ifcProxy = pProxyControl->GetTargetInterface();
|
||||
if (!m_ifcProxy)
|
||||
{
|
||||
Reset();
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the IMarshallLink on the proxy object interface to link to the proxy and connect to IDataSend...
|
||||
sdv::ps::IMarshallLink* pMarshallLink = m_ptrMarshallObject.GetInterface<sdv::ps::IMarshallLink>();
|
||||
if (!pMarshallLink)
|
||||
{
|
||||
Reset();
|
||||
return {};
|
||||
}
|
||||
pMarshallLink->Link(this);
|
||||
|
||||
// Store the channel.
|
||||
m_pConnector = &rConnector;
|
||||
|
||||
// Store the ID...
|
||||
m_tMarshallID = tMarshallID;
|
||||
|
||||
return m_ifcProxy;
|
||||
}
|
||||
|
||||
bool CMarshallObject::InitializeAsStub(uint32_t uiStubIndex, sdv::interface_t ifc)
|
||||
{
|
||||
if (!ifc) return false;
|
||||
|
||||
m_eType = EType::stub;
|
||||
|
||||
// Create marshall ID from index and a random number.
|
||||
sdv::ps::TMarshallID tMarshallID = { 0, GetProcessID(), uiStubIndex, static_cast<uint32_t>(rand()) };
|
||||
|
||||
// Get the stub creation interface from the repository
|
||||
sdv::core::IRepositoryMarshallCreate* pMarshallCreate =
|
||||
sdv::core::GetObject<sdv::core::IRepositoryMarshallCreate>("RepositoryService");
|
||||
if (!pMarshallCreate)
|
||||
{
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
m_ptrMarshallObject = pMarshallCreate->CreateStubObject(ifc.id());
|
||||
if (!m_ptrMarshallObject)
|
||||
{
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the stub ID and control value
|
||||
sdv::ps::IMarshallObjectIdent* pObjectIdent = m_ptrMarshallObject.GetInterface<sdv::ps::IMarshallObjectIdent>();
|
||||
if (!pObjectIdent)
|
||||
{
|
||||
Reset();
|
||||
return {};
|
||||
}
|
||||
pObjectIdent->SetIdentification(tMarshallID);
|
||||
|
||||
// Link the object to the stub.
|
||||
sdv::ps::IStubLink* psStubLink = m_ptrMarshallObject.GetInterface<sdv::ps::IStubLink>();
|
||||
if (!psStubLink)
|
||||
{
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
psStubLink->Link(ifc);
|
||||
|
||||
// Get the marshall interface
|
||||
m_pMarshall = m_ptrMarshallObject.GetInterface<sdv::ps::IMarshall>();
|
||||
if (!m_pMarshall)
|
||||
{
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Everything is successful. Store the ID...
|
||||
m_tMarshallID = tMarshallID;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::ps::TMarshallID CMarshallObject::GetMarshallID() const
|
||||
{
|
||||
return m_tMarshallID;
|
||||
}
|
||||
|
||||
sdv::interface_t CMarshallObject::GetProxy()
|
||||
{
|
||||
return m_ifcProxy;
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> CMarshallObject::Call(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqInputData)
|
||||
{
|
||||
// Differentiate between proxy and stub processing
|
||||
if (m_eType == EType::proxy)
|
||||
{
|
||||
// Make the call through the connector.
|
||||
if (!m_pConnector) throw sdv::ps::XMarshallNotInitialized();
|
||||
return m_pConnector->MakeCall(m_tMarshallID, m_tStubID, seqInputData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make the call through the stored marshall object.
|
||||
if (!m_pMarshall) throw sdv::ps::XMarshallNotInitialized();
|
||||
return m_pMarshall->Call(seqInputData);
|
||||
}
|
||||
}
|
||||
|
||||
117
sdv_services/ipc_com/marshall_object.h
Normal file
117
sdv_services/ipc_com/marshall_object.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef MARSHALL_OBJECT_H
|
||||
#define MARSHALL_OBJECT_H
|
||||
|
||||
#include <interfaces/ipc.h>
|
||||
#include <support/component_impl.h>
|
||||
#include <support/interface_ptr.h>
|
||||
#include <interfaces/core_ps.h>
|
||||
|
||||
// Forward declarations
|
||||
class CCommunicationControl;
|
||||
class CChannelConnector;
|
||||
|
||||
inline sdv::process::TProcessID GetProcessID()
|
||||
{
|
||||
static sdv::process::TProcessID tProcessID = 0;
|
||||
if (!tProcessID)
|
||||
{
|
||||
const sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject<sdv::process::IProcessInfo>("ProcessControlService");
|
||||
if (!pProcessInfo) return 0;
|
||||
tProcessID = pProcessInfo->GetProcessID();
|
||||
}
|
||||
return tProcessID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Storage class for a proxy or a stub object.
|
||||
*/
|
||||
class CMarshallObject : public sdv::ps::IMarshall
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rcontrol Reference to the communication control class.
|
||||
*/
|
||||
CMarshallObject(CCommunicationControl& rcontrol);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CMarshallObject();
|
||||
|
||||
/**
|
||||
* @brief Is this a valid marshal object?
|
||||
* @return Returns whether the marshal object is valid.
|
||||
*/
|
||||
bool IsValid() const;
|
||||
|
||||
/**
|
||||
* @brief Reset the marshal object.
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* @brief Initialize the marshall object as proxy.
|
||||
* @param[in] uiProxyIndex The index of this proxy; becoming part of the Proxy ID.
|
||||
* @param[in] id The ID of the interface this object marshalls the calls for.
|
||||
* @param[in] tStubID The stub ID this proxy is communicating to.
|
||||
* @param[in] rConnector Reference to channel connector.
|
||||
* @return Returns a pointer to proxy interface or empty when the initialization failed.
|
||||
*/
|
||||
sdv::interface_t InitializeAsProxy(uint32_t uiProxyIndex, sdv::interface_id id, sdv::ps::TMarshallID tStubID,
|
||||
CChannelConnector& rConnector);
|
||||
|
||||
/**
|
||||
* @brief Initialize the marshall object as stub.
|
||||
* @param[in] uiStubIndex The index of this stub; becoming part of the Stub ID.
|
||||
* @param[in] ifc Interface to the object to be marshalled to.
|
||||
* @return Returns 'true' when initialization was successful; 'false' when not.
|
||||
*/
|
||||
bool InitializeAsStub(uint32_t uiStubIndex, sdv::interface_t ifc);
|
||||
|
||||
/**
|
||||
* @brief Return the proxy/stub ID.
|
||||
* @details The marshall ID consist of an index to easily access the marshalling details and a control value to increase higher
|
||||
* security. The control value is a randomly generated value used in the communication to check whether the marshall object ID
|
||||
* is valid. Both index and control value must be known by the caller for the call to succeed. If one is wrong, the call won't
|
||||
* be made.
|
||||
* @return The ID of this marshall object.
|
||||
*/
|
||||
sdv::ps::TMarshallID GetMarshallID() const;
|
||||
|
||||
/**
|
||||
* @brief Return the proxy to the interface.
|
||||
* @return Proxy interface.
|
||||
*/
|
||||
sdv::interface_t GetProxy();
|
||||
|
||||
/**
|
||||
* @brief Marshall a function call. Ovverload of sdv::ps::IMarshall::Call.
|
||||
* @remarks This function call is synchronous and does not return until the call has been finalized or a timeout
|
||||
* exception has occurred.
|
||||
* @remarks The sequence contains all data to make the call. It is important that the data in the sequence is
|
||||
* complete and in the correct order.
|
||||
* @param[inout] seqInputData Reference to sequence of input data pointers. The first data pointer contains the
|
||||
* marshalling header. The second contains the parameters (if available) and the others contain raw data pointers
|
||||
* (if available). The call is allowed to change the sequence to be able to add additional information during the
|
||||
* communication without having to copy the existing data.
|
||||
* @return Sequence of output data pointers. The first data pointer contains the marshalling header. The second
|
||||
* contains the return value and parameters (if available) and the others contain raw data pointers (if available).
|
||||
*/
|
||||
virtual sdv::sequence<sdv::pointer<uint8_t>> Call(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqInputData) override;
|
||||
|
||||
private:
|
||||
/// Marshall object type
|
||||
enum class EType {unknown, proxy, stub};
|
||||
|
||||
CCommunicationControl& m_rcontrol; ///< Reference to the communication control class.
|
||||
EType m_eType = EType::unknown; ///< Type of object.
|
||||
sdv::ps::TMarshallID m_tMarshallID = {}; ///< The ID of this marshall object.
|
||||
sdv::ps::IMarshall* m_pMarshall = nullptr; ///< The marshall object pointer (only used for a stub).
|
||||
sdv::TObjectPtr m_ptrMarshallObject; ///< The marshall object.
|
||||
sdv::ps::TMarshallID m_tStubID = {}; ///< Stub ID (only used for a proxy).
|
||||
sdv::interface_t m_ifcProxy = {}; ///< Proxy interface (only used for a proxy).
|
||||
CChannelConnector* m_pConnector = nullptr; ///< Pointer to the connector (only used for a proxy).
|
||||
};
|
||||
|
||||
#endif // !defined MARSHALL_OBJECT_H
|
||||
Reference in New Issue
Block a user