mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-04-18 10:38:16 +00:00
Update sdv_packager (#6)
This commit is contained in:
36
sdv_services/uds_unix_sockets/CMakeLists.txt
Normal file
36
sdv_services/uds_unix_sockets/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
#*******************************************************************************
|
||||
# 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
|
||||
#*******************************************************************************
|
||||
|
||||
if(UNIX)
|
||||
# Define project
|
||||
project(uds_unix_sockets VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
# Define target
|
||||
add_library(uds_unix_sockets SHARED
|
||||
"channel_mgnt.h"
|
||||
"channel_mgnt.cpp"
|
||||
"connection.h"
|
||||
"connection.cpp"
|
||||
)
|
||||
|
||||
target_link_libraries(uds_unix_sockets rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
target_link_options(uds_unix_sockets PRIVATE)
|
||||
target_include_directories(uds_unix_sockets PRIVATE ./include/)
|
||||
set_target_properties(uds_unix_sockets PROPERTIES PREFIX "")
|
||||
set_target_properties(uds_unix_sockets PROPERTIES SUFFIX ".sdv")
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(uds_unix_sockets CompileCoreIDL)
|
||||
|
||||
# Appending the service in the service list
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_unix_sockets PARENT_SCOPE)
|
||||
|
||||
endif()
|
||||
161
sdv_services/uds_unix_sockets/channel_mgnt.cpp
Normal file
161
sdv_services/uds_unix_sockets/channel_mgnt.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
|
||||
#include "channel_mgnt.h"
|
||||
#include "connection.h"
|
||||
|
||||
#include <support/toml.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Anonymous namespace for internal helpers
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* @brief Parse a semicolon-separated list of key=value pairs
|
||||
*
|
||||
* Expected input format:
|
||||
* "key1=value1;key2=value2;key3=value3;"
|
||||
*
|
||||
* Whitespace is not trimmed and empty entries are ignored
|
||||
* Keys without '=' are skipped
|
||||
*
|
||||
* Example:
|
||||
* Input: "proto=uds;path=/tmp/test.sock;"
|
||||
* Output: { {"proto","uds"}, {"path","/tmp/test.sock"} }
|
||||
*
|
||||
* @param[in] s Raw string containing "key=value;" pairs
|
||||
*
|
||||
* @return A map of parsed key/value pairs (order not preserved)
|
||||
*/
|
||||
static std::map<std::string, std::string> ParseKV(const std::string& s)
|
||||
{
|
||||
std::map<std::string, std::string> kv;
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
|
||||
while (std::getline(ss, item, ';'))
|
||||
{
|
||||
auto pos = item.find('=');
|
||||
if (pos != std::string::npos)
|
||||
kv[item.substr(0, pos)] = item.substr(pos + 1);
|
||||
}
|
||||
return kv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clamp an AF_UNIX pathname to Linux `sockaddr_un::sun_path` size
|
||||
*
|
||||
* Linux allows paths up to ~108 bytes inside `sun_path`
|
||||
* If the input string exceeds the allowed size, it is truncated so that:
|
||||
* resulting_length <= sizeof(sockaddr_un::sun_path) - 1
|
||||
*
|
||||
* @param[in] p Original path string
|
||||
*
|
||||
* @return A safe, clamped path that fits inside `sun_path`
|
||||
*/
|
||||
static std::string ClampSunPath(const std::string& p)
|
||||
{
|
||||
constexpr size_t MaxLen = sizeof(sockaddr_un::sun_path);
|
||||
return (p.size() < MaxLen) ? p : p.substr(0, MaxLen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Directory selection (/run/user/<uid>/sdv or /tmp/sdv)
|
||||
std::string CUnixDomainSocketsChannelMgnt::MakeUserRuntimeDir()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "/run/user/" << ::getuid();
|
||||
|
||||
struct stat st{};
|
||||
if (::stat(oss.str().c_str(), &st) == 0)
|
||||
{
|
||||
std::string path = oss.str() + "/sdv";
|
||||
::mkdir(path.c_str(), 0770);
|
||||
return path;
|
||||
}
|
||||
|
||||
::mkdir("/tmp/sdv", 0770);
|
||||
return "/tmp/sdv";
|
||||
}
|
||||
|
||||
bool CUnixDomainSocketsChannelMgnt::OnInitialize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CUnixDomainSocketsChannelMgnt::OnShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
// Endpoint creation (server)
|
||||
sdv::ipc::SChannelEndpoint CUnixDomainSocketsChannelMgnt::CreateEndpoint(const sdv::u8string& ssEndpointConfig)
|
||||
{
|
||||
const std::string baseDir = MakeUserRuntimeDir();
|
||||
std::string name = "UDS_" + std::to_string(::getpid());
|
||||
std::string path = baseDir + "/" + name + ".sock";
|
||||
|
||||
if (!ssEndpointConfig.empty())
|
||||
{
|
||||
sdv::toml::CTOMLParser cfg(ssEndpointConfig.c_str());
|
||||
|
||||
auto nameNode = cfg.GetDirect("IpcChannel.Name");
|
||||
if (nameNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
name = static_cast<std::string>(nameNode.GetValue());
|
||||
|
||||
auto pathNode = cfg.GetDirect("IpcChannel.Path");
|
||||
if (pathNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
path = static_cast<std::string>(pathNode.GetValue());
|
||||
else
|
||||
path = baseDir + "/" + name + ".sock";
|
||||
}
|
||||
|
||||
path = ClampSunPath(path);
|
||||
|
||||
// Use a shared_ptr and store it to keep the server connection alive
|
||||
auto server = std::make_shared<CUnixSocketConnection>(-1, true, path);
|
||||
m_ServerConnections.push_back(server);
|
||||
|
||||
sdv::ipc::SChannelEndpoint ep{};
|
||||
ep.pConnection = static_cast<IInterfaceAccess*>(server.get());
|
||||
ep.ssConnectString = server->GetConnectionString();
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Access existing endpoint (server or client)
|
||||
sdv::IInterfaceAccess* CUnixDomainSocketsChannelMgnt::Access(const sdv::u8string& ssConnectString)
|
||||
{
|
||||
const auto kv = ParseKV(static_cast<std::string>(ssConnectString));
|
||||
const bool isServer = (kv.count("role") && kv.at("role") == "server");
|
||||
const std::string path = kv.count("path") ? kv.at("path") : (MakeUserRuntimeDir() + "/UDS_auto.sock");
|
||||
|
||||
if (isServer)
|
||||
{
|
||||
auto server = std::make_shared<CUnixSocketConnection>(-1, true, path);
|
||||
m_ServerConnections.push_back(server);
|
||||
return static_cast<IInterfaceAccess*>(server.get());
|
||||
}
|
||||
|
||||
// Client: allocated raw pointer (expected to be managed by SDV framework)
|
||||
auto* client = new CUnixSocketConnection(-1, false, path);
|
||||
return static_cast<IInterfaceAccess*>(client);
|
||||
}
|
||||
|
||||
#endif // defined(__unix__)
|
||||
90
sdv_services/uds_unix_sockets/channel_mgnt.h
Normal file
90
sdv_services/uds_unix_sockets/channel_mgnt.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
#ifndef UNIX_SOCKET_CHANNEL_MGNT_H
|
||||
#define UNIX_SOCKET_CHANNEL_MGNT_H
|
||||
|
||||
#include <support/component_impl.h>
|
||||
#include <interfaces/ipc.h>
|
||||
|
||||
class CUnixSocketConnection;
|
||||
|
||||
/**
|
||||
* @brief IPC channel management class for Unix Domain Sockets communication.
|
||||
*/
|
||||
class CUnixDomainSocketsChannelMgnt :
|
||||
public sdv::CSdvObject,
|
||||
public sdv::ipc::ICreateEndpoint,
|
||||
public sdv::ipc::IChannelAccess
|
||||
{
|
||||
public:
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IChannelAccess)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::ICreateEndpoint)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("UnixDomainSocketsChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize.
|
||||
* @return Returns 'true' when the initialization was successful, 'false' when not.
|
||||
*/
|
||||
virtual bool OnInitialize() override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown.
|
||||
*/
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Create IPC connection object and return the endpoint information. Overload of
|
||||
* sdv::ipc::ICreateEndpoint::CreateEndpoint.
|
||||
* @details The endpoints are generated using either a size and a name based on the provided channel configuration or if no
|
||||
* configuration is supplied a default size of 10k and a randomly generated name. The following configuration
|
||||
* can be supplied:
|
||||
* @code
|
||||
* [IpcChannel]
|
||||
* Name = "CHANNEL_1234"
|
||||
* Size = 10240
|
||||
* @endcode
|
||||
* @param[in] ssChannelConfig Optional channel type specific endpoint configuration.
|
||||
* @return IPC connection object
|
||||
*/
|
||||
sdv::ipc::SChannelEndpoint CreateEndpoint(/*in*/ const sdv::u8string& ssChannelConfig) override;
|
||||
|
||||
/**
|
||||
* @brief Create a connection object from the channel connection parameters string
|
||||
* @param[in] ssConnectString Reference to the string containing the channel connection parameters.
|
||||
* @return Pointer to IInterfaceAccess interface of the connection object or NULL when the object cannot be created.
|
||||
*/
|
||||
sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override;
|
||||
|
||||
private:
|
||||
|
||||
// Helper: choose runtime dir (/run/user/<uid>/sdv) or fallback (/tmp/sdv)
|
||||
static std::string MakeUserRuntimeDir();
|
||||
|
||||
std::vector<std::shared_ptr<CUnixSocketConnection>> m_ServerConnections;
|
||||
|
||||
};
|
||||
DEFINE_SDV_OBJECT(CUnixDomainSocketsChannelMgnt)
|
||||
|
||||
#endif // UNIX_SOCKET_CHANNEL_MGNT_H
|
||||
#endif // defined(__unix__)
|
||||
1166
sdv_services/uds_unix_sockets/connection.cpp
Normal file
1166
sdv_services/uds_unix_sockets/connection.cpp
Normal file
File diff suppressed because it is too large
Load Diff
392
sdv_services/uds_unix_sockets/connection.h
Normal file
392
sdv_services/uds_unix_sockets/connection.h
Normal file
@@ -0,0 +1,392 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include <interfaces/ipc.h>
|
||||
#include <support/component_impl.h>
|
||||
#include <support/interface_ptr.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
/**
|
||||
* @brief Local IPC connection over Unix Domain Sockets (UDS).
|
||||
*
|
||||
* Frames on the wire are sent as size-prefixed buffers:
|
||||
* [ uint32_t packetSize ][ SDV message bytes ... ]
|
||||
*
|
||||
* SDV protocol decoding (SMsgHdr / EMsgType / fragmentation)
|
||||
*/
|
||||
class CUnixSocketConnection
|
||||
: public std::enable_shared_from_this<CUnixSocketConnection>
|
||||
, public sdv::IInterfaceAccess
|
||||
, public sdv::IObjectDestroy
|
||||
, public sdv::ipc::IDataSend
|
||||
, public sdv::ipc::IConnect
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a UDS connection.
|
||||
* @param preconfiguredFd Already-open FD (>=0) or -1 if none.
|
||||
* @param acceptConnectionRequired true for server (must accept()); false for client.
|
||||
* @param udsPath Filesystem path of the UDS socket.
|
||||
*/
|
||||
CUnixSocketConnection(int preconfiguredFd, bool acceptConnectionRequired, const std::string& udsPath);
|
||||
|
||||
/** @brief Virtual destructor. */
|
||||
virtual ~CUnixSocketConnection();
|
||||
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IConnect)
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectDestroy)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/** @brief Returns the connection string (proto/role/path/timeout). */
|
||||
std::string GetConnectionString();
|
||||
|
||||
// ---------- IDataSend ----------
|
||||
/** @brief Send a sequence of data buffers (may be fragmented). */
|
||||
bool SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) override;
|
||||
|
||||
// ---------- IConnect ----------
|
||||
/** @brief Start asynchronous connect/accept worker. */
|
||||
bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override;
|
||||
|
||||
/**
|
||||
* @brief Wait for the connection to become 'connected'.
|
||||
* @param uiWaitMs Timeout in milliseconds (0 = immediate, 0xFFFFFFFFu = infinite).
|
||||
* @return true if connected within the timeout; false otherwise.
|
||||
*/
|
||||
bool WaitForConnection(/*in*/ uint32_t uiWaitMs) override;
|
||||
|
||||
/** @brief Optionally cancel WaitForConnection (no-op in current implementation). */
|
||||
void CancelWait() override;
|
||||
|
||||
/** @brief Disconnect and teardown threads/FDs; sets status to 'disconnected'. */
|
||||
void Disconnect() override;
|
||||
|
||||
// ---------- IConnect: event callbacks ----------
|
||||
/** @brief Register a status event callback (no-op storage in UDS). */
|
||||
uint64_t RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override;
|
||||
|
||||
/** @brief Unregister a previously registered callback (no-op storage in UDS). */
|
||||
void UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) override;
|
||||
|
||||
/** @brief Get current connection status. */
|
||||
sdv::ipc::EConnectStatus GetStatus() const override;
|
||||
|
||||
/** @brief Destroy object (IObjectDestroy). */
|
||||
void DestroyObject() override;
|
||||
|
||||
/** @brief Set status and notify listeners (callback-safe). */
|
||||
void SetStatus(sdv::ipc::EConnectStatus eStatus);
|
||||
|
||||
/** @brief @return true if this side is server (needs accept()), false otherwise. */
|
||||
bool IsServer() const;
|
||||
|
||||
// ---------- Protocol types ----------
|
||||
/** @brief SDV message type. */
|
||||
enum class EMsgType : uint32_t
|
||||
{
|
||||
sync_request = 0, ///< Sync request (version check; no payload).
|
||||
sync_answer = 1, ///< Sync answer (version check; no payload).
|
||||
connect_request = 10, ///< Connection initiation (SConnectMsg).
|
||||
connect_answer = 11, ///< Connection answer (SConnectMsg).
|
||||
connect_term = 90, ///< Connection terminated.
|
||||
data = 0x10000000,///< Data message.
|
||||
data_fragment = 0x10000001 ///< Data fragment if payload exceeds frame size.
|
||||
};
|
||||
|
||||
/** @brief SDV base message header. */
|
||||
struct SMsgHdr
|
||||
{
|
||||
uint32_t uiVersion; ///< Protocol version.
|
||||
EMsgType eType; ///< Message type.
|
||||
};
|
||||
|
||||
/** @brief Connection initiation message (extends SMsgHdr). */
|
||||
struct SConnectMsg : SMsgHdr
|
||||
{
|
||||
sdv::process::TProcessID tProcessID; ///< Process ID for lifetime monitoring.
|
||||
};
|
||||
|
||||
/** @brief Fragmented data message header. */
|
||||
struct SFragmentedMsgHdr : SMsgHdr
|
||||
{
|
||||
uint32_t uiTotalLength; ///< Total payload length across all fragments.
|
||||
uint32_t uiOffset; ///< Current byte offset into the total payload.
|
||||
};
|
||||
|
||||
// ---------- Minimal SDV message wrapper ----------
|
||||
class CMessage
|
||||
{
|
||||
public:
|
||||
explicit CMessage(std::vector<uint8_t>&& data) : m_Data(std::move(data)) {}
|
||||
|
||||
/** @return Pointer to message bytes (may be null if empty). */
|
||||
const uint8_t* GetData() const { return m_Data.empty() ? nullptr : m_Data.data(); }
|
||||
|
||||
/** @return Message size in bytes. */
|
||||
uint32_t GetSize() const { return static_cast<uint32_t>(m_Data.size()); }
|
||||
|
||||
/** @return Interpreted SDV base header (or a fallback). */
|
||||
SMsgHdr GetMsgHdr() const
|
||||
{
|
||||
if (GetSize() < sizeof(SMsgHdr)) return SMsgHdr{0, EMsgType::connect_term};
|
||||
return *reinterpret_cast<const SMsgHdr*>(GetData());
|
||||
}
|
||||
|
||||
/** @return SDV connect header (or default if undersized). */
|
||||
SConnectMsg GetConnectHdr() const
|
||||
{
|
||||
if (GetSize() < sizeof(SConnectMsg)) return SConnectMsg{};
|
||||
return *reinterpret_cast<const SConnectMsg*>(GetData());
|
||||
}
|
||||
|
||||
/** @return SDV fragmented header (or default if undersized). */
|
||||
SFragmentedMsgHdr GetFragmentedHdr() const
|
||||
{
|
||||
if (GetSize() < sizeof(SFragmentedMsgHdr)) return SFragmentedMsgHdr{};
|
||||
return *reinterpret_cast<const SFragmentedMsgHdr*>(GetData());
|
||||
}
|
||||
|
||||
/** @return true if the SDV envelope is well-formed. */
|
||||
bool IsValid() const
|
||||
{
|
||||
if (!GetData() || GetSize() < sizeof(SMsgHdr)) return false;
|
||||
SMsgHdr hdr = GetMsgHdr();
|
||||
switch (hdr.eType)
|
||||
{
|
||||
case EMsgType::sync_request:
|
||||
case EMsgType::sync_answer:
|
||||
case EMsgType::connect_term:
|
||||
case EMsgType::data:
|
||||
return true;
|
||||
case EMsgType::connect_request:
|
||||
case EMsgType::connect_answer:
|
||||
return GetSize() >= sizeof(SConnectMsg);
|
||||
case EMsgType::data_fragment:
|
||||
return GetSize() >= sizeof(SFragmentedMsgHdr);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> m_Data;
|
||||
};
|
||||
|
||||
/** @brief Receive-time data reassembly context. */
|
||||
struct SDataContext
|
||||
{
|
||||
uint32_t uiTotalSize = 0; ///< Total payload size (without SDV header).
|
||||
uint32_t uiCurrentOffset = 0; ///< Current filled byte count.
|
||||
size_t nChunkIndex = 0; ///< Current chunk index being filled.
|
||||
uint32_t uiChunkOffset = 0; ///< Offset within the current chunk.
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqDataChunks; ///< Output buffers.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Event callback structure.
|
||||
*/
|
||||
struct SEventCallback
|
||||
{
|
||||
uint64_t uiCookie = 0; ///< Registration cookie
|
||||
sdv::ipc::IConnectEventCallback* pCallback = nullptr; ///< Pointer to the callback. Could be NULL when the callback
|
||||
///< was deleted.
|
||||
};
|
||||
|
||||
/** @brief UDS transport maximum frame size (safety cap). */
|
||||
static constexpr uint32_t kMaxUdsPacketSize = 64u * 1024u * 1024u; // 64 MiB
|
||||
|
||||
// ---------- Transport helpers ----------
|
||||
/**
|
||||
* @brief Accept incoming client (server side).
|
||||
* @return Returns a connected socket FD or -1 on error.
|
||||
* @deprecated The poll-based accept loop in ConnectWorker() is used instead.
|
||||
*/
|
||||
int AcceptConnection();
|
||||
|
||||
/**
|
||||
* @brief Read exactly 'bufferLength' bytes from m_Fd into 'buffer' (blocking).
|
||||
* @return true on success; false on EOF/error.
|
||||
*/
|
||||
bool ReadNumberOfBytes(char* buffer, uint32_t bufferLength);
|
||||
|
||||
/**
|
||||
* @brief Read UDS transport header (size prefix) into packetSize.
|
||||
* @param[out] packetSize SDV message size to follow.
|
||||
* @return true if the header was read and valid; false otherwise.
|
||||
*/
|
||||
bool ReadTransportHeader(uint32_t& packetSize);
|
||||
|
||||
/**
|
||||
* @brief Send an SDV message as a UDS frame: [packetSize][payload].
|
||||
* @param pData Pointer to serialized SDV payload.
|
||||
* @param uiDataLength Payload size.
|
||||
* @return true if fully sent; false otherwise.
|
||||
*/
|
||||
bool SendSizedPacket(const void* pData, uint32_t uiDataLength);
|
||||
|
||||
/**
|
||||
* @brief Receive loop: read UDS frames and dispatch to the SDV state machine.
|
||||
*/
|
||||
void ReceiveMessages();
|
||||
|
||||
/**
|
||||
* @brief Handle an incoming sync_request message.
|
||||
*
|
||||
* Sent by the peer during the initial handshake.
|
||||
* The server replies with sync_answer.
|
||||
*
|
||||
* @param message SDV envelope containing the sync_request header.
|
||||
*/
|
||||
void ReceiveSyncRequest(const CMessage& message);
|
||||
|
||||
/**
|
||||
* @brief Handle an incoming sync_answer message.
|
||||
*
|
||||
* Received by the client after sending sync_request.
|
||||
* Triggers the next handshake step: the client sends connect_request.
|
||||
*
|
||||
* @param message SDV envelope containing the sync_answer header.
|
||||
*/
|
||||
void ReceiveSyncAnswer(const CMessage& message);
|
||||
|
||||
/**
|
||||
* @brief Handle an incoming connect_request message.
|
||||
*
|
||||
* Sent by the peer to request establishment of a logical SDV connection.
|
||||
* The server replies with connect_answer.
|
||||
*
|
||||
* @param message SDV envelope containing the connect_request header.
|
||||
*/
|
||||
void ReceiveConnectRequest(const CMessage& message);
|
||||
|
||||
/**
|
||||
* @brief Handle an incoming connect_answer message.
|
||||
*
|
||||
* Sent by the peer after receiving our connect_request.
|
||||
* Marks completion of the SDV handshake on the client side.
|
||||
*
|
||||
* @param message SDV envelope containing the connect_answer header.
|
||||
*/
|
||||
void ReceiveConnectAnswer(const CMessage& message);
|
||||
|
||||
/**
|
||||
* @brief Handle an incoming connect_term message.
|
||||
*
|
||||
* Indicates that the peer requests immediate termination of the connection.
|
||||
* Sets status to disconnected and stops the RX loop.
|
||||
*
|
||||
* @param message SDV envelope containing the connect_term header.
|
||||
*/
|
||||
void ReceiveConnectTerm(const CMessage& message);
|
||||
|
||||
/**
|
||||
* @brief Handle a non-fragmented SDV data message.
|
||||
*
|
||||
* Format:
|
||||
* [SMsgHdr][LengthTable][DataChunks...]
|
||||
* The function decodes the table, allocates chunk buffers,
|
||||
* copies the full payload, and dispatches it via IDataReceiveCallback.
|
||||
*
|
||||
* @param rMessage SDV envelope containing the full data frame.
|
||||
* @param rsDataCtxt Reassembly context used to extract chunks.
|
||||
*/
|
||||
void ReceiveDataMessage(const CMessage& rMessage, SDataContext& rsDataCtxt);
|
||||
|
||||
/**
|
||||
* @brief Handle a fragmented SDV data message.
|
||||
*
|
||||
* Format:
|
||||
* [SFragmentedMsgHdr][(LengthTable only in first fragment)][PayloadSlice]
|
||||
*
|
||||
* This function appends each slice into the appropriate chunk buffer.
|
||||
* When the final fragment is received and all chunks are complete,
|
||||
* the assembled data is dispatched via IDataReceiveCallback.
|
||||
*
|
||||
* @param rMessage SDV envelope containing the current fragment.
|
||||
* @param rsDataCtxt Reassembly context shared across fragments.
|
||||
*/
|
||||
void ReceiveDataFragmentMessage(const CMessage& rMessage, SDataContext& rsDataCtxt);
|
||||
|
||||
/**
|
||||
* @brief Read the data size table (buffer count + each buffer size).
|
||||
* @param rMessage SDV message containing the table.
|
||||
* @param rsDataCtxt Output reassembly context; buffers are allocated.
|
||||
* @return Offset within message data where the payload begins; 0 on error.
|
||||
*/
|
||||
uint32_t ReadDataTable(const CMessage& rMessage, SDataContext& rsDataCtxt);
|
||||
|
||||
/**
|
||||
* @brief Copy payload bytes from 'rMessage' into the buffers allocated by ReadDataTable.
|
||||
* Multiple calls may be required for fragmented messages.
|
||||
* @param rMessage SDV message holding the current data/fragment.
|
||||
* @param uiOffset Offset within 'rMessage' where payload starts in this frame.
|
||||
* @param rsDataCtxt Reassembly context to be filled; callback fires when complete.
|
||||
* @return true if progress was made / completion achieved, false if invalid.
|
||||
*/
|
||||
bool ReadDataChunk(const CMessage& rMessage, uint32_t uiOffset, SDataContext& rsDataCtxt);
|
||||
|
||||
// ---------- Internal threading ----------
|
||||
/** @brief Connect worker (server accept loop or client connect retry). */
|
||||
void ConnectWorker();
|
||||
/** @brief Start RX thread (precondition: status=connected, FD valid). */
|
||||
void StartReceiveThread_Unsafe();
|
||||
/**
|
||||
* @brief Stop workers and close sockets, then optionally unlink path.
|
||||
* @param unlinkPath If true and server, unlink the socket path on exit.
|
||||
*/
|
||||
void StopThreadsAndCloseSockets(bool unlinkPath);
|
||||
|
||||
private:
|
||||
//Transport state
|
||||
int m_Fd { -1 }; ///< Active connection FD.
|
||||
int m_ListenFd { -1 }; ///< Server-side listening FD.
|
||||
bool m_AcceptConnectionRequired { false };
|
||||
std::string m_UdsPath;
|
||||
|
||||
//Threads & control
|
||||
std::atomic<bool> m_StopReceiveThread { false };
|
||||
std::atomic<bool> m_StopConnectThread { false };
|
||||
std::thread m_ReceiveThread;
|
||||
std::thread m_ConnectThread;
|
||||
|
||||
//Status & synchronization
|
||||
std::condition_variable m_StateCv;
|
||||
std::atomic<sdv::ipc::EConnectStatus> m_eStatus { sdv::ipc::EConnectStatus::uninitialized };
|
||||
sdv::ipc::IDataReceiveCallback* m_pReceiver { nullptr };
|
||||
sdv::ipc::IConnectEventCallback* m_pEvent { nullptr };
|
||||
std::mutex m_MtxConnect;
|
||||
std::condition_variable m_CvConnect;
|
||||
std::mutex m_StateMtx; ///< Protects receiver/event assignment.
|
||||
std::list<SEventCallback> m_lstEventCallbacks; ///< List containing event callbacks
|
||||
std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list
|
||||
|
||||
//TX synchronization
|
||||
std::mutex m_SendMtx;
|
||||
};
|
||||
|
||||
#endif // CONNECTION_H
|
||||
#endif // defined(__unix__)
|
||||
Reference in New Issue
Block a user