tunnel component & update vehicle abstraction example (#8)

This commit is contained in:
tompzf
2026-04-02 17:37:00 +02:00
committed by GitHub
parent 6ed5fdb951
commit 07cf4f654b
94 changed files with 9268 additions and 830 deletions

3
.gitignore vendored
View File

@@ -19,7 +19,7 @@ DoxygenWarning.txt
packages/ packages/
windows_vapi/.vs windows_vapi/.vs
#idl & dbc #idl & dbc & vss
export/interfaces/*.h export/interfaces/*.h
export/interfaces/ps/ export/interfaces/ps/
export/interfaces/serdes/ export/interfaces/serdes/
@@ -27,6 +27,7 @@ export/interfaces/serdes/
*/*/*/generated/ */*/*/generated/
*/*/generated/ */*/generated/
*/generated/ */generated/
*/*/generated2/*
#CMake intermediate files and folders #CMake intermediate files and folders

View File

@@ -118,3 +118,4 @@ add_subdirectory(system_demo_example)
add_subdirectory(door_demo_example) add_subdirectory(door_demo_example)
add_subdirectory(auto_headlamp_example) add_subdirectory(auto_headlamp_example)
add_subdirectory(open_trunk_example) add_subdirectory(open_trunk_example)
add_subdirectory(vehicle_abstraction_example)

View File

@@ -65,7 +65,7 @@ execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/autoheadlig
# REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following # REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following
# below are the build steps to build the components that were generated. # below are the build steps to build the components that were generated.
message("Include: auto headlight proxy/stub for vehicle devices and basic services") message("Include: auto headlight proxy/stub for basic services")
include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files) include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files)
add_subdirectory(generated/vss_files/ps) add_subdirectory(generated/vss_files/ps)
@@ -107,7 +107,6 @@ add_executable(auto_headlight_app
autoheadlight_app/autoheadlight_simulate.cpp autoheadlight_app/autoheadlight_simulate.cpp
autoheadlight_app/autoheadlight_console.h autoheadlight_app/autoheadlight_console.h
autoheadlight_app/autoheadlight_console.cpp autoheadlight_app/autoheadlight_console.cpp
autoheadlight_app/signal_names.h
) )
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")

View File

@@ -12,7 +12,7 @@
#include <fstream> #include <fstream>
#include <support/app_control.h> #include <support/app_control.h>
#include <support/signal_support.h> #include <support/signal_support.h>
#include "signal_names.h" #include "../generated/vss_files/signal_identifier.h"
#include "autoheadlight_simulate.h" #include "autoheadlight_simulate.h"
#include "autoheadlight_console.h" #include "autoheadlight_console.h"

View File

@@ -108,14 +108,14 @@ void CConsole::PrintHeader()
bool CConsole::PrepareDataConsumers() bool CConsole::PrepareDataConsumers()
{ {
// Vehicle Device // Vehicle Device
auto pVDCurrentLatitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLatitude_Device").GetInterface<vss::Vehicle::Position::CurrentLatitudeDevice::IVSS_CurrentLatitude>(); auto pVDCurrentLatitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLatitude_Device").GetInterface<vss::Vehicle::Position::CurrentLatitudeDevice::IVSS_ReadCurrentLatitude>();
if (!pVDCurrentLatitudeSvc) if (!pVDCurrentLatitudeSvc)
{ {
SDV_LOG_ERROR("Could not get interface 'IVSS_GetVDCurrentLatitude': [CAutoHeadlightService]"); SDV_LOG_ERROR("Could not get interface 'IVSS_GetVDCurrentLatitude': [CAutoHeadlightService]");
return false; return false;
} }
auto pVDCurrentLongitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLongitude_Device").GetInterface<vss::Vehicle::Position::CurrentLongitudeDevice::IVSS_CurrentLongitude>(); auto pVDCurrentLongitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLongitude_Device").GetInterface<vss::Vehicle::Position::CurrentLongitudeDevice::IVSS_ReadCurrentLongitude>();
if (!pVDCurrentLongitudeSvc) if (!pVDCurrentLongitudeSvc)
{ {
SDV_LOG_ERROR("Could not get interface 'IVSS_GetVDCurrentLongitude': [CAutoHeadlightService]"); SDV_LOG_ERROR("Could not get interface 'IVSS_GetVDCurrentLongitude': [CAutoHeadlightService]");
@@ -212,11 +212,11 @@ void CConsole::ResetSignals()
SetCursorPos(g_sCursor); SetCursorPos(g_sCursor);
// Vehicle Device // Vehicle Device
auto pVDCurrentLatitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLatitude_Device").GetInterface<vss::Vehicle::Position::CurrentLatitudeDevice::IVSS_CurrentLatitude>(); auto pVDCurrentLatitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLatitude_Device").GetInterface<vss::Vehicle::Position::CurrentLatitudeDevice::IVSS_ReadCurrentLatitude>();
if (pVDCurrentLatitudeSvc) if (pVDCurrentLatitudeSvc)
pVDCurrentLatitudeSvc->UnregisterCurrentLatitudeEvent(static_cast<vss::Vehicle::Position::CurrentLatitudeDevice::IVSS_WriteCurrentLatitude_Event*> (this)); pVDCurrentLatitudeSvc->UnregisterCurrentLatitudeEvent(static_cast<vss::Vehicle::Position::CurrentLatitudeDevice::IVSS_WriteCurrentLatitude_Event*> (this));
auto pVDCurrentLongitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLongitude_Device").GetInterface<vss::Vehicle::Position::CurrentLongitudeDevice::IVSS_CurrentLongitude>(); auto pVDCurrentLongitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLongitude_Device").GetInterface<vss::Vehicle::Position::CurrentLongitudeDevice::IVSS_ReadCurrentLongitude>();
if (pVDCurrentLongitudeSvc) if (pVDCurrentLongitudeSvc)
pVDCurrentLongitudeSvc->UnregisterCurrentLongitudeEvent(static_cast<vss::Vehicle::Position::CurrentLongitudeDevice::IVSS_WriteCurrentLongitude_Event*> (this)); pVDCurrentLongitudeSvc->UnregisterCurrentLongitudeEvent(static_cast<vss::Vehicle::Position::CurrentLongitudeDevice::IVSS_WriteCurrentLongitude_Event*> (this));

View File

@@ -19,7 +19,7 @@
#include <support/component_impl.h> #include <support/component_impl.h>
#include <support/timer.h> #include <support/timer.h>
#include <atomic> #include <atomic>
#include "signal_names.h" #include "../generated/vss_files/signal_identifier.h"
#include <fcntl.h> #include <fcntl.h>
#include "vss_vehiclepositioncurrentlatitude_vd_rx.h" #include "vss_vehiclepositioncurrentlatitude_vd_rx.h"
#include "vss_vehiclepositioncurrentlongitude_vd_rx.h" #include "vss_vehiclepositioncurrentlongitude_vd_rx.h"
@@ -187,7 +187,7 @@ private:
* @param[in] sPos The location to print the value at. * @param[in] sPos The location to print the value at.
* @param[in] rssName Reference to the value. * @param[in] rssName Reference to the value.
* @param[in] tValue The value. * @param[in] tValue The value.
* @param[in] rssStatus Status, becuse we have signals of type bool * @param[in] rssStatus Status, because we have signals of type bool
*/ */
template <typename TValue> template <typename TValue>
void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus); void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus);
@@ -204,7 +204,6 @@ private:
std::thread m_threadReadTxSignals; ///< Simulation datalink thread. std::thread m_threadReadTxSignals; ///< Simulation datalink thread.
bool m_bThreadStarted = false; ///< Set when initialized. bool m_bThreadStarted = false; ///< Set when initialized.
std::atomic_bool m_bRunning = false; ///< When set, the application is running. std::atomic_bool m_bRunning = false; ///< When set, the application is running.
mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message
sdv::core::CSignal m_signalCurrentLatitude; ///< Signal Current latitude sdv::core::CSignal m_signalCurrentLatitude; ///< Signal Current latitude
sdv::core::CSignal m_signalCurrentLongitude; ///< Signal Current longitude sdv::core::CSignal m_signalCurrentLongitude; ///< Signal Current longitude
@@ -244,7 +243,7 @@ inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, T
std::string(nValueNameLen - std::min(rssName.size(), static_cast<size_t>(nValueNameLen - 1)) - 1, '.') << std::string(nValueNameLen - std::min(rssName.size(), static_cast<size_t>(nValueNameLen - 1)) - 1, '.') <<
" " << std::fixed << std::setprecision(6) << tValue << " " << rssUnits << " "; " " << std::fixed << std::setprecision(6) << tValue << " " << rssUnits << " ";
std::lock_guard<std::mutex> lock(m_mPrintToConsole); std::lock_guard<std::mutex> lock(m_mtxPrintToConsole);
SetCursorPos(sPos); SetCursorPos(sPos);
std::cout << sstreamValueText.str(); std::cout << sstreamValueText.str();
} }

View File

@@ -15,7 +15,7 @@
#include <support/app_control.h> #include <support/app_control.h>
#include <support/component_impl.h> #include <support/component_impl.h>
#include <support/timer.h> #include <support/timer.h>
#include "signal_names.h" #include "../generated/vss_files/signal_identifier.h"
// VSS interfaces - located in ../generated/vss_files/include // VSS interfaces - located in ../generated/vss_files/include
#include "vss_vehiclepositioncurrentlatitude_bs_rx.h" #include "vss_vehiclepositioncurrentlatitude_bs_rx.h"

View File

@@ -1,35 +0,0 @@
/********************************************************************************
* 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
********************************************************************************/
/**
* namespace for the signal names
* in case /generated/vss_files/signal_identifier.h
* exists, use the file, otherwise define the namespace
*/
#ifndef SIGNAL_NAMES_H
#define SIGNAL_NAMES_H
#ifdef __has_include
#if __has_include("../generated/vss_files/signal_identifier.h")
#include "../generated/vss_files/signal_identifier.h"
#else
namespace headlight
{
// Data Dispatch Service signal names to dbc variable names C-type RX/TX vss name space
static std::string dsFCurrentLatitude = "CAN_Input.Current_Latitude" ; ///< float RX Vehicle.Position.CurrentLatitude
static std::string dsFCurrentLongitude = "CAN_Input.Current_Longitude" ; ///< float RX Vehicle.Position.CurrentLongitude
static std::string dsBHeadlightLowBeam = "CAN_Output.HeadLight_LowBeam"; ///< bool TX Vehicle.Body.Light.Front.Lowbeam
} // headlight
#endif
#endif
#endif // ! defined SIGNAL_NAMES_H

View File

@@ -132,7 +132,6 @@ add_executable(door_demo_example
"door_app/door_application.cpp" "door_app/door_application.cpp"
"door_app/include/console.h" "door_app/include/console.h"
"door_app/console.cpp" "door_app/console.cpp"
"door_app/include/signal_names.h"
"door_service/lock_doors_thread.h") "door_service/lock_doors_thread.h")
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@@ -156,7 +155,6 @@ add_executable(door_external_app
"door_app/door_extern_application.cpp" "door_app/door_extern_application.cpp"
"door_app/include/console.h" "door_app/include/console.h"
"door_app/console.cpp" "door_app/console.cpp"
"door_app/include/signal_names.h"
"door_service/lock_doors_thread.h") "door_service/lock_doors_thread.h")
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")

View File

@@ -144,7 +144,7 @@ bool CConsole::PrepareDataConsumers()
auto basicServiceL1 = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Service").GetInterface<vss::Vehicle::Body::Door::Axle::_01::LeftService::IVSS_GetIsOpenL1>(); auto basicServiceL1 = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Service").GetInterface<vss::Vehicle::Body::Door::Axle::_01::LeftService::IVSS_GetIsOpenL1>();
if (!basicServiceL1) if (!basicServiceL1)
{ {
SDV_LOG_ERROR("Could not get interface 'LeftService::IVSS_IsOpen': [CConsole]"); SDV_LOG_ERROR("Could not get interface 'LeftService::IVSS_ReadIsOpen': [CConsole]");
return false; return false;
} }
else else
@@ -207,7 +207,7 @@ void CConsole::ResetSignals()
// Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional // Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional
auto vehicleDevice = auto vehicleDevice =
sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface < vss::Vehicle::Body::Door::Axle::_01 ::LeftDevice::IVSS_IsOpen > (); sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface < vss::Vehicle::Body::Door::Axle::_01 ::LeftDevice::IVSS_ReadIsOpen > ();
if (vehicleDevice) if (vehicleDevice)
vehicleDevice->UnregisterIsOpenEvent(dynamic_cast<vss::Vehicle::Body::Door::Axle::_01::LeftDevice::IVSS_WriteIsOpen_Event*> (this)); vehicleDevice->UnregisterIsOpenEvent(dynamic_cast<vss::Vehicle::Body::Door::Axle::_01::LeftDevice::IVSS_WriteIsOpen_Event*> (this));
@@ -227,7 +227,7 @@ void CConsole::ResetSignals()
if (basicServiceR2) if (basicServiceR2)
basicServiceR2->UnregisterOnSignalChangeOfRightDoorIsOpen02(dynamic_cast<vss::Vehicle::Body::Door::Axle::_02::RightService::IVSS_SetIsOpenR2_Event*> (this)); basicServiceR2->UnregisterOnSignalChangeOfRightDoorIsOpen02(dynamic_cast<vss::Vehicle::Body::Door::Axle::_02::RightService::IVSS_SetIsOpenR2_Event*> (this));
// Unregister the data link signalss // Unregister the data link signals
if (m_SignalFrontLeftDoorIsOpen) if (m_SignalFrontLeftDoorIsOpen)
m_SignalFrontLeftDoorIsOpen.Reset(); m_SignalFrontLeftDoorIsOpen.Reset();
if (m_SignalFrontRightDoorIsOpen) if (m_SignalFrontRightDoorIsOpen)
@@ -338,10 +338,10 @@ bool CConsole::PrepareDataConsumersForStandAlone()
} }
// Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional // Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional
auto vehicleDevice = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface<vss::Vehicle::Body::Door::Axle::_01::LeftDevice::IVSS_IsOpen>(); auto vehicleDevice = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface<vss::Vehicle::Body::Door::Axle::_01::LeftDevice::IVSS_ReadIsOpen>();
if (!vehicleDevice) if (!vehicleDevice)
{ {
SDV_LOG_ERROR("Could not get interface 'LeftDevice::IVSS_IsOpen': [CConsole]"); SDV_LOG_ERROR("Could not get interface 'LeftDevice::IVSS_ReadIsOpen': [CConsole]");
return false; return false;
} }
vehicleDevice->RegisterIsOpenEvent(dynamic_cast<vss::Vehicle::Body::Door::Axle::_01::LeftDevice::IVSS_WriteIsOpen_Event*> (this)); vehicleDevice->RegisterIsOpenEvent(dynamic_cast<vss::Vehicle::Body::Door::Axle::_01::LeftDevice::IVSS_WriteIsOpen_Event*> (this));

View File

@@ -9,7 +9,7 @@
********************************************************************************/ ********************************************************************************/
#include "../door_app/include/door_application.h" #include "../door_app/include/door_application.h"
#include "../door_app/include/signal_names.h" #include "../generated/vss_files/signal_identifier.h"
#ifdef _WIN32 #ifdef _WIN32
#include <conio.h> // Needed for _kbhit #include <conio.h> // Needed for _kbhit

View File

@@ -9,7 +9,7 @@
********************************************************************************/ ********************************************************************************/
#include "../door_app/include/door_extern_application.h" #include "../door_app/include/door_extern_application.h"
#include "../door_app/include/signal_names.h" #include "../generated/vss_files/signal_identifier.h"
#ifdef _WIN32 #ifdef _WIN32
#include <conio.h> // Needed for _kbhit #include <conio.h> // Needed for _kbhit

View File

@@ -19,7 +19,7 @@
#include <support/app_control.h> #include <support/app_control.h>
#include <support/component_impl.h> #include <support/component_impl.h>
#include <support/timer.h> #include <support/timer.h>
#include "signal_names.h" #include "../../generated/vss_files/signal_identifier.h"
#include <fcntl.h> #include <fcntl.h>
#include <atomic> #include <atomic>
@@ -202,7 +202,7 @@ private:
* @param[in] sPos The location to print the value at. * @param[in] sPos The location to print the value at.
* @param[in] rssName Reference to the value. * @param[in] rssName Reference to the value.
* @param[in] tValue The value. * @param[in] tValue The value.
* @param[in] rssStatus Status, becuse we have signals of type bool * @param[in] rssStatus Status, because we have signals of type bool
*/ */
template <typename TValue> template <typename TValue>
void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus); void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus);
@@ -219,7 +219,6 @@ private:
bool m_bThreadStarted = false; ///< Set when initialized. bool m_bThreadStarted = false; ///< Set when initialized.
std::atomic_bool m_bRunning = false; ///< When set, the application is running. std::atomic_bool m_bRunning = false; ///< When set, the application is running.
bool m_isExternalApp = false; ///< True when we have an external application bool m_isExternalApp = false; ///< True when we have an external application
mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message
std::thread m_threadReadTxSignals; ///< Simulation datalink thread. std::thread m_threadReadTxSignals; ///< Simulation datalink thread.
@@ -269,7 +268,7 @@ inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, T
" " << std::fixed << std::setprecision(2) << tValue << " " << rssUnits << " " << std::fixed << std::setprecision(2) << tValue << " " << rssUnits <<
std::string(nEndNameLen - std::min(endName.size(), static_cast<size_t>(nEndNameLen - 1)) - 1, ' '); std::string(nEndNameLen - std::min(endName.size(), static_cast<size_t>(nEndNameLen - 1)) - 1, ' ');
std::lock_guard<std::mutex> lock(m_mPrintToConsole); std::lock_guard<std::mutex> lock(m_mtxPrintToConsole);
SetCursorPos(sPos); SetCursorPos(sPos);
std::cout << sstreamValueText.str(); std::cout << sstreamValueText.str();
} }

View File

@@ -64,7 +64,7 @@ private:
bool IsSDVFrameworkEnvironmentSet(); bool IsSDVFrameworkEnvironmentSet();
/** /**
* @brief Loac config file and register vehicle device and basic service. * @brief Load config file and register vehicle device and basic service.
* @remarks It is expected that each config file has the complete door: * @remarks It is expected that each config file has the complete door:
* vehicle device & basic service for input and output * vehicle device & basic service for input and output
* @param[in] inputMsg message string to be printed on console in case of success and failure * @param[in] inputMsg message string to be printed on console in case of success and failure

View File

@@ -21,12 +21,11 @@ public:
/** /**
* @brief Start and initialize the application control and load vehicle devices and * @brief Start and initialize the application control and load vehicle devices and
* basic services depending on the numerb of doors * basic services depending on the number of doors
* @return Return true on success otherwise false * @return Return true on success otherwise false
*/ */
bool Initialize(); bool Initialize();
/** /**
* @brief Run loop as long as user input does not exit * @brief Run loop as long as user input does not exit
* Allow user to open/close each door. * Allow user to open/close each door.

View File

@@ -1,33 +0,0 @@
/********************************************************************************
* 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
********************************************************************************/
/**
* namespace for the signal names
* in case /interfaces/signal_identifier.h
* exists, use the file, otherwise define the namespace
*/
#ifndef SIGNAL_NAMES_H
#define SIGNAL_NAMES_H
namespace doors
{
// Data Dispatch Service signal names to dbc variable names C-type RX/TX vss name space
static std::string dsLeftDoorIsOpen01 = "CAN_Input_L1.Door01LeftIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._01.Left
static std::string dsRightDoorIsOpen01 = "CAN_Input_R1.Door01RightIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._01.Right
static std::string dsLeftDoorIsOpen02 = "CAN_Input_L2.Door02LeftIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._02.Left
static std::string dsRightDoorIsOpen02 = "CAN_Input_R2.Door02RightIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._02.Right
static std::string dsLeftLatch01 = "CAN_Output.LockDoor01Left"; ///< bool TX Vehicle.Body.Door.Axle._01.Left
static std::string dsRightLatch01 = "CAN_Output.LockDoor01Right"; ///< bool TX Vehicle.Body.Door.Axle._01.Right
static std::string dsLeftLatch02 = "CAN_Output.LockDoor02Left"; ///< bool TX Vehicle.Body.Door.Axle._02.Left
static std::string dsRightLatch02 = "CAN_Output.LockDoor02Right"; ///< bool TX Vehicle.Body.Door.Axle._02.Right
} // doors
#endif // SIGNAL_NAMES_H

View File

@@ -30,7 +30,7 @@ include_directories(${SDV_FRAMEWORK_DEV_INCLUDE})
# services are generated during the configuration phase of CMake. This is necessary, since CMakeFiles.txt files are generated and # services are generated during the configuration phase of CMake. This is necessary, since CMakeFiles.txt files are generated and
# they have to be available during the configuration phase of CMake to be taken into the build process. Requisite for the code # they have to be available during the configuration phase of CMake to be taken into the build process. Requisite for the code
# generation during the configuration time of CMake is the availability of the tools to do the generation. Hence the tools cannot be # generation during the configuration time of CMake is the availability of the tools to do the generation. Hence the tools cannot be
# created during the build process, which is executed after the configuraiton process. # created during the build process, which is executed after the configuration process.
# Execute sdv_vss_util to create IDL files for devices and basic services. # Execute sdv_vss_util to create IDL files for devices and basic services.
message("Create interface code for devices and basic services of open trunk example.") message("Create interface code for devices and basic services of open trunk example.")
@@ -46,7 +46,7 @@ execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/v
# We need proxies and stubs for basic services to give access to the interfaces for complex services ans applications: --ps_lib_namedemo_proxystub # We need proxies and stubs for basic services to give access to the interfaces for complex services and applications: --ps_lib_namedemo_proxystub
message("Compiling vss_vehiclebodytrunk_bs_tx.idl") message("Compiling vss_vehiclebodytrunk_bs_tx.idl")
execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodytrunk_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_nametrunk_proxystub) execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodytrunk_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_nametrunk_proxystub)
message("Compiling vss_vehiclespeed_bs_rx.idl") message("Compiling vss_vehiclespeed_bs_rx.idl")
@@ -78,7 +78,7 @@ add_subdirectory(generated/can_dl)
# REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following # REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following
# below are the build steps to build the components that were generated. # below are the build steps to build the components that were generated.
message("Include: trunk proxy/stub for vehicle devices and basic services") message("Include: trunk proxy/stub for basic services")
include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files) include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files)
add_subdirectory(generated/vss_files/ps) add_subdirectory(generated/vss_files/ps)
@@ -116,7 +116,6 @@ add_executable(open_trunk_example
open_trunk_app/trunk_application.h open_trunk_app/trunk_application.h
open_trunk_app/console.cpp open_trunk_app/console.cpp
open_trunk_app/console.h open_trunk_app/console.h
open_trunk_app/signal_names.h
) )
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")

View File

@@ -34,8 +34,8 @@ const CConsole::SConsolePos g_sComment3{ 24, 1 };
const CConsole::SConsolePos g_sComment4{ 25, 1 }; const CConsole::SConsolePos g_sComment4{ 25, 1 };
const CConsole::SConsolePos g_sComment5{ 26, 1 }; const CConsole::SConsolePos g_sComment5{ 26, 1 };
const CConsole::SConsolePos g_sSeparator5{ 28, 1 }; const CConsole::SConsolePos g_sSeparator5{ 28, 1 };
const CConsole::SConsolePos g_sComplexServcie1{ 30, 1 }; const CConsole::SConsolePos g_sComplexService1{ 30, 1 };
const CConsole::SConsolePos g_sComplexServcie2{ 31, 1 }; const CConsole::SConsolePos g_sComplexService2{ 31, 1 };
const CConsole::SConsolePos g_sSeparator6{ 33, 1 }; const CConsole::SConsolePos g_sSeparator6{ 33, 1 };
const CConsole::SConsolePos g_sControlDescription{ 35, 1 }; const CConsole::SConsolePos g_sControlDescription{ 35, 1 };
const CConsole::SConsolePos g_sCursor{ 36, 1 }; const CConsole::SConsolePos g_sCursor{ 36, 1 };
@@ -111,12 +111,12 @@ void CConsole::PrintHeader(uint32_t uiInstance)
PrintText(g_sSeparator4, "============================================================================"); PrintText(g_sSeparator4, "============================================================================");
PrintText(g_sComment1, "The complex service which checks the speed of the vehicle can be seen"); PrintText(g_sComment1, "The complex service which checks the speed of the vehicle can be seen");
PrintText(g_sComment2, "as an ASIL A/B component and will block the call from QM."); PrintText(g_sComment2, "as an ASIL A/B component and will block the call from QM.");
PrintText(g_sComment3, "The extern apllication can be seen as a QM function."); PrintText(g_sComment3, "The extern application can be seen as a QM function.");
PrintText(g_sComment4, "If this example would run in a mixed critical environment the connection"); PrintText(g_sComment4, "If this example would run in a mixed critical environment the connection");
PrintText(g_sComment5, "from QM to basic service interface would be forbidden."); PrintText(g_sComment5, "from QM to basic service interface would be forbidden.");
PrintText(g_sSeparator5, "============================================================================"); PrintText(g_sSeparator5, "============================================================================");
PrintText(g_sComplexServcie1, "Basic Service Interface not available."); PrintText(g_sComplexService1, "Basic Service Interface not available.");
PrintText(g_sComplexServcie2, "Complex Service Interface not available."); PrintText(g_sComplexService2, "Complex Service Interface not available.");
PrintText(g_sSeparator6, "----------------------------------------------------------------------------"); PrintText(g_sSeparator6, "----------------------------------------------------------------------------");
PrintText(g_sControlDescription, "Press 'X' to quit; 'C' to clear screen, '1' or '2' to open trunk "); PrintText(g_sControlDescription, "Press 'X' to quit; 'C' to clear screen, '1' or '2' to open trunk ");
} }
@@ -130,7 +130,7 @@ bool CConsole::PrepareDataConsumers()
PrintValue(g_sDataLinkSpeed, "Vehicle Speed RX", m_SpeedDL, "m/s"); PrintValue(g_sDataLinkSpeed, "Vehicle Speed RX", m_SpeedDL, "m/s");
// Registrate for the vehicle device & basic service of the speed. // Registrate for the vehicle device & basic service of the speed.
auto vehicleDevice = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface<vss::Vehicle::SpeedDevice::IVSS_Speed>(); auto vehicleDevice = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface<vss::Vehicle::SpeedDevice::IVSS_ReadSpeed>();
if (vehicleDevice) if (vehicleDevice)
{ {
PrintValue(g_sVehicleDeviceSpeed, "Vehicle Speed RX", m_SpeedVD, "m/s"); PrintValue(g_sVehicleDeviceSpeed, "Vehicle Speed RX", m_SpeedVD, "m/s");
@@ -147,15 +147,15 @@ bool CConsole::PrepareDataConsumers()
// Request the basic service for opening the trunk. // Request the basic service for opening the trunk.
m_pTrunkSvc = sdv::core::GetObject("Vehicle.Body.Trunk_Service").GetInterface<vss::Vehicle::Body::TrunkService::IVSS_SetOpen>(); m_pTrunkSvc = sdv::core::GetObject("Vehicle.Body.Trunk_Service").GetInterface<vss::Vehicle::Body::TrunkService::IVSS_SetOpen>();
if (m_pTrunkSvc) if (m_pTrunkSvc)
PrintText(g_sComplexServcie1, "Basic Service available"); PrintText(g_sComplexService1, "Basic Service available");
else else
PrintText(g_sComplexServcie1, "Basic Service NOT available"); PrintText(g_sComplexService1, "Basic Service NOT available");
m_pTrunkComplexService = sdv::core::GetObject("Open Trunk Service").GetInterface<ITrunkKitService>(); m_pTrunkComplexService = sdv::core::GetObject("Open Trunk Service").GetInterface<ITrunkKitService>();
if (m_pTrunkComplexService) if (m_pTrunkComplexService)
PrintText(g_sComplexServcie2, "Complex Service available"); PrintText(g_sComplexService2, "Complex Service available");
else else
PrintText(g_sComplexServcie2, "Complex Service NOT available"); PrintText(g_sComplexService2, "Complex Service NOT available");
return true; return true;
} }
@@ -165,7 +165,7 @@ void CConsole::ResetSignals()
// Set the cursor position at the end // Set the cursor position at the end
SetCursorPos(g_sCursor); SetCursorPos(g_sCursor);
auto vehicleDevice = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface<vss::Vehicle::SpeedDevice::IVSS_Speed>(); auto vehicleDevice = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface<vss::Vehicle::SpeedDevice::IVSS_ReadSpeed>();
if (vehicleDevice) if (vehicleDevice)
vehicleDevice->UnregisterSpeedEvent(dynamic_cast<vss::Vehicle::SpeedDevice::IVSS_WriteSpeed_Event*> (this)); vehicleDevice->UnregisterSpeedEvent(dynamic_cast<vss::Vehicle::SpeedDevice::IVSS_WriteSpeed_Event*> (this));
@@ -173,7 +173,7 @@ void CConsole::ResetSignals()
if (basicService) if (basicService)
basicService->UnregisterOnSignalChangeOfVehicleSpeed(dynamic_cast<vss::Vehicle::SpeedService::IVSS_SetSpeed_Event*> (this)); basicService->UnregisterOnSignalChangeOfVehicleSpeed(dynamic_cast<vss::Vehicle::SpeedService::IVSS_SetSpeed_Event*> (this));
// Unregister the data link signalss // Unregister the data link signals
if (m_SignalSpeed) if (m_SignalSpeed)
m_SignalSpeed.Reset(); m_SignalSpeed.Reset();
} }
@@ -291,25 +291,25 @@ void CConsole::RunUntilBreak()
{ {
case 'c': case 'c':
case 'C': case 'C':
PrintText(g_sComplexServcie1, " "); PrintText(g_sComplexService1, " ");
PrintText(g_sComplexServcie2, " "); PrintText(g_sComplexService2, " ");
break; break;
case '1': case '1':
if (m_pTrunkSvc) if (m_pTrunkSvc)
{ {
if (m_pTrunkSvc->SetOpen(true)) if (m_pTrunkSvc->SetOpen(true))
PrintText(g_sComplexServcie1, "Open trunk via basic service - will not be available in Mixed-Critical mode!"); PrintText(g_sComplexService1, "Open trunk via basic service - will not be available in Mixed-Critical mode!");
else else
PrintText(g_sComplexServcie1, "Open trunk via basic service failed."); PrintText(g_sComplexService1, "Open trunk via basic service failed.");
} }
break; break;
case '2': case '2':
if (m_pTrunkComplexService) if (m_pTrunkComplexService)
{ {
if (m_pTrunkComplexService->PopTrunk()) if (m_pTrunkComplexService->PopTrunk())
PrintText(g_sComplexServcie2, "Safety open trunk via complex service."); PrintText(g_sComplexService2, "Safety open trunk via complex service.");
else else
PrintText(g_sComplexServcie2, "Safety open trunk via complex service failed, car is moving"); PrintText(g_sComplexService2, "Safety open trunk via complex service failed, car is moving");
} }
break; break;
case 'x': case 'x':

View File

@@ -18,7 +18,8 @@
#include <support/app_control.h> #include <support/app_control.h>
#include <support/component_impl.h> #include <support/component_impl.h>
#include <support/timer.h> #include <support/timer.h>
#include "signal_names.h" #include "../generated/vss_files/signal_identifier.h"
#include <fcntl.h> #include <fcntl.h>
#include "../generated/vss_files/vss_vehiclebodytrunk_bs_tx.h" #include "../generated/vss_files/vss_vehiclebodytrunk_bs_tx.h"
#include "../generated/vss_files/vss_vehiclespeed_bs_rx.h" #include "../generated/vss_files/vss_vehiclespeed_bs_rx.h"
@@ -139,7 +140,7 @@ private:
* @param[in] sPos The location to print the value at. * @param[in] sPos The location to print the value at.
* @param[in] rssName Reference to the value. * @param[in] rssName Reference to the value.
* @param[in] tValue The value. * @param[in] tValue The value.
* @param[in] rssStatus Status, becuse we have signals of type bool * @param[in] rssStatus Status, because we have signals of type bool
*/ */
template <typename TValue> template <typename TValue>
void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus); void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus);
@@ -154,7 +155,6 @@ private:
mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message
bool m_bRunning = false; ///< When set, the application is running. bool m_bRunning = false; ///< When set, the application is running.
mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message
sdv::core::CSignal m_SignalSpeed; ///< Speed sdv::core::CSignal m_SignalSpeed; ///< Speed
float m_SpeedDL = 0.0; ///< Speed Data Link float m_SpeedDL = 0.0; ///< Speed Data Link
@@ -186,7 +186,7 @@ inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, T
std::string(nValueNameLen - std::min(rssName.size(), static_cast<size_t>(nValueNameLen - 1)) - 1, '.') << std::string(nValueNameLen - std::min(rssName.size(), static_cast<size_t>(nValueNameLen - 1)) - 1, '.') <<
" " << std::fixed << std::setprecision(2) << tValue << " " << rssUnits << " "; " " << std::fixed << std::setprecision(2) << tValue << " " << rssUnits << " ";
std::lock_guard<std::mutex> lock(m_mPrintToConsole); std::lock_guard<std::mutex> lock(m_mtxPrintToConsole);
SetCursorPos(sPos); SetCursorPos(sPos);
std::cout << sstreamValueText.str(); std::cout << sstreamValueText.str();
} }

View File

@@ -1,35 +0,0 @@
/********************************************************************************
* 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
********************************************************************************/
/**
* namespace for the signal names
* in case /interfaces/signal_identifier.h
* exists, use the file, otherwise define the namespace
*/
#ifndef SIGNAL_NAMES_H
#define SIGNAL_NAMES_H
#ifdef __has_include
#if __has_include("../interfaces/signal_identifier.h")
#include "../interfaces/signal_identifier.h"
#else
namespace trunk
{
// Data Dispatch Service signal names to dbc variable names C-type RX/TX vss name space
static std::string dsVehicleSpeed = "CAN_Input.Speed"; ///< bool RX Vehicle.Speed
static std::string dsTrunk = "CAN_Output.OpenTrunk"; ///< bool TX Vehicle.Body.Trunk
} // trunk
#endif
#endif
#endif // SIGNAL_NAMES_H

View File

@@ -9,7 +9,7 @@
********************************************************************************/ ********************************************************************************/
#include "trunk_application.h" #include "trunk_application.h"
#include "signal_names.h" #include "../generated/vss_files/signal_identifier.h"
#ifdef _WIN32 #ifdef _WIN32
#include <conio.h> // Needed for _kbhit #include <conio.h> // Needed for _kbhit

View File

@@ -11,7 +11,6 @@
#include <string> #include <string>
#include <support/app_control.h> #include <support/app_control.h>
#include <support/signal_support.h> #include <support/signal_support.h>
#include "vss_vehiclespeed_bs_rx.h"
/** /**
@@ -47,7 +46,7 @@ private:
bool IsSDVFrameworkEnvironmentSet(); bool IsSDVFrameworkEnvironmentSet();
/** /**
* @brief Loac config file and register vehicle device and basic service. * @brief Load config file and register vehicle device and basic service.
* @param[in] inputMsg message string to be printed on console in case of success and failure * @param[in] inputMsg message string to be printed on console in case of success and failure
* @param[in] configFileName config toml file name * @param[in] configFileName config toml file name
* @return Return true on success otherwise false * @return Return true on success otherwise false

View File

@@ -30,7 +30,7 @@ include_directories(${SDV_FRAMEWORK_DEV_INCLUDE})
# services are generated during the configuration phase of CMake. This is necessary, since CMakeFiles.txt files are generated and # services are generated during the configuration phase of CMake. This is necessary, since CMakeFiles.txt files are generated and
# they have to be available during the configuration phase of CMake to be taken into the build process. Requisite for the code # they have to be available during the configuration phase of CMake to be taken into the build process. Requisite for the code
# generation during the configuration time of CMake is the availability of the tools to do the generation. Hence the tools cannot be # generation during the configuration time of CMake is the availability of the tools to do the generation. Hence the tools cannot be
# created during the build process, which is executed after the configuraiton process. # created during the build process, which is executed after the configuration process.
# Execute sdv_vss_util to create IDL files for devices and basic services. # Execute sdv_vss_util to create IDL files for devices and basic services.
message("Create interface code for devices and basic services of demo example.") message("Create interface code for devices and basic services of demo example.")
@@ -46,7 +46,7 @@ execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/v
message("Compiling vss_vehiclespeed_vd_rx.idl") message("Compiling vss_vehiclespeed_vd_rx.idl")
execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclespeed_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclespeed_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps)
# We need proxies and stubs for basic services to give access to the interfaces for complex services ans applications: --ps_lib_namedemo_proxystub # We need proxies and stubs for basic services to give access to the interfaces for complex services and applications: --ps_lib_namedemo_proxystub
message("Compiling vss_vehiclechassisrearaxlerowwheel_bs_tx.idl") message("Compiling vss_vehiclechassisrearaxlerowwheel_bs_tx.idl")
execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclechassisrearaxlerowwheel_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedemo_proxystub) execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclechassisrearaxlerowwheel_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedemo_proxystub)
message("Compiling vss_vehiclechassissteeringwheelangle_bs_rx.idl") message("Compiling vss_vehiclechassissteeringwheelangle_bs_rx.idl")
@@ -81,7 +81,7 @@ add_subdirectory(generated/can_dl)
# REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following # REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following
# below are the build steps to build the components that were generated. # below are the build steps to build the components that were generated.
message("Include: demo proxy/stub for vehicle devices and basic services") message("Include: demo proxy/stub for basic services")
include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files) include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files)
add_subdirectory(generated/vss_files/ps) add_subdirectory(generated/vss_files/ps)
@@ -120,7 +120,6 @@ set_target_properties(demo_complex_service PROPERTIES SUFFIX ".sdv")
# Define the executable # Define the executable
add_executable(system_extern_example add_executable(system_extern_example
example_app/system_extern_example.cpp example_app/system_extern_example.cpp
example_app/signal_names.h
) )
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@@ -145,7 +144,6 @@ add_executable(system_demo_example
example_app/control.cpp example_app/control.cpp
example_app/console.h example_app/console.h
example_app/console.cpp example_app/console.cpp
example_app/signal_names.h
) )
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")

View File

@@ -253,7 +253,7 @@ void CConsole::RunUntilBreak()
// Set the cursor position at the end // Set the cursor position at the end
SetCursorPos(g_sCursor); SetCursorPos(g_sCursor);
// Unregister the data link signalss // Unregister the data link signals
if (m_signalSteeringWheel) m_signalSteeringWheel.Reset(); if (m_signalSteeringWheel) m_signalSteeringWheel.Reset();
if (m_signalSpeed) m_signalSpeed.Reset(); if (m_signalSpeed) m_signalSpeed.Reset();
if (m_signalRearAxleAngle) m_signalRearAxleAngle.Reset(); if (m_signalRearAxleAngle) m_signalRearAxleAngle.Reset();

View File

@@ -18,7 +18,7 @@
#include <support/app_control.h> #include <support/app_control.h>
#include <support/component_impl.h> #include <support/component_impl.h>
#include <support/timer.h> #include <support/timer.h>
#include "signal_names.h" #include "../generated/vss_files/signal_identifier.h"
#ifdef __unix__ #ifdef __unix__
#include <termios.h> // Needed for tcgetattr and fcntl #include <termios.h> // Needed for tcgetattr and fcntl

View File

@@ -29,7 +29,7 @@
#endif #endif
#endif #endif
#include "signal_names.h" #include "../generated/vss_files/signal_identifier.h"
/** /**
* @brief Utility handler for example demos * @brief Utility handler for example demos

View File

@@ -1,35 +0,0 @@
/********************************************************************************
* 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
********************************************************************************/
/**
* namespace for the signal names
* in case /generated/vss_files/signal_identifier.h
* exists, use the file, otherwise define the namespace
*/
#ifndef SIGNAL_NAMES_H
#define SIGNAL_NAMES_H
#ifdef __has_include
#if __has_include("../generated/vss_files/signal_identifier.h")
#include "../generated/vss_files/signal_identifier.h"
#else
namespace demo
{
static std::string dsWheelAngle = "CAN_Input.SteeringWheel"; ///< float RX Vehicle.Chassis.SteeringWheel.Angle
static std::string dsVehicleSpeed = "CAN_Input.Speed"; ///< float RX Vehicle.Speed
static std::string dsAxleAngle = "CAN_Output.RearAngle"; ///< float TX Vehicle.Chassis.RearAxle.Row.Wheel
static std::string dsLiveCounter = "CAN_Output.IsActiveCounter"; ///< uint8_t TX Vehicle.Software.Application.IsActiveCounter
} // demo
#endif
#endif
#endif // ! defined SIGNAL_NAMES_H

View File

@@ -0,0 +1,123 @@
#*******************************************************************************
# 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
#*******************************************************************************
project(SystemDemoExample)
# Use new policy for project version settings and default warning level
cmake_policy(SET CMP0048 NEW) # requires CMake 3.14
cmake_policy(SET CMP0092 NEW) # requires CMake 3.15
set(CMAKE_CXX_STANDARD 17)
# Libary symbols are hidden by default
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
# Include directory to the core framework
include_directories(${SDV_FRAMEWORK_DEV_INCLUDE})
######################################################################################################################################################################
# preparation
######################################################################################################################################################################
# REMARK: The code generation for the proxy/stub, interface definitions and serialization, the vehicle devices and the basic
# services are generated during the configuration phase of CMake. This is necessary, since CMakeFiles.txt files are generated and
# they have to be available during the configuration phase of CMake to be taken into the build process. Requisite for the code
# generation during the configuration time of CMake is the availability of the tools to do the generation. Hence the tools cannot be
# created during the build process, which is executed after the configuration process.
# Execute sdv_vss_util to create IDL files for devices and basic services.
# REMARK: We need 2 cvs definition files because we want to create 2 identical components.
# Both need to have the same input signal as well as the same output interface. Output must be vehicle speed in km/h.
# One of the component has to convert the value from m/s to km/h, the other one can pass the value directly.
message("Create interface code for devices and basic services of vehicle abstraction example.")
execute_process(COMMAND "${SDV_VSS_UTIL}" "${PROJECT_SOURCE_DIR}/vehicle_abstraction_example.csv" "-O${PROJECT_SOURCE_DIR}/generated/" --prefixabstraction --version1.0.0.1 --enable_components)
execute_process(COMMAND "${SDV_VSS_UTIL}" "${PROJECT_SOURCE_DIR}/vehicle_abstraction_example_ms.csv" "-O${PROJECT_SOURCE_DIR}/generated2/" --prefixabstraction --version1.0.0.1 --enable_components)
# Execute the IDL compiler for the VSS interfaces to digest interface code. Compile with --no_ps as we do not need proxies and stubs as we do not like to expose these interfaces for complex services or applications
message("Compiling vss_vehiclespeed_vd_rx.idl")
execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclespeed_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps)
message("Compiling vss_vehiclespeed_vd_rx.idl")
execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated2/vss_files/vss_vehiclespeed_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated2/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated2/vss_files/ --no_ps)
# We need proxies and stubs for basic services to give access to the interfaces for complex services and applications: --ps_lib_namedemo_proxystub
message("Compiling vss_vehiclespeed_bs_rx.idl")
execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclespeed_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_nameabstraction_proxystub)
# Execute sdv_dbc_util to create data link code & FMU code.
message("Create data link for vehicle abstraction example")
execute_process(COMMAND ${SDV_DBC_UTIL} "${PROJECT_SOURCE_DIR}/vehicle_abstraction_example.dbc" "-O${PROJECT_SOURCE_DIR}/generated/" --nodesabstraction --version1.0.0.1 --dl_lib_namecan_dl_abstraction)
######################################################################################################################################################################
# data link component
######################################################################################################################################################################
# REMARK: CAN data link code was generated during the configuration phase of CMake. Following below is the build step to build the
# component that was generated.
message("Include: example component can_dl_abstraction")
add_subdirectory(generated/can_dl)
#######################################################################################################################################################################
## vehicle devices and basic services
#######################################################################################################################################################################
# REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following
# below are the build steps to build the components that were generated.
message("Include: vehicle abstraction proxy/stub for basic services")
include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files)
add_subdirectory(generated/vss_files/ps)
# REMARK: We create 2 'vd_*' components with identical input and output interface. Therefore we needed 2 csv definition files for their creation.
# The difference is: one passes the input value to output, the other one needs to convert the value from m/s to km/h to match the unit.
add_subdirectory(generated/vss_files/vd_vehiclespeedkmh)
add_subdirectory(generated2/vss_files/vd_vehiclespeedms)
# REMARK: Because the output interface of the 'vd_*' components is identical (km/h), we need only one bs* component which expects to get a value in km/h.
add_subdirectory(generated/vss_files/bs_vehiclespeed)
######################################################################################################################################################################
# vehicle abstraction application
######################################################################################################################################################################
# Define the executable
add_executable(vehicle_abstraction_example
vehicle_abstraction_app/vehicle_abstraction_example.cpp
vehicle_abstraction_app/vehicle_abstraction_application.cpp
vehicle_abstraction_app/vehicle_abstraction_application.h
vehicle_abstraction_app/console.cpp
vehicle_abstraction_app/console.h
)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if (WIN32)
target_link_libraries(vehicle_abstraction_example Ws2_32 Winmm Rpcrt4.lib)
else()
target_link_libraries(vehicle_abstraction_example ${CMAKE_DL_LIBS} rt ${CMAKE_THREAD_LIBS_INIT})
endif()
else()
target_link_libraries(vehicle_abstraction_example Rpcrt4.lib)
endif()
# Copy the config files
file (COPY ${PROJECT_SOURCE_DIR}/config/can_com_simulation_vehicle_abstraction_ms.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config)
file (COPY ${PROJECT_SOURCE_DIR}/config/can_com_simulation_vehicle_abstraction_kmh.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config)
file (COPY ${PROJECT_SOURCE_DIR}/config/data_dispatch_vehicle_abstraction.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config)
file (COPY ${PROJECT_SOURCE_DIR}/config/data_link_vehicle_abstraction.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config)
file (COPY ${PROJECT_SOURCE_DIR}/config/task_timer_vehicle.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config)
file (COPY ${PROJECT_SOURCE_DIR}/config/vehicle_abstraction_device_kmh.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config)
file (COPY ${PROJECT_SOURCE_DIR}/config/vehicle_abstraction_device_ms.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config)
file (COPY ${PROJECT_SOURCE_DIR}/config/vehicle_abstraction_basic_service.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config)
# Copy the ASC files used by can_com_sim.sdv which includes the CAN messages. One file includes vehicle speed in m/s, the other file in km/h.
file (COPY ${PROJECT_SOURCE_DIR}/vehicle_abstraction_example_receiver_ms.asc DESTINATION ${CMAKE_BINARY_DIR}/bin)
file (COPY ${PROJECT_SOURCE_DIR}/vehicle_abstraction_example_receiver_kmh.asc DESTINATION ${CMAKE_BINARY_DIR}/bin)

View File

@@ -0,0 +1,9 @@
[Configuration]
Version = 100
[[Component]]
Path = "can_com_sim.sdv"
Class = "CAN_Com_Sim"
[Component.Parameters]
Source="vehicle_abstraction_example_receiver_kmh.asc"
Target="vehicle_abstraction_example_writer_kmh.asc"

View File

@@ -0,0 +1,9 @@
[Configuration]
Version = 100
[[Component]]
Path = "can_com_sim.sdv"
Class = "CAN_Com_Sim"
[Component.Parameters]
Source="vehicle_abstraction_example_receiver_ms.asc"
Target="vehicle_abstraction_example_writer_ms.asc"

View File

@@ -0,0 +1,8 @@
[Configuration]
Version = 100
[[Component]]
Path = "data_dispatch_service.sdv"
Class = "DataDispatchService"

View File

@@ -0,0 +1,6 @@
[Configuration]
Version = 100
[[Component]]
Path = "can_dl_abstraction.sdv"
Class = "CAN_data_link"

View File

@@ -0,0 +1,6 @@
[Configuration]
Version = 100
[[Component]]
Path = "task_timer.sdv"
Class = "TaskTimerService"

View File

@@ -0,0 +1,12 @@
[Configuration]
Version = 100
[[Component]]
Path = "abstraction_bs_vehiclespeed_rx.sdv"
Class = "Vehicle.Speed_Service"

View File

@@ -0,0 +1,6 @@
[Configuration]
Version = 100
[[Component]]
Path = "abstraction_vd_vehiclespeedkmh_rx.sdv"
Class = "Vehicle.Speed_Device"

View File

@@ -0,0 +1,8 @@
[Configuration]
Version = 100
[[Component]]
Path = "abstraction_vd_vehiclespeedms_rx.sdv"
Class = "Vehicle.Speed_Device"

View File

@@ -0,0 +1,316 @@
/********************************************************************************
* 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
********************************************************************************/
#include "console.h"
#ifdef _WIN32
#include <conio.h> // Needed for _kbhit
#else
#include <fcntl.h>
#endif
const CConsole::SConsolePos g_sTitle{ 1, 1 };
const CConsole::SConsolePos g_sSubTitle1{ 2, 1 };
const CConsole::SConsolePos g_sSubTitle2{3, 1};
const CConsole::SConsolePos g_sSeparator1{ 5, 1 };
const CConsole::SConsolePos g_sDataUnit{ 7, 1 };
const CConsole::SConsolePos g_sDataLinkSpeed{ 8, 1 };
const CConsole::SConsolePos g_sSeparator2{ 10, 1 };
const CConsole::SConsolePos g_sDeviceServiceSpeed{ 12, 1 };
const CConsole::SConsolePos g_sSeparator3{ 14, 1 };
const CConsole::SConsolePos g_sBasicServiceSpeed{ 16, 1 };
const CConsole::SConsolePos g_sSeparator4{ 18, 1 };
const CConsole::SConsolePos g_sComment1{ 20, 1 };
const CConsole::SConsolePos g_sComment2{ 21, 1 };
const CConsole::SConsolePos g_sComment3{ 23, 1 };
const CConsole::SConsolePos g_sComment4{ 24, 1 };
const CConsole::SConsolePos g_sComment5{ 25, 1 };
const CConsole::SConsolePos g_sComment6{ 27, 1 };
const CConsole::SConsolePos g_sComment7{ 28, 1 };
const CConsole::SConsolePos g_sSeparator5{ 30, 1 };
const CConsole::SConsolePos g_sControlDescription{ 32, 1 };
const CConsole::SConsolePos g_sCursor{ 33, 1 };
CConsole::CConsole()
{
#ifdef _WIN32
// Enable ANSI escape codes
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut != INVALID_HANDLE_VALUE && GetConsoleMode(hStdOut, &m_dwConsoleOutMode))
SetConsoleMode(hStdOut, m_dwConsoleOutMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (hStdIn != INVALID_HANDLE_VALUE && GetConsoleMode(hStdIn, &m_dwConsoleInMode))
SetConsoleMode(hStdIn, m_dwConsoleInMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT));
#elif defined __unix__
// Disable echo
tcgetattr(STDIN_FILENO, &m_sTermAttr);
struct termios sTermAttrTemp = m_sTermAttr;
sTermAttrTemp.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &sTermAttrTemp);
m_iFileStatus = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, m_iFileStatus | O_NONBLOCK);
#else
#error The OS is not supported!
#endif
}
CConsole::~CConsole()
{
SetCursorPos(g_sCursor);
#ifdef _WIN32
// Return to the stored console mode
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut != INVALID_HANDLE_VALUE)
SetConsoleMode(hStdOut, m_dwConsoleOutMode);
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (hStdIn != INVALID_HANDLE_VALUE)
SetConsoleMode(hStdIn, m_dwConsoleInMode);
#elif defined __unix__
// Return the previous file status flags.
fcntl(STDIN_FILENO, F_SETFL, m_iFileStatus);
// Return to previous terminal state
tcsetattr(STDIN_FILENO, TCSANOW, &m_sTermAttr);
#endif
}
void CConsole::PrintHeader()
{
// Clear the screen...
std::cout << "\x1b[2J";
// Print the titles
PrintText(g_sTitle, "Vehicle Abstraction: ");
PrintText(g_sSubTitle1, "This example demonstrates that the vehicle function implementation is independent of the vehicle itself.");
PrintText(g_sSubTitle2, "CAN bus vehicle 1 => km/h, CAN bus vehicle 2 => m/s.");
PrintText(g_sSeparator1, "====================================================================================================================");
PrintText(g_sDataUnit, m_DataUnit);
PrintText(g_sDataLinkSpeed, "Data Link not available.");
PrintText(g_sSeparator2, "--------------------------------------------------------------------------------------------------------------------");
PrintText(g_sDeviceServiceSpeed, "Platform Abstraction Interface not available.");
PrintText(g_sSeparator3, "--------------------------------------------------------------------------------------------------------------------");
PrintText(g_sBasicServiceSpeed, "Basic Service Interface not available.");
PrintText(g_sSeparator4, "====================================================================================================================");
PrintText(g_sComment1, "The dispatch service displays the value that is written by the CAN message read from the ASC file.");
PrintText(g_sComment2, "The speed should be increased by steps of 10 km/h.");
PrintText(g_sComment3, "The 'Platform Abstraction' component is responsible for the vehicle abstraction and converts the value if necessary.");
PrintText(g_sComment4, "The example contains 2 components for 'Platform Abstraction' with identical output interfaces.");
PrintText(g_sComment5, "Depending on the input (m/s or km/h) the correct 'Platform Abstraction' component must be loaded.");
PrintText(g_sComment6, "Therefore, only one speed sensor component is required, which always receives the speed in km/h.");
PrintText(g_sComment7, "The vehicle function does not require any logic that depends on the specific vehicle.");
PrintText(g_sSeparator5, "====================================================================================================================");
PrintText(g_sControlDescription, "Press 'X' to quit;");
}
bool CConsole::PrepareDataConsumers()
{
sdv::core::CDispatchService dispatch;
m_SignalSpeed = dispatch.Subscribe(abstraction::dsVehicleSpeed, [&](sdv::any_t value) { CallbackSpeed(value); });
if (m_SignalSpeed)
{
auto unit = m_Unit;
unit.append(" [ data dispatch service - input from CAN bus ] ");
PrintValue(g_sDataLinkSpeed, "Vehicle Speed RX", m_SpeedDataLink, unit);
}
auto deviceServiceSpeed = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface<vss::Vehicle::SpeedDevice::IVSS_ReadSpeed>();
if (deviceServiceSpeed)
{
PrintValue(g_sDeviceServiceSpeed, "Vehicle Speed RX", m_PlatformSpeed, "km/h [ Output of Platform Abstraction, not accessible by application ] ");
deviceServiceSpeed->RegisterSpeedEvent(dynamic_cast<vss::Vehicle::SpeedDevice::IVSS_WriteSpeed_Event*> (this));
}
auto basicServiceSpeed = sdv::core::GetObject("Vehicle.Speed_Service").GetInterface<vss::Vehicle::SpeedService::IVSS_GetSpeed>();
if (basicServiceSpeed)
{
PrintValue(g_sBasicServiceSpeed, "Vehicle Speed RX", m_BasicSpeed, "km/h [ Output of Speed Sensor Service, accessible by application ] ");
basicServiceSpeed->RegisterOnSignalChangeOfVehicleSpeed(dynamic_cast<vss::Vehicle::SpeedService::IVSS_SetSpeed_Event*> (this));
}
return true;
}
void CConsole::WriteSpeed( float value)
{
if (m_PlatformSpeed != value)
{
m_PlatformSpeed = value;
PrintValue(g_sDeviceServiceSpeed, "Vehicle Speed RX", m_PlatformSpeed, "km/h [ Output of Platform Abstraction, not accessible by application ] ");
}
}
void CConsole::SetSpeed( float value)
{
if (m_BasicSpeed != value)
{
m_BasicSpeed= value;
PrintValue(g_sBasicServiceSpeed, "Vehicle Speed RX", m_BasicSpeed, "km/h [ Output of Speed Sensor Service, accessible by application ] ");
}
}
void CConsole::ResetSignals()
{
// Set the cursor position at the end
SetCursorPos(g_sCursor);
auto deviceServiceSpeed = sdv::core::GetObject("Vehicle.Speed_Devicee").GetInterface<vss::Vehicle::SpeedDevice::IVSS_ReadSpeed>();
if (deviceServiceSpeed)
{
deviceServiceSpeed->UnregisterSpeedEvent(dynamic_cast<vss::Vehicle::SpeedDevice::IVSS_WriteSpeed_Event*> (this));
}
auto basicServiceSpeed = sdv::core::GetObject("Vehicle.Speed_Service").GetInterface<vss::Vehicle::SpeedService::IVSS_GetSpeed>();
if (basicServiceSpeed)
{
basicServiceSpeed->UnregisterOnSignalChangeOfVehicleSpeed(dynamic_cast<vss::Vehicle::SpeedService::IVSS_SetSpeed_Event*> (this));
}
if (m_SignalSpeed)
m_SignalSpeed.Reset();
}
void CConsole::CallbackSpeed(sdv::any_t value)
{
if (m_SpeedDataLink != value.get<float>())
{
m_SpeedDataLink = value.get<float>();
auto unit = m_Unit;
unit.append(" [ data dispatch service - input from CAN bus ] ");
PrintValue(g_sDataLinkSpeed, "Vehicle Speed RX", m_SpeedDataLink, unit);
}
}
CConsole::SConsolePos CConsole::GetCursorPos() const
{
SConsolePos sPos{};
std::cout << "\033[6n";
char buff[128];
int indx = 0;
for(;;) {
int cc = std::cin.get();
buff[indx] = (char)cc;
indx++;
if(cc == 'R') {
buff[indx + 1] = '\0';
break;
}
}
int iRow = 0, iCol = 0;
sscanf(buff, "\x1b[%d;%dR", &iRow, &iCol);
sPos.uiRow = static_cast<uint32_t>(iRow);
sPos.uiCol = static_cast<uint32_t>(iCol);
fseek(stdin, 0, SEEK_END);
return sPos;
}
void CConsole::SetCursorPos(SConsolePos sPos)
{
std::cout << "\033[" << sPos.uiRow << ";" << sPos.uiCol << "H";
}
void CConsole::PrintText(SConsolePos sPos, const std::string& rssText)
{
auto text = rssText;
while (text.length() < 76)
text.append(" ");
std::lock_guard<std::mutex> lock(m_mtxPrintToConsole);
SetCursorPos(sPos);
std::cout << text;
}
bool CConsole::KeyHit()
{
#ifdef _WIN32
return _kbhit();
#elif __unix__
int ch = getchar();
if (ch != EOF) {
ungetc(ch, stdin);
return true;
}
return false;
#endif
}
char CConsole::GetChar()
{
#ifdef _WIN32
return static_cast<char>(_getch());
#else
return getchar();
#endif
}
void CConsole::RunUntilBreak()
{
bool bRunning = true;
while (bRunning)
{
// Check for a key
if (!KeyHit())
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
// Get a keyboard value (if there is any).
char c = GetChar();
switch (c)
{
case 'x':
case 'X':
bRunning = false;
break;
default:
break;
}
}
}
bool CConsole::SelectInputDataUnits()
{
// Clear the screen and goto top...
std::cout << "\x1b[2J\033[0;0H";
std::cout << "Click '1' to select km/h or '2' to select m/s";
bool bIsKmh = false;
char c = '0';
while(c != '1' && c != '2')
{
c = GetChar();
switch (c)
{
case '1':
m_DataUnit = "[ --- Input data in km/h --- ]";
m_Unit = "km/h";
bIsKmh = true;
break;
case '2':
m_DataUnit = "[ --- Input data in m/s --- ]";
m_Unit = "m/s";
bIsKmh = false;
break;
default:
break;
}
}
return bIsKmh;
}

View File

@@ -0,0 +1,200 @@
/********************************************************************************
* 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
********************************************************************************/
#ifndef CONSOLE_OUTPUT_H
#define CONSOLE_OUTPUT_H
#include <iostream>
#include <string>
#include <functional>
#include <support/signal_support.h>
#include <support/app_control.h>
#include <support/component_impl.h>
#include <support/timer.h>
#include "../generated/vss_files/signal_identifier.h"
#include <fcntl.h>
#include "../generated/vss_files/vss_vehiclespeed_vd_rx.h"
#include "../generated/vss_files/vss_vehiclespeed_bs_rx.h"
#ifdef __unix__
#include <termios.h> // Needed for tcgetattr and fcntl
#include <unistd.h>
#endif
/**
* @brief Console operation class.
* @details This class retrieves RX data from the data dispatch service, platform abstraction & basic service of speed on event change.
* Furthermore, it shows TX value by polling the RX signals.
*/
class CConsole : public vss::Vehicle::SpeedDevice::IVSS_WriteSpeed_Event
, public vss::Vehicle::SpeedService::IVSS_SetSpeed_Event
{
public:
/**
* @brief Screen position structure
*/
struct SConsolePos
{
uint32_t uiRow; ///< Row position (starts at 1)
uint32_t uiCol; ///< Column position (starts at 1)
};
/**
* @brief Constructor
*/
CConsole();
/**
* @brief Destructor
*/
~CConsole();
/**
* @brief Print the header.
*/
void PrintHeader();
/**
* @brief Prepare the data consumers..
* @return Returns whether the preparation of the data consumers was successful or not.
*/
bool PrepareDataConsumers();
/**
* @brief Run loop as long as user input does not exit
*/
void RunUntilBreak();
/**
* @brief Let the user decide which unit the input data should have
* @return Return true if inout data should be km/h, otherwise false
*/
bool SelectInputDataUnits();
/**
* @brief Write vehicleSpeed signal
* @param[in] value vehicleSpeed
*/
void WriteSpeed( float value) override;
/**
* @brief Set vehicleSpeed signal
* @param[in] value vehicleSpeed
*/
void SetSpeed( float value) override;
/**
* @brief For gracefully shutdown all signals need to be reset.
*/
void ResetSignals();
private:
/**
* @brief Callback function for speed.
* @param[in] value The value of the signal to update.
*/
void CallbackSpeed(sdv::any_t value);
/**
* @brief Key hit check. Windows uses the _kbhit function; POSIX emulates this.
* @return Returns whether a key has been pressed.
*/
bool KeyHit();
/**
* @brief Get the character from the keyboard buffer if pressed.
* @return Returns the character from the keyboard buffer.
*/
char GetChar();
/**
* @brief Get the cursor position of the console.
* @return The cursor position.
*/
SConsolePos GetCursorPos() const;
/**
* @brief Set the current cursor position for the console.
* @param[in] sPos Console position to place the current cursor at.
*/
void SetCursorPos(SConsolePos sPos);
/**
* @brief Print text at a specific location.
* @param[in] sPos The location to print text at.
* @param[in] rssText Reference to the text to print.
*/
void PrintText(SConsolePos sPos, const std::string& rssText);
/**
* @brief Print a value string at a specific location.
* @tparam TValue Type of value.
* @param[in] sPos The location to print the value at.
* @param[in] rssName Reference to the value.
* @param[in] tValue The value.
* @param[in] rssStatus Status, because we have signals of type bool
*/
template <typename TValue>
void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus);
/**
* @brief Align string between name and value.
* @param[in] message Reference to the message to align.
* @param[in] desiredLength The desired length or 0 when no length is specified.
* @return The aligned string.
*/
std::string AlignString(const std::string& message, uint32_t desiredLength = 0);
mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message
bool m_bRunning = false; ///< When set, the application is running.
std::string m_DataUnit = "[ --- Input data in m/s --- ]";
std::string m_Unit = "m/s";
sdv::core::CSignal m_SignalSpeed; ///< Signal to subscribe to the speed signal in dispatch service
float m_SpeedDataLink = 0.0; ///< Data Link value, either km/h or m/s
float m_PlatformSpeed = 0.0; ///< Generalized Speed
float m_BasicSpeed = 0.0; ///< Generalized Speed
#ifdef _WIN32
DWORD m_dwConsoleOutMode = 0u; ///< The console mode before switching on ANSI support.
DWORD m_dwConsoleInMode = 0u; ///< The console mode before switching on ANSI support.
#elif defined __unix__
struct termios m_sTermAttr{}; ///< The terminal attributes before disabling echo.
int m_iFileStatus = 0; ///< The file status flags for STDIN.
#else
#error The OS is not supported!
#endif
};
template <typename TValue>
inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssUnits)
{
const size_t nValueNameLen = 26;
std::stringstream sstreamValueText;
sstreamValueText << rssName <<
std::string(nValueNameLen - std::min(rssName.size(), static_cast<size_t>(nValueNameLen - 1)) - 1, '.') <<
" " << std::fixed << std::setprecision(2) << tValue << " " << rssUnits << " ";
std::lock_guard<std::mutex> lock(m_mtxPrintToConsole);
SetCursorPos(sPos);
std::cout << sstreamValueText.str();
}
template <>
inline void CConsole::PrintValue<bool>(SConsolePos sPos, const std::string& rssName, bool bValue, const std::string& rssStatus)
{;
PrintValue(sPos, rssName, bValue ? "" : "", rssStatus);
}
#endif // !define CONSOLE_OUTPUT_H

View File

@@ -0,0 +1,103 @@
/********************************************************************************
* 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
********************************************************************************/
#include "vehicle_abstraction_application.h"
#include "../generated/vss_files/signal_identifier.h"
#ifdef _WIN32
#include <conio.h> // Needed for _kbhit
#else
#include <fcntl.h>
#endif
bool CAbstractionControl::LoadConfigFile(const std::string& inputMsg, const std::string& configFileName)
{
std::string msg = inputMsg;
if (m_appcontrol.LoadConfig(configFileName) == sdv::core::EConfigProcessResult::successful)
{
msg.append("ok\n");
std::cout << msg.c_str();
return true;
}
msg.append("FAILED.\n");
std::cout << msg.c_str();
return false;
}
bool CAbstractionControl::IsSDVFrameworkEnvironmentSet()
{
const char* envVariable = std::getenv("SDV_FRAMEWORK_RUNTIME");
if (envVariable)
{
return true;
}
return false;
}
bool CAbstractionControl::Initialize(bool bSimulate)
{
if (m_bInitialized)
return true;
m_bSimulate = bSimulate;
if (!IsSDVFrameworkEnvironmentSet())
{
// if SDV_FRAMEWORK_RUNTIME environment variable is not set we need to set the Framework Runtime directory
m_appcontrol.SetFrameworkRuntimeDirectory("../../bin");
}
if (!m_appcontrol.Startup(""))
return false;
m_appcontrol.SetConfigMode();
bool bResult = LoadConfigFile("Load dispatch service: ", "data_dispatch_vehicle_abstraction.toml");
bResult &= LoadConfigFile("Load task_timer_vehicle: ", "task_timer_vehicle.toml");
if (bSimulate)
{
bResult &= LoadConfigFile("Load can_com_simulation_vehicle_abstraction_kmh: ", "can_com_simulation_vehicle_abstraction_kmh.toml");
bResult &= LoadConfigFile("Load data_link_vehicle_abstraction: ", "data_link_vehicle_abstraction.toml");
bResult &= LoadConfigFile("Load vehicle_abstraction_kmh: ", "vehicle_abstraction_device_kmh.toml");
}
else
{
bResult &= LoadConfigFile("Load can_com_simulation_vehicle_abstraction_ms: ", "can_com_simulation_vehicle_abstraction_ms.toml");
bResult &= LoadConfigFile("Load data_link_vehicle_abstraction: ", "data_link_vehicle_abstraction.toml");
bResult &= LoadConfigFile("Load vehicle_abstraction_ms: ", "vehicle_abstraction_device_ms.toml");
}
bResult &= LoadConfigFile("Load vehicle_abstraction_basic_service: ", "vehicle_abstraction_basic_service.toml");
if (!bResult)
{
SDV_LOG_ERROR("One or more configurations could not be loaded. Cannot continue.");
return false;
}
m_bInitialized = true;
return true;
}
void CAbstractionControl::Shutdown()
{
if (!m_bInitialized)
m_appcontrol.Shutdown();
m_bInitialized = false;
}
void CAbstractionControl::SetRunningMode()
{
m_appcontrol.SetRunningMode();
}

View File

@@ -0,0 +1,60 @@
/********************************************************************************
* 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
********************************************************************************/
#include <string>
#include <support/app_control.h>
#include <support/signal_support.h>
/**
* @brief Application Class of the vehicle abstraction example
*/
class CAbstractionControl
{
public:
/**
* @brief Start and initialize the application control and load
* platform abstraction components and basic services
* @param[in] bSimulate If signals should be simulated or CAN input signals should be used
* @return Return true on success otherwise false
*/
bool Initialize(bool bSimulate);
/**
* @brief After initialization/configuration the system mode needs to be set to running mode
*/
void SetRunningMode();
/**
* @brief Shutdown the system.
*/
void Shutdown();
private:
/**
* @brief check if SDV_FRAMEWORK_RUNTIME environment variable exists
* @return Return true if environment variable is found otherwise false
*/
bool IsSDVFrameworkEnvironmentSet();
/**
* @brief Load config file and register vehicle device and basic service.
* @param[in] inputMsg message string to be printed on console in case of success and failure
* @param[in] configFileName config toml file name
* @return Return true on success otherwise false
*/
bool LoadConfigFile(const std::string& inputMsg, const std::string& configFileName);
sdv::app::CAppControl m_appcontrol; ///< App-control of SDV V-API.
bool m_bInitialized = false; ///< Set when initialized.
bool m_bSimulate = false;
};

View File

@@ -0,0 +1,47 @@
/********************************************************************************
* 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
********************************************************************************/
#include <iostream>
#include <cstdlib> // for std::strtol
#include <fstream>
#include <filesystem>
#include <sstream>
#include <ostream>
#include "vehicle_abstraction_application.h"
#include "console.h"
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain([[maybe_unused]] int argc, [[maybe_unused]] wchar_t* argv[])
{
#else
extern "C" int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
{
#endif
CConsole visual_obj;
CAbstractionControl appobj;
if (!appobj.Initialize(visual_obj.SelectInputDataUnits()))
{
std::cout << "ERROR: Failed to initialize application control." << std::endl;
return 0;
}
visual_obj.PrintHeader();
visual_obj.PrepareDataConsumers();
appobj.SetRunningMode();
visual_obj.RunUntilBreak();
visual_obj.ResetSignals();
appobj.Shutdown();
return 0;
}

View File

@@ -0,0 +1,4 @@
;Class name;Function name;Signal name;vss;Signal direction;type;DBC CAN name includes CAN message name
;;;;;;;
VD;VehicleSpeedKmh;Speed;vehicleSpeed;Vehicle.Speed;RX;float;CAN_Input.Speed_General
BS;VehicleSpeed;Speed;vehicleSpeed;Vehicle.Speed;RX;float;Vehicle.Speed
1 Class name Function name Signal name vss Signal direction type DBC CAN name includes CAN message name
2
3 VD VehicleSpeedKmh Speed vehicleSpeed Vehicle.Speed RX float CAN_Input.Speed_General
4 BS VehicleSpeed Speed vehicleSpeed Vehicle.Speed RX float Vehicle.Speed

View File

@@ -0,0 +1,115 @@
VERSION "PrivateCAN"
NS_ :
NS_DESC_
CM_
BA_DEF_
BA_
VAL_
CAT_DEF_
CAT_
FILTER
BA_DEF_DEF_
EV_DATA_
ENVVAR_DATA_
SGTYPE_
SGTYPE_VAL_
BA_DEF_SGTYPE_
BA_SGTYPE_
SIG_TYPE_REF_
VAL_TABLE_
SIG_GROUP_
SIG_VALTYPE_
SIGTYPE_VALTYPE_
BO_TX_BU_
BA_DEF_REL_
BA_REL_
BA_DEF_DEF_REL_
BU_SG_REL_
BU_EV_REL_
BU_BO_REL_
SG_MUL_VAL_
BS_:
BU_: abstraction
VAL_TABLE_ Fault_Codes 27 "UKWN" 26 "VEHSPDMAX_EXDD" 25 "STS_ALIVE" 24 "STEER_NOT_E2E_MODE" 23 "OTA_SPD" 22 "OTA_TIMER_DOWNLOAD_FAILED" 21 "OTA_MAX_TIME" 20 "CUBIXAD_STEERSTREQ_NOTACTV" 19 "CUBIXAD_DRVSTREQ_NOTACTV" 18 "SFTYDRV_INTV" 17 "LSDC_ALIVE" 16 "CUBIXAD_ALIVE" 15 "IBC_MAB_NO_PRIO" 14 "IBC_NOT_RDY" 13 "IBC_ALIVE" 12 "LSDC_GEAR" 11 "LSDC_SPD" 10 "LSDC_ACCL" 9 "IBC_NOT_MAB_MOD" 8 "GOLDBOX_ALIVE" 7 "CUBIXAD_GEAR" 6 "CUBIXAD_SPD_TESTTRACK" 5 "DRVREQCHG" 4 "RDY_TIMER" 3 "SFTY_CDN_FAILED" 2 "ACTVNCHK_SPD" 1 "ACTVNCHK_TIMR" 0 "NONE" ;
VAL_TABLE_ TestMapID 6 "E_TESTMAPID_UNDEFINED" 5 "E_TESTMAPID_TEST_DRIVE" 4 "E_TESTMAPID_AD_AREA" 3 "E_TESTMAPID_STUTT_ARENA" 2 "E_TESTMAPID_ZF_LASTMILE" 1 "E_TESTMAPID_ZF_TESTTRACK_2" 0 "E_TESTMAPID_NONE" ;
VAL_TABLE_ CtrlReqStates 7 "CtrlSts3b_RESERVED_4" 6 "CtrlSts3b_RESERVED_3" 5 "CtrlSts3b_RESERVED_2" 4 "CtrlSts3b_RESERVED_1" 3 "CtrlSts3b_ERROR" 2 "CtrlSts3b_CONTROL_REQUESTED" 1 "CtrlSts3b_CONTROL_NOT_REQUESTED" 0 "CtrlSts3b_INIT" ;
VAL_TABLE_ SteerActrReSts 7 "Diag" 6 "Inactive" 5 "Ramping" 4 "Yellow" 3 "Red" 2 "Normal" 1 "Pending" 0 "Initialisation" ;
VAL_TABLE_ SwtPark1 1 "SwtParkActv" 0 "SwtParkNotActv" ;
VAL_TABLE_ PE_State 2 "ERROR" 1 "INIT" 0 "NO_ERROR" ;
VAL_TABLE_ SSM_Req 7 "HMS_TAKEOVER" 6 "RESERVED" 5 "RELESE_VIA_RAMP" 4 "DRIVEOFF" 3 "HOLD_STANDBY" 2 "PARK" 1 "HOLD" 0 "NO_REQUEST" ;
VAL_TABLE_ IBC_StandStillMode 12 "SSM_ERROR" 11 "SSM_INIT" 10 "SSM_DRIVEOFF_STANDBY_ACTIVE" 9 "SSM_HOLD_STANDBY_ACTIVE" 8 "SSM_HILL_SLIPPOFF_DETECTED" 7 "SSM_RELEASE_REQ_FROM_DRIVER" 6 "SSM_RELEASE_REQ_ACTIVE" 5 "SSM_DRIVEOFF_ACTIVE" 4 "SSM_PARK_RETAINED_ACTIVE" 3 "SSM_PARK_ACTIVE" 2 "SSM_PARK_REQUESTED" 1 "SSM_HOLD_ACTIVE" 0 "SSM_NO_ACTIVE_FUNCTION" ;
VAL_TABLE_ AppTgtStDrv 3 "ACTIVE" 2 "READY" 1 "RESERVED" 0 "NOT_ACTIVE" ;
VAL_TABLE_ IBC_Status 4 "IBC_MAB_ERR_COMM" 3 "IBC_MAB_NO_PRIO" 2 "IBC_IN_MAB_MODE" 1 "IBC_READY" 0 "IBC_NOT_READY_FAILED" ;
VAL_TABLE_ GearLvrIndcn 7 "GearLvrIndcn2_Undefd" 6 "GearLvrIndcn2_Resd2" 5 "GearLvrIndcn2_Resd1" 4 "GearLvrIndcn2_ManModeIndcn" 3 "GearLvrIndcn2_DrvIndcn" 2 "GearLvrIndcn2_NeutIndcn" 1 "GearLvrIndcn2_RvsIndcn" 0 "GearLvrIndcn2_ParkIndcn" ;
VAL_TABLE_ LvlgAdjReq 7 "LvlgAdjReq_Resd2" 6 "LvlgAdjReq_Resd1" 5 "LvlgAdjReq_Ll2" 4 "LvlgAdjReq_Ll1" 3 "LvlgAdjReq_Nrh" 2 "LvlgAdjReq_Hl1" 1 "LvlgAdjReq_Hl2" 0 "LvlgAdjReq_Ukwn" ;
VAL_TABLE_ DrvModReq 15 "Err" 14 "Rock" 13 "Mud" 12 "Sand" 11 "Snow" 10 "Power" 9 "Hybrid" 8 "Pure_EV" 7 "Race" 6 "Adaptive" 5 "Offroad_CrossTerrain" 4 "Individual" 3 "Dynamic_Sport" 2 "Comfort_Normal" 1 "ECO" 0 "Undefd" ;
VAL_TABLE_ MAB_Info_Message 4 "DRV_GEARLVR_TO_P" 3 "DRV_P_TO_D" 2 "LSDC_DI_NOT_PSBL" 1 "LSDC_ENA_NOT_POSSIBLE" 0 "NONE" ;
VAL_TABLE_ MAB_OvrdTool_Sts 11 "HACKATHON" 10 "OTA" 9 "INIT" 8 "FINSHD" 7 "FLT" 6 "CUBIX_AD" 5 "SAVE_THE_SPOILER" 4 "LSDC" 3 "RDY" 2 "ACTVN_CHK" 1 "NO_MANIPULATION" 0 "NONE" ;
VAL_TABLE_ HMI_Drvr_Req 9 "FCT_DEACTVN_REQ" 8 "FCT_ACTVN_OTA_CFMD" 7 "FCT_ACTVN_OTA_REQ" 6 "FCT_ACTVN_SAVETHESPOILER_CFMD" 5 "FCT_ACTVN_SAVETHESPOILER_REQ" 4 "FCT_ACTVN_LSDC_CFMD" 3 "FCT_ACTVN_LSDC_REQ" 2 "FCT_ACTVN_CUBIXAD_CFMD" 1 "FCT_ACTVN_CUBIXAD_REQ" 0 "FCT_ACTVN_NONE" ;
VAL_TABLE_ Info_Message 4 "DRV_GEARLVR_TO_P" 3 "DRV_P_TO_D" 2 "LSDC_DI_NOT_PSBL" 1 "LSDC_ENA_NOT_POSSIBLE" 0 "NONE" ;
VAL_TABLE_ HMI_Fct_Req 8 "FCT_DEACTVN_REQ" 7 "FCT_ACTVN_OTA_REQ" 6 "FCT_ACTVN_SAVETHESPOILER_CFMD" 5 "FCT_ACTVN_SAVETHESPOILER_REQ" 4 "FCT_ACTVN_LSDC_CFMD" 3 "FCT_ACTVN_LSDC_REQ" 2 "FCT_ACTVN_AI4MTN_CFMD" 1 "FCT_ACTVN_AI4MTN_REQ" 0 "FCT_ACTVN_NONE" ;
VAL_TABLE_ SOVD_states 2 "SOVD_SHOWCASE_ACTIVE" 1 "SOVD_SHOWCASE_DEACTIVE" 0 "SOVD_NONE" ;
VAL_TABLE_ OTA_states 7 "OTA_DOWNLOAD_FAILED" 6 "OTA_INSTALL_FAILED" 5 "OTA_INSTALL_FINISHED" 4 "OTA_INSTALL_START" 3 "OTA_DOWNLOAD_START" 2 "OTA_SCHEDULED" 1 "OTA_STANDBY" 0 "OTA_NONE" ;
BO_ 1 CAN_Input: 8 Vector__XXX
SG_ Speed_General : 7|15@0+ (0.00391,0) [0|128.11897] "km/h" abstraction
CM_ SG_ 1 Speed_General "Vehicle speed used in a example to demonstrate vehicel abstraction. One data is in m/s, the otherdata is km/h";
BA_DEF_ "Baudrate" INT 1000 1000000;
BA_DEF_ "BusType" STRING ;
BA_DEF_ "DBName" STRING ;
BA_DEF_ "ProtocolType" STRING ;
BA_DEF_ BU_ "NmAsrNode" ENUM "No","Yes";
BA_DEF_ BU_ "NmAsrNodeIdentifier" INT 0 255;
BA_DEF_ BO_ "GenMsgCycleTime" INT 0 65536;
BA_DEF_ BO_ "GenMsgCycleTimeFast" FLOAT 0 300000;
BA_DEF_ BO_ "GenMsgDelayTime" INT 0 65536;
BA_DEF_ BO_ "GenMsgNrOfRepetition" INT 0 100000;
BA_DEF_ BO_ "GenMsgSendType" ENUM "cyclic","spontaneous","not-used","not-used","not-used","cyclicAndSpontaneous","not-used","cyclicIfActive","NoMsgSendType";
BA_DEF_ BO_ "GenMsgStartDelayTime" INT 0 65536;
BA_DEF_ SG_ "GenSigSendType" ENUM "Cyclic","OnWrite","OnWriteWithRepetition","OnChange","OnChangeWithRepetition","IfActive","IfActiveWithRepetition","NoSigSendType";
BA_DEF_ SG_ "GenSigStartValue" HEX 0 80000000;
BA_DEF_ BO_ "GenMsgILSupport" ENUM "No","Yes";
BA_DEF_ BO_ "NmAsrMessage" ENUM "No","Yes";
BA_DEF_ "NmAsrBaseAddress" HEX 0 536870911;
BA_DEF_ "NmAsrMessageCount" INT 0 255;
BA_DEF_ BU_ "NodeLayerModules" STRING ;
BA_DEF_ BU_ "ILused" ENUM "No","Yes";
BA_DEF_ SG_ "GenSigFuncType" ENUM "NoFunction","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","CHK","CNTR","n/a","n/a","n/a","CNTR_AR_01","CRC_AR_01_BOTH","CRC_AR_01_ALT","CRC_AR_01_LOW","CRC_AR_01_NIBBLE","CNTR_AR_04","CRC_AR_04A","CNTR_AR_05","CRC_AR_05";
BA_DEF_ SG_ "GenSigDataID" STRING ;
BA_DEF_ SG_ "SigGroup" STRING ;
BA_DEF_DEF_ "Baudrate" 1000;
BA_DEF_DEF_ "BusType" "";
BA_DEF_DEF_ "DBName" "";
BA_DEF_DEF_ "ProtocolType" "";
BA_DEF_DEF_ "NmAsrNode" "No";
BA_DEF_DEF_ "NmAsrNodeIdentifier" 0;
BA_DEF_DEF_ "GenMsgCycleTime" 0;
BA_DEF_DEF_ "GenMsgCycleTimeFast" 0;
BA_DEF_DEF_ "GenMsgDelayTime" 0;
BA_DEF_DEF_ "GenMsgNrOfRepetition" 0;
BA_DEF_DEF_ "GenMsgSendType" "NoMsgSendType";
BA_DEF_DEF_ "GenMsgStartDelayTime" 0;
BA_DEF_DEF_ "GenSigSendType" "NoSigSendType";
BA_DEF_DEF_ "GenSigStartValue" 0;
BA_DEF_DEF_ "GenMsgILSupport" "Yes";
BA_DEF_DEF_ "NmAsrMessage" "No";
BA_DEF_DEF_ "NmAsrBaseAddress" 1280;
BA_DEF_DEF_ "NmAsrMessageCount" 64;
BA_DEF_DEF_ "NodeLayerModules" "CANoeILNLSPA.dll";
BA_DEF_DEF_ "ILused" "Yes";
BA_DEF_DEF_ "GenSigFuncType" "NoFunction";
BA_DEF_DEF_ "GenSigDataID" "";
BA_DEF_DEF_ "SigGroup" "";
BA_ "ProtocolType" "CAN";
BA_ "BusType" "CAN";
BA_ "Baudrate" 500000;
BA_ "DBName" "PrivateCAN";

View File

@@ -0,0 +1,3 @@
;Class name;Function name;Signal name;vss;Signal direction;type;DBC CAN name includes CAN message name;
;;;;;;;;
VD;VehicleSpeedMs;Speed;vehicleSpeed;Vehicle.Speed;RX;float;CAN_Input.Speed_General;"float vehicleSpeed = value.get<float>() * 3.6f;"
1 Class name Function name Signal name vss Signal direction type DBC CAN name includes CAN message name
2
3 VD VehicleSpeedMs Speed vehicleSpeed Vehicle.Speed RX float CAN_Input.Speed_General float vehicleSpeed = value.get<float>() * 3.6f;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -72,9 +72,9 @@ module sdv
}; };
/** /**
* @brief Connection status enumeration * @brief Connection state enumeration
*/ */
enum EConnectStatus : uint32 enum EConnectState : uint32
{ {
uninitialized = 0, ///< Initialization via IConnect pending/required uninitialized = 0, ///< Initialization via IConnect pending/required
initializing = 1, ///< Initiation channel access. initializing = 1, ///< Initiation channel access.
@@ -96,8 +96,8 @@ module sdv
{ {
/** /**
* @brief Establish a connection and start sending/receiving messages. * @brief Establish a connection and start sending/receiving messages.
* @param[in] pReceiver Callback interface for receiving data and connect status. * @param[in] pReceiver Callback interface for receiving data and connect state.
* @return Returns 'true' when a channel connection could be initiated. Use GetStatus or IConnectEventCallback to * @return Returns 'true' when a channel connection could be initiated. Use GetConnectState or IConnectEventCallback to
* check the connection state. * check the connection state.
*/ */
boolean AsyncConnect(in IInterfaceAccess pReceiver); boolean AsyncConnect(in IInterfaceAccess pReceiver);
@@ -116,33 +116,32 @@ module sdv
void CancelWait(); void CancelWait();
/** /**
* @brief Disconnect from a connection. This will set the connect status to disconnected and release the interface * @brief Disconnect from a connection. This will set the connect state to disconnected and release the interface
* used for the status events. * used for the state events.
*/ */
void Disconnect(); void Disconnect();
/** /**
* @brief Register event callback interface. * @brief Register event callback interface.
* @details Register a connection status event callback interface. The exposed interface must be of type * @details Register a connection state event callback interface. The exposed interface must be of type
* IConnectEventCallback. The registration will exist until a call to the unregister function with the returned cookie * IConnectEventCallback. The registration will exist until a call to the unregister function with the returned cookie
* or until the connection is terminated. * or until the connection is terminated.
* @param[in] pEventCallback Pointer to the object exposing the IConnectEventCallback interface. * @param[in] pEventCallback Pointer to the object exposing the IConnectEventCallback interface.
* @return The cookie assigned to the registration or 0 when the registration wasn't successful. * @return The cookie assigned to the registration or 0 when the registration wasn't successful.
*/ */
uint64 RegisterStatusEventCallback(in IInterfaceAccess pEventCallback); uint64 RegisterStateEventCallback(in IInterfaceAccess pEventCallback);
/** /**
* @brief Unregister the status event callback with the returned cookie from the registration. * @brief Unregister the state event callback with the returned cookie from the registration.
* @param[in] uiCookie The cookie returned by a previous call to the registration function. * @param[in] uiCookie The cookie returned by a previous call to the registration function.
*/ */
void UnregisterStatusEventCallback(in uint64 uiCookie); void UnregisterStateEventCallback(in uint64 uiCookie);
/** /**
* @brief Gets the current status of the IPC whether it is initialized/connected/disconnected or having connection * @brief Get the current state of the IPC conection.
* error. * @return Returns connection state.
* @return Returns retrieved status of the IPC.
*/ */
EConnectStatus GetStatus() const; EConnectState GetConnectState() const;
}; };
/** /**
@@ -151,10 +150,10 @@ module sdv
interface IConnectEventCallback interface IConnectEventCallback
{ {
/** /**
* @brief Set the current status. * @brief Set the current connect state.
* @param[in] eConnectStatus The connection status. * @param[in] eConnectState The connection state.
*/ */
void SetStatus(in EConnectStatus eConnectStatus); void SetConnectState(in EConnectState eConnectState);
}; };
/** /**

View File

@@ -900,7 +900,7 @@ namespace sdv
// Lock the parameter map - do not allow any more changes // Lock the parameter map - do not allow any more changes
LockParamMap(); LockParamMap();
// Set status // Set state
m_eObjectState = EObjectState::shutdown_in_progress; m_eObjectState = EObjectState::shutdown_in_progress;
// Inform derived class // Inform derived class
@@ -1001,7 +1001,7 @@ namespace sdv
END_SDV_INTERFACE_MAP() END_SDV_INTERFACE_MAP()
private: private:
std::atomic<EObjectState> m_eObjectState = EObjectState::initialization_pending; ///< Object status std::atomic<EObjectState> m_eObjectState = EObjectState::initialization_pending; ///< Object state
std::string m_ssObjectConfig; ///< Copy of the configuration TOML. std::string m_ssObjectConfig; ///< Copy of the configuration TOML.
}; };

View File

@@ -32,17 +32,17 @@ struct SConnectEventCallbackWrapper : sdv::IInterfaceAccess, sdv::ipc::IConnectE
END_SDV_INTERFACE_MAP() END_SDV_INTERFACE_MAP()
/** /**
* @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. * @brief Set the current connect state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState.
* @param[in] eConnectStatus The connection status. * @param[in] eConnectState The connection state.
*/ */
virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override
{ {
switch (eConnectStatus) switch (eConnectState)
{ {
case sdv::ipc::EConnectStatus::disconnected: case sdv::ipc::EConnectState::disconnected:
case sdv::ipc::EConnectStatus::disconnected_forced: case sdv::ipc::EConnectState::disconnected_forced:
case sdv::ipc::EConnectStatus::connection_error: case sdv::ipc::EConnectState::connection_error:
case sdv::ipc::EConnectStatus::communication_error: case sdv::ipc::EConnectState::communication_error:
{ {
auto pRequestShutdown = sdv::core::GetObject<sdv::app::IAppShutdownRequest>("AppControlService"); auto pRequestShutdown = sdv::core::GetObject<sdv::app::IAppShutdownRequest>("AppControlService");
if (!pRequestShutdown) break; if (!pRequestShutdown) break;
@@ -237,13 +237,13 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
return APP_CONTROL_INVALID_ISOLATION_CONFIG; return APP_CONTROL_INVALID_ISOLATION_CONFIG;
} }
SConnectEventCallbackWrapper sConnectEventWrapper; SConnectEventCallbackWrapper sConnectEventWrapper;
uint64_t uiCookie = pConnect->RegisterStatusEventCallback(&sConnectEventWrapper); uint64_t uiCookie = pConnect->RegisterStateEventCallback(&sConnectEventWrapper);
// Connect to the core repository // Connect to the core repository
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl"); sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
if (!pConnectionControl) if (!pConnectionControl)
{ {
pConnect->UnregisterStatusEventCallback(uiCookie); pConnect->UnregisterStateEventCallback(uiCookie);
if (!bSilent) if (!bSilent)
std::cerr << "ERROR: " << COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl; std::cerr << "ERROR: " << COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR; return COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR;
@@ -252,7 +252,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
sdv::com::TConnectionID tConnection = pConnectionControl->AssignClientEndpoint(ptrChannelEndpoint, 3000, pCoreRepo); sdv::com::TConnectionID tConnection = pConnectionControl->AssignClientEndpoint(ptrChannelEndpoint, 3000, pCoreRepo);
if (!tConnection.uiControl || !pCoreRepo) if (!tConnection.uiControl || !pCoreRepo)
{ {
pConnect->UnregisterStatusEventCallback(uiCookie); pConnect->UnregisterStateEventCallback(uiCookie);
if (!bSilent) if (!bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << std::endl; std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << std::endl;
return CONNECT_SDV_SERVER_ERROR; return CONNECT_SDV_SERVER_ERROR;
@@ -262,7 +262,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
sdv::core::ILinkCoreRepository* pLinkCoreRepo = sdv::core::GetObject<sdv::core::ILinkCoreRepository>("RepositoryService"); sdv::core::ILinkCoreRepository* pLinkCoreRepo = sdv::core::GetObject<sdv::core::ILinkCoreRepository>("RepositoryService");
if (!pLinkCoreRepo) if (!pLinkCoreRepo)
{ {
pConnect->UnregisterStatusEventCallback(uiCookie); pConnect->UnregisterStateEventCallback(uiCookie);
if (!bSilent) if (!bSilent)
std::cerr << "ERROR: " << LINK_REPO_SERVICE_ERROR_MSG << std::endl; std::cerr << "ERROR: " << LINK_REPO_SERVICE_ERROR_MSG << std::endl;
return LINK_REPO_SERVICE_ERROR; return LINK_REPO_SERVICE_ERROR;
@@ -281,7 +281,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
sdv::core::IRepositoryUtilityCreate* pCreateUtility = sdv::core::GetObject<sdv::core::IRepositoryUtilityCreate>("RepositoryService"); sdv::core::IRepositoryUtilityCreate* pCreateUtility = sdv::core::GetObject<sdv::core::IRepositoryUtilityCreate>("RepositoryService");
if (!pRepositoryInfo || !pRepositoryControl || !pObjectAccess || !pCreateUtility) if (!pRepositoryInfo || !pRepositoryControl || !pObjectAccess || !pCreateUtility)
{ {
pConnect->UnregisterStatusEventCallback(uiCookie); pConnect->UnregisterStateEventCallback(uiCookie);
if (!bSilent) if (!bSilent)
std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl; std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR; return REPOSITORY_SERVICE_ACCESS_ERROR;
@@ -356,7 +356,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
} }
// Shutdwown // Shutdwown
pConnect->UnregisterStatusEventCallback(uiCookie); pConnect->UnregisterStateEventCallback(uiCookie);
parser.Clear(); parser.Clear();
pLinkCoreRepo->UnlinkCoreRepository(); pLinkCoreRepo->UnlinkCoreRepository();
appcontrol.Shutdown(); appcontrol.Shutdown();

View File

@@ -314,10 +314,10 @@ std::string CVSSBSCodingRX::Code_BS_RXConstructor(const SSignalVDDefinition& sig
mapKeywords["signal_name"] = function.signalName; mapKeywords["signal_name"] = function.signalName;
return ReplaceKeywords(R"code( return ReplaceKeywords(R"code(
auto %signal_name%Device = sdv::core::GetObject("%vd_vss_original%_Device").GetInterface<vss::%vd_vssWithColons%Device::IVSS_%vd_function_name%>(); auto %signal_name%Device = sdv::core::GetObject("%vd_vss_original%_Device").GetInterface<vss::%vd_vssWithColons%Device::IVSS_Read%vd_function_name%>();
if (!%signal_name%Device) if (!%signal_name%Device)
{ {
SDV_LOG_ERROR("Could not get interface 'IVSS_%vd_function_name%': [CBasicService%vd_class_name%]"); SDV_LOG_ERROR("Could not get interface 'IVSS_Read%vd_function_name%': [CBasicService%vd_class_name%]");
throw std::runtime_error("%vd_vss_original% mode device not found"); throw std::runtime_error("%vd_vss_original% mode device not found");
} }
%signal_name%Device->Register%vd_function_name%Event(dynamic_cast<vss::%vd_vssWithColons%Device::IVSS_Write%vd_function_name%_Event*> (this)); %signal_name%Device->Register%vd_function_name%Event(dynamic_cast<vss::%vd_vssWithColons%Device::IVSS_Write%vd_function_name%_Event*> (this));
@@ -337,7 +337,7 @@ std::string CVSSBSCodingRX::Code_BS_RXDestructor(const SSignalVDDefinition& sign
mapKeywords["vd_function_name"] = functionVD.functionName; mapKeywords["vd_function_name"] = functionVD.functionName;
return ReplaceKeywords(R"code( return ReplaceKeywords(R"code(
auto %vd_signal_name%Device = sdv::core::GetObject("%vd_vss_original%_Device").GetInterface<vss::%vd_vssWithColons%Device::IVSS_%vd_function_name%>(); auto %vd_signal_name%Device = sdv::core::GetObject("%vd_vss_original%_Device").GetInterface<vss::%vd_vssWithColons%Device::IVSS_Read%vd_function_name%>();
if (%vd_signal_name%Device) if (%vd_signal_name%Device)
{ {
%vd_signal_name%Device->Unregister%vd_function_name%Event(dynamic_cast<vss::%vd_vssWithColons%Device::IVSS_Write%vd_function_name%_Event*> (this)); %vd_signal_name%Device->Unregister%vd_function_name%Event(dynamic_cast<vss::%vd_vssWithColons%Device::IVSS_Write%vd_function_name%_Event*> (this));

View File

@@ -30,7 +30,7 @@ const char szRXVehicleDeviceHeaderTemplate[] = R"code(/**
#include "../signal_identifier.h" #include "../signal_identifier.h"
/** /**
* @brief Vehicle device %vss_original% * @brief Platform abstraction %vss_original%
*/ */
class CVehicleDevice%class_name% class CVehicleDevice%class_name%
: public sdv::CSdvObject : public sdv::CSdvObject
@@ -128,7 +128,7 @@ public:
BEGIN_SDV_INTERFACE_MAP() BEGIN_SDV_INTERFACE_MAP()
%rx_bs_interface_entry_list% END_SDV_INTERFACE_MAP() %rx_bs_interface_entry_list% END_SDV_INTERFACE_MAP()
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::sensor)
DECLARE_OBJECT_CLASS_NAME("%vss_original%_Service") DECLARE_OBJECT_CLASS_NAME("%vss_original%_Service")
/** /**

View File

@@ -30,7 +30,7 @@ const char szTXVehicleDeviceHeaderTemplate[] = R"code(/**
/** /**
* @brief Vehicle device %vss_shorten_no_dot% * @brief Platform abstraction %vss_shorten_no_dot%
*/ */
class CVehicleDevice%class_name% class CVehicleDevice%class_name%
: public sdv::CSdvObject : public sdv::CSdvObject
@@ -124,7 +124,7 @@ public:
BEGIN_SDV_INTERFACE_MAP() BEGIN_SDV_INTERFACE_MAP()
%tx_bs_interface_entry_list% END_SDV_INTERFACE_MAP() %tx_bs_interface_entry_list% END_SDV_INTERFACE_MAP()
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::actuator)
DECLARE_OBJECT_CLASS_NAME("%vss_original%_Service") DECLARE_OBJECT_CLASS_NAME("%vss_original%_Service")
/** /**

View File

@@ -193,9 +193,9 @@ std::string CVSSVDCodingRX::Code_RXIDLDeviceInterface(const std::string& spaces,
%multiple_spaces%}; %multiple_spaces%};
%multiple_spaces%/** %multiple_spaces%/**
%multiple_spaces%* @brief IVSS_%function_name% abstract %prefix% interface %multiple_spaces%* @brief IVSS_Read%function_name% abstract %prefix% interface
%multiple_spaces%*/ %multiple_spaces%*/
%multiple_spaces%interface IVSS_%function_name% %multiple_spaces%interface IVSS_Read%function_name%
%multiple_spaces%{ %multiple_spaces%{
%multiple_spaces% /** %multiple_spaces% /**
%multiple_spaces% * @brief Register Write%function_name% event on signal change %multiple_spaces% * @brief Register Write%function_name% event on signal change
@@ -276,7 +276,7 @@ std::string CVSSVDCodingRX::Code_VD_RXInterface(const std::string & functionName
mapKeywords["function_name"] = functionName; mapKeywords["function_name"] = functionName;
mapKeywords["vss_shorten_with_colons"] = vssShortenWithColons; mapKeywords["vss_shorten_with_colons"] = vssShortenWithColons;
return ReplaceKeywords(R"code( , public vss::%vss_shorten_with_colons%Device::IVSS_%function_name% return ReplaceKeywords(R"code( , public vss::%vss_shorten_with_colons%Device::IVSS_Read%function_name%
)code", mapKeywords); )code", mapKeywords);
} }
@@ -286,7 +286,7 @@ std::string CVSSVDCodingRX::Code_VD_RXInterfaceEntry(const std::string& function
mapKeywords["function_name"] = functionName; mapKeywords["function_name"] = functionName;
mapKeywords["vssWithColons"] = vssWithColons; mapKeywords["vssWithColons"] = vssWithColons;
return ReplaceKeywords(R"code( SDV_INTERFACE_ENTRY(vss::%vssWithColons%Device::IVSS_%function_name%) return ReplaceKeywords(R"code( SDV_INTERFACE_ENTRY(vss::%vssWithColons%Device::IVSS_Read%function_name%)
)code", mapKeywords); )code", mapKeywords);
} }
@@ -305,7 +305,7 @@ std::string CVSSVDCodingRX::Code_VD_RXReAndUnregisterEvent( const std::string& v
void Register%function_name%Event(vss::%vssWithColons%Device::IVSS_Write%function_name%_Event* event) override; void Register%function_name%Event(vss::%vssWithColons%Device::IVSS_Write%function_name%_Event* event) override;
/** /**
* @brief Unregister IVSS_%function_name%_Event * @brief Unregister IVSS_Read%function_name%_Event
* @param[in] event function * @param[in] event function
*/ */
void Unregister%function_name%Event(vss::%vssWithColons%Device::IVSS_Write%function_name%_Event* event) override; void Unregister%function_name%Event(vss::%vssWithColons%Device::IVSS_Write%function_name%_Event* event) override;
@@ -339,7 +339,7 @@ std::string CVSSVDCodingRX::Code_VD_RXPrivateHeaderPart(const SFunctionVDDefinit
*/ */
void ExecuteAllCallBacksFor%start_with_uppercase%(sdv::any_t value); void ExecuteAllCallBacksFor%start_with_uppercase%(sdv::any_t value);
sdv::core::CSignal m_%signal_name%Signal; ///< Signal of the vehicle device sdv::core::CSignal m_%signal_name%Signal; ///< Signal of the platform abstraction
mutable std::mutex m_%signal_name%MutexCallbacks; ///< Mutex protecting m_%signal_name%Callbacks mutable std::mutex m_%signal_name%MutexCallbacks; ///< Mutex protecting m_%signal_name%Callbacks
std::set<vss::%vssWithColons%Device::IVSS_Write%function_name%_Event*> m_%signal_name%Callbacks; ///< collection of events to be called std::set<vss::%vssWithColons%Device::IVSS_Write%function_name%_Event*> m_%signal_name%Callbacks; ///< collection of events to be called
)code", mapKeywords); )code", mapKeywords);

View File

@@ -212,8 +212,10 @@ add_subdirectory(task_timer)
add_subdirectory(ipc_com) add_subdirectory(ipc_com)
add_subdirectory(ipc_connect) add_subdirectory(ipc_connect)
add_subdirectory(ipc_shared_mem) add_subdirectory(ipc_shared_mem)
add_subdirectory(uds_win_sockets)
add_subdirectory(uds_unix_sockets) add_subdirectory(uds_unix_sockets)
add_subdirectory(uds_unix_tunnel)
add_subdirectory(uds_win_sockets)
add_subdirectory(uds_win_tunnel)
add_subdirectory(process_control) add_subdirectory(process_control)
add_subdirectory(hardware_ident) add_subdirectory(hardware_ident)
add_subdirectory(manifest_util) add_subdirectory(manifest_util)

View File

@@ -82,7 +82,7 @@ public:
private: private:
sdv::TInterfaceAccessPtr m_ptrObject; ///< Smart pointer to the object. sdv::TInterfaceAccessPtr m_ptrObject; ///< Smart pointer to the object.
sdv::IObjectControl* m_pObjectControl = nullptr; ///< Pointer to the object control of the application sdv::IObjectControl* m_pObjectControl = nullptr; ///< Pointer to the object control of the application
sdv::EObjectState m_eObjectState = sdv::EObjectState::initialization_pending; ///< Object status (in case there is no object control). sdv::EObjectState m_eObjectState = sdv::EObjectState::initialization_pending; ///< Object state (in case there is no object control).
}; };
#endif // !defined ISOLATION_OBJECT_MONITOR_H #endif // !defined ISOLATION_OBJECT_MONITOR_H

View File

@@ -55,7 +55,7 @@ CChannelConnector::~CChannelConnector()
sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface<sdv::ipc::IConnect>();
if (pConnection) if (pConnection)
{ {
if (m_uiConnectionStatusCookie) pConnection->UnregisterStatusEventCallback(m_uiConnectionStatusCookie); if (m_uiConnectStateCookie) pConnection->UnregisterStateEventCallback(m_uiConnectStateCookie);
pConnection->Disconnect(); pConnection->Disconnect();
} }
@@ -78,8 +78,8 @@ bool CChannelConnector::ServerConnect(sdv::IInterfaceAccess* pObject, bool bAllo
// Establish connection... // Establish connection...
sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface<sdv::ipc::IConnect>();
if (!pConnection) return false; if (!pConnection) return false;
m_uiConnectionStatusCookie = pConnection->RegisterStatusEventCallback(this); m_uiConnectStateCookie = pConnection->RegisterStateEventCallback(this);
return m_uiConnectionStatusCookie != 0 && pConnection->AsyncConnect(this); return m_uiConnectStateCookie != 0 && pConnection->AsyncConnect(this);
} }
sdv::IInterfaceAccess* CChannelConnector::ClientConnect(uint32_t uiTimeoutMs) sdv::IInterfaceAccess* CChannelConnector::ClientConnect(uint32_t uiTimeoutMs)
@@ -99,8 +99,8 @@ sdv::IInterfaceAccess* CChannelConnector::ClientConnect(uint32_t uiTimeoutMs)
// Establish connection... // Establish connection...
sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface<sdv::ipc::IConnect>();
if (pConnection) m_uiConnectionStatusCookie = pConnection->RegisterStatusEventCallback(this); if (pConnection) m_uiConnectStateCookie = pConnection->RegisterStateEventCallback(this);
if (!pConnection || m_uiConnectionStatusCookie == 0 || !pConnection->AsyncConnect(this) || if (!pConnection || m_uiConnectStateCookie == 0 || !pConnection->AsyncConnect(this) ||
!pConnection->WaitForConnection(uiTimeoutMs)) !pConnection->WaitForConnection(uiTimeoutMs))
{ {
SDV_LOG_ERROR("Could not establish a connection!"); SDV_LOG_ERROR("Could not establish a connection!");
@@ -113,17 +113,17 @@ sdv::IInterfaceAccess* CChannelConnector::ClientConnect(uint32_t uiTimeoutMs)
bool CChannelConnector::IsConnected() const bool CChannelConnector::IsConnected() const
{ {
return m_eConnectStatus == sdv::ipc::EConnectStatus::connected; return m_eConnectState == sdv::ipc::EConnectState::connected;
} }
void CChannelConnector::SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) void CChannelConnector::SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState)
{ {
auto eConnectStatusTemp = m_eConnectStatus; auto eConnectStateTemp = m_eConnectState;
m_eConnectStatus = eConnectStatus; m_eConnectState = eConnectState;
switch (m_eConnectStatus) switch (m_eConnectState)
{ {
case sdv::ipc::EConnectStatus::disconnected: case sdv::ipc::EConnectState::disconnected:
case sdv::ipc::EConnectStatus::disconnected_forced: case sdv::ipc::EConnectState::disconnected_forced:
// Invalidate the proxy objects. // Invalidate the proxy objects.
for (auto& rvtProxyObject : m_mapProxyObjects) for (auto& rvtProxyObject : m_mapProxyObjects)
rvtProxyObject.second.reset(); rvtProxyObject.second.reset();
@@ -131,7 +131,7 @@ void CChannelConnector::SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus
if (m_eEndpointType == EEndpointType::server) if (m_eEndpointType == EEndpointType::server)
{ {
// Report information (but only once) // Report information (but only once)
if (sdv::app::ConsoleIsVerbose() && eConnectStatusTemp != sdv::ipc::EConnectStatus::disconnected) if (sdv::app::ConsoleIsVerbose() && eConnectStateTemp != sdv::ipc::EConnectState::disconnected)
std::cout << "Client disconnected (ID#" << m_tConnectionID.uiIdent << ")" << std::endl; std::cout << "Client disconnected (ID#" << m_tConnectionID.uiIdent << ")" << std::endl;
// Remove the connection if reconnection is not enabled (normal case). // Remove the connection if reconnection is not enabled (normal case).
@@ -139,7 +139,7 @@ void CChannelConnector::SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus
m_rcontrol.RemoveConnection(m_tConnectionID); m_rcontrol.RemoveConnection(m_tConnectionID);
} }
break; break;
case sdv::ipc::EConnectStatus::connected: case sdv::ipc::EConnectState::connected:
if (m_eEndpointType == EEndpointType::server) if (m_eEndpointType == EEndpointType::server)
{ {
// Report information // Report information

View File

@@ -71,10 +71,10 @@ public:
bool IsConnected() const; bool IsConnected() const;
/** /**
* @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. * @brief Set the current connect statue. Overload of sdv::ipc::IConnectEventCallback::SetConnectState.
* @param[in] eConnectStatus The connection status. * @param[in] eConnectState The connection state.
*/ */
virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override; virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override;
/** /**
* @brief Callback to be called by the IPC connection when receiving a data packet. Overload of * @brief Callback to be called by the IPC connection when receiving a data packet. Overload of
@@ -139,14 +139,14 @@ private:
} eState = EState::initialized; ///< Data processing state. } eState = EState::initialized; ///< Data processing state.
sdv::sequence<sdv::pointer<uint8_t>> seqResult; ///< The result data. sdv::sequence<sdv::pointer<uint8_t>> seqResult; ///< The result data.
std::mutex mtxWaitForResult; ///< Mutex to protect result processing. std::mutex mtxWaitForResult; ///< Mutex to protect result processing.
std::condition_variable cvWaitForResult; ///< Condition variable to trigger result processing. std::condition_variable cvWaitForResult; ///< Condition var to trigger result processing.
}; };
CCommunicationControl& m_rcontrol; ///< Reference to the communication control class. CCommunicationControl& m_rcontrol; ///< Reference to the communication control class.
sdv::TObjectPtr m_ptrChannelEndpoint; ///< Managed pointer to the channel endpoint. sdv::TObjectPtr m_ptrChannelEndpoint; ///< Managed pointer to the channel endpoint.
uint64_t m_uiConnectionStatusCookie = 0; ///< Connection status cookie (received after registration). uint64_t m_uiConnectStateCookie = 0; ///< Connection state cookie (received after registration).
std::shared_ptr<CMarshallObject> m_ptrInitialMarshallObject; ///< Initial marshall object used after a connect event. 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. sdv::ipc::EConnectState m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< Current connection state.
bool m_bAllowReconnect = false; ///< When set, allow reconnection of the server. bool m_bAllowReconnect = false; ///< When set, allow reconnection of the server.
EEndpointType m_eEndpointType = EEndpointType::client; ///< Endpoint type of this connector. EEndpointType m_eEndpointType = EEndpointType::client; ///< Endpoint type of this connector.
std::recursive_mutex m_mtxMarshallObjects; ///< Synchronize access to the marshall object vector. std::recursive_mutex m_mtxMarshallObjects; ///< Synchronize access to the marshall object vector.

View File

@@ -25,21 +25,21 @@ inline sdv::process::TProcessID GetProcessID()
return pProcessInfo ? pProcessInfo->GetProcessID() : 0; return pProcessInfo ? pProcessInfo->GetProcessID() : 0;
} }
inline std::string ConnectState(sdv::ipc::EConnectStatus eState) inline std::string ConnectState2String(sdv::ipc::EConnectState eState)
{ {
switch (eState) switch (eState)
{ {
case sdv::ipc::EConnectStatus::uninitialized: return "uninitialized"; case sdv::ipc::EConnectState::uninitialized: return "uninitialized";
case sdv::ipc::EConnectStatus::initializing: return "initializing"; case sdv::ipc::EConnectState::initializing: return "initializing";
case sdv::ipc::EConnectStatus::initialized: return "initialized"; case sdv::ipc::EConnectState::initialized: return "initialized";
case sdv::ipc::EConnectStatus::connecting: return "connecting"; case sdv::ipc::EConnectState::connecting: return "connecting";
case sdv::ipc::EConnectStatus::negotiating: return "negotiating"; case sdv::ipc::EConnectState::negotiating: return "negotiating";
case sdv::ipc::EConnectStatus::connection_error: return "connection_error"; case sdv::ipc::EConnectState::connection_error: return "connection_error";
case sdv::ipc::EConnectStatus::connected: return "connected"; case sdv::ipc::EConnectState::connected: return "connected";
case sdv::ipc::EConnectStatus::communication_error: return "communication_error"; case sdv::ipc::EConnectState::communication_error: return "communication_error";
case sdv::ipc::EConnectStatus::disconnected: return "disconnected"; case sdv::ipc::EConnectState::disconnected: return "disconnected";
case sdv::ipc::EConnectStatus::disconnected_forced: return "disconnected_forced"; case sdv::ipc::EConnectState::disconnected_forced: return "disconnected_forced";
case sdv::ipc::EConnectStatus::terminating: return "terminating"; case sdv::ipc::EConnectState::terminating: return "terminating";
default: return "unknown"; default: return "unknown";
} }
} }
@@ -86,11 +86,11 @@ CConnection::~CConnection()
#endif #endif
// If still connected, disconnect. // If still connected, disconnect.
if (m_eStatus == sdv::ipc::EConnectStatus::connected) if (m_eConnectState == sdv::ipc::EConnectState::connected)
Disconnect(); Disconnect();
// Set to terminating to allow the threads to shut down. // Set to terminating to allow the threads to shut down.
m_eStatus = sdv::ipc::EConnectStatus::terminating; m_eConnectState = sdv::ipc::EConnectState::terminating;
// Stop the receive thread to prevent accepting any more messages from the server. // Stop the receive thread to prevent accepting any more messages from the server.
if (m_threadReceive.joinable()) if (m_threadReceive.joinable())
@@ -146,19 +146,19 @@ uint32_t CConnection::Send(const void* pData, uint32_t uiDataLength)
#if ENABLE_REPORTING >= 2 #if ENABLE_REPORTING >= 2
switch (((SMsgHdr*)pData)->eType) switch (((SMsgHdr*)pData)->eType)
{ {
case EMsgType::sync_request: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND SYNC_REQUEST (", ConnectState(m_eStatus), ")"); break; case EMsgType::sync_request: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND SYNC_REQUEST (", ConnectState2String(m_eConnectState), ")"); break;
case EMsgType::sync_answer: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND SYNC_ANSWER (", ConnectState(m_eStatus), ")"); break; case EMsgType::sync_answer: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND SYNC_ANSWER (", ConnectState2String(m_eConnectState), ")"); break;
case EMsgType::connect_request: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_REQUEST (", ConnectState(m_eStatus), ")"); break; case EMsgType::connect_request: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_REQUEST (", ConnectState2String(m_eConnectState), ")"); break;
case EMsgType::connect_answer: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_ANSWER (", ConnectState(m_eStatus), ")"); break; case EMsgType::connect_answer: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_ANSWER (", ConnectState2String(m_eConnectState), ")"); break;
case EMsgType::connect_term: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_TERM (", ConnectState(m_eStatus), ")"); break; case EMsgType::connect_term: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_TERM (", ConnectState2String(m_eConnectState), ")"); break;
#if ENABLE_REPORTING >= 3 #if ENABLE_REPORTING >= 3
case EMsgType::data: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA ", uiDataLength - sizeof(SMsgHdr), " bytes (", ConnectState(m_eStatus), ")"); break; case EMsgType::data: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA ", uiDataLength - sizeof(SMsgHdr), " bytes (", ConnectState2String(m_eConnectState), ")"); break;
case EMsgType::data_fragment: TRACE(m_bServer ? "SERVER" : "CLIENT", " RECEIVE DATA FRAGMENT ", uiDataLength - sizeof(SFragmentedMsgHdr), " bytes (", ConnectState(m_eStatus), ")"); break; case EMsgType::data_fragment: TRACE(m_bServer ? "SERVER" : "CLIENT", " RECEIVE DATA FRAGMENT ", uiDataLength - sizeof(SFragmentedMsgHdr), " bytes (", ConnectState2String(m_eConnectState), ")"); break;
#else #else
case EMsgType::data: break; case EMsgType::data: break;
case EMsgType::data_fragment: break; case EMsgType::data_fragment: break;
#endif #endif
default: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND UNKNOWN (", ConnectState(m_eStatus), ")"); break; default: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND UNKNOWN (", ConnectState2String(m_eConnectState), ")"); break;
} }
#endif #endif
@@ -186,10 +186,10 @@ bool CConnection::SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqDa
} }
TRACE("Send a sequence of data with ", seqData.size(), " pointers with the length {", sstreamReport.str(), "} bytes"); TRACE("Send a sequence of data with ", seqData.size(), " pointers with the length {", sstreamReport.str(), "} bytes");
#endif #endif
// Only allow sending messages when the status is connected // Only allow sending messages when the state is connected
if (m_eStatus != sdv::ipc::EConnectStatus::connected) if (m_eConnectState != sdv::ipc::EConnectState::connected)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return false; return false;
} }
@@ -260,7 +260,7 @@ bool CConnection::SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqDa
auto optPacket = m_sender.Reserve(uiAllocSize); auto optPacket = m_sender.Reserve(uiAllocSize);
if (!optPacket) if (!optPacket)
{ {
if (m_eStatus == sdv::ipc::EConnectStatus::connected) if (m_eConnectState == sdv::ipc::EConnectState::connected)
SDV_LOG_ERROR("Could not reserve a buffer to send a message of ", uiDataSize, " bytes."); SDV_LOG_ERROR("Could not reserve a buffer to send a message of ", uiDataSize, " bytes.");
return false; return false;
} }
@@ -277,7 +277,7 @@ bool CConnection::SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqDa
uiMsgOffset = sizeof(SFragmentedMsgHdr); uiMsgOffset = sizeof(SFragmentedMsgHdr);
#if ENABLE_REPORTING >= 3 #if ENABLE_REPORTING >= 3
TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA FRAGMENT ", uiOffset, "-", uiOffset + uiDataSize - 1, " of ", uiRequiredSize, " bytes (", ConnectState(m_eStatus), ")"); TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA FRAGMENT ", uiOffset, "-", uiOffset + uiDataSize - 1, " of ", uiRequiredSize, " bytes (", ConnectState2String(m_eConnectState), ")");
#endif #endif
} }
else else
@@ -288,7 +288,7 @@ bool CConnection::SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqDa
uiMsgOffset = sizeof(SMsgHdr); uiMsgOffset = sizeof(SMsgHdr);
#if ENABLE_REPORTING >= 3 #if ENABLE_REPORTING >= 3
TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA ", uiRequiredSize, " bytes (", ConnectState(m_eStatus), ")"); TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA ", uiRequiredSize, " bytes (", ConnectState2String(m_eConnectState), ")");
#endif #endif
} }
@@ -352,29 +352,29 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
std::unique_lock<std::mutex> lock(m_mtxConnect); std::unique_lock<std::mutex> lock(m_mtxConnect);
// Allowed to connect? // Allowed to connect?
if (m_eStatus != sdv::ipc::EConnectStatus::uninitialized) if (m_eConnectState != sdv::ipc::EConnectState::uninitialized)
{ {
for (auto& rprEventCallback : m_lstEventCallbacks) for (auto& rprEventCallback : m_lstEventCallbacks)
if (rprEventCallback.pCallback && rprEventCallback.uiCookie) if (rprEventCallback.pCallback && rprEventCallback.uiCookie)
rprEventCallback.pCallback->SetStatus(sdv::ipc::EConnectStatus::connection_error); rprEventCallback.pCallback->SetConnectState(sdv::ipc::EConnectState::connection_error);
return false; return false;
} }
SetStatus(sdv::ipc::EConnectStatus::initializing); SetConnectState(sdv::ipc::EConnectState::initializing);
// Initialized? // Initialized?
if (!m_sender.IsValid() && !m_receiver.IsValid()) if (!m_sender.IsValid() && !m_receiver.IsValid())
{ {
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
SDV_LOG_ERROR("Could not establish connection: sender(", (m_sender.IsValid() ? "valid" : "invalid"), ") receiver(", SDV_LOG_ERROR("Could not establish connection: sender(", (m_sender.IsValid() ? "valid" : "invalid"), ") receiver(",
(m_receiver.IsValid() ? "valid" : "invalid"), ")"); (m_receiver.IsValid() ? "valid" : "invalid"), ")");
SetStatus(sdv::ipc::EConnectStatus::uninitialized); SetConnectState(sdv::ipc::EConnectState::uninitialized);
return false; return false;
} }
// Assign the receiver // Assign the receiver
m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IDataReceiveCallback>(); m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IDataReceiveCallback>();
SetStatus(sdv::ipc::EConnectStatus::initialized); SetConnectState(sdv::ipc::EConnectState::initialized);
// Start the receiving thread (wait until started). // Start the receiving thread (wait until started).
m_threadReceive = std::thread(&CConnection::ReceiveMessages, this); m_threadReceive = std::thread(&CConnection::ReceiveMessages, this);
@@ -397,13 +397,13 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
bool CConnection::WaitForConnection(/*in*/ uint32_t uiWaitMs) bool CConnection::WaitForConnection(/*in*/ uint32_t uiWaitMs)
{ {
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
if (m_eStatus == sdv::ipc::EConnectStatus::connected) if (m_eConnectState == sdv::ipc::EConnectState::connected)
TRACE("Not waiting for a connection - already connected"); TRACE("Not waiting for a connection - already connected");
else else
TRACE("Waiting for a connection of ", uiWaitMs, "ms"); TRACE("Waiting for a connection of ", uiWaitMs, "ms");
#endif #endif
if (m_eStatus == sdv::ipc::EConnectStatus::connected) return true; if (m_eConnectState == sdv::ipc::EConnectState::connected) return true;
std::unique_lock<std::mutex> lock(m_mtxConnect); std::unique_lock<std::mutex> lock(m_mtxConnect);
@@ -413,13 +413,13 @@ bool CConnection::WaitForConnection(/*in*/ uint32_t uiWaitMs)
m_cvConnect.wait_for(lock, std::chrono::milliseconds(uiWaitMs)); m_cvConnect.wait_for(lock, std::chrono::milliseconds(uiWaitMs));
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
if (m_eStatus == sdv::ipc::EConnectStatus::connected) if (m_eConnectState == sdv::ipc::EConnectState::connected)
TRACE("Waiting finished - connection established"); TRACE("Waiting finished - connection established");
else else
TRACE("Waiting finished - timeout occurred"); TRACE("Waiting finished - timeout occurred");
#endif #endif
return m_eStatus == sdv::ipc::EConnectStatus::connected; return m_eConnectState == sdv::ipc::EConnectState::connected;
} }
void CConnection::CancelWait() void CConnection::CancelWait()
@@ -440,19 +440,19 @@ void CConnection::Disconnect()
// Cancel any waits, just in case // Cancel any waits, just in case
CancelWait(); CancelWait();
// Set the disconnect status // Set the disconnect state
sdv::ipc::EConnectStatus eStatus = m_eStatus; sdv::ipc::EConnectState eConnectState = m_eConnectState;
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
// Release the interface // Release the interface
m_pReceiver = nullptr; m_pReceiver = nullptr;
// If connected, send termination message. // If connected, send termination message.
switch (eStatus) switch (eConnectState)
{ {
case sdv::ipc::EConnectStatus::connecting: case sdv::ipc::EConnectState::connecting:
case sdv::ipc::EConnectStatus::negotiating: case sdv::ipc::EConnectState::negotiating:
case sdv::ipc::EConnectStatus::connected: case sdv::ipc::EConnectState::connected:
Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::connect_term }); Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::connect_term });
break; break;
default: default:
@@ -464,7 +464,7 @@ void CConnection::Disconnect()
#endif #endif
} }
uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) uint64_t CConnection::RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback)
{ {
if (!pEventCallback) return 0; if (!pEventCallback) return 0;
sdv::ipc::IConnectEventCallback* pCallback = pEventCallback->GetInterface<sdv::ipc::IConnectEventCallback>(); sdv::ipc::IConnectEventCallback* pCallback = pEventCallback->GetInterface<sdv::ipc::IConnectEventCallback>();
@@ -477,7 +477,7 @@ uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess*
return uiCookie; return uiCookie;
} }
void CConnection::UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) void CConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie)
{ {
std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks); std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
auto itEventCallback = std::find_if(m_lstEventCallbacks.begin(), m_lstEventCallbacks.end(), auto itEventCallback = std::find_if(m_lstEventCallbacks.begin(), m_lstEventCallbacks.end(),
@@ -486,9 +486,9 @@ void CConnection::UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie)
itEventCallback->pCallback = nullptr; itEventCallback->pCallback = nullptr;
} }
sdv::ipc::EConnectStatus CConnection::GetStatus() const sdv::ipc::EConnectState CConnection::GetConnectState() const
{ {
return m_eStatus; return m_eConnectState;
} }
void CConnection::DestroyObject() void CConnection::DestroyObject()
@@ -500,8 +500,8 @@ void CConnection::DestroyObject()
// Disconnect // Disconnect
Disconnect(); Disconnect();
// Set termination status. // Set termination state.
SetStatus(sdv::ipc::EConnectStatus::terminating); SetConnectState(sdv::ipc::EConnectState::terminating);
// Clear all events callbacks (if not done so already) // Clear all events callbacks (if not done so already)
std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks); std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
@@ -523,32 +523,32 @@ void CConnection::DestroyObject()
#endif #endif
} }
void CConnection::SetStatus(sdv::ipc::EConnectStatus eStatus) void CConnection::SetConnectState(sdv::ipc::EConnectState eConnectState)
{ {
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
TRACE(m_bServer ? "SERVER" : "CLIENT", " Changing connect status from '", ConnectState(m_eStatus), "' to '", ConnectState(eStatus), "'"); TRACE(m_bServer ? "SERVER" : "CLIENT", " Changing connect state from '", ConnectState2String(m_eConnectState), "' to '", ConnectState2String(eConnectState), "'");
#endif #endif
// Do not change the status when terminated. // Do not change the state when terminated.
if (m_eStatus == sdv::ipc::EConnectStatus::terminating) if (m_eConnectState == sdv::ipc::EConnectState::terminating)
return; return;
// Only set the member variable if the status is not communication_error // Only set the member variable if the state is not communication_error
if (eStatus != sdv::ipc::EConnectStatus::communication_error) if (eConnectState != sdv::ipc::EConnectState::communication_error)
m_eStatus = eStatus; m_eConnectState = eConnectState;
std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks); std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
for (auto& rprEventCallback : m_lstEventCallbacks) for (auto& rprEventCallback : m_lstEventCallbacks)
{ {
if (rprEventCallback.pCallback && rprEventCallback.uiCookie) if (rprEventCallback.pCallback && rprEventCallback.uiCookie)
rprEventCallback.pCallback->SetStatus(eStatus); rprEventCallback.pCallback->SetConnectState(eConnectState);
} }
// If disconnected by force update the disconnect status. // If disconnected by force update the disconnect state.
if (m_eStatus == sdv::ipc::EConnectStatus::disconnected_forced) if (m_eConnectState == sdv::ipc::EConnectState::disconnected_forced)
m_eStatus = sdv::ipc::EConnectStatus::disconnected; m_eConnectState = sdv::ipc::EConnectState::disconnected;
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
TRACE("Status updated..."); TRACE("State updated...");
#endif #endif
} }
@@ -574,7 +574,7 @@ void CConnection::ReceiveMessages()
if (!m_sender.IsValid() || !m_receiver.IsValid()) if (!m_sender.IsValid() || !m_receiver.IsValid())
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_ERROR("No valid shared memory for receiving."); SDV_LOG_ERROR("No valid shared memory for receiving.");
lock.unlock(); lock.unlock();
m_cvStartConnect.notify_all(); m_cvStartConnect.notify_all();
@@ -590,7 +590,7 @@ void CConnection::ReceiveMessages()
std::chrono::high_resolution_clock::time_point tpLastReceiveLoop = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point tpLastReceiveLoop = std::chrono::high_resolution_clock::now();
#endif #endif
SDataContext sDataCtxt; SDataContext sDataCtxt;
while (m_eStatus != sdv::ipc::EConnectStatus::terminating) while (m_eConnectState != sdv::ipc::EConnectState::terminating)
{ {
#ifdef TIME_TRACKING #ifdef TIME_TRACKING
std::chrono::high_resolution_clock::time_point tpTrackNow = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point tpTrackNow = std::chrono::high_resolution_clock::now();
@@ -604,7 +604,7 @@ void CConnection::ReceiveMessages()
{ {
// Start communication, but only if connection is client based. Server based should not start the communication. If // Start communication, but only if connection is client based. Server based should not start the communication. If
// there is no client, the server would otherwise fill its send-buffer. Repeat sending every 500ms. // there is no client, the server would otherwise fill its send-buffer. Repeat sending every 500ms.
if (!m_bServer && (/*m_eStatus == sdv::ipc::EConnectStatus::disconnected ||*/ m_eStatus == sdv::ipc::EConnectStatus::initialized)) if (!m_bServer && (/*m_eConnectState == sdv::ipc::EConnectState::disconnected ||*/ m_eConnectState == sdv::ipc::EConnectState::initialized))
{ {
// Send request // Send request
auto tpNow = std::chrono::high_resolution_clock::now(); auto tpNow = std::chrono::high_resolution_clock::now();
@@ -629,7 +629,7 @@ void CConnection::ReceiveMessages()
if (!message.IsValid()) if (!message.IsValid())
{ {
auto ssError = m_receiver.GetError(); auto ssError = m_receiver.GetError();
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_ERROR("The message is invalid (invalid size or invalid type)."); SDV_LOG_ERROR("The message is invalid (invalid size or invalid type).");
continue; continue;
} }
@@ -638,7 +638,7 @@ void CConnection::ReceiveMessages()
message.PrintHeader(*this); message.PrintHeader(*this);
// Extra check to prevent race condition. // Extra check to prevent race condition.
if (m_eStatus == sdv::ipc::EConnectStatus::terminating) break; if (m_eConnectState == sdv::ipc::EConnectState::terminating) break;
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
switch (message.GetMsgHdr().eType) switch (message.GetMsgHdr().eType)
@@ -846,7 +846,7 @@ bool CConnection::ReadDataChunk(CMessage& rMessage, uint32_t uiOffset, SDataCont
std::unique_lock<std::mutex> lockReceive(m_mtxReceive); std::unique_lock<std::mutex> lockReceive(m_mtxReceive);
while (m_queueReceive.size() >= 16) while (m_queueReceive.size() >= 16)
{ {
if (m_eStatus == sdv::ipc::EConnectStatus::terminating) return false; if (m_eConnectState == sdv::ipc::EConnectState::terminating) return false;
m_cvReceiveProcessed.wait_for(lockReceive, std::chrono::milliseconds(100)); m_cvReceiveProcessed.wait_for(lockReceive, std::chrono::milliseconds(100));
} }
@@ -868,7 +868,7 @@ bool CConnection::ReadDataChunk(CMessage& rMessage, uint32_t uiOffset, SDataCont
#if ENABLE_DECOUPLING > 0 #if ENABLE_DECOUPLING > 0
void CConnection::DecoupleReceive() void CConnection::DecoupleReceive()
{ {
while (m_eStatus != sdv::ipc::EConnectStatus::terminating) while (m_eConnectState != sdv::ipc::EConnectState::terminating)
{ {
// Wait for data // Wait for data
std::unique_lock<std::mutex> lock(m_mtxReceive); std::unique_lock<std::mutex> lock(m_mtxReceive);
@@ -887,7 +887,7 @@ void CConnection::DecoupleReceive()
size_t nSize = 0; size_t nSize = 0;
for (const sdv::pointer<uint8_t>& ptrData : seqData) for (const sdv::pointer<uint8_t>& ptrData : seqData)
nSize += ptrData.size(); nSize += ptrData.size();
TRACE(m_bServer ? "SERVER" : "CLIENT", " DECOUPLED REVEICE DATA ", nSize, " bytes (", ConnectState(m_eStatus), ")"); TRACE(m_bServer ? "SERVER" : "CLIENT", " DECOUPLED REVEICE DATA ", nSize, " bytes (", ConnectState2String(m_eConnectState), ")");
#endif #endif
// Process the data // Process the data
@@ -903,28 +903,28 @@ void CConnection::ReceiveSyncRequest(const CMessage& rMessage)
{ {
if (rMessage.GetSize() != sizeof(SMsgHdr)) if (rMessage.GetSize() != sizeof(SMsgHdr))
{ {
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
SDV_LOG_ERROR("Sync request received but with incorrect structure size ", rMessage.GetSize(), " in the request, but ", SDV_LOG_ERROR("Sync request received but with incorrect structure size ", rMessage.GetSize(), " in the request, but ",
sizeof(SMsgHdr), " needed!"); sizeof(SMsgHdr), " needed!");
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
return; return;
} }
// Check for compatibility // Check for compatibility
if (rMessage.GetMsgHdr().uiVersion != SDVFrameworkInterfaceVersion) if (rMessage.GetMsgHdr().uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
SDV_LOG_ERROR("Sync request received for an incompatible communication; interface version ", rMessage.GetMsgHdr().uiVersion, SDV_LOG_ERROR("Sync request received for an incompatible communication; interface version ", rMessage.GetMsgHdr().uiVersion,
" requested, but ", SDVFrameworkInterfaceVersion, " needed!"); " requested, but ", SDVFrameworkInterfaceVersion, " needed!");
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
return; return;
} }
else else
{ {
// Start connecting // Start connecting
if (m_eStatus == sdv::ipc::EConnectStatus::disconnected || m_eStatus == sdv::ipc::EConnectStatus::initialized) if (m_eConnectState == sdv::ipc::EConnectState::disconnected || m_eConnectState == sdv::ipc::EConnectState::initialized)
{ {
SetStatus(sdv::ipc::EConnectStatus::connecting); SetConnectState(sdv::ipc::EConnectState::connecting);
// Send an answer // Send an answer
Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::sync_answer }); Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::sync_answer });
@@ -935,18 +935,18 @@ void CConnection::ReceiveSyncRequest(const CMessage& rMessage)
void CConnection::ReceiveConnectRequest(const CMessage& rMessage) void CConnection::ReceiveConnectRequest(const CMessage& rMessage)
{ {
// Start negotiating // Start negotiating
if (m_eStatus == sdv::ipc::EConnectStatus::connecting) if (m_eConnectState == sdv::ipc::EConnectState::connecting)
{ {
// The connect message contains the process ID to monitor. // The connect message contains the process ID to monitor.
m_rWatchDog.AddMonitor(rMessage.GetConnectHdr().tProcessID, this); m_rWatchDog.AddMonitor(rMessage.GetConnectHdr().tProcessID, this);
// Replay to the request // Replay to the request
SetStatus(sdv::ipc::EConnectStatus::negotiating); SetConnectState(sdv::ipc::EConnectState::negotiating);
Send(SConnectMsg{ {SDVFrameworkInterfaceVersion, EMsgType::connect_answer}, Send(SConnectMsg{ {SDVFrameworkInterfaceVersion, EMsgType::connect_answer},
static_cast<sdv::process::TProcessID>(GetProcessID()) }); static_cast<sdv::process::TProcessID>(GetProcessID()) });
// Connected // Connected
SetStatus(sdv::ipc::EConnectStatus::connected); SetConnectState(sdv::ipc::EConnectState::connected);
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
TRACE("Trigger connected"); TRACE("Trigger connected");
#endif #endif
@@ -956,34 +956,34 @@ void CConnection::ReceiveConnectRequest(const CMessage& rMessage)
void CConnection::ReceiveSyncAnswer(const CMessage& rMessage) void CConnection::ReceiveSyncAnswer(const CMessage& rMessage)
{ {
if (m_eStatus != sdv::ipc::EConnectStatus::disconnected && m_eStatus != sdv::ipc::EConnectStatus::initialized) if (m_eConnectState != sdv::ipc::EConnectState::disconnected && m_eConnectState != sdv::ipc::EConnectState::initialized)
return; return;
// Check for compatibility // Check for compatibility
if (rMessage.GetMsgHdr().uiVersion != SDVFrameworkInterfaceVersion) if (rMessage.GetMsgHdr().uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_ERROR("Sync answer received for an incompatible communication; interface version ", SDV_LOG_ERROR("Sync answer received for an incompatible communication; interface version ",
rMessage.GetMsgHdr().uiVersion, " requested, but ", SDVFrameworkInterfaceVersion, " needed!"); rMessage.GetMsgHdr().uiVersion, " requested, but ", SDVFrameworkInterfaceVersion, " needed!");
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
return; return;
} }
// Start negotiating // Start negotiating
SetStatus(sdv::ipc::EConnectStatus::negotiating); SetConnectState(sdv::ipc::EConnectState::negotiating);
Send(SConnectMsg{ {SDVFrameworkInterfaceVersion, EMsgType::connect_request}, GetProcessID() }); Send(SConnectMsg{ {SDVFrameworkInterfaceVersion, EMsgType::connect_request}, GetProcessID() });
} }
void CConnection::ReceiveConnectAnswer(const CMessage& rMessage) void CConnection::ReceiveConnectAnswer(const CMessage& rMessage)
{ {
// Connection established // Connection established
if (m_eStatus == sdv::ipc::EConnectStatus::negotiating) if (m_eConnectState == sdv::ipc::EConnectState::negotiating)
{ {
// The connect message contains the process ID to monitor. // The connect message contains the process ID to monitor.
m_rWatchDog.AddMonitor(rMessage.GetConnectHdr().tProcessID, this); m_rWatchDog.AddMonitor(rMessage.GetConnectHdr().tProcessID, this);
// Connected // Connected
SetStatus(sdv::ipc::EConnectStatus::connected); SetConnectState(sdv::ipc::EConnectState::connected);
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
TRACE("Trigger connected..."); TRACE("Trigger connected...");
#endif #endif
@@ -993,7 +993,7 @@ void CConnection::ReceiveConnectAnswer(const CMessage& rMessage)
void CConnection::ReceiveConnectTerm(CMessage& /*rMessage*/) void CConnection::ReceiveConnectTerm(CMessage& /*rMessage*/)
{ {
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
m_rWatchDog.RemoveMonitor(this); m_rWatchDog.RemoveMonitor(this);
// Cancel any outstanding write... and reset the read position of the sender (otherwise any outstanding data will have // Cancel any outstanding write... and reset the read position of the sender (otherwise any outstanding data will have
@@ -1016,7 +1016,7 @@ void CConnection::ReceiveDataMessage(CMessage& rMessage, SDataContext& rsDataCtx
uint32_t uiOffset = ReadDataTable(rMessage, rsDataCtxt); uint32_t uiOffset = ReadDataTable(rMessage, rsDataCtxt);
if (!uiOffset) if (!uiOffset)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
@@ -1033,7 +1033,7 @@ void CConnection::ReceiveDataMessage(CMessage& rMessage, SDataContext& rsDataCtx
// Read data // Read data
if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt))
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
@@ -1057,7 +1057,7 @@ void CConnection::ReceiveDataFragementMessage(CMessage& rMessage, SDataContext&
uiOffset = ReadDataTable(rMessage, rsDataCtxt); uiOffset = ReadDataTable(rMessage, rsDataCtxt);
if (!uiOffset) if (!uiOffset)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
@@ -1075,7 +1075,7 @@ void CConnection::ReceiveDataFragementMessage(CMessage& rMessage, SDataContext&
// Read data chunk // Read data chunk
if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt))
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
} }
@@ -1147,19 +1147,19 @@ void CConnection::CMessage::PrintHeader([[maybe_unused]] const CConnection& rCon
#if ENABLE_REPORTING >= 2 #if ENABLE_REPORTING >= 2
switch (GetMsgHdr().eType) switch (GetMsgHdr().eType)
{ {
case EMsgType::sync_request: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE SYNC_REQUEST (", ConnectState(rConnection.GetStatus()), ")"); break; case EMsgType::sync_request: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE SYNC_REQUEST (", ConnectState2String(rConnection.GetConnectState()), ")"); break;
case EMsgType::sync_answer: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE SYNC_ANSWER (", ConnectState(rConnection.GetStatus()), ")"); break; case EMsgType::sync_answer: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE SYNC_ANSWER (", ConnectState2String(rConnection.GetConnectState()), ")"); break;
case EMsgType::connect_request: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_REQUEST (", ConnectState(rConnection.GetStatus()), ")"); break; case EMsgType::connect_request: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_REQUEST (", ConnectState2String(rConnection.GetConnectState()), ")"); break;
case EMsgType::connect_answer: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_ANSWER (", ConnectState(rConnection.GetStatus()), ")"); break; case EMsgType::connect_answer: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_ANSWER (", ConnectState2String(rConnection.GetConnectState()), ")"); break;
case EMsgType::connect_term: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_TERM (", ConnectState(rConnection.GetStatus()), ")"); break; case EMsgType::connect_term: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_TERM (", ConnectState2String(rConnection.GetConnectState()), ")"); break;
#if ENABLE_REPORTING >= 3 #if ENABLE_REPORTING >= 3
case EMsgType::data: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE DATA ", GetSize() - sizeof(SMsgHdr), " bytes (", ConnectState(rConnection.GetStatus()), ")"); break; case EMsgType::data: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE DATA ", GetSize() - sizeof(SMsgHdr), " bytes (", ConnectState2String(rConnection.GetConnectState()), ")"); break;
case EMsgType::data_fragment: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE DATA FRAGMENT ", GetSize() - sizeof(SFragmentedMsgHdr), " bytes (", ConnectState(rConnection.GetStatus()), ")"); break; case EMsgType::data_fragment: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE DATA FRAGMENT ", GetSize() - sizeof(SFragmentedMsgHdr), " bytes (", ConnectState2String(rConnection.GetConnectState()), ")"); break;
#else #else
case EMsgType::data: break; case EMsgType::data: break;
case EMsgType::data_fragment: break; case EMsgType::data_fragment: break;
#endif #endif
default: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE UNKNOWN version=", static_cast<uint32_t>(GetMsgHdr().uiVersion), " type=", static_cast<uint32_t>(GetMsgHdr().eType), "(", ConnectState(rConnection.GetStatus()), ")"); break; default: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE UNKNOWN version=", static_cast<uint32_t>(GetMsgHdr().uiVersion), " type=", static_cast<uint32_t>(GetMsgHdr().eType), "(", ConnectState2String(rConnection.GetConnectState()), ")"); break;
} }
#endif #endif
} }

View File

@@ -106,7 +106,7 @@ public:
* @brief Establish a connection and start sending/receiving messages. Overload of * @brief Establish a connection and start sending/receiving messages. Overload of
* sdv::ipc::IConnect::AsyncConnect. * sdv::ipc::IConnect::AsyncConnect.
* @param[in] pReceiver The message has to be forwarded. * @param[in] pReceiver The message has to be forwarded.
* @return Returns 'true' when a connection could be established. Use IConnectStatus or IConnectEventCallback to check the * @return Returns 'true' when a connection could be established. Use IConnect or IConnectEventCallback to check the
* connection state. * connection state.
*/ */
virtual bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override; virtual bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override;
@@ -127,33 +127,33 @@ public:
// Suppress cppcheck warning. The destructor calls Disconnect without dynamic binding. This is correct so. // Suppress cppcheck warning. The destructor calls Disconnect without dynamic binding. This is correct so.
// cppcheck-suppress virtualCallInConstructor // cppcheck-suppress virtualCallInConstructor
/** /**
* @brief Disconnect from a connection. This will set the connect status to disconnected. Overload of * @brief Disconnect from a connection. This will set the connect state to disconnected. Overload of
* sdv::ipc::IConnect::Disconnect. * sdv::ipc::IConnect::Disconnect.
*/ */
virtual void Disconnect() override; virtual void Disconnect() override;
/** /**
* @brief Register event callback interface. Overload of sdv::ipc::IConnect::RegisterStatusEventCallback. * @brief Register event callback interface. Overload of sdv::ipc::IConnect::RegisterStateEventCallback.
* @details Register a connection status event callback interface. The exposed interface must be of type * @details Register a connection state event callback interface. The exposed interface must be of type
* IConnectEventCallback. The registration will exist until a call to the unregister function with the returned cookie * IConnectEventCallback. The registration will exist until a call to the unregister function with the returned cookie
* or until the connection is terminated. * or until the connection is terminated.
* @param[in] pEventCallback Pointer to the object exposing the IConnectEventCallback interface. * @param[in] pEventCallback Pointer to the object exposing the IConnectEventCallback interface.
* @return The cookie assigned to the registration. * @return The cookie assigned to the registration.
*/ */
virtual uint64_t RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; virtual uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override;
/** /**
* @brief Unregister the status event callback with the returned cookie from the registration. Overload of * @brief Unregister the state event callback with the returned cookie from the registration. Overload of
* sdv::ipc::IConnect::UnregisterStatusEventCallback. * sdv::ipc::IConnect::UnregisterStateEventCallback.
* @param[in] uiCookie The cookie returned by a previous call to the registration function. * @param[in] uiCookie The cookie returned by a previous call to the registration function.
*/ */
virtual void UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) override; virtual void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override;
/** /**
* @brief Get status of the connection. Overload of sdv::ipc::IConnect::GetStatus. * @brief Get the current state of the IPC conection. Overload of sdv::ipc::IConnect::GetConnectState.
* @return Returns the ipc::EConnectStatus struct * @return Returns connection state.
*/ */
virtual sdv::ipc::EConnectStatus GetStatus() const override; virtual sdv::ipc::EConnectState GetConnectState() const override;
/** /**
* @brief Destroy the object. Overload of IObjectDestroy::DestroyObject. * @brief Destroy the object. Overload of IObjectDestroy::DestroyObject.
@@ -162,10 +162,10 @@ public:
virtual void DestroyObject() override; virtual void DestroyObject() override;
/** /**
* @brief Set the connection status and if needed call the event callback. * @brief Set the connection state and if needed call the event callback.
* @param[in] eStatus The new status. * @param[in] eConnectState The new state.
*/ */
void SetStatus(sdv::ipc::EConnectStatus eStatus); void SetConnectState(sdv::ipc::EConnectState eConnectState);
/** /**
* @brief Returns whether this is a server connection or a client connection. * @brief Returns whether this is a server connection or a client connection.
@@ -258,14 +258,14 @@ private:
CSharedMemBufferTx m_sender; ///< Shared buffer for sending. CSharedMemBufferTx m_sender; ///< Shared buffer for sending.
CSharedMemBufferRx m_receiver; ///< Shared buffer for receiving. CSharedMemBufferRx m_receiver; ///< Shared buffer for receiving.
std::thread m_threadReceive; ///< Thread which receives data from the socket. std::thread m_threadReceive; ///< Thread which receives data from the socket.
std::atomic<sdv::ipc::EConnectStatus> m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< the status of the connection std::atomic<sdv::ipc::EConnectState> m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< the state of the connection
sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages to if available sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages to if available
std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list. Only locking when std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list. Only locking when
///< inserting. ///< inserting.
std::list<SEventCallback> m_lstEventCallbacks; ///< List containing event callbacks. New callbacks will std::list<SEventCallback> m_lstEventCallbacks; ///< List containing event callbacks. New callbacks will
///< be inserted in front (called first). Removed ///< be inserted in front (called first). Removed
///< callbacks are NULL; the entry stays to allow ///< callbacks are NULL; the entry stays to allow
///< removal during a SetStatus call. ///< removal during a SetConnectState call.
mutable std::mutex m_mtxSend; ///< Synchronize all packages to be sent. mutable std::mutex m_mtxSend; ///< Synchronize all packages to be sent.
std::mutex m_mtxConnect; ///< Connection mutex. std::mutex m_mtxConnect; ///< Connection mutex.
std::condition_variable m_cvConnect; ///< Connection variable for connecting. std::condition_variable m_cvConnect; ///< Connection variable for connecting.

View File

@@ -196,7 +196,7 @@ void CWatchDog::ProcessTerminated(/*in*/ sdv::process::TProcessID tProcessID, /*
// Inform the connection about the removed process. // Inform the connection about the removed process.
for (auto& rptrConnection : vecDisconnectedConnections) for (auto& rptrConnection : vecDisconnectedConnections)
{ {
rptrConnection->SetStatus(sdv::ipc::EConnectStatus::disconnected_forced); rptrConnection->SetConnectState(sdv::ipc::EConnectState::disconnected_forced);
#if ENABLE_REPORTING > 0 #if ENABLE_REPORTING > 0
TRACE("Forced disconnection for PID#", tProcessID); TRACE("Forced disconnection for PID#", tProcessID);

View File

@@ -13,16 +13,13 @@ if(UNIX)
project(uds_unix_sockets VERSION 1.0 LANGUAGES CXX) project(uds_unix_sockets VERSION 1.0 LANGUAGES CXX)
# Define target # Define target
add_library(uds_unix_sockets SHARED add_library(uds_unix_sockets STATIC
"channel_mgnt.h" channel_mgnt.cpp
"channel_mgnt.cpp" connection.cpp
"connection.h"
"connection.cpp"
) )
target_link_libraries(uds_unix_sockets rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) 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/) target_include_directories(uds_unix_sockets PRIVATE ./include/)
set_target_properties(uds_unix_sockets PROPERTIES PREFIX "") set_target_properties(uds_unix_sockets PROPERTIES PREFIX "")
set_target_properties(uds_unix_sockets PROPERTIES SUFFIX ".sdv") set_target_properties(uds_unix_sockets PROPERTIES SUFFIX ".sdv")

View File

@@ -105,7 +105,7 @@ CUnixSocketConnection::CUnixSocketConnection(int preconfiguredFd,
, m_UdsPath(udsPath) , m_UdsPath(udsPath)
, m_StopReceiveThread(false) , m_StopReceiveThread(false)
, m_StopConnectThread(false) , m_StopConnectThread(false)
, m_eStatus(sdv::ipc::EConnectStatus::uninitialized) , m_eConnectState(sdv::ipc::EConnectState::uninitialized)
, m_pReceiver(nullptr) , m_pReceiver(nullptr)
, m_pEvent(nullptr) , m_pEvent(nullptr)
{ {
@@ -170,10 +170,10 @@ bool CUnixSocketConnection::SendData(sdv::sequence<sdv::pointer<uint8_t>>& seqDa
#endif #endif
// Only send when connected and FD valid // Only send when connected and FD valid
if (m_eStatus != sdv::ipc::EConnectStatus::connected || m_Fd < 0) if (m_eConnectState != sdv::ipc::EConnectState::connected || m_Fd < 0)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][TX] Send requested while not connected or FD invalid (status=", static_cast<int>(m_eStatus.load()), ")"); SDV_LOG_WARNING("[UDS][TX] Send requested while not connected or FD invalid (state=", static_cast<int>(m_eConnectState.load()), ")");
return false; return false;
} }
@@ -328,7 +328,7 @@ bool CUnixSocketConnection::SendData(sdv::sequence<sdv::pointer<uint8_t>>& seqDa
return true; return true;
} }
uint64_t CUnixSocketConnection::RegisterStatusEventCallback(sdv::IInterfaceAccess* pEventCallback) uint64_t CUnixSocketConnection::RegisterStateEventCallback(sdv::IInterfaceAccess* pEventCallback)
{ {
if (!pEventCallback) return 0; if (!pEventCallback) return 0;
@@ -351,7 +351,7 @@ uint64_t CUnixSocketConnection::RegisterStatusEventCallback(sdv::IInterfaceAcces
return cookie; return cookie;
} }
void CUnixSocketConnection::UnregisterStatusEventCallback(uint64_t uiCookie) void CUnixSocketConnection::UnregisterStateEventCallback(uint64_t uiCookie)
{ {
if (!uiCookie) return; if (!uiCookie) return;
@@ -386,7 +386,7 @@ bool CUnixSocketConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
std::lock_guard<std::mutex> lk(m_StateMtx); std::lock_guard<std::mutex> lk(m_StateMtx);
m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IDataReceiveCallback>(); m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IDataReceiveCallback>();
m_pEvent = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IConnectEventCallback>(); m_pEvent = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IConnectEventCallback>();
m_eStatus = sdv::ipc::EConnectStatus::initializing; m_eConnectState = sdv::ipc::EConnectState::initializing;
// Reset stop flags // Reset stop flags
m_StopReceiveThread.store(false); m_StopReceiveThread.store(false);
@@ -450,22 +450,22 @@ int CUnixSocketConnection::AcceptConnection() // deprecated
bool CUnixSocketConnection::WaitForConnection(uint32_t uiWaitMs) bool CUnixSocketConnection::WaitForConnection(uint32_t uiWaitMs)
{ {
if (m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected) return true; if (m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected) return true;
std::unique_lock<std::mutex> lk(m_MtxConnect); std::unique_lock<std::mutex> lk(m_MtxConnect);
if (uiWaitMs == 0xFFFFFFFFu) if (uiWaitMs == 0xFFFFFFFFu)
{ {
m_CvConnect.wait(lk, [this]{ m_CvConnect.wait(lk, [this]{
return m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected; return m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected;
}); });
return true; return true;
} }
if (uiWaitMs == 0u) if (uiWaitMs == 0u)
return (m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected); return (m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected);
return m_CvConnect.wait_for(lk, std::chrono::milliseconds(uiWaitMs), return m_CvConnect.wait_for(lk, std::chrono::milliseconds(uiWaitMs),
[this]{ return m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected; }); [this]{ return m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected; });
} }
void CUnixSocketConnection::CancelWait() void CUnixSocketConnection::CancelWait()
@@ -476,33 +476,33 @@ void CUnixSocketConnection::CancelWait()
void CUnixSocketConnection::Disconnect() void CUnixSocketConnection::Disconnect()
{ {
StopThreadsAndCloseSockets(/*unlinkPath*/ false); StopThreadsAndCloseSockets(/*unlinkPath*/ false);
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
} }
sdv::ipc::EConnectStatus CUnixSocketConnection::GetStatus() const sdv::ipc::EConnectState CUnixSocketConnection::GetConnectState() const
{ {
return m_eStatus; return m_eConnectState;
} }
void CUnixSocketConnection::DestroyObject() void CUnixSocketConnection::DestroyObject()
{ {
m_StopReceiveThread.store(true); m_StopReceiveThread.store(true);
m_eStatus = sdv::ipc::EConnectStatus::disconnected; m_eConnectState = sdv::ipc::EConnectState::disconnected;
} }
void CUnixSocketConnection::SetStatus(sdv::ipc::EConnectStatus eStatus) void CUnixSocketConnection::SetConnectState(sdv::ipc::EConnectState eConnectState)
{ {
// Update internal state atomically and wake up waiters. // Update internal state atomically and wake up waiters.
{ {
std::lock_guard<std::mutex> lk(m_MtxConnect); std::lock_guard<std::mutex> lk(m_MtxConnect);
m_eStatus.store(eStatus, std::memory_order_release); m_eConnectState.store(eConnectState, std::memory_order_release);
} }
m_CvConnect.notify_all(); m_CvConnect.notify_all();
// Notify the legacy single-listener (kept for backward compatibility). // Notify the legacy single-listener (kept for backward compatibility).
try try
{ {
m_pEvent->SetStatus(eStatus); m_pEvent->SetConnectState(eConnectState);
} }
catch (...) { /* swallow: user callback must not crash transport */ } catch (...) { /* swallow: user callback must not crash transport */ }
@@ -515,7 +515,7 @@ void CUnixSocketConnection::SetStatus(sdv::ipc::EConnectStatus eStatus)
if (!entry.pCallback) { needCompact = true; continue; } if (!entry.pCallback) { needCompact = true; continue; }
try try
{ {
entry.pCallback->SetStatus(eStatus); entry.pCallback->SetConnectState(eConnectState);
} }
catch (...) { /* swallow per-listener */ } catch (...) { /* swallow per-listener */ }
} }
@@ -588,7 +588,7 @@ void CUnixSocketConnection::ConnectWorker()
if (!EnsureDir(dir)) if (!EnsureDir(dir))
{ {
SDV_LOG_ERROR("[UDS][Server] ensure_dir('", dir, "') failed: ", std::strerror(errno)); SDV_LOG_ERROR("[UDS][Server] ensure_dir('", dir, "') failed: ", std::strerror(errno));
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return; return;
} }
@@ -596,7 +596,7 @@ void CUnixSocketConnection::ConnectWorker()
if (m_ListenFd < 0) if (m_ListenFd < 0)
{ {
SDV_LOG_ERROR("[UDS][Server] socket() failed: ", std::strerror(errno)); SDV_LOG_ERROR("[UDS][Server] socket() failed: ", std::strerror(errno));
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return; return;
} }
@@ -608,7 +608,7 @@ void CUnixSocketConnection::ConnectWorker()
{ {
SDV_LOG_ERROR("[UDS][Server] bind('", m_UdsPath, "') failed: ", std::strerror(errno)); SDV_LOG_ERROR("[UDS][Server] bind('", m_UdsPath, "') failed: ", std::strerror(errno));
::close(m_ListenFd); m_ListenFd = -1; ::close(m_ListenFd); m_ListenFd = -1;
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return; return;
} }
@@ -618,7 +618,7 @@ void CUnixSocketConnection::ConnectWorker()
{ {
SDV_LOG_ERROR("[UDS][Server] listen() failed: ", std::strerror(errno)); SDV_LOG_ERROR("[UDS][Server] listen() failed: ", std::strerror(errno));
::close(m_ListenFd); m_ListenFd = -1; ::close(m_ListenFd); m_ListenFd = -1;
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return; return;
} }
@@ -670,12 +670,12 @@ void CUnixSocketConnection::ConnectWorker()
} }
if (clientFd < 0) if (clientFd < 0)
{ {
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return; return;
} }
m_Fd = clientFd; m_Fd = clientFd;
SetStatus(sdv::ipc::EConnectStatus::connected); SetConnectState(sdv::ipc::EConnectState::connected);
StartReceiveThread_Unsafe(); StartReceiveThread_Unsafe();
return; return;
} }
@@ -686,7 +686,7 @@ void CUnixSocketConnection::ConnectWorker()
if (m_Fd < 0) if (m_Fd < 0)
{ {
SDV_LOG_ERROR("[UDS][Client] socket() failed: ", std::strerror(errno)); SDV_LOG_ERROR("[UDS][Client] socket() failed: ", std::strerror(errno));
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return; return;
} }
@@ -702,7 +702,7 @@ void CUnixSocketConnection::ConnectWorker()
{ {
if (::connect(m_Fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == 0) if (::connect(m_Fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == 0)
{ {
SetStatus(sdv::ipc::EConnectStatus::connected); SetConnectState(sdv::ipc::EConnectState::connected);
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
TRACE("[UDS][Client] Connected"); TRACE("[UDS][Client] Connected");
#endif #endif
@@ -719,19 +719,19 @@ void CUnixSocketConnection::ConnectWorker()
} }
::close(m_Fd); m_Fd = -1; ::close(m_Fd); m_Fd = -1;
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return; return;
} }
} }
catch (const std::exception& ex) catch (const std::exception& ex)
{ {
SDV_LOG_ERROR("[UDS][ConnectWorker] exception: ", ex.what()); SDV_LOG_ERROR("[UDS][ConnectWorker] exception: ", ex.what());
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
} }
catch (...) catch (...)
{ {
SDV_LOG_ERROR("[UDS][ConnectWorker] unknown exception"); SDV_LOG_ERROR("[UDS][ConnectWorker] unknown exception");
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
} }
} }
@@ -741,7 +741,7 @@ void CUnixSocketConnection::ReceiveSyncAnswer(const CMessage& message)
const auto hdr = message.GetMsgHdr(); const auto hdr = message.GetMsgHdr();
if (hdr.uiVersion != SDVFrameworkInterfaceVersion) if (hdr.uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] sync_answer with invalid version"); SDV_LOG_WARNING("[UDS][RX] sync_answer with invalid version");
return; return;
} }
@@ -772,13 +772,13 @@ void CUnixSocketConnection::ReceiveMessages()
while (!m_StopReceiveThread.load()) while (!m_StopReceiveThread.load())
{ {
if (m_eStatus == sdv::ipc::EConnectStatus::terminating) break; if (m_eConnectState == sdv::ipc::EConnectState::terminating) break;
// Snapshot FD // Snapshot FD
const int fd = m_Fd; const int fd = m_Fd;
if (fd < 0) if (fd < 0)
{ {
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
SDV_LOG_WARNING("[UDS][RX] FD invalidated -> disconnected"); SDV_LOG_WARNING("[UDS][RX] FD invalidated -> disconnected");
break; break;
} }
@@ -789,7 +789,7 @@ void CUnixSocketConnection::ReceiveMessages()
if (pr == 0) if (pr == 0)
{ {
if (!m_AcceptConnectionRequired && (m_eStatus == sdv::ipc::EConnectStatus::initialized)) if (!m_AcceptConnectionRequired && (m_eConnectState == sdv::ipc::EConnectState::initialized))
{ {
auto now = std::chrono::high_resolution_clock::now(); auto now = std::chrono::high_resolution_clock::now();
if (std::chrono::duration<double>(now - tpStart).count() > 0.5) if (std::chrono::duration<double>(now - tpStart).count() > 0.5)
@@ -805,7 +805,7 @@ void CUnixSocketConnection::ReceiveMessages()
if (pr < 0 || (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) if (pr < 0 || (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) != 0)
{ {
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
SDV_LOG_WARNING("[UDS][RX] poll() hangup/error -> disconnected"); SDV_LOG_WARNING("[UDS][RX] poll() hangup/error -> disconnected");
break; break;
} }
@@ -815,7 +815,7 @@ void CUnixSocketConnection::ReceiveMessages()
uint32_t packetSize = 0; uint32_t packetSize = 0;
if (!ReadTransportHeader(packetSize)) if (!ReadTransportHeader(packetSize))
{ {
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
SDV_LOG_WARNING("[UDS][RX] Invalid/missing transport header -> disconnected"); SDV_LOG_WARNING("[UDS][RX] Invalid/missing transport header -> disconnected");
break; break;
} }
@@ -824,7 +824,7 @@ void CUnixSocketConnection::ReceiveMessages()
std::vector<uint8_t> payload(packetSize); std::vector<uint8_t> payload(packetSize);
if (!ReadNumberOfBytes(reinterpret_cast<char*>(payload.data()), packetSize)) if (!ReadNumberOfBytes(reinterpret_cast<char*>(payload.data()), packetSize))
{ {
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected"); SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected");
break; break;
} }
@@ -832,12 +832,12 @@ void CUnixSocketConnection::ReceiveMessages()
CMessage msg(std::move(payload)); CMessage msg(std::move(payload));
if (!msg.IsValid()) if (!msg.IsValid())
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] Invalid SDV message (envelope)"); SDV_LOG_WARNING("[UDS][RX] Invalid SDV message (envelope)");
continue; continue;
} }
if (m_eStatus == sdv::ipc::EConnectStatus::terminating) break; if (m_eConnectState == sdv::ipc::EConnectState::terminating) break;
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
switch (msg.GetMsgHdr().eType) switch (msg.GetMsgHdr().eType)
@@ -874,12 +874,12 @@ void CUnixSocketConnection::ReceiveMessages()
catch (const std::exception& ex) catch (const std::exception& ex)
{ {
SDV_LOG_ERROR("[UDS][RX] exception: ", ex.what()); SDV_LOG_ERROR("[UDS][RX] exception: ", ex.what());
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
} }
catch (...) catch (...)
{ {
SDV_LOG_ERROR("[UDS][RX] unknown exception"); SDV_LOG_ERROR("[UDS][RX] unknown exception");
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
} }
} }
@@ -888,7 +888,7 @@ void CUnixSocketConnection::ReceiveSyncRequest(const CMessage& message)
const auto hdr = message.GetMsgHdr(); const auto hdr = message.GetMsgHdr();
if (hdr.uiVersion != SDVFrameworkInterfaceVersion) if (hdr.uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] sync_request with invalid version"); SDV_LOG_WARNING("[UDS][RX] sync_request with invalid version");
return; return;
} }
@@ -907,7 +907,7 @@ void CUnixSocketConnection::ReceiveConnectRequest(const CMessage& message)
const auto hdr = message.GetConnectHdr(); const auto hdr = message.GetConnectHdr();
if (hdr.uiVersion != SDVFrameworkInterfaceVersion) if (hdr.uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] connect_request with invalid version"); SDV_LOG_WARNING("[UDS][RX] connect_request with invalid version");
return; return;
} }
@@ -927,19 +927,19 @@ void CUnixSocketConnection::ReceiveConnectAnswer(const CMessage& message)
const auto hdr = message.GetConnectHdr(); const auto hdr = message.GetConnectHdr();
if (hdr.uiVersion != SDVFrameworkInterfaceVersion) if (hdr.uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] connect_answer with invalid version"); SDV_LOG_WARNING("[UDS][RX] connect_answer with invalid version");
return; return;
} }
// Fully established // Fully established
SetStatus(sdv::ipc::EConnectStatus::connected); SetConnectState(sdv::ipc::EConnectState::connected);
} }
void CUnixSocketConnection::ReceiveConnectTerm(const CMessage& /*message*/) void CUnixSocketConnection::ReceiveConnectTerm(const CMessage& /*message*/)
{ {
// Peer requested termination // Peer requested termination
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
m_StopReceiveThread.store(true); m_StopReceiveThread.store(true);
} }
@@ -999,7 +999,7 @@ void CUnixSocketConnection::ReceiveDataMessage(const CMessage& rMessage, SDataCo
uint32_t uiOffset = ReadDataTable(rMessage, rsDataCtxt); uint32_t uiOffset = ReadDataTable(rMessage, rsDataCtxt);
if (!uiOffset) if (!uiOffset)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] Invalid data table"); SDV_LOG_WARNING("[UDS][RX] Invalid data table");
return; return;
} }
@@ -1012,7 +1012,7 @@ void CUnixSocketConnection::ReceiveDataMessage(const CMessage& rMessage, SDataCo
if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt))
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] Failed to read data chunk"); SDV_LOG_WARNING("[UDS][RX] Failed to read data chunk");
return; return;
} }
@@ -1034,7 +1034,7 @@ void CUnixSocketConnection::ReceiveDataFragmentMessage(const CMessage& rMessage,
uiOffset = ReadDataTable(rMessage, rsDataCtxt); uiOffset = ReadDataTable(rMessage, rsDataCtxt);
if (!uiOffset) if (!uiOffset)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] Invalid fragmented data table"); SDV_LOG_WARNING("[UDS][RX] Invalid fragmented data table");
return; return;
} }
@@ -1048,7 +1048,7 @@ void CUnixSocketConnection::ReceiveDataFragmentMessage(const CMessage& rMessage,
if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt))
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] Failed to read fragmented chunk"); SDV_LOG_WARNING("[UDS][RX] Failed to read fragmented chunk");
return; return;
} }

View File

@@ -82,24 +82,24 @@ public:
/** @brief Optionally cancel WaitForConnection (no-op in current implementation). */ /** @brief Optionally cancel WaitForConnection (no-op in current implementation). */
void CancelWait() override; void CancelWait() override;
/** @brief Disconnect and teardown threads/FDs; sets status to 'disconnected'. */ /** @brief Disconnect and teardown threads/FDs; sets state to 'disconnected'. */
void Disconnect() override; void Disconnect() override;
// ---------- IConnect: event callbacks ---------- // ---------- IConnect: event callbacks ----------
/** @brief Register a status event callback (no-op storage in UDS). */ /** @brief Register a state event callback (no-op storage in UDS). */
uint64_t RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override;
/** @brief Unregister a previously registered callback (no-op storage in UDS). */ /** @brief Unregister a previously registered callback (no-op storage in UDS). */
void UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) override; void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override;
/** @brief Get current connection status. */ /** @brief Get current connection state. */
sdv::ipc::EConnectStatus GetStatus() const override; sdv::ipc::EConnectState GetConnectState() const override;
/** @brief Destroy object (IObjectDestroy). */ /** @brief Destroy object (IObjectDestroy). */
void DestroyObject() override; void DestroyObject() override;
/** @brief Set status and notify listeners (callback-safe). */ /** @brief Set state and notify listeners (callback-safe). */
void SetStatus(sdv::ipc::EConnectStatus eStatus); void SetConnectState(sdv::ipc::EConnectState eConnectState);
/** @brief @return true if this side is server (needs accept()), false otherwise. */ /** @brief @return true if this side is server (needs accept()), false otherwise. */
bool IsServer() const; bool IsServer() const;
@@ -297,7 +297,7 @@ public:
* @brief Handle an incoming connect_term message. * @brief Handle an incoming connect_term message.
* *
* Indicates that the peer requests immediate termination of the connection. * Indicates that the peer requests immediate termination of the connection.
* Sets status to disconnected and stops the RX loop. * Sets state to disconnected and stops the RX loop.
* *
* @param message SDV envelope containing the connect_term header. * @param message SDV envelope containing the connect_term header.
*/ */
@@ -352,7 +352,7 @@ public:
// ---------- Internal threading ---------- // ---------- Internal threading ----------
/** @brief Connect worker (server accept loop or client connect retry). */ /** @brief Connect worker (server accept loop or client connect retry). */
void ConnectWorker(); void ConnectWorker();
/** @brief Start RX thread (precondition: status=connected, FD valid). */ /** @brief Start RX thread (precondition: state=connected, FD valid). */
void StartReceiveThread_Unsafe(); void StartReceiveThread_Unsafe();
/** /**
* @brief Stop workers and close sockets, then optionally unlink path. * @brief Stop workers and close sockets, then optionally unlink path.
@@ -373,9 +373,9 @@ private:
std::thread m_ReceiveThread; std::thread m_ReceiveThread;
std::thread m_ConnectThread; std::thread m_ConnectThread;
//Status & synchronization //State & synchronization
std::condition_variable m_StateCv; std::condition_variable m_StateCv;
std::atomic<sdv::ipc::EConnectStatus> m_eStatus { sdv::ipc::EConnectStatus::uninitialized }; std::atomic<sdv::ipc::EConnectState> m_eConnectState { sdv::ipc::EConnectState::uninitialized };
sdv::ipc::IDataReceiveCallback* m_pReceiver { nullptr }; sdv::ipc::IDataReceiveCallback* m_pReceiver { nullptr };
sdv::ipc::IConnectEventCallback* m_pEvent { nullptr }; sdv::ipc::IConnectEventCallback* m_pEvent { nullptr };
std::mutex m_MtxConnect; std::mutex m_MtxConnect;

View File

@@ -0,0 +1,33 @@
#*******************************************************************************
# 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_tunnel VERSION 1.0 LANGUAGES CXX)
# Define target
add_library(uds_unix_tunnel STATIC
channel_mgnt.cpp
connection.cpp
)
target_link_libraries(uds_unix_tunnel rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
target_include_directories(uds_unix_tunnel PRIVATE ./include/)
set_target_properties(uds_unix_tunnel PROPERTIES PREFIX "")
set_target_properties(uds_unix_tunnel PROPERTIES SUFFIX ".sdv")
# Build dependencies
add_dependencies(uds_unix_tunnel CompileCoreIDL)
# Appending the service in the service list
set(SDV_Service_List ${SDV_Service_List} uds_unix_tunnel PARENT_SCOPE)
endif()

View File

@@ -0,0 +1,178 @@
#if defined(__unix__)
#include "channel_mgnt.h"
#include "connection.h" // CUnixTunnelConnection
#include "../sdv_services/uds_unix_sockets/connection.h" // CUnixSocketConnection
#include <support/toml.h>
#include <sstream>
#include <map>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
namespace
{
/**
* @brief Parses a semicolon-separated list of key=value pairs into a map.
*
* Example input: "proto=tunnel;role=server;path=/tmp/tunnel.sock;"
* Example output: { {"proto","tunnel"}, {"role","server"}, {"path","/tmp/tunnel.sock"} }
*
* @param s The input string to parse.
* @return Map of key-value pairs.
*/
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 Clamps an AF_UNIX pathname to the maximum allowed size for sockaddr_un::sun_path.
*
* @param p The path to clamp.
* @return The clamped path string.
*/
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);
}
} // anonymous namespace
std::string CUnixTunnelChannelMgnt::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 CUnixTunnelChannelMgnt::OnInitialize()
{
return true;
}
void CUnixTunnelChannelMgnt::OnShutdown()
{
// Actual cleanup is handled by destructors of CUnixTunnelConnection
// and CUnixSocketConnection (shared_ptr).
}
sdv::ipc::SChannelEndpoint CUnixTunnelChannelMgnt::CreateEndpoint(
const sdv::u8string& ssChannelConfig)
{
sdv::ipc::SChannelEndpoint endpoint{};
const std::string baseDir = MakeUserRuntimeDir();
std::string name = "TUNNEL_" + std::to_string(::getpid());
std::string path = baseDir + "/" + name + ".sock";
// Parse optional TOML config for custom name/path
if (!ssChannelConfig.empty())
{
sdv::toml::CTOMLParser cfg(ssChannelConfig.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);
// Create underlying UDS server transport
auto udsServer = std::make_shared<CUnixSocketConnection>(
-1,
/*acceptConnectionRequired*/ true,
path);
// Create tunnel wrapper on top of UDS
auto tunnelServer = std::make_shared<CUnixTunnelConnection>(
udsServer,
/*channelId*/ 0u);
m_ServerTunnels.push_back(tunnelServer);
endpoint.pConnection = static_cast<sdv::IInterfaceAccess*>(tunnelServer.get());
endpoint.ssConnectString = "proto=tunnel;role=server;path=" + path + ";";
return endpoint;
}
sdv::IInterfaceAccess* CUnixTunnelChannelMgnt::Access(
const sdv::u8string& ssConnectString)
{
const auto kv = ParseKV(static_cast<std::string>(ssConnectString));
// Only handle proto=tunnel
if (!kv.count("proto") || kv.at("proto") != "tunnel")
{
return nullptr;
}
const bool isServer =
(kv.count("role") && kv.at("role") == "server");
const std::string path =
kv.count("path")
? kv.at("path")
: (MakeUserRuntimeDir() + "/TUNNEL_auto.sock");
if (isServer)
{
// For simplicity, create a new server tunnel instance for each Access().
// The SDV framework is expected to call Access(serverCS) only once in normal cases.
auto udsServer = std::make_shared<CUnixSocketConnection>(
-1,
/*acceptConnectionRequired*/ true,
path);
auto tunnelServer = std::make_shared<CUnixTunnelConnection>(
udsServer,
/*channelId*/ 0u);
m_ServerTunnels.push_back(tunnelServer);
return static_cast<sdv::IInterfaceAccess*>(tunnelServer.get());
}
// Client: allocate raw pointer (expected to be managed by SDV framework via IObjectDestroy)
auto udsClient = std::make_shared<CUnixSocketConnection>(
-1,
/*acceptConnectionRequired*/ false,
path);
auto* tunnelClient =
new CUnixTunnelConnection(udsClient, /*channelId*/ 0u);
return static_cast<sdv::IInterfaceAccess*>(tunnelClient);
}
#endif // defined(__unix__)

View File

@@ -0,0 +1,140 @@
/********************************************************************************
* 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_TUNNEL_CHANNEL_MGNT_H
#define UNIX_TUNNEL_CHANNEL_MGNT_H
#include <support/component_impl.h>
#include <interfaces/ipc.h>
#include "../sdv_services/uds_unix_sockets/channel_mgnt.h" // existing UDS transport
class CUnixTunnelConnection;
/**
* @brief Initialize WinSock on Windows (idempotent).
*
* This helper ensures WSAStartup() is called only once in the process.
* On non-Windows platforms, this is a no-op and always returns success.
*
* @return 0 on success, otherwise a WinSock error code (Windows only).
*/
inline int StartUpWinSock()
{
#ifdef _WIN32
static bool isInitialized = false;
if (isInitialized)
{
return 0;
}
WSADATA wsaData {};
const int error = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (error != 0)
{
SDV_LOG_ERROR("WSAStartup failed with error: ", std::to_string(error));
}
else
{
SDV_LOG_INFO("WSAStartup initialized");
isInitialized = true;
}
return error;
#else
// Non-Windows: nothing to do. Return success for symmetry
return 0;
#endif
}
/**
* @class CUnixTunnelChannelMgnt
* @brief IPC channel management class for Unix Domain Socket tunnel communication.
*
* This manager exposes the "tunnel" IPC type, similar to UDS, with channel multiplexing planned.
* It provides creation and access to tunnel endpoints, manages server-side tunnel lifetimes,
* and integrates with the SDV object/component framework.
*/
class CUnixTunnelChannelMgnt :
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("UnixTunnelChannelControl")
DECLARE_OBJECT_CLASS_ALIAS("TunnelChannelControl")
DECLARE_DEFAULT_OBJECT_NAME("TunnelChannelControl")
DECLARE_OBJECT_SINGLETON()
/**
* @brief Destructor for CUnixTunnelChannelMgnt.
*/
virtual ~CUnixTunnelChannelMgnt() = default;
/**
* @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 Creates a tunnel endpoint (server side) and returns endpoint info.
*
* Optionally uses a TOML config string for custom endpoint parameters.
*
* @param ssChannelConfig Optional config string (TOML).
* @return The channel endpoint structure.
*/
sdv::ipc::SChannelEndpoint CreateEndpoint(const sdv::u8string& ssChannelConfig) override;
/**
* @brief Creates or accesses a connection object from the channel connect string.
*
* Parses the connect string and returns a pointer to the appropriate connection access interface.
*
* @param ssConnectString The channel connect string.
* @return Pointer to connection access interface.
*/
sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override;
private:
/**
* @brief Helper: chooses runtime dir (/run/user/<uid>/sdv) or fallback (/tmp/sdv).
*
* Used for determining the directory path for runtime sockets.
*
* @return Directory path for runtime sockets.
*/
static std::string MakeUserRuntimeDir();
/**
* @brief Keeps server-side tunnel connections alive for the lifetime of the manager.
*
* This ensures that server tunnel objects are not destroyed while the manager is active.
*/
std::vector<std::shared_ptr<CUnixTunnelConnection>> m_ServerTunnels;
};
DEFINE_SDV_OBJECT(CUnixTunnelChannelMgnt)
#endif // UNIX_TUNNEL_CHANNEL_MGNT_H
#endif // defined(__unix__)

View File

@@ -0,0 +1,230 @@
/********************************************************************************
* 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 "connection.h"
/**
* @brief Constructs a tunnel connection wrapper on top of an existing UDS transport.
* @param transport Shared pointer to the underlying UDS transport.
* @param channelId Logical channel ID for this tunnel instance.
*/
CUnixTunnelConnection::CUnixTunnelConnection(
std::shared_ptr<CUnixSocketConnection> transport,
uint16_t channelId)
: m_Transport(std::move(transport))
, m_ChannelId(channelId)
{
// No additional initialization required; acts as a thin wrapper.
}
/**
* @brief Prepends a tunnel header and forwards the data to the underlying transport.
* @param seqData Sequence of message buffers to send (may be modified).
* @return true if data was sent successfully, false otherwise.
*/
bool CUnixTunnelConnection::SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
{
if (!m_Transport)
{
return false;
}
// Build tunnel header buffer
sdv::pointer<uint8_t> hdrBuf;
hdrBuf.resize(sizeof(STunnelHeader));
STunnelHeader hdr{};
hdr.uiChannelId = m_ChannelId; // Logical channel for this connection
hdr.uiFlags = 0; // Reserved for future use
// Copy header structure into the first buffer (little-endian host layout)
std::memcpy(hdrBuf.get(), &hdr, sizeof(STunnelHeader));
// Compose new sequence: [header] + original payload chunks
sdv::sequence<sdv::pointer<uint8_t>> seqWithHdr;
seqWithHdr.push_back(hdrBuf);
for (auto& chunk : seqData)
{
seqWithHdr.push_back(chunk);
}
return m_Transport->SendData(seqWithHdr);
}
/**
* @brief Starts asynchronous connect and registers upper-layer callbacks.
* @param pReceiver Pointer to callback interface for data and state notifications.
* @return true if connect started, false otherwise.
*/
bool CUnixTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver)
{
if (!m_Transport)
{
return false;
}
// Store upper-layer callbacks (safe for null)
{
std::lock_guard<std::mutex> lock(m_CallbackMtx);
sdv::TInterfaceAccessPtr acc(pReceiver);
m_pUpperReceiver = acc.GetInterface<sdv::ipc::IDataReceiveCallback>();
m_pUpperEvent = acc.GetInterface<sdv::ipc::IConnectEventCallback>();
}
// Register this tunnel as the data/event receiver in the UDS transport.
// Do NOT pass pReceiver to UDS, only to our upper fields!
return m_Transport->AsyncConnect(this);
}
bool CUnixTunnelConnection::WaitForConnection(/*in*/ uint32_t uiWaitMs)
{
if (!m_Transport)
{
return false;
}
return m_Transport->WaitForConnection(uiWaitMs);
}
void CUnixTunnelConnection::CancelWait()
{
if (!m_Transport)
{
return;
}
m_Transport->CancelWait();
}
void CUnixTunnelConnection::Disconnect()
{
if (!m_Transport)
{
return;
}
m_Transport->Disconnect();
// Clear upper-layer callbacks (thread-safe)
std::lock_guard<std::mutex> lock(m_CallbackMtx);
m_pUpperReceiver = nullptr;
m_pUpperEvent = nullptr;
}
uint64_t CUnixTunnelConnection::RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback)
{
if (!m_Transport)
{
return 0ULL;
}
// Directly forward to the underlying transport. This allows external
// components to receive connect-state changes without the tunnel
// having to implement IConnectEventCallback itself
return m_Transport->RegisterStateEventCallback(pEventCallback);
}
void CUnixTunnelConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie)
{
if (!m_Transport || uiCookie == 0ULL)
{
return;
}
m_Transport->UnregisterStateEventCallback(uiCookie);
}
sdv::ipc::EConnectState CUnixTunnelConnection::GetConnectState() const
{
if (!m_Transport)
{
// Reasonable default if transport is missing
return sdv::ipc::EConnectState::uninitialized;
}
return m_Transport->GetConnectState();
}
void CUnixTunnelConnection::DestroyObject()
{
// Disconnect underlying transport and clear callbacks.
Disconnect();
std::lock_guard<std::mutex> lock(m_CallbackMtx);
m_Transport.reset();
}
void CUnixTunnelConnection::ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
{
#ifdef DEBUG_TUNNEL_RECEIVE
// Optional debug: count every call and print buffer size.
static std::atomic<uint64_t> s_counter{0};
auto id = ++s_counter;
std::cerr << "[Tunnel] ReceiveData call #" << id
<< ", seqData.size=" << seqData.size() << std::endl;
#endif
if (seqData.empty())
{
return;
}
// Extract and validate tunnel header, then remove it
const auto& hdrChunk = seqData[0];
if (hdrChunk.size() < sizeof(STunnelHeader))
{
return;
}
STunnelHeader hdr{};
std::memcpy(&hdr, hdrChunk.get(), sizeof(STunnelHeader));
seqData.erase(seqData.begin()); // remove header chunk
// Forward rest of data to upper-layer receiver (set by AsyncConnect)
sdv::ipc::IDataReceiveCallback* upper = nullptr;
{
std::lock_guard<std::mutex> lock(m_CallbackMtx);
upper = m_pUpperReceiver;
}
if (upper)
{
upper->ReceiveData(seqData);
}
}
void CUnixTunnelConnection::SetChannelId(uint16_t channelId)
{
m_ChannelId = channelId;
}
void CUnixTunnelConnection::SetConnectState(sdv::ipc::EConnectState state)
{
sdv::ipc::IConnectEventCallback* upper = nullptr;
{
std::lock_guard<std::mutex> lock(m_CallbackMtx);
upper = m_pUpperEvent;
}
if (upper)
{
try
{
upper->SetConnectState(state);
}
catch (...)
{
// Never let user callback crash the transport.
}
}
}
#endif // defined(__unix__)

View File

@@ -0,0 +1,191 @@
/********************************************************************************
* 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_TUNNEL_CONNECTION_H
#define UNIX_SOCKET_TUNNEL_CONNECTION_H
#include <interfaces/ipc.h>
#include <support/component_impl.h>
#include <support/interface_ptr.h>
#include <atomic>
#include <cstdint>
#include <mutex>
#include <memory>
#include <thread>
#include <vector>
#include <unordered_map>
#include "../sdv_services/uds_unix_sockets/connection.h" // existing UDS transport
/**
* @class CUnixTunnelConnection
* @brief Logical tunnel connection on top of a shared Unix Domain Socket (UDS) transport.
*
* This class wraps an existing CUnixSocketConnection (physical tunnel port) and provides
* optional tunneling capabilities such as a prepended tunnel header (channelId, flags).
* It is designed to enable demultiplexing of incoming payloads per logical channel in future versions.
*
* Currently, this acts as a simple pass-through wrapper. Tunnel header parsing and multi-channel
* routing are planned as future improvements (see TODOs in implementation).
*/
class CUnixTunnelConnection :
public sdv::IInterfaceAccess,
public sdv::IObjectDestroy,
public sdv::ipc::IDataSend,
public sdv::ipc::IConnect,
public sdv::ipc::IDataReceiveCallback,
public sdv::ipc::IConnectEventCallback
{
public:
/**
* @struct STunnelHeader
* @brief Small header prepended to each tunneled SDV message.
*
* Used for logical channel identification and control flags. This enables future support for
* multiplexing and advanced features (QoS, direction, etc.).
*/
struct STunnelHeader
{
uint16_t uiChannelId; ///< Logical channel ID (e.g., IPC_x / REMOTE_IPC_x)
uint16_t uiFlags; ///< Reserved for future use (QoS, direction, etc.)
};
/**
* @brief Constructs a tunnel connection wrapper.
* @param transport Shared pointer to the underlying UDS transport (physical tunnel port).
* @param channelId Default logical channel ID for this object view (may be ignored if full demux is implemented later).
*/
explicit CUnixTunnelConnection(
std::shared_ptr<CUnixSocketConnection> transport,
uint16_t channelId);
/**
* @brief Destructor. Cleans up resources if needed.
*/
virtual ~CUnixTunnelConnection() = default;
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend)
SDV_INTERFACE_ENTRY(sdv::ipc::IConnect)
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
SDV_INTERFACE_ENTRY(sdv::IObjectDestroy)
END_SDV_INTERFACE_MAP()
// ---------- IDataSend ----------
/**
* @brief Sends a sequence of buffers via the tunnel.
*
* Prepends a STunnelHeader to the outgoing message buffers and forwards them to the underlying UDS transport.
* In the current implementation, the channel header is always present but not yet used for multiplexing.
*
* @param seqData Sequence of message buffers (may be modified by callee).
* @return true on successful send, false otherwise.
*/
bool SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) override;
// ---------- IConnect ----------
/**
* @brief Starts asynchronous connect on the underlying transport and registers this object as receiver.
* @param pReceiver Pointer to callback interface for data and state notifications.
* @return true if connect started, false otherwise.
*/
bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override;
/**
* @brief Waits until the underlying transport becomes 'connected'.
*
* Forwards to CUnixSocketConnection::WaitForConnection.
* @param uiWaitMs Timeout in milliseconds to wait.
* @return true if connection established, false on timeout or error.
*/
bool WaitForConnection(/*in*/ uint32_t uiWaitMs) override;
/**
* @brief Cancels any pending connect or wait operation.
* Delegated to the underlying transport, if needed.
*/
void CancelWait() override;
/**
* @brief Disconnects the tunnel and underlying transport.
*/
void Disconnect() override;
/**
* @brief Registers a state event callback (forwards to transport).
* @param pEventCallback Pointer to event callback interface.
* @return Registration cookie (nonzero) or 0 on failure.
*/
uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override;
/**
* @brief Unregisters a previously registered state event callback.
* @param uiCookie Registration cookie returned by RegisterStateEventCallback.
*/
void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override;
/**
* @brief Gets the current state from the underlying transport.
* @return The current connection state.
*/
sdv::ipc::EConnectState GetConnectState() const override;
// ---------- IObjectDestroy ----------
/**
* @brief Releases and cleans up all resources associated with this object.
*/
void DestroyObject() override;
// ---------- IDataReceiveCallback ----------
/**
* @brief Receives data from the underlying UDS transport.
*
* Expects the first chunk of seqData to be a STunnelHeader, strips and processes it,
* and delivers the remaining payload to the upper-layer receiver registered via AsyncConnect.
*
* @param seqData Sequence of received message buffers (header chunk is removed by this call).
*/
void ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) override;
// ---------- IConnectEventCallback ----------
/**
* @brief Forwards state changes from the underlying UDS transport to the upper layer.
* @param state New connection state.
*/
void SetConnectState(sdv::ipc::EConnectState state) override;
/**
* @brief Assigns a logical channel ID for this connection.
* Optional helper; you may extend with more routing metadata for advanced tunnel use-cases.
* @param channelId The logical channel ID to set.
*/
void SetChannelId(uint16_t channelId);
/**
* @brief Get the logical channel ID for this connection
* @return The channel ID
*/
uint16_t GetChannelId() const noexcept { return m_ChannelId; }
private:
std::shared_ptr<CUnixSocketConnection> m_Transport; ///< shared physical tunnel port
uint16_t m_ChannelId {0}; ///< default logical channel id
sdv::ipc::IDataReceiveCallback* m_pUpperReceiver {nullptr}; ///< Callback to upper layer (data receive)
sdv::ipc::IConnectEventCallback* m_pUpperEvent {nullptr}; ///< Callback to upper layer (state event)
mutable std::mutex m_CallbackMtx; ///< Mutex to guard callback access
};
#endif // UNIX_SOCKET_TUNNEL_CONNECTION_H
#endif // defined(__unix__)

View File

@@ -13,16 +13,22 @@ if(WIN32)
project(uds_win_sockets VERSION 1.0 LANGUAGES CXX) project(uds_win_sockets VERSION 1.0 LANGUAGES CXX)
# Define target # Define target
add_library(uds_win_sockets SHARED add_library(uds_win_sockets STATIC
"channel_mgnt.h" channel_mgnt.cpp
"channel_mgnt.cpp" connection.cpp
"connection.h" )
"connection.cpp")
target_link_libraries(uds_win_sockets ${CMAKE_THREAD_LIBS_INIT} Ws2_32.lib) target_include_directories(uds_win_sockets
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
./include/
)
target_link_options(uds_win_sockets PRIVATE) target_link_libraries(uds_win_sockets
target_include_directories(uds_win_sockets PRIVATE ./include/) PUBLIC
${CMAKE_THREAD_LIBS_INIT}
Ws2_32.lib
)
set_target_properties(uds_win_sockets PROPERTIES PREFIX "") set_target_properties(uds_win_sockets PROPERTIES PREFIX "")
set_target_properties(uds_win_sockets PROPERTIES SUFFIX ".sdv") set_target_properties(uds_win_sockets PROPERTIES SUFFIX ".sdv")

View File

@@ -10,6 +10,7 @@
* Contributors: * Contributors:
* Denisa Ros - initial API and implementation * Denisa Ros - initial API and implementation
********************************************************************************/ ********************************************************************************/
#ifdef _WIN32
#include "channel_mgnt.h" #include "channel_mgnt.h"
#include "connection.h" #include "connection.h"
@@ -323,15 +324,12 @@ static SOCKET ConnectUnixSocket(
const auto deadline = std::chrono::steady_clock::now() + const auto deadline = std::chrono::steady_clock::now() +
std::chrono::milliseconds(totalTimeoutMs); std::chrono::milliseconds(totalTimeoutMs);
int lastError = 0;
while (true) while (true)
{ {
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) if (s == INVALID_SOCKET)
{ {
lastError = WSAGetLastError(); SDV_LOG_ERROR("[AF_UNIX] socket() FAIL (client), WSA=", WSAGetLastError());
SDV_LOG_ERROR("[AF_UNIX] socket() FAIL (client), WSA=", lastError);
return INVALID_SOCKET; return INVALID_SOCKET;
} }
@@ -341,7 +339,7 @@ static SOCKET ConnectUnixSocket(
return s; return s;
} }
lastError = WSAGetLastError(); int lastError = WSAGetLastError();
closesocket(s); closesocket(s);
if (std::chrono::steady_clock::now() >= deadline) if (std::chrono::steady_clock::now() >= deadline)
@@ -364,7 +362,7 @@ bool CSocketsChannelMgnt::OnInitialize()
return true; return true;
} }
void CSocketsChannelMgnt::OnServerClosed(const std::string& udsPath, CConnection* ptr) void CSocketsChannelMgnt::OnServerClosed(const std::string& udsPath, CWinsockConnection* ptr)
{ {
std::lock_guard<std::mutex> lock(m_udsMtx); std::lock_guard<std::mutex> lock(m_udsMtx);
@@ -411,8 +409,8 @@ sdv::ipc::SChannelEndpoint CSocketsChannelMgnt::CreateEndpoint(const sdv::u8stri
return {}; return {};
} }
// Server-side CConnection, it will accept() a client on first use // Server-side CWinsockConnection, it will accept() a client on first use
auto server = std::make_shared<CConnection>(listenSocket, /*acceptRequired*/ true); auto server = std::make_shared<CWinsockConnection>(listenSocket, /*acceptRequired*/ true);
{ {
std::lock_guard<std::mutex> lock(m_udsMtx); std::lock_guard<std::mutex> lock(m_udsMtx);
@@ -481,5 +479,7 @@ sdv::IInterfaceAccess* CSocketsChannelMgnt::Access(const sdv::u8string& cs)
// Client-side connection object (acceptRequired=false) // Client-side connection object (acceptRequired=false)
// Ownership is transferred to the caller (VAPI runtime) // Ownership is transferred to the caller (VAPI runtime)
return new CConnection(s, /*acceptRequired*/ false); return new CWinsockConnection(s, /*acceptRequired*/ false);
} }
#endif

View File

@@ -10,9 +10,10 @@
* Contributors: * Contributors:
* Denisa Ros - initial API and implementation * Denisa Ros - initial API and implementation
********************************************************************************/ ********************************************************************************/
#ifdef _WIN32
#ifndef CHANNEL_MGNT_H #ifndef WIN_SOCKETS_CHANNEL_MGNT_H
#define CHANNEL_MGNT_H #define WIN_SOCKETS_CHANNEL_MGNT_H
#include <support/component_impl.h> #include <support/component_impl.h>
#include <interfaces/ipc.h> #include <interfaces/ipc.h>
@@ -150,6 +151,8 @@ public:
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl") DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
DECLARE_OBJECT_SINGLETON() DECLARE_OBJECT_SINGLETON()
virtual ~CSocketsChannelMgnt() = default;
/** /**
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize. * @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. * @return Returns 'true' when the initialization was successful, 'false' when not.
@@ -169,7 +172,7 @@ public:
* The endpoint is implemented as a local AF_UNIX server socket * The endpoint is implemented as a local AF_UNIX server socket
* (listen socket) on Windows. The connect string has the format: * (listen socket) on Windows. The connect string has the format:
* *
* proto=uds;path=&lt;udsPath&gt; * proto=uds;path=<udsPath>;
* *
* If no configuration is provided or no path is specified, a default * If no configuration is provided or no path is specified, a default
* path is used (under %LOCALAPPDATA%/sdv) * path is used (under %LOCALAPPDATA%/sdv)
@@ -193,7 +196,7 @@ public:
* Overload of sdv::ipc::IChannelAccess::Access * Overload of sdv::ipc::IChannelAccess::Access
* *
* The connect string is expected to contain: * The connect string is expected to contain:
* proto=uds;path=&lt;udsPath&gt; * proto=uds;path=<udsPath>;
* *
* For the first Access() call with a given path, the server-side * For the first Access() call with a given path, the server-side
* connection object created by CreateEndpoint() can be returned. * connection object created by CreateEndpoint() can be returned.
@@ -206,18 +209,17 @@ public:
sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override; sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override;
/** /**
* @brief Called by a CConnection instance when the server side is closed * @brief Called by a CWinsockConnection instance when the server side is closed
* *
* Used to clean up internal registries for a given UDS path * Used to clean up internal registries for a given UDS path
* *
* @param udsPath Path to the UDS endpoint * @param ptr Pointer to the CWinsockConnection instance that was closed
* @param ptr Pointer to the CConnection instance that was closed
*/ */
void OnServerClosed(const std::string& udsPath, CConnection* ptr); void OnServerClosed(const std::string& udsPath, CWinsockConnection* ptr);
private: private:
/// @brief Registry of AF_UNIX server connections keyed by normalized UDS path /// @brief Registry of AF_UNIX server connections keyed by normalized UDS path
std::map<std::string, std::shared_ptr<CConnection>> m_udsServers; std::map<std::string, std::shared_ptr<CWinsockConnection>> m_udsServers;
/** /**
* @brief Set of UDS paths that already returned their server-side * @brief Set of UDS paths that already returned their server-side
@@ -234,4 +236,5 @@ private:
// SDV object factory macro // SDV object factory macro
DEFINE_SDV_OBJECT(CSocketsChannelMgnt) DEFINE_SDV_OBJECT(CSocketsChannelMgnt)
#endif // ! defined CHANNEL_MGNT_H #endif // ! defined WIN_SOCKETS_CHANNEL_MGNT_H
#endif

View File

@@ -10,7 +10,7 @@
* Contributors: * Contributors:
* Denisa Ros - initial API and implementation * Denisa Ros - initial API and implementation
********************************************************************************/ ********************************************************************************/
#ifdef _WIN32
#include "connection.h" #include "connection.h"
#include <numeric> #include <numeric>
@@ -18,8 +18,8 @@
#include <cstring> #include <cstring>
#include <chrono> #include <chrono>
CConnection::CConnection(SOCKET preconfiguredSocket, bool acceptConnectionRequired) CWinsockConnection::CWinsockConnection(unsigned long long preconfiguredSocket, bool acceptConnectionRequired)
: m_ConnectionStatus(sdv::ipc::EConnectStatus::uninitialized) : m_ConnectionState(sdv::ipc::EConnectState::uninitialized)
, m_AcceptConnectionRequired(acceptConnectionRequired) , m_AcceptConnectionRequired(acceptConnectionRequired)
, m_CancelWait(false) , m_CancelWait(false)
{ {
@@ -30,23 +30,34 @@ CConnection::CConnection(SOCKET preconfiguredSocket, bool acceptConnectionRequir
if (m_AcceptConnectionRequired) if (m_AcceptConnectionRequired)
{ {
// Server side: we own a listening socket, active socket is not yet accepted // Server side: we own a listening socket, active socket is not yet accepted
m_ListenSocket = preconfiguredSocket; m_ListenSocket = static_cast<SOCKET>(preconfiguredSocket);
m_ConnectionSocket = INVALID_SOCKET; m_ConnectionSocket = INVALID_SOCKET;
} }
else else
{ {
// Client side: we already have a connected socket // Client side: we already have a connected socket
m_ListenSocket = INVALID_SOCKET; m_ListenSocket = INVALID_SOCKET;
m_ConnectionSocket = preconfiguredSocket; m_ConnectionSocket = static_cast<SOCKET>(preconfiguredSocket);
} }
} }
CConnection::~CConnection() /*CWinsockConnection::CWinsockConnection()
: m_ConnectionState(sdv::ipc::EConnectState::uninitialized)
, m_AcceptConnectionRequired(false)
, m_CancelWait(false)
{
m_ListenSocket = INVALID_SOCKET;
m_ConnectionSocket = INVALID_SOCKET;
std::fill(std::begin(m_SendBuffer), std::end(m_SendBuffer), '\0');
std::fill(std::begin(m_ReceiveBuffer), std::end(m_ReceiveBuffer), '\0');
}*/
CWinsockConnection::~CWinsockConnection()
{ {
try try
{ {
StopThreadsAndCloseSockets(); StopThreadsAndCloseSockets();
m_ConnectionStatus = sdv::ipc::EConnectStatus::disconnected; m_ConnectionState = sdv::ipc::EConnectState::disconnected;
} }
catch (...) catch (...)
{ {
@@ -54,11 +65,11 @@ CConnection::~CConnection()
} }
} }
void CConnection::SetStatus(sdv::ipc::EConnectStatus status) void CWinsockConnection::SetConnectState(sdv::ipc::EConnectState state)
{ {
{ {
std::lock_guard<std::mutex> lk(m_MtxConnect); std::lock_guard<std::mutex> lk(m_MtxConnect);
m_ConnectionStatus.store(status, std::memory_order_release); m_ConnectionState.store(state, std::memory_order_release);
} }
// Wake up any waiter // Wake up any waiter
@@ -69,7 +80,7 @@ void CConnection::SetStatus(sdv::ipc::EConnectStatus status)
{ {
try try
{ {
m_pEvent->SetStatus(status); m_pEvent->SetConnectState(state);
} }
catch (...) catch (...)
{ {
@@ -78,7 +89,7 @@ void CConnection::SetStatus(sdv::ipc::EConnectStatus status)
} }
} }
int32_t CConnection::Send(const char* data, int32_t dataLength) int32_t CWinsockConnection::Send(const char* data, int32_t dataLength)
{ {
int32_t total = 0; int32_t total = 0;
@@ -88,7 +99,7 @@ int32_t CConnection::Send(const char* data, int32_t dataLength)
if (n == SOCKET_ERROR) if (n == SOCKET_ERROR)
{ {
SDV_LOG_ERROR("send failed with error: ", std::to_string(WSAGetLastError())); SDV_LOG_ERROR("send failed with error: ", std::to_string(WSAGetLastError()));
m_ConnectionStatus = sdv::ipc::EConnectStatus::communication_error; m_ConnectionState = sdv::ipc::EConnectState::communication_error;
m_ConnectionSocket = INVALID_SOCKET; m_ConnectionSocket = INVALID_SOCKET;
return (total > 0) ? total : SOCKET_ERROR; return (total > 0) ? total : SOCKET_ERROR;
} }
@@ -98,7 +109,7 @@ int32_t CConnection::Send(const char* data, int32_t dataLength)
return total; return total;
} }
int CConnection::SendExact(const char* data, int len) int CWinsockConnection::SendExact(const char* data, int len)
{ {
int total = 0; int total = 0;
@@ -115,13 +126,13 @@ int CConnection::SendExact(const char* data, int len)
return total; return total;
} }
bool CConnection::SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) bool CWinsockConnection::SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
{ {
// Must be connected and have a valid socket // Must be connected and have a valid socket
if (m_ConnectionStatus != sdv::ipc::EConnectStatus::connected || if (m_ConnectionState != sdv::ipc::EConnectState::connected ||
m_ConnectionSocket == INVALID_SOCKET) m_ConnectionSocket == INVALID_SOCKET)
{ {
m_ConnectionStatus = sdv::ipc::EConnectStatus::communication_error; m_ConnectionState = sdv::ipc::EConnectState::communication_error;
return false; return false;
} }
@@ -288,11 +299,11 @@ bool CConnection::SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqDa
return (sentOffset == required); return (sentOffset == required);
} }
SOCKET CConnection::AcceptConnection() SOCKET CWinsockConnection::AcceptConnection()
{ {
if (m_ListenSocket == INVALID_SOCKET) if (m_ListenSocket == INVALID_SOCKET)
{ {
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return INVALID_SOCKET; return INVALID_SOCKET;
} }
@@ -310,7 +321,7 @@ SOCKET CConnection::AcceptConnection()
if (sr == SOCKET_ERROR) if (sr == SOCKET_ERROR)
{ {
SDV_LOG_ERROR("[AF_UNIX] select(listen) FAIL, WSA=", WSAGetLastError()); SDV_LOG_ERROR("[AF_UNIX] select(listen) FAIL, WSA=", WSAGetLastError());
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return INVALID_SOCKET; return INVALID_SOCKET;
} }
@@ -329,7 +340,7 @@ SOCKET CConnection::AcceptConnection()
} }
SDV_LOG_ERROR("[AF_UNIX] accept FAIL, WSA=", err); SDV_LOG_ERROR("[AF_UNIX] accept FAIL, WSA=", err);
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return INVALID_SOCKET; return INVALID_SOCKET;
} }
@@ -338,11 +349,11 @@ SOCKET CConnection::AcceptConnection()
} }
SDV_LOG_ERROR("[AF_UNIX] accept canceled (stop flag)"); SDV_LOG_ERROR("[AF_UNIX] accept canceled (stop flag)");
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return INVALID_SOCKET; return INVALID_SOCKET;
} }
bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) bool CWinsockConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
{ {
// Store callbacks // Store callbacks
m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IDataReceiveCallback>(); m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IDataReceiveCallback>();
@@ -360,14 +371,14 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
m_ConnectThread.join(); m_ConnectThread.join();
// Start the connect worker // Start the connect worker
m_ConnectThread = std::thread(&CConnection::ConnectWorker, this); m_ConnectThread = std::thread(&CWinsockConnection::ConnectWorker, this);
return true; return true;
} }
bool CConnection::WaitForConnection(uint32_t uiWaitMs) bool CWinsockConnection::WaitForConnection(uint32_t uiWaitMs)
{ {
if (m_ConnectionStatus.load(std::memory_order_acquire) == if (m_ConnectionState.load(std::memory_order_acquire) ==
sdv::ipc::EConnectStatus::connected) sdv::ipc::EConnectState::connected)
{ {
return true; return true;
} }
@@ -380,16 +391,16 @@ bool CConnection::WaitForConnection(uint32_t uiWaitMs)
lk, lk,
[this] [this]
{ {
return m_ConnectionStatus.load(std::memory_order_acquire) == return m_ConnectionState.load(std::memory_order_acquire) ==
sdv::ipc::EConnectStatus::connected; sdv::ipc::EConnectState::connected;
}); });
return true; return true;
} }
if (uiWaitMs == 0u) // zero wait if (uiWaitMs == 0u) // zero wait
{ {
return (m_ConnectionStatus.load(std::memory_order_acquire) == return (m_ConnectionState.load(std::memory_order_acquire) ==
sdv::ipc::EConnectStatus::connected); sdv::ipc::EConnectState::connected);
} }
// finite wait // finite wait
@@ -398,24 +409,24 @@ bool CConnection::WaitForConnection(uint32_t uiWaitMs)
std::chrono::milliseconds(uiWaitMs), std::chrono::milliseconds(uiWaitMs),
[this] [this]
{ {
return m_ConnectionStatus.load(std::memory_order_acquire) == return m_ConnectionState.load(std::memory_order_acquire) ==
sdv::ipc::EConnectStatus::connected; sdv::ipc::EConnectState::connected;
}); });
} }
void CConnection::CancelWait() void CWinsockConnection::CancelWait()
{ {
m_CancelWait.store(true); m_CancelWait.store(true);
} }
void CConnection::Disconnect() void CWinsockConnection::Disconnect()
{ {
m_CancelWait.store(true); m_CancelWait.store(true);
StopThreadsAndCloseSockets(); StopThreadsAndCloseSockets();
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
} }
uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) uint64_t CWinsockConnection::RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback)
{ {
// Extract IConnectEventCallback interface // Extract IConnectEventCallback interface
m_pEvent = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface<sdv::ipc::IConnectEventCallback>(); m_pEvent = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface<sdv::ipc::IConnectEventCallback>();
@@ -424,7 +435,7 @@ uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess*
return (m_pEvent != nullptr) ? 1ULL : 0ULL; return (m_pEvent != nullptr) ? 1ULL : 0ULL;
} }
void CConnection::UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) void CWinsockConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie)
{ {
// Only one callback supported -> cookie value is 1 // Only one callback supported -> cookie value is 1
if (uiCookie == 1ULL) if (uiCookie == 1ULL)
@@ -433,21 +444,21 @@ void CConnection::UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie)
} }
} }
sdv::ipc::EConnectStatus CConnection::GetStatus() const sdv::ipc::EConnectState CWinsockConnection::GetConnectState() const
{ {
return m_ConnectionStatus; return m_ConnectionState;
} }
void CConnection::DestroyObject() void CWinsockConnection::DestroyObject()
{ {
m_StopReceiveThread = true; m_StopReceiveThread = true;
m_StopConnectThread = true; m_StopConnectThread = true;
StopThreadsAndCloseSockets(); StopThreadsAndCloseSockets();
m_ConnectionStatus = sdv::ipc::EConnectStatus::disconnected; m_ConnectionState = sdv::ipc::EConnectState::disconnected;
} }
void CConnection::ConnectWorker() void CWinsockConnection::ConnectWorker()
{ {
try try
{ {
@@ -456,18 +467,18 @@ void CConnection::ConnectWorker()
// SERVER SIDE // SERVER SIDE
if (m_ListenSocket == INVALID_SOCKET) if (m_ListenSocket == INVALID_SOCKET)
{ {
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return; return;
} }
SetStatus(sdv::ipc::EConnectStatus::initializing); SetConnectState(sdv::ipc::EConnectState::initializing);
SDV_LOG_INFO("[AF_UNIX] Srv ConnectWorker start: listen=%llu", SDV_LOG_INFO("[AF_UNIX] Srv ConnectWorker start: listen=%llu",
static_cast<uint64_t>(m_ListenSocket)); static_cast<uint64_t>(m_ListenSocket));
SOCKET c = AcceptConnection(); SOCKET c = AcceptConnection();
SDV_LOG_INFO("[AF_UNIX] Srv AcceptConnection returned: sock=%llu status=%d", SDV_LOG_INFO("[AF_UNIX] Srv AcceptConnection returned: sock=%llu state=%d",
static_cast<uint64_t>(c), static_cast<uint64_t>(c),
static_cast<int>(m_ConnectionStatus.load())); static_cast<int>(m_ConnectionState.load()));
if (c == INVALID_SOCKET) if (c == INVALID_SOCKET)
{ {
@@ -475,7 +486,7 @@ void CConnection::ConnectWorker()
{ {
try try
{ {
m_pEvent->SetStatus(m_ConnectionStatus); m_pEvent->SetConnectState(m_ConnectionState);
} }
catch (...) catch (...)
{ {
@@ -485,7 +496,7 @@ void CConnection::ConnectWorker()
} }
m_ConnectionSocket = c; m_ConnectionSocket = c;
SetStatus(sdv::ipc::EConnectStatus::connected); SetConnectState(sdv::ipc::EConnectState::connected);
StartReceiveThread_Unsafe(); StartReceiveThread_Unsafe();
return; return;
} }
@@ -494,22 +505,22 @@ void CConnection::ConnectWorker()
// CLIENT SIDE // CLIENT SIDE
if (m_ConnectionSocket == INVALID_SOCKET) if (m_ConnectionSocket == INVALID_SOCKET)
{ {
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
return; return;
} }
} }
// Client side: socket is already connected // Client side: socket is already connected
SetStatus(sdv::ipc::EConnectStatus::connected); SetConnectState(sdv::ipc::EConnectState::connected);
StartReceiveThread_Unsafe(); StartReceiveThread_Unsafe();
} }
catch (...) catch (...)
{ {
SetStatus(sdv::ipc::EConnectStatus::connection_error); SetConnectState(sdv::ipc::EConnectState::connection_error);
} }
} }
void CConnection::StartReceiveThread_Unsafe() void CWinsockConnection::StartReceiveThread_Unsafe()
{ {
if (m_ReceiveThread.joinable()) if (m_ReceiveThread.joinable())
{ {
@@ -517,10 +528,10 @@ void CConnection::StartReceiveThread_Unsafe()
} }
m_StopReceiveThread.store(false); m_StopReceiveThread.store(false);
m_ReceiveThread = std::thread(&CConnection::ReceiveMessages, this); m_ReceiveThread = std::thread(&CWinsockConnection::ReceiveMessages, this);
} }
void CConnection::StopThreadsAndCloseSockets() void CWinsockConnection::StopThreadsAndCloseSockets()
{ {
// Signal stop // Signal stop
m_StopReceiveThread.store(true); m_StopReceiveThread.store(true);
@@ -574,7 +585,7 @@ void CConnection::StopThreadsAndCloseSockets()
static_cast<uint64_t>(s)); static_cast<uint64_t>(s));
} }
bool CConnection::ReadNumberOfBytes(char* buffer, uint32_t length) bool CWinsockConnection::ReadNumberOfBytes(char* buffer, uint32_t length)
{ {
uint32_t received = 0; uint32_t received = 0;
@@ -607,7 +618,7 @@ bool CConnection::ReadNumberOfBytes(char* buffer, uint32_t length)
return (received == length); return (received == length);
} }
bool CConnection::ValidateHeader(const SMsgHeader& msgHeader) bool CWinsockConnection::ValidateHeader(const SMsgHeader& msgHeader)
{ {
// Kept only for compatibility with any legacy users (not used in SDV path) // Kept only for compatibility with any legacy users (not used in SDV path)
if ((msgHeader.msgStart == m_MsgStart) && (msgHeader.msgEnd == m_MsgEnd)) if ((msgHeader.msgStart == m_MsgStart) && (msgHeader.msgEnd == m_MsgEnd))
@@ -618,7 +629,7 @@ bool CConnection::ValidateHeader(const SMsgHeader& msgHeader)
} }
uint32_t CConnection::ReadDataTable(const CMessage& message, SDataContext& dataCtx) uint32_t CWinsockConnection::ReadDataTable(const CMessage& message, SDataContext& dataCtx)
{ {
uint32_t offset = 0; uint32_t offset = 0;
@@ -691,7 +702,7 @@ uint32_t CConnection::ReadDataTable(const CMessage& message, SDataContext& dataC
return offset; return offset;
} }
bool CConnection::ReadDataChunk(const CMessage& message, uint32_t offset, SDataContext& dataCtx) bool CWinsockConnection::ReadDataChunk(const CMessage& message, uint32_t offset, SDataContext& dataCtx)
{ {
if (offset < sizeof(SMsgHdr)) if (offset < sizeof(SMsgHdr))
return false; return false;
@@ -743,12 +754,12 @@ bool CConnection::ReadDataChunk(const CMessage& message, uint32_t offset, SDataC
return true; return true;
} }
void CConnection::ReceiveSyncRequest(const CMessage& message) void CWinsockConnection::ReceiveSyncRequest(const CMessage& message)
{ {
const auto hdr = message.GetMsgHdr(); const auto hdr = message.GetMsgHdr();
if (hdr.uiVersion != SDVFrameworkInterfaceVersion) if (hdr.uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
@@ -764,12 +775,12 @@ void CConnection::ReceiveSyncRequest(const CMessage& message)
return; return;
} }
void CConnection::ReceiveSyncAnswer(const CMessage& message) void CWinsockConnection::ReceiveSyncAnswer(const CMessage& message)
{ {
const auto hdr = message.GetMsgHdr(); const auto hdr = message.GetMsgHdr();
if (hdr.uiVersion != SDVFrameworkInterfaceVersion) if (hdr.uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
@@ -786,12 +797,12 @@ void CConnection::ReceiveSyncAnswer(const CMessage& message)
return; return;
} }
void CConnection::ReceiveConnectRequest(const CMessage& message) void CWinsockConnection::ReceiveConnectRequest(const CMessage& message)
{ {
const auto hdr = message.GetConnectHdr(); const auto hdr = message.GetConnectHdr();
if (hdr.uiVersion != SDVFrameworkInterfaceVersion) if (hdr.uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
@@ -808,42 +819,42 @@ void CConnection::ReceiveConnectRequest(const CMessage& message)
return; return;
} }
void CConnection::ReceiveConnectAnswer(const CMessage& message) void CWinsockConnection::ReceiveConnectAnswer(const CMessage& message)
{ {
const auto hdr = message.GetConnectHdr(); const auto hdr = message.GetConnectHdr();
if (hdr.uiVersion != SDVFrameworkInterfaceVersion) if (hdr.uiVersion != SDVFrameworkInterfaceVersion)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
// Handshake complete (client side) // Handshake complete (client side)
SetStatus(sdv::ipc::EConnectStatus::connected); SetConnectState(sdv::ipc::EConnectState::connected);
} }
void CConnection::ReceiveConnectTerm(const CMessage& /*message*/) void CWinsockConnection::ReceiveConnectTerm(const CMessage& /*message*/)
{ {
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
m_StopReceiveThread.store(true); m_StopReceiveThread.store(true);
} }
void CConnection::ReceiveDataMessage(const CMessage& message, SDataContext& dataCtx) void CWinsockConnection::ReceiveDataMessage(const CMessage& message, SDataContext& dataCtx)
{ {
const uint32_t payloadOffset = ReadDataTable(message, dataCtx); const uint32_t payloadOffset = ReadDataTable(message, dataCtx);
if (payloadOffset == 0) if (payloadOffset == 0)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
if (!ReadDataChunk(message, payloadOffset, dataCtx)) if (!ReadDataChunk(message, payloadOffset, dataCtx))
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
} }
void CConnection::ReceiveDataFragmentMessage(const CMessage& message, SDataContext& dataCtx) void CWinsockConnection::ReceiveDataFragmentMessage(const CMessage& message, SDataContext& dataCtx)
{ {
uint32_t offset = static_cast<uint32_t>(sizeof(SFragmentedMsgHdr)); uint32_t offset = static_cast<uint32_t>(sizeof(SFragmentedMsgHdr));
@@ -852,19 +863,19 @@ void CConnection::ReceiveDataFragmentMessage(const CMessage& message, SDataConte
offset = ReadDataTable(message, dataCtx); offset = ReadDataTable(message, dataCtx);
if (offset == 0) if (offset == 0)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
} }
if (!ReadDataChunk(message, offset, dataCtx)) if (!ReadDataChunk(message, offset, dataCtx))
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
return; return;
} }
} }
void CConnection::ReceiveMessages() void CWinsockConnection::ReceiveMessages()
{ {
try try
{ {
@@ -898,14 +909,14 @@ void CConnection::ReceiveMessages()
if (!ReadNumberOfBytes(reinterpret_cast<char*>(&packetSize), if (!ReadNumberOfBytes(reinterpret_cast<char*>(&packetSize),
sizeof(packetSize))) sizeof(packetSize)))
{ {
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected"); SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected");
break; break;
} }
if (packetSize == 0 || packetSize > kMaxUdsPacketSize) if (packetSize == 0 || packetSize > kMaxUdsPacketSize)
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
break; break;
} }
@@ -914,7 +925,7 @@ void CConnection::ReceiveMessages()
if (!ReadNumberOfBytes(reinterpret_cast<char*>(payload.data()), if (!ReadNumberOfBytes(reinterpret_cast<char*>(payload.data()),
packetSize)) packetSize))
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
SDV_LOG_WARNING("[UDS][RX] Invalid SDV message (envelope)"); SDV_LOG_WARNING("[UDS][RX] Invalid SDV message (envelope)");
break; break;
} }
@@ -923,7 +934,7 @@ void CConnection::ReceiveMessages()
CMessage msg(std::move(payload)); CMessage msg(std::move(payload));
if (!msg.IsValid()) if (!msg.IsValid())
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetConnectState(sdv::ipc::EConnectState::communication_error);
continue; continue;
} }
@@ -941,12 +952,13 @@ void CConnection::ReceiveMessages()
break; break;
} }
if (m_ConnectionStatus == sdv::ipc::EConnectStatus::terminating) if (m_ConnectionState == sdv::ipc::EConnectState::terminating)
break; break;
} }
} }
catch (...) catch (...)
{ {
SetStatus(sdv::ipc::EConnectStatus::disconnected); SetConnectState(sdv::ipc::EConnectState::disconnected);
} }
} }
#endif

View File

@@ -10,9 +10,9 @@
* Contributors: * Contributors:
* Denisa Ros - initial API and implementation * Denisa Ros - initial API and implementation
********************************************************************************/ ********************************************************************************/
#ifdef _WIN32
#ifndef CONNECTION_H #ifndef WIN_SOCKETS_CONNECTION_H
#define CONNECTION_H #define WIN_SOCKETS_CONNECTION_H
#include <interfaces/ipc.h> #include <interfaces/ipc.h>
#include <support/interface_ptr.h> #include <support/interface_ptr.h>
@@ -35,6 +35,7 @@
# endif # endif
#endif #endif
/// @brief Legacy framing markers for the old message header (not used by SDV envelope) /// @brief Legacy framing markers for the old message header (not used by SDV envelope)
constexpr uint32_t m_MsgStart = 0x01020304; ///< Value to mark the start of the legacy message header constexpr uint32_t m_MsgStart = 0x01020304; ///< Value to mark the start of the legacy message header
constexpr uint32_t m_MsgEnd = 0x05060708; ///< Value to mark the end of the legacy message header constexpr uint32_t m_MsgEnd = 0x05060708; ///< Value to mark the end of the legacy message header
@@ -67,10 +68,10 @@ struct SMsgHeader
* *
* Exposes: * Exposes:
* - sdv::ipc::IDataSend : sending SDV-framed messages * - sdv::ipc::IDataSend : sending SDV-framed messages
* - sdv::ipc::IConnect : async connect / wait / status / events * - sdv::ipc::IConnect : async connect / wait / state / events
* - sdv::IObjectDestroy : explicit destruction hook for SDV runtime * - sdv::IObjectDestroy : explicit destruction hook for SDV runtime
*/ */
class CConnection class CWinsockConnection
: public sdv::IInterfaceAccess : public sdv::IInterfaceAccess
, public sdv::ipc::IDataSend , public sdv::ipc::IDataSend
, public sdv::ipc::IConnect , public sdv::ipc::IConnect
@@ -80,19 +81,19 @@ public:
/** /**
* @brief default constructor used by create endpoint - allocates new buffers m_Sender and m_Receiver * @brief default constructor used by create endpoint - allocates new buffers m_Sender and m_Receiver
*/ */
CConnection(); CWinsockConnection();
/** /**
* @brief access existing connection * @brief access existing connection
* @param[in] preconfiguredSocket Prepared socket for the connection * @param[in] preconfiguredSocket Prepared socket for the connection
* @param[in] acceptConnectionRequired If true connection has to be accepted before receive thread can be started * @param[in] acceptConnectionRequired If true connection has to be accepted before receive thread can be started
*/ */
CConnection(SOCKET preconfiguredSocket, bool acceptConnectionRequired); CWinsockConnection(unsigned long long preconfiguredSocket, bool acceptConnectionRequired);
/** /**
* @brief Virtual destructor needed for "delete this;" * @brief Virtual destructor needed for "delete this;"
*/ */
virtual ~CConnection(); virtual ~CWinsockConnection();
BEGIN_SDV_INTERFACE_MAP() BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend) SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend)
@@ -122,7 +123,7 @@ public:
* @param[in] pReceiver Object that exposes IDataReceiveCallback and * @param[in] pReceiver Object that exposes IDataReceiveCallback and
* optionally IConnectEventCallback * optionally IConnectEventCallback
* @return true when the connect worker was started successfully * @return true when the connect worker was started successfully
* Use GetStatus / WaitForConnection / callbacks for status * Use GetConnectState / WaitForConnection / callbacks for state
*/ */
bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override; bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override;
@@ -132,7 +133,7 @@ public:
* Overload of sdv::ipc::IConnect::WaitForConnection * Overload of sdv::ipc::IConnect::WaitForConnection
* *
* @param[in] uiWaitMs * @param[in] uiWaitMs
* - 0 : do not wait, just check current status * - 0 : do not wait, just check current state
* - 0xFFFFFFFF: wait indefinitely * - 0xFFFFFFFF: wait indefinitely
* - otherwise : wait up to uiWaitMs milliseconds * - otherwise : wait up to uiWaitMs milliseconds
* @return true if the connection is in connected state when returning * @return true if the connection is in connected state when returning
@@ -149,36 +150,36 @@ public:
/** /**
* @brief Disconnect from the peer and stop I/O threads * @brief Disconnect from the peer and stop I/O threads
* *
* This sets the status to disconnected and releases the event interface * This sets the state to disconnected and releases the event interface
*/ */
void Disconnect() override; void Disconnect() override;
/** /**
* @brief Register event callback interface for connection status updates * @brief Register event callback interface for connection state updates
* *
* Overload of sdv::ipc::IConnect::RegisterStatusEventCallback * Overload of sdv::ipc::IConnect::RegisterStateEventCallback
* *
* @param[in] pEventCallback Pointer to an object exposing * @param[in] pEventCallback Pointer to an object exposing
* sdv::ipc::IConnectEventCallback. * sdv::ipc::IConnectEventCallback.
* @return A registration cookie (1 = valid, 0 = failure) * @return A registration cookie (1 = valid, 0 = failure)
*/ */
uint64_t RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override;
/** /**
* @brief Unregister the status event callback using its cookie * @brief Unregister the state event callback using its cookie
* *
* Overload of sdv::ipc::IConnect::UnregisterStatusEventCallback * Overload of sdv::ipc::IConnect::UnregisterStateEventCallback
* *
* @param[in] uiCookie Cookie returned by RegisterStatusEventCallback * @param[in] uiCookie Cookie returned by RegisterStateEventCallback
*/ */
void UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) override; void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override;
/** /**
* @brief Get current status of the connection * @brief Get current state of the connection
* *
* @return Current sdv::ipc::EConnectStatus * @return Current sdv::ipc::EConnectState
*/ */
sdv::ipc::EConnectStatus GetStatus() const override; sdv::ipc::EConnectState GetConnectState() const override;
/** /**
* @brief Destroy the object * @brief Destroy the object
@@ -196,7 +197,7 @@ private:
std::thread m_ConnectThread; std::thread m_ConnectThread;
std::atomic<bool> m_StopReceiveThread{false}; ///< bool variable to stop thread std::atomic<bool> m_StopReceiveThread{false}; ///< bool variable to stop thread
std::atomic<bool> m_StopConnectThread{false}; std::atomic<bool> m_StopConnectThread{false};
std::atomic<sdv::ipc::EConnectStatus> m_ConnectionStatus; ///< the status of the connection std::atomic<sdv::ipc::EConnectState> m_ConnectionState; ///< the state of the connection
sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages if available sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages if available
sdv::ipc::IConnectEventCallback* m_pEvent = nullptr; ///< Event receiver sdv::ipc::IConnectEventCallback* m_pEvent = nullptr; ///< Event receiver
@@ -217,7 +218,7 @@ private:
/// @brief Server accept loop / client connect confirmation /// @brief Server accept loop / client connect confirmation
void ConnectWorker(); void ConnectWorker();
/// @brief Start the RX thread (pre: status=connected, socket valid) /// @brief Start the RX thread (pre: state=connected, socket valid)
void StartReceiveThread_Unsafe(); void StartReceiveThread_Unsafe();
/// @brief Stop worker threads and close all sockets cleanly /// @brief Stop worker threads and close all sockets cleanly
@@ -451,7 +452,7 @@ private:
* @brief Handle an incoming connect_term message * @brief Handle an incoming connect_term message
* *
* Indicates that the peer requests immediate termination of the connection * Indicates that the peer requests immediate termination of the connection
* Sets status to disconnected and stops the RX loop * Sets state to disconnected and stops the RX loop
* *
* @param message SDV envelope containing the connect_term header * @param message SDV envelope containing the connect_term header
*/ */
@@ -503,8 +504,9 @@ private:
*/ */
bool ReadDataChunk(const CMessage& rMessage, uint32_t uiOffset, SDataContext& rsDataCtxt); bool ReadDataChunk(const CMessage& rMessage, uint32_t uiOffset, SDataContext& rsDataCtxt);
/// @brief Centralized status update (notifies waiters and callbacks) /// @brief Centralized state update (notifies waiters and callbacks)
void SetStatus(sdv::ipc::EConnectStatus status); void SetConnectState(sdv::ipc::EConnectState state);
}; };
#endif // CONNECTION_H #endif // WIN_SOCKETS_CONNECTION_H
#endif

View File

@@ -0,0 +1,40 @@
#*******************************************************************************
# 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
#*******************************************************************************
# Define project
project(uds_win_tunnel VERSION 1.0 LANGUAGES CXX)
# Define target
add_library(uds_win_tunnel STATIC
channel_mgnt.cpp
connection.cpp
)
target_link_libraries(uds_win_tunnel
PRIVATE
uds_win_sockets
Ws2_32.lib
${CMAKE_THREAD_LIBS_INIT}
)
target_include_directories(uds_win_tunnel
PRIVATE
./include/
../uds_win_sockets/
)
set_target_properties(uds_win_tunnel PROPERTIES PREFIX "")
set_target_properties(uds_win_tunnel PROPERTIES SUFFIX ".sdv")
# Build dependencies
add_dependencies(uds_win_tunnel CompileCoreIDL)
# Appending the service in the service list
set(SDV_Service_List ${SDV_Service_List} uds_win_tunnel PARENT_SCOPE)

View File

@@ -0,0 +1,414 @@
/********************************************************************************
* 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
********************************************************************************/
#ifdef _WIN32
#include "channel_mgnt.h"
#include "../../global/base64.h"
#include <support/toml.h>
#include <interfaces/process.h>
#include <future>
#pragma push_macro("interface")
#undef interface
#pragma push_macro("GetObject")
#undef GetObject
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <WinSock2.h>
#include <Windows.h>
#include <ws2tcpip.h>
#include <afunix.h>
#include <array>
#pragma pop_macro("GetObject")
#pragma pop_macro("interface")
extern int StartUpWinSock();
namespace
{
/**
* @brief Parse a tunnel connect/config string and extract the path.
*
* Expected format:
* "proto=tunnel;path=<something>;"
*
* Behavior:
* - If "proto=tunnel" missing -> false
* - If "path=" missing -> true and outPath.clear()
*
* @param[in] cs The connect/config string to parse.
* @param[out] outPath The extracted path, or empty if not found.
* @return true if parsing succeeded, false otherwise.
*/
static bool ParseTunnelPath(const std::string& cs, std::string& outPath)
{
constexpr const char* protoKey = "proto=tunnel";
constexpr const char* pathKey = "path=";
if (cs.find(protoKey) == std::string::npos)
{
return false;
}
const auto p = cs.find(pathKey);
if (p == std::string::npos)
{
outPath.clear();
return true;
}
const auto start = p + std::strlen(pathKey);
const auto end = cs.find(';', start);
if (end == std::string::npos)
{
outPath = cs.substr(start);
}
else
{
outPath = cs.substr(start, end - start);
}
return true;
}
/**
* @brief Expands Windows environment variables in a string (e.g., %TEMP%).
* @param[in] in Input string possibly containing environment variables.
* @return String with environment variables expanded, or original if expansion fails.
*/
static std::string ExpandEnvVars(const std::string& in)
{
if (in.find('%') == std::string::npos)
{
return in;
}
char buf[4096] = {};
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast<DWORD>(sizeof(buf)));
if (n > 0 && n < sizeof(buf))
{
return std::string(buf);
}
return in;
}
/**
* @brief Clamps a UDS path to the maximum allowed by SOCKADDR_UN.
* @param[in] p The input path.
* @return The clamped path.
*/
static std::string ClampUdsPath(const std::string& p)
{
SOCKADDR_UN tmp{};
constexpr auto kMax = sizeof(tmp.sun_path) - 1;
if (p.size() <= kMax)
{
return p;
}
return p.substr(0, kMax);
}
// Only for logging basename
/**
* @brief Normalizes a raw UDS path for Windows, extracting the basename and ensuring a default if empty.
* @param[in] raw The raw path string.
* @return The normalized basename, clamped to max length.
*/
static std::string NormalizeUdsPathForWindows(const std::string& raw)
{
std::string p = ExpandEnvVars(raw);
const size_t pos = p.find_last_of("/\\");
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
if (base.empty())
{
base = "sdv_tunnel.sock";
}
SDV_LOG_INFO("[AF_UNIX][Tunnel] Normalize raw='", raw, "' -> base='", base, "'");
return ClampUdsPath(base);
}
/**
* @brief Creates a short, safe UDS path in the Windows temp directory.
* @param[in] raw The raw path string.
* @return The full path in %TEMP%\sdv\, clamped to max length.
*/
static std::string MakeShortWinUdsPath(const std::string& raw)
{
std::string p = ExpandEnvVars(raw);
const size_t pos = p.find_last_of("/\\");
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
if (base.empty())
{
base = "sdv_tunnel.sock";
}
std::string dir = ExpandEnvVars("%TEMP%\\sdv\\");
CreateDirectoryA(dir.c_str(), nullptr);
const std::string full = dir + base;
return ClampUdsPath(full);
}
/**
* @brief Creates an AF_UNIX listen socket at the specified path.
* @param[in] rawPath The raw path for the socket.
* @return The created socket handle, or INVALID_SOCKET on failure.
*/
static SOCKET CreateUnixListenSocket(const std::string& rawPath)
{
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (listen), WSA=", WSAGetLastError());
return INVALID_SOCKET;
}
std::string udsPath = MakeShortWinUdsPath(rawPath);
SOCKADDR_UN addr{};
addr.sun_family = AF_UNIX;
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
const int addrlen = static_cast<int>(
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
::remove(udsPath.c_str());
if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) == SOCKET_ERROR)
{
SDV_LOG_ERROR("[AF_UNIX][Tunnel] bind FAIL, WSA=",
WSAGetLastError(), ", path='", udsPath, "'");
closesocket(s);
return INVALID_SOCKET;
}
if (listen(s, SOMAXCONN) == SOCKET_ERROR)
{
SDV_LOG_ERROR("[AF_UNIX][Tunnel] listen FAIL, WSA=",
WSAGetLastError(), ", path='", udsPath, "'");
closesocket(s);
return INVALID_SOCKET;
}
SDV_LOG_INFO("[AF_UNIX][Tunnel] bind+listen OK, path='", udsPath, "'");
return s;
}
/**
* @brief Connects to an AF_UNIX socket at the specified path, retrying until timeout.
* @param[in] rawPath The raw path to connect to.
* @param[in] totalTimeoutMs Total timeout in milliseconds.
* @param[in] retryDelayMs Delay between retries in milliseconds.
* @return The connected socket handle, or INVALID_SOCKET on failure.
*/
static SOCKET ConnectUnixSocket(
const std::string& rawPath,
uint32_t totalTimeoutMs,
uint32_t retryDelayMs)
{
const std::string udsPath = MakeShortWinUdsPath(rawPath);
SOCKADDR_UN addr{};
addr.sun_family = AF_UNIX;
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
const int addrlen = static_cast<int>(
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
const auto deadline = std::chrono::steady_clock::now() +
std::chrono::milliseconds(totalTimeoutMs);
int lastError = 0;
while (true)
{
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
lastError = WSAGetLastError();
SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (client), WSA=", lastError);
return INVALID_SOCKET;
}
if (connect(s, reinterpret_cast<const sockaddr*>(&addr), addrlen) == 0)
{
SDV_LOG_INFO("[AF_UNIX][Tunnel] connect OK, path='", udsPath, "'");
return s;
}
lastError = WSAGetLastError();
closesocket(s);
if (std::chrono::steady_clock::now() >= deadline)
{
SDV_LOG_ERROR("[AF_UNIX][Tunnel] connect TIMEOUT, last WSA=",
lastError, ", path='", udsPath, "'");
return INVALID_SOCKET;
}
std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs));
}
}
} // anonymous namespace
bool CSocketsTunnelChannelMgnt::OnInitialize()
{
return true;
}
void CSocketsTunnelChannelMgnt::OnShutdown()
{}
// -------- Server bookkeeping (optional) --------
void CSocketsTunnelChannelMgnt::OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr)
{
std::lock_guard<std::mutex> lock(m_udsMtx);
auto it = m_udsServers.find(udsPath);
if (it != m_udsServers.end() && it->second.get() == ptr)
{
m_udsServers.erase(it);
}
m_udsServerClaimed.erase(udsPath);
}
// -------- ICreateEndpoint --------
sdv::ipc::SChannelEndpoint CSocketsTunnelChannelMgnt::CreateEndpoint(const sdv::u8string& cfgStr)
{
sdv::ipc::SChannelEndpoint ep{};
if (StartUpWinSock() != 0)
{
SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in CreateEndpoint");
return ep;
}
// Optional TOML config: [IpcChannel] Path = "..."
std::string udsRaw;
if (!cfgStr.empty())
{
//for toml file
bool isTOML = cfgStr.find('=') == std::string::npos;
if(isTOML)
{
sdv::toml::CTOMLParser cfg(cfgStr.c_str());
auto pathNode = cfg.GetDirect("IpcChannel.Path");
if (pathNode.GetType() == sdv::toml::ENodeType::node_string)
{
udsRaw = static_cast<std::string>(pathNode.GetValue());
}
}
//for connect string
if (udsRaw.empty())
{
const std::string s(cfgStr);
const std::string key = "path=";
auto pos = s.find(key);
if (pos != std::string::npos)
{
auto end = s.find(';', pos + key.size());
if (end == std::string::npos)
udsRaw = s.substr(pos + key.size());
else
udsRaw = s.substr(pos + key.size(), end - pos - key.size());
}
}
}
if (udsRaw.empty())
{
udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock";
}
std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw);
SDV_LOG_INFO("[AF_UNIX][Tunnel] endpoint udsPath=", udsPathBase);
SOCKET listenSocket = CreateUnixListenSocket(udsPathBase);
if (listenSocket == INVALID_SOCKET)
{
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to create listen socket for endpoint: ", udsPathBase);
return ep;
}
auto serverTransport = std::make_shared<CWinsockConnection>( static_cast<unsigned long long>(listenSocket), true);
auto serverTunnel = std::make_shared<CWinTunnelConnection>(serverTransport, /*channelId*/ static_cast<uint16_t>(0u));
{
std::lock_guard<std::mutex> lock(m_udsMtx);
m_udsServers[udsPathBase] = serverTunnel;
m_udsServerClaimed.erase(udsPathBase);
}
ep.pConnection = static_cast<sdv::IInterfaceAccess*>(serverTunnel.get());
ep.ssConnectString = "proto=tunnel;role=server;path=" + udsPathBase + ";";
return ep;
}
sdv::IInterfaceAccess* CSocketsTunnelChannelMgnt::Access(const sdv::u8string& cs)
{
if (StartUpWinSock() != 0)
{
SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in Access()" );
return nullptr;
}
std::string connectStr = static_cast<std::string>(cs);
std::string udsRaw;
if (!ParseTunnelPath(connectStr, udsRaw))
{
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Invalid tunnel connect string: ", connectStr);
return nullptr;
}
if (udsRaw.empty())
{
udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock";
}
std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw);
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access udsPath=", udsPathBase);
const bool isServer =
(connectStr.find("role=server") != std::string::npos);
{
std::lock_guard<std::mutex> lock(m_udsMtx);
auto it = m_udsServers.find(udsPathBase);
if (isServer && it != m_udsServers.end() && it->second != nullptr)
{
if (!m_udsServerClaimed.count(udsPathBase))
{
m_udsServerClaimed.insert(udsPathBase);
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> RETURN SERVER for ", udsPathBase);
return it->second.get(); // Ownership: managed by m_udsServers (do not delete)
}
}
}
// CLIENT: create AF_UNIX client socket and wrap it in a tunnel
SOCKET s = ConnectUnixSocket(udsPathBase, /*totalTimeoutMs*/ 5000, /*retryDelayMs*/ 50);
if (s == INVALID_SOCKET)
{
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to connect client socket for ", udsPathBase);
return nullptr;
}
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> CREATE CLIENT for ", udsPathBase);
auto clientTransport = std::make_shared<CWinsockConnection>(s, /*acceptRequired*/ false);
// Ownership: The returned pointer must be managed and deleted by the SDV framework via IObjectDestroy
auto* tunnelClient = new CWinTunnelConnection(clientTransport, /*channelId*/ 0u);
return static_cast<sdv::IInterfaceAccess*>(tunnelClient);
}
#endif

View File

@@ -0,0 +1,106 @@
/********************************************************************************
* 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
********************************************************************************/
#ifdef _WIN32
#ifndef WIN_TUNNEL_CHANNEL_MGNT_H
#define WIN_TUNNEL_CHANNEL_MGNT_H
#include <support/component_impl.h>
#include <interfaces/ipc.h>
#include "../sdv_services/uds_win_sockets/channel_mgnt.h"
#include "../sdv_services/uds_win_sockets/connection.h"
#include "connection.h"
#include <mutex>
#include <map>
#include <memory>
#include <set>
#include <string>
// Winsock headers are required for SOCKET / AF_UNIX / WSAStartup
// NOTE: The actual initialization is done via StartUpWinSock()
#include <ws2tcpip.h>
class CWinTunnelConnection;
/**
* @class CSocketsTunnelChannelMgnt
* @brief IPC channel management class for Windows AF_UNIX tunnel communication.
*
* Similar to CSocketsChannelMgnt (proto=uds), but:
* - uses CWinTunnelConnection (tunnel wrapper) on top of CWinsockConnection
* - uses proto=tunnel in connect strings
*
* Provides creation and access to tunnel endpoints, manages server-side tunnel lifetimes,
* and integrates with the SDV object/component framework.
*/
class CSocketsTunnelChannelMgnt :
public sdv::CSdvObject,
public sdv::ipc::ICreateEndpoint,
public sdv::ipc::IChannelAccess
{
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::ipc::IChannelAccess)
SDV_INTERFACE_ENTRY(sdv::ipc::ICreateEndpoint)
END_SDV_INTERFACE_MAP()
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
DECLARE_OBJECT_CLASS_NAME("WinTunnelChannelControl")
DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl")
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
DECLARE_OBJECT_SINGLETON()
virtual ~CSocketsTunnelChannelMgnt() = default;
/**
* @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 Creates a tunnel endpoint (server side) and returns endpoint info.
* @param[in] cfgStr Optional config string (TOML or connect string).
* @return The channel endpoint structure.
*/
sdv::ipc::SChannelEndpoint CreateEndpoint(const sdv::u8string& cfgStr) override;
/**
* @brief Creates or accesses a connection object from the channel connect string.
* @param[in] cs The channel connect string.
* @return Pointer to connection access interface.
*/
sdv::IInterfaceAccess* Access(const sdv::u8string& cs) override;
/**
* @brief Called by server tunnel when closing (bookkeeping).
* @param[in] udsPath The UDS path for the server.
* @param[in] ptr Pointer to the tunnel connection being closed.
*/
void OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr);
private:
std::mutex m_udsMtx;
std::map<std::string, std::shared_ptr<CWinTunnelConnection>> m_udsServers;
std::set<std::string> m_udsServerClaimed;
};
DEFINE_SDV_OBJECT(CSocketsTunnelChannelMgnt)
#endif // ! defined WIN_TUNNEL_CHANNEL_MGNT_H
#endif

View File

@@ -0,0 +1,226 @@
/************************************************************
* 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
************************************************************/
#ifdef _WIN32
#include "connection.h"
CWinTunnelConnection::CWinTunnelConnection(
std::shared_ptr<CWinsockConnection> transport,
uint16_t channelId)
: m_Transport(std::move(transport))
, m_ChannelId(channelId)
{
// No additional initialization required; acts as a thin wrapper.
}
bool CWinTunnelConnection::SendData(
/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
{
if (!m_Transport)
{
SDV_LOG_ERROR("[WinTunnel] SendData failed: transport is null");
return false;
}
// Build tunnel header buffer
sdv::pointer<uint8_t> hdrBuf;
hdrBuf.resize(sizeof(STunnelHeader));
STunnelHeader hdr{};
hdr.uiChannelId = m_ChannelId; // Logical channel for this connection
hdr.uiFlags = 0; // Reserved for future use
std::memcpy(hdrBuf.get(), &hdr, sizeof(STunnelHeader));
// Compose new sequence: [header] + original payload chunks
sdv::sequence<sdv::pointer<uint8_t>> seqWithHdr;
seqWithHdr.push_back(hdrBuf);
for (auto& chunk : seqData)
{
seqWithHdr.push_back(chunk);
}
bool result = m_Transport->SendData(seqWithHdr);
if (!result) {
SDV_LOG_ERROR("[WinTunnel] SendData failed in underlying transport");
}
return result;
}
bool CWinTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver)
{
if (!m_Transport)
{
SDV_LOG_ERROR("[WinTunnel] AsyncConnect failed: transport is null");
return false;
}
// Store upper-layer callbacks (safe for null)
{
std::lock_guard<std::mutex> lock(m_CallbackMtx);
sdv::TInterfaceAccessPtr acc(pReceiver);
m_pUpperReceiver = acc.GetInterface<sdv::ipc::IDataReceiveCallback>();
m_pUpperEvent = acc.GetInterface<sdv::ipc::IConnectEventCallback>();
}
// Register this tunnel as the data/event receiver in the AF_UNIX transport.
bool result = m_Transport->AsyncConnect(this);
if (!result) {
SDV_LOG_ERROR("[WinTunnel] AsyncConnect failed in underlying transport");
}
return result;
}
bool CWinTunnelConnection::WaitForConnection(/*in*/ uint32_t uiWaitMs)
{
if (!m_Transport)
{
SDV_LOG_ERROR("[WinTunnel] WaitForConnection failed: transport is null");
return false;
}
return m_Transport->WaitForConnection(uiWaitMs);
}
void CWinTunnelConnection::CancelWait()
{
if (!m_Transport)
{
SDV_LOG_ERROR("[WinTunnel] CancelWait failed: transport is null");
return;
}
m_Transport->CancelWait();
}
void CWinTunnelConnection::Disconnect()
{
if (!m_Transport)
{
SDV_LOG_ERROR("[WinTunnel] Disconnect failed: transport is null");
return;
}
m_Transport->Disconnect();
// Clear upper-layer callbacks (thread-safe)
{
std::lock_guard<std::mutex> lock(m_CallbackMtx);
m_pUpperReceiver = nullptr;
m_pUpperEvent = nullptr;
}
}
uint64_t CWinTunnelConnection::RegisterStateEventCallback(
/*in*/ sdv::IInterfaceAccess* pEventCallback)
{
if (!m_Transport)
{
SDV_LOG_ERROR("[WinTunnel] RegisterStateEventCallback failed: transport is null");
return 0ULL;
}
// Forward directly to underlying CWinsockConnection
return m_Transport->RegisterStateEventCallback(pEventCallback);
}
void CWinTunnelConnection::UnregisterStateEventCallback(
/*in*/ uint64_t uiCookie)
{
if (!m_Transport || uiCookie == 0ULL)
{
SDV_LOG_ERROR("[WinTunnel] UnregisterStateEventCallback failed: transport is null or cookie is 0");
return;
}
m_Transport->UnregisterStateEventCallback(uiCookie);
}
sdv::ipc::EConnectState CWinTunnelConnection::GetConnectState() const
{
if (!m_Transport)
{
return sdv::ipc::EConnectState::uninitialized;
}
return m_Transport->GetConnectState();
}
void CWinTunnelConnection::DestroyObject()
{
Disconnect();
std::lock_guard<std::mutex> lock(m_CallbackMtx);
m_Transport.reset();
}
void CWinTunnelConnection::ReceiveData(
/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
{
// Expect at least one chunk (the tunnel header)
if (seqData.empty())
{
SDV_LOG_ERROR("[WinTunnel] ReceiveData: empty sequence");
return; // nothing to do
}
const auto& hdrChunk = seqData[0];
if (hdrChunk.size() < sizeof(STunnelHeader))
{
SDV_LOG_ERROR("[WinTunnel] ReceiveData: invalid tunnel header size");
// Invalid tunnel frame; drop it for now (could set communication_error)
return;
}
STunnelHeader hdr{};
std::memcpy(&hdr, hdrChunk.get(), sizeof(STunnelHeader));
// TODO: use channelId for multiplexing later
// Build payload-only sequence: drop header chunk, keep others
sdv::sequence<sdv::pointer<uint8_t>> payload;
for (size_t i = 1; i < seqData.size(); ++i)
{
payload.push_back(seqData[i]);
}
if (m_pUpperReceiver)
{
try {
m_pUpperReceiver->ReceiveData(payload); // header stripped
} catch (...) {
SDV_LOG_ERROR("[WinTunnel] Exception in upper receiver's ReceiveData");
}
}
}
void CWinTunnelConnection::SetConnectState(sdv::ipc::EConnectState state)
{
sdv::ipc::IConnectEventCallback* upper = nullptr;
{
std::lock_guard<std::mutex> lock(m_CallbackMtx);
upper = m_pUpperEvent;
}
if (upper)
{
try
{
upper->SetConnectState(state);
}
catch (...)
{
SDV_LOG_ERROR("[WinTunnel] Exception in upper event callback's SetConnectState");
// Never let user callback crash the transport.
}
}
}
#endif

View File

@@ -0,0 +1,168 @@
/************************************************************
* 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
************************************************************/
#ifdef _WIN32
#ifndef UDS_WIN_TUNNEL_CONNECTION_H
#define UDS_WIN_TUNNEL_CONNECTION_H
#include <interfaces/ipc.h>
#include <support/component_impl.h>
#include <support/interface_ptr.h>
#include <WinSock2.h>
#include <atomic>
#include <cstdint>
#include <mutex>
#include <memory>
#include <thread>
#include <vector>
#include "../sdv_services/uds_win_sockets/connection.h" // existing AF_UNIX transport: CWinsockConnection
/**
* @brief Logical tunnel connection on top of a shared Windows AF_UNIX transport.
*
* This class does NOT create sockets by itself. It wraps an existing
* CWinsockConnection (Winsock AF_UNIX) and adds:
* - tunnel header (channelId, flags)
* - (later) demultiplexing of incoming payloads per logical channel.
*/
class CWinTunnelConnection :
public sdv::IInterfaceAccess,
public sdv::IObjectDestroy,
public sdv::ipc::IDataSend,
public sdv::ipc::IConnect,
public sdv::ipc::IDataReceiveCallback,
public sdv::ipc::IConnectEventCallback
{
public:
/**
* @struct STunnelHeader
* @brief Header prepended to each tunneled SDV message for logical channel identification and flags.
*/
struct STunnelHeader
{
uint16_t uiChannelId; ///< Logical channel ID (IPC_x / REMOTE_IPC_x)
uint16_t uiFlags; ///< Reserved for future use (QoS, direction, etc.)
};
/**
* @brief Construct a tunnel wrapper over an existing AF_UNIX transport.
* @param[in] transport Shared pointer to the underlying AF_UNIX transport.
* @param[in] channelId Logical channel ID for this tunnel instance.
*/
explicit CWinTunnelConnection(
std::shared_ptr<CWinsockConnection> transport,
uint16_t channelId);
/**
* @brief Destructor.
*/
virtual ~CWinTunnelConnection() = default;
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend)
SDV_INTERFACE_ENTRY(sdv::ipc::IConnect)
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
SDV_INTERFACE_ENTRY(sdv::IObjectDestroy)
END_SDV_INTERFACE_MAP()
// ---------- IDataSend ----------
/**
* @brief Send a sequence of buffers via the tunnel.
* @param[in,out] seqData Sequence of message buffers (may be modified by callee).
* @return true on successful send, false otherwise.
*/
bool SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) override;
// ---------- IConnect ----------
/**
* @brief Start asynchronous connect and register this object as receiver.
* @param[in] pReceiver Pointer to callback interface for data and state notifications.
* @return true if connect started, false otherwise.
*/
bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override;
/**
* @brief Wait until the underlying transport becomes 'connected'.
* @param[in] uiWaitMs Timeout in milliseconds to wait.
* @return true if connection established, false on timeout or error.
*/
bool WaitForConnection(/*in*/ uint32_t uiWaitMs) override;
/**
* @brief Cancel any pending connect or wait operation.
*/
void CancelWait() override;
/**
* @brief Disconnect the tunnel and underlying transport.
*/
void Disconnect() override;
/**
* @brief Register a state event callback (forwards to transport).
* @param[in] pEventCallback Pointer to event callback interface.
* @return Registration cookie (nonzero) or 0 on failure.
*/
uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override;
/**
* @brief Unregister a previously registered state event callback.
* @param[in] uiCookie Registration cookie returned by RegisterStateEventCallback.
*/
void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override;
/**
* @brief Get the current state from the underlying transport.
* @return The current connection state.
*/
sdv::ipc::EConnectState GetConnectState() const override;
// ---------- IObjectDestroy ----------
/**
* @brief Release and clean up all resources associated with this object.
*/
void DestroyObject() override;
// ---------- IDataReceiveCallback ----------
/**
* @brief Receive data from the underlying AF_UNIX transport.
* @param[in,out] seqData Sequence of received message buffers (header chunk is removed by this call).
*/
void ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) override;
// ---------- IConnectEventCallback ----------
/**
* @brief Forward state changes from the underlying transport to the upper layer.
* @param[in] state New connection state.
*/
void SetConnectState(sdv::ipc::EConnectState state) override;
// Helpers
void SetChannelId(uint16_t channelId) { m_ChannelId = channelId; }
uint16_t GetChannelId() const noexcept { return m_ChannelId; }
private:
std::shared_ptr<CWinsockConnection> m_Transport; ///< shared physical tunnel port
uint16_t m_ChannelId{0}; ///< default logical channel id
// Upper layer callbacks (original VAPI receiver)
sdv::ipc::IDataReceiveCallback* m_pUpperReceiver{nullptr};
sdv::ipc::IConnectEventCallback* m_pUpperEvent{nullptr};
mutable std::mutex m_CallbackMtx;
};
#endif // UDS_WIN_TUNNEL_CONNECTION_H
#endif

View File

@@ -85,12 +85,10 @@ add_subdirectory(unit_tests/sdv_control)
add_subdirectory(unit_tests/sdv_macro_test) add_subdirectory(unit_tests/sdv_macro_test)
add_subdirectory(unit_tests/install_package_composer) add_subdirectory(unit_tests/install_package_composer)
add_subdirectory(unit_tests/path_match) add_subdirectory(unit_tests/path_match)
if(UNIX)
add_subdirectory(unit_tests/unix_sockets) add_subdirectory(unit_tests/unix_sockets)
endif() add_subdirectory(unit_tests/unix_tunnel)
if(WIN32)
add_subdirectory(unit_tests/win_sockets) add_subdirectory(unit_tests/win_sockets)
endif() add_subdirectory(unit_tests/win_tunnel)
add_subdirectory(component_tests/config) add_subdirectory(component_tests/config)
add_subdirectory(component_tests/logger) add_subdirectory(component_tests/logger)
add_subdirectory(component_tests/data_dispatch_service) add_subdirectory(component_tests/data_dispatch_service)

View File

@@ -1085,9 +1085,9 @@ ViewFilter = "Fatal"
// Disconnect from the client // Disconnect from the client
sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(pClientConnect, nullptr); ASSERT_NE(pClientConnect, nullptr);
EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
control.RemoveConnection(tConnectionID); control.RemoveConnection(tConnectionID);
EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected);
ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. ptrClientEndpoint.Clear(); // Lifetime taken over by communication control.
// Create another client endpoint // Create another client endpoint
@@ -1155,9 +1155,9 @@ ViewFilter = "Fatal"
// Disconnect from the client // Disconnect from the client
sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(pClientConnect, nullptr); ASSERT_NE(pClientConnect, nullptr);
EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
control.RemoveConnection(tConnectionID); control.RemoveConnection(tConnectionID);
EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected);
ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. ptrClientEndpoint.Clear(); // Lifetime taken over by communication control.
// Create another client endpoint // Create another client endpoint

View File

@@ -117,14 +117,14 @@ public:
} }
/** /**
* @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. * @brief Set the current state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState.
* @param[in] eConnectStatus The connection status. * @param[in] eConnectState The connection state.
*/ */
virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override
{ {
switch (eConnectStatus) switch (eConnectState)
{ {
case sdv::ipc::EConnectStatus::disconnected: case sdv::ipc::EConnectState::disconnected:
// Disconnect only when connected before // Disconnect only when connected before
if (m_bConnected) if (m_bConnected)
{ {
@@ -133,7 +133,7 @@ public:
m_cvDisconnect.notify_all(); m_cvDisconnect.notify_all();
} }
break; break;
case sdv::ipc::EConnectStatus::disconnected_forced: case sdv::ipc::EConnectState::disconnected_forced:
// Disconnect only when connected before // Disconnect only when connected before
if (m_bConnected) if (m_bConnected)
{ {
@@ -142,7 +142,7 @@ public:
m_cvDisconnect.notify_all(); m_cvDisconnect.notify_all();
} }
break; break;
case sdv::ipc::EConnectStatus::connected: case sdv::ipc::EConnectState::connected:
m_bConnected = true; m_bConnected = true;
break; break;
default: default:
@@ -339,11 +339,11 @@ extern "C" int main(int argc, char* argv[])
if (!ptrControlConnection) return -12; if (!ptrControlConnection) return -12;
pControlConnect = ptrControlConnection.GetInterface<sdv::ipc::IConnect>(); pControlConnect = ptrControlConnection.GetInterface<sdv::ipc::IConnect>();
if (!pControlConnect) return -13; if (!pControlConnect) return -13;
uiControlEventCookie = pControlConnect->RegisterStatusEventCallback(&receiverControl); uiControlEventCookie = pControlConnect->RegisterStateEventCallback(&receiverControl);
if (!uiControlEventCookie) return -20; if (!uiControlEventCookie) return -20;
if (!pControlConnect->AsyncConnect(&receiverControl)) return -14; if (!pControlConnect->AsyncConnect(&receiverControl)) return -14;
if (!pControlConnect->WaitForConnection(250)) return -5; // Note: Connection should be possible within 250ms. if (!pControlConnect->WaitForConnection(250)) return -5; // Note: Connection should be possible within 250ms.
if (pControlConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -15; if (pControlConnect->GetConnectState() != sdv::ipc::EConnectState::connected) return -15;
TRACE(bServer ? "Server" : "Client", ": Connected to control channel..."); TRACE(bServer ? "Server" : "Client", ": Connected to control channel...");
} }
@@ -393,11 +393,11 @@ Size = 1024000
sdv::ipc::IConnect* pDataConnect = ptrDataConnection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pDataConnect = ptrDataConnection.GetInterface<sdv::ipc::IConnect>();
uint64_t uiDataEventCookie = 0; uint64_t uiDataEventCookie = 0;
if (!pDataConnect) return -3; if (!pDataConnect) return -3;
uiDataEventCookie = pDataConnect->RegisterStatusEventCallback(&receiverData); uiDataEventCookie = pDataConnect->RegisterStateEventCallback(&receiverData);
if (!uiDataEventCookie) return -21; if (!uiDataEventCookie) return -21;
if (!pDataConnect->AsyncConnect(&receiverData)) return -4; if (!pDataConnect->AsyncConnect(&receiverData)) return -4;
if (!pDataConnect->WaitForConnection(10000)) return -5; // Note: Connection should be possible within 10000ms. if (!pDataConnect->WaitForConnection(10000)) return -5; // Note: Connection should be possible within 10000ms.
if (pDataConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -5; if (pDataConnect->GetConnectState() != sdv::ipc::EConnectState::connected) return -5;
TRACE("Connected to data channel... waiting for data exchange"); TRACE("Connected to data channel... waiting for data exchange");
@@ -430,11 +430,11 @@ Size = 1024000
} }
// Initiate shutdown // Initiate shutdown
if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStatusEventCallback(uiDataEventCookie); if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStateEventCallback(uiDataEventCookie);
ptrDataConnection.Clear(); ptrDataConnection.Clear();
mgntDataMgntChannel.Shutdown(); mgntDataMgntChannel.Shutdown();
if (mgntDataMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -6; if (mgntDataMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -6;
if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStatusEventCallback(uiControlEventCookie); if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStateEventCallback(uiControlEventCookie);
ptrControlConnection.Clear(); ptrControlConnection.Clear();
mgntControlMgntChannel.Shutdown(); mgntControlMgntChannel.Shutdown();
if (mgntControlMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -16; if (mgntControlMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -16;

View File

@@ -89,33 +89,33 @@ public:
} }
/** /**
* @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. * @brief Set the current state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState.
* @param[in] eConnectStatus The connection status. * @param[in] eConnectState The connection state.
*/ */
virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override
{ {
switch (eConnectStatus) switch (eConnectState)
{ {
case sdv::ipc::EConnectStatus::connection_error: case sdv::ipc::EConnectState::connection_error:
m_bConnectError = true; m_bConnectError = true;
break; break;
case sdv::ipc::EConnectStatus::communication_error: case sdv::ipc::EConnectState::communication_error:
m_bCommError = true; m_bCommError = true;
break; break;
case sdv::ipc::EConnectStatus::disconnected_forced: case sdv::ipc::EConnectState::disconnected_forced:
m_bForcedDisconnect = true; m_bForcedDisconnect = true;
break; break;
default: default:
break; break;
} }
m_eStatus = eConnectStatus; m_eConnectState = eConnectState;
} }
/** /**
* @brief Return the received connection status. * @brief Return the received connection state.
* @return The received connection status. * @return The received connection state.
*/ */
sdv::ipc::EConnectStatus GetReceivedStatus() const { return m_eStatus; } sdv::ipc::EConnectState GetReceivedState() const { return m_eConnectState; }
/** /**
* @brief Has a connection error occurred? * @brief Has a connection error occurred?
@@ -136,9 +136,9 @@ public:
bool ForcedDisconnectOccurred() const { return m_bForcedDisconnect; } bool ForcedDisconnectOccurred() const { return m_bForcedDisconnect; }
/** /**
* @brief Reset the received status event flags. * @brief Reset the received state event flags.
*/ */
void ResetStatusEvents() void ResetStateEvents()
{ {
m_bConnectError = false; m_bConnectError = false;
m_bCommError = false; m_bCommError = false;
@@ -182,7 +182,7 @@ private:
sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function. sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function.
mutable std::mutex m_mtxData; ///< Protect data access. mutable std::mutex m_mtxData; ///< Protect data access.
sdv::sequence<sdv::pointer<uint8_t>> m_seqDataCopy; ///< Copy of the data. sdv::sequence<sdv::pointer<uint8_t>> m_seqDataCopy; ///< Copy of the data.
sdv::ipc::EConnectStatus m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current received status. sdv::ipc::EConnectState m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< Current received state.
bool m_bConnectError = false; ///< Connection error ocurred. bool m_bConnectError = false; ///< Connection error ocurred.
bool m_bCommError = false; ///< Communication error occurred. bool m_bCommError = false; ///< Communication error occurred.
bool m_bForcedDisconnect = false; ///< Force disconnect. bool m_bForcedDisconnect = false; ///< Force disconnect.
@@ -355,8 +355,8 @@ TEST(SharedMemChannelService, WaitForConnection)
// Wait for connection with timeout // Wait for connection with timeout
EXPECT_FALSE(pConnection->WaitForConnection(100)); EXPECT_FALSE(pConnection->WaitForConnection(100));
EXPECT_TRUE(pConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(pConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
pConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); pConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
// Wait for connection for infinite period with cancel. // Wait for connection for infinite period with cancel.
std::thread threadCancelWait([&]() std::thread threadCancelWait([&]()
@@ -366,8 +366,8 @@ TEST(SharedMemChannelService, WaitForConnection)
}); });
EXPECT_FALSE(pConnection->WaitForConnection(0xffffffff)); // Note: wait indefinitely. Cancel get called in 500ms. EXPECT_FALSE(pConnection->WaitForConnection(0xffffffff)); // Note: wait indefinitely. Cancel get called in 500ms.
threadCancelWait.join(); threadCancelWait.join();
EXPECT_TRUE(pConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(pConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
pConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); pConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
EXPECT_NO_THROW(ptrConnection.Clear()); EXPECT_NO_THROW(ptrConnection.Clear());
@@ -427,8 +427,8 @@ TEST(SharedMemChannelService, AsyncConnect)
ASSERT_NE(pServerConnect, nullptr); ASSERT_NE(pServerConnect, nullptr);
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection. EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection.
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized ||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
// Try send; should fail, since not connected // Try send; should fail, since not connected
EXPECT_FALSE(pServerSend->SendData(seqPattern)); EXPECT_FALSE(pServerSend->SendData(seqPattern));
@@ -439,8 +439,8 @@ TEST(SharedMemChannelService, AsyncConnect)
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
// Try send; should succeed, since connected // Try send; should succeed, since connected
EXPECT_TRUE(pServerSend->SendData(seqPattern)); EXPECT_TRUE(pServerSend->SendData(seqPattern));
@@ -520,39 +520,39 @@ TEST(SharedMemChannelService, EstablishConnectionEvents)
EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred()); EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred());
EXPECT_FALSE(pServerSend->SendData(seqPattern)); EXPECT_FALSE(pServerSend->SendData(seqPattern));
EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred()); // No events registered yet... only after a call to AsyncConnect. EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred()); // No events registered yet... only after a call to AsyncConnect.
receiverServer.ResetStatusEvents(); receiverServer.ResetStateEvents();
// Establish the server connection // Establish the server connection
sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(pServerConnect, nullptr); ASSERT_NE(pServerConnect, nullptr);
EXPECT_NE(pServerConnect->RegisterStatusEventCallback(&receiverServer), 0); EXPECT_NE(pServerConnect->RegisterStateEventCallback(&receiverServer), 0);
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection. EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection.
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized ||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::initialized ||
receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting); receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting);
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
// Try send; should fail, since not connected // Try send; should fail, since not connected
EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred()); EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred());
EXPECT_FALSE(pServerSend->SendData(seqPattern)); EXPECT_FALSE(pServerSend->SendData(seqPattern));
EXPECT_TRUE(receiverServer.CommunicationbErrorOccurred()); EXPECT_TRUE(receiverServer.CommunicationbErrorOccurred());
receiverServer.ResetStatusEvents(); receiverServer.ResetStateEvents();
// Establish the client connection // Establish the client connection
sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(pClientConnection, nullptr); ASSERT_NE(pClientConnection, nullptr);
EXPECT_NE(pClientConnection->RegisterStatusEventCallback(&receiverClient), 0); EXPECT_NE(pClientConnection->RegisterStateEventCallback(&receiverClient), 0);
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
EXPECT_FALSE(receiverClient.ConnectionErrorOccurred()); EXPECT_FALSE(receiverClient.ConnectionErrorOccurred());
EXPECT_EQ(receiverServer.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(receiverServer.GetReceivedState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(receiverClient.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(receiverClient.GetReceivedState(), sdv::ipc::EConnectState::connected);
// Try send; should succeed, since connected // Try send; should succeed, since connected
EXPECT_TRUE(pServerSend->SendData(seqPattern)); EXPECT_TRUE(pServerSend->SendData(seqPattern));
@@ -621,8 +621,8 @@ TEST(SharedMemChannelService, EstablishReconnect)
ASSERT_NE(pServerConnect, nullptr); ASSERT_NE(pServerConnect, nullptr);
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection. EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection.
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized ||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
std::cout << "Connect client 1..." << std::endl; std::cout << "Connect client 1..." << std::endl;
// Establish the client1 connection // Establish the client1 connection
@@ -631,8 +631,8 @@ TEST(SharedMemChannelService, EstablishReconnect)
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient1)); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient1));
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
std::cout << "Disconnect client 1..." << std::endl; std::cout << "Disconnect client 1..." << std::endl;
@@ -640,10 +640,10 @@ TEST(SharedMemChannelService, EstablishReconnect)
EXPECT_NO_THROW(ptrClient1Connection.Clear()); EXPECT_NO_THROW(ptrClient1Connection.Clear());
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate. std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate.
EXPECT_NO_THROW(mgntClient1.Shutdown()); EXPECT_NO_THROW(mgntClient1.Shutdown());
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected || EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected ||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
std::cout << "Connect client 2... (eStatus = " << (uint32_t) pServerConnect->GetStatus() << ")" << std::endl; std::cout << "Connect client 2... (eConnectState = " << (uint32_t) pServerConnect->GetConnectState() << ")" << std::endl;
// Establish the client2 connection // Establish the client2 connection
sdv::TObjectPtr ptrClient2Connection = mgntClient2.Access(sChannelEndpoint.ssConnectString); sdv::TObjectPtr ptrClient2Connection = mgntClient2.Access(sChannelEndpoint.ssConnectString);
@@ -653,8 +653,8 @@ TEST(SharedMemChannelService, EstablishReconnect)
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient2)); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient2));
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
std::cout << "Disconnect client 2..." << std::endl; std::cout << "Disconnect client 2..." << std::endl;
@@ -662,8 +662,8 @@ TEST(SharedMemChannelService, EstablishReconnect)
EXPECT_NO_THROW(ptrClient2Connection.Clear()); EXPECT_NO_THROW(ptrClient2Connection.Clear());
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate
EXPECT_NO_THROW(mgntClient2.Shutdown()); EXPECT_NO_THROW(mgntClient2.Shutdown());
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected || EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected ||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
std::cout << "Shutdown server..." << std::endl; std::cout << "Shutdown server..." << std::endl;
@@ -702,13 +702,13 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
// Establish the server connection // Establish the server connection
sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(pServerConnect, nullptr); ASSERT_NE(pServerConnect, nullptr);
EXPECT_NE(pServerConnect->RegisterStatusEventCallback(&receiverServer), 0); EXPECT_NE(pServerConnect->RegisterStateEventCallback(&receiverServer), 0);
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection. EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection.
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized ||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::initialized ||
receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting); receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting);
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
std::cout << "Connect client 1..." << std::endl; std::cout << "Connect client 1..." << std::endl;
@@ -716,16 +716,16 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
// Establish the client1 connection // Establish the client1 connection
sdv::ipc::IConnect* pClientConnection = ptrClient1Connection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pClientConnection = ptrClient1Connection.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(pClientConnection, nullptr); ASSERT_NE(pClientConnection, nullptr);
EXPECT_NE(pClientConnection->RegisterStatusEventCallback(&receiverClient1), 0); EXPECT_NE(pClientConnection->RegisterStateEventCallback(&receiverClient1), 0);
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient1)); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient1));
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
EXPECT_FALSE(receiverClient1.ConnectionErrorOccurred()); EXPECT_FALSE(receiverClient1.ConnectionErrorOccurred());
EXPECT_EQ(receiverServer.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(receiverServer.GetReceivedState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(receiverClient1.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(receiverClient1.GetReceivedState(), sdv::ipc::EConnectState::connected);
std::cout << "Disconnect client 1..." << std::endl; std::cout << "Disconnect client 1..." << std::endl;
@@ -733,10 +733,10 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
EXPECT_NO_THROW(ptrClient1Connection.Clear()); EXPECT_NO_THROW(ptrClient1Connection.Clear());
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate. std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate.
EXPECT_NO_THROW(mgntClient1.Shutdown()); EXPECT_NO_THROW(mgntClient1.Shutdown());
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected || EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected ||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::disconnected || EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::disconnected ||
receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting); receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting);
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
std::cout << "Connect client 2..." << std::endl; std::cout << "Connect client 2..." << std::endl;
@@ -746,16 +746,16 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
EXPECT_TRUE(ptrClient2Connection); EXPECT_TRUE(ptrClient2Connection);
pClientConnection = ptrClient2Connection.GetInterface<sdv::ipc::IConnect>(); pClientConnection = ptrClient2Connection.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(pClientConnection, nullptr); ASSERT_NE(pClientConnection, nullptr);
EXPECT_NE(pClientConnection->RegisterStatusEventCallback(&receiverClient2), 0); EXPECT_NE(pClientConnection->RegisterStateEventCallback(&receiverClient2), 0);
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient2)); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient2));
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
EXPECT_FALSE(receiverClient2.ConnectionErrorOccurred()); EXPECT_FALSE(receiverClient2.ConnectionErrorOccurred());
EXPECT_EQ(receiverServer.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(receiverServer.GetReceivedState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(receiverClient2.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(receiverClient2.GetReceivedState(), sdv::ipc::EConnectState::connected);
std::cout << "Disconnect client 2..." << std::endl; std::cout << "Disconnect client 2..." << std::endl;
@@ -763,10 +763,10 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
EXPECT_NO_THROW(ptrClient2Connection.Clear()); EXPECT_NO_THROW(ptrClient2Connection.Clear());
std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Note: Handle connection terminate std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Note: Handle connection terminate
EXPECT_NO_THROW(mgntClient2.Shutdown()); EXPECT_NO_THROW(mgntClient2.Shutdown());
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected || EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected ||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::disconnected || EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::disconnected ||
receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting); receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting);
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
std::cout << "Shutdown server..." << std::endl; std::cout << "Shutdown server..." << std::endl;
@@ -828,7 +828,7 @@ Mode = "Essential")code"));
ASSERT_NE(pServerConnect, nullptr); ASSERT_NE(pServerConnect, nullptr);
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
// Try send; should succeed, since connected // Try send; should succeed, since connected
EXPECT_TRUE(pServerSend->SendData(seqPattern)); EXPECT_TRUE(pServerSend->SendData(seqPattern));
@@ -909,11 +909,11 @@ Mode = "Essential")code"));
ASSERT_NE(pServerConnect, nullptr); ASSERT_NE(pServerConnect, nullptr);
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
// Try send; should succeed, since connected // Try send; should succeed, since connected
auto tpStart = std::chrono::high_resolution_clock::now(); auto tpStart = std::chrono::high_resolution_clock::now();
while (pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connected) while (pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connected)
{ {
EXPECT_TRUE(pServerSend->SendData(seqPattern)); EXPECT_TRUE(pServerSend->SendData(seqPattern));
std::this_thread::sleep_for(std::chrono::milliseconds(300)); std::this_thread::sleep_for(std::chrono::milliseconds(300));
@@ -925,7 +925,7 @@ Mode = "Essential")code"));
break; // Max 4000 ms break; // Max 4000 ms
} }
} }
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected);
EXPECT_NO_THROW(ptrServerConnection.Clear()); EXPECT_NO_THROW(ptrServerConnection.Clear());
@@ -984,14 +984,14 @@ Mode = "Essential")code"));
// Establish the server connection // Establish the server connection
sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(pServerConnect, nullptr); ASSERT_NE(pServerConnect, nullptr);
EXPECT_NE(pServerConnect->RegisterStatusEventCallback(&receiverServer), 0); EXPECT_NE(pServerConnect->RegisterStateEventCallback(&receiverServer), 0);
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
// Try send; should succeed, since connected // Try send; should succeed, since connected
auto tpStart = std::chrono::high_resolution_clock::now(); auto tpStart = std::chrono::high_resolution_clock::now();
while (pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connected) while (pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connected)
{ {
EXPECT_TRUE(pServerSend->SendData(seqPattern)); EXPECT_TRUE(pServerSend->SendData(seqPattern));
std::this_thread::sleep_for(std::chrono::milliseconds(300)); std::this_thread::sleep_for(std::chrono::milliseconds(300));
@@ -1004,7 +1004,7 @@ Mode = "Essential")code"));
} }
} }
EXPECT_TRUE(receiverServer.ForcedDisconnectOccurred()); EXPECT_TRUE(receiverServer.ForcedDisconnectOccurred());
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected);
EXPECT_NO_THROW(ptrServerConnection.Clear()); EXPECT_NO_THROW(ptrServerConnection.Clear());
@@ -1048,7 +1048,7 @@ Mode = "Essential")code"));
ASSERT_NE(pControlConnect1, nullptr); ASSERT_NE(pControlConnect1, nullptr);
EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1)); EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1));
EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pControlConnect1->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pControlConnect1->GetConnectState(), sdv::ipc::EConnectState::connected);
// Wait for data // Wait for data
sdv::sequence<sdv::pointer<uint8_t>> seqDataConnectString; sdv::sequence<sdv::pointer<uint8_t>> seqDataConnectString;
@@ -1084,7 +1084,7 @@ Mode = "Essential")code"));
ASSERT_NE(pControlConnect2, nullptr); ASSERT_NE(pControlConnect2, nullptr);
EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2)); EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2));
EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pControlConnect2->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pControlConnect2->GetConnectState(), sdv::ipc::EConnectState::connected);
// Wait for process termination // Wait for process termination
pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff); pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff);
@@ -1131,7 +1131,7 @@ Mode = "Essential")code"));
ASSERT_NE(pControlConnect1, nullptr); ASSERT_NE(pControlConnect1, nullptr);
EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1)); EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1));
EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 5000ms. EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 5000ms.
EXPECT_EQ(pControlConnect1->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pControlConnect1->GetConnectState(), sdv::ipc::EConnectState::connected);
// Wait for connection string (max. 500*10ms). // Wait for connection string (max. 500*10ms).
sdv::sequence<sdv::pointer<uint8_t>> seqDataConnectString; sdv::sequence<sdv::pointer<uint8_t>> seqDataConnectString;
@@ -1167,7 +1167,7 @@ Mode = "Essential")code"));
ASSERT_NE(pControlConnect2, nullptr); ASSERT_NE(pControlConnect2, nullptr);
EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2)); EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2));
EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pControlConnect2->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pControlConnect2->GetConnectState(), sdv::ipc::EConnectState::connected);
// Wait for process termination // Wait for process termination
pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff); pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff);
@@ -1212,7 +1212,7 @@ Mode = "Essential")code"));
ASSERT_NE(pControlConnect1, nullptr); ASSERT_NE(pControlConnect1, nullptr);
EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1)); EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1));
EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pControlConnect1->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pControlConnect1->GetConnectState(), sdv::ipc::EConnectState::connected);
// Wait for connection string (max. 500*10ms). // Wait for connection string (max. 500*10ms).
sdv::sequence<sdv::pointer<uint8_t>> seqDataConnectString; sdv::sequence<sdv::pointer<uint8_t>> seqDataConnectString;
@@ -1248,7 +1248,7 @@ Mode = "Essential")code"));
ASSERT_NE(pControlConnect2, nullptr); ASSERT_NE(pControlConnect2, nullptr);
EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2)); EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2));
EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pControlConnect2->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pControlConnect2->GetConnectState(), sdv::ipc::EConnectState::connected);
// Wait for process termination // Wait for process termination
pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff); pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff);

View File

@@ -115,7 +115,7 @@ public:
// Wait until there is no activity any more. // Wait until there is no activity any more.
std::chrono::high_resolution_clock::time_point tpTickSent = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point tpTickSent = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point tpTickReceive = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point tpTickReceive = std::chrono::high_resolution_clock::now();
while (m_eStatus != sdv::ipc::EConnectStatus::disconnected) while (m_eConnectState != sdv::ipc::EConnectState::disconnected)
{ {
std::unique_lock<std::mutex> lock(m_mtxData); std::unique_lock<std::mutex> lock(m_mtxData);
m_cvReceived.wait_for(lock, std::chrono::milliseconds(50)); m_cvReceived.wait_for(lock, std::chrono::milliseconds(50));
@@ -141,33 +141,33 @@ public:
} }
/** /**
* @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. * @brief Set the current state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState.
* @param[in] eConnectStatus The connection status. * @param[in] eConnectState The connection state.
*/ */
virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override
{ {
switch (eConnectStatus) switch (eConnectState)
{ {
case sdv::ipc::EConnectStatus::connection_error: case sdv::ipc::EConnectState::connection_error:
m_bConnectError = true; m_bConnectError = true;
break; break;
case sdv::ipc::EConnectStatus::communication_error: case sdv::ipc::EConnectState::communication_error:
m_bCommError = true; m_bCommError = true;
break; break;
case sdv::ipc::EConnectStatus::disconnected_forced: case sdv::ipc::EConnectState::disconnected_forced:
m_bForcedDisconnect = true; m_bForcedDisconnect = true;
break; break;
default: default:
break; break;
} }
m_eStatus = eConnectStatus; m_eConnectState = eConnectState;
} }
/** /**
* @brief Return the received connection status. * @brief Return the received connection state.
* @return The received connection status. * @return The received connection state.
*/ */
sdv::ipc::EConnectStatus GetReceivedStatus() const { return m_eStatus; } sdv::ipc::EConnectState GetReceivedState() const { return m_eConnectState; }
/** /**
* @brief Has a connection error occurred? * @brief Has a connection error occurred?
@@ -207,9 +207,9 @@ public:
} }
/** /**
* @brief Reset the received status event flags. * @brief Reset the received state event flags.
*/ */
void ResetStatusEvents() void ResetStateEvents()
{ {
m_bConnectError = false; m_bConnectError = false;
m_bCommError = false; m_bCommError = false;
@@ -256,7 +256,7 @@ private:
sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function. sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function.
mutable std::mutex m_mtxData; ///< Protect data access. mutable std::mutex m_mtxData; ///< Protect data access.
std::queue<sdv::pointer<uint8_t>> m_queueDataCopy; ///< Copy of the data. std::queue<sdv::pointer<uint8_t>> m_queueDataCopy; ///< Copy of the data.
std::atomic<sdv::ipc::EConnectStatus> m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current received status. std::atomic<sdv::ipc::EConnectState> m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< Current received state.
bool m_bConnectError = false; ///< Connection error ocurred. bool m_bConnectError = false; ///< Connection error ocurred.
bool m_bCommError = false; ///< Communication error occurred. bool m_bCommError = false; ///< Communication error occurred.
bool m_bForcedDisconnect = false; ///< Force disconnect. bool m_bForcedDisconnect = false; ///< Force disconnect.
@@ -322,8 +322,8 @@ Size = 1024000
ASSERT_NE(pServerConnection, nullptr); ASSERT_NE(pServerConnection, nullptr);
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection. EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection.
EXPECT_TRUE(pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(pServerConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
// Establish the client connection // Establish the client connection
sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>();
@@ -331,8 +331,8 @@ Size = 1024000
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server. EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server.
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
appcontrol.SetRunningMode(); appcontrol.SetRunningMode();
// Try send; should succeed, since connected // Try send; should succeed, since connected
@@ -419,8 +419,8 @@ Size = 1024000
ASSERT_NE(pServerConnection, nullptr); ASSERT_NE(pServerConnection, nullptr);
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection. EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection.
EXPECT_TRUE(pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(pServerConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
// Establish the client connection // Establish the client connection
sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>();
@@ -428,8 +428,8 @@ Size = 1024000
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server. EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server.
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
appcontrol.SetRunningMode(); appcontrol.SetRunningMode();
// Try send; should succeed, since connected // Try send; should succeed, since connected
@@ -551,8 +551,8 @@ Size = 1024000
ASSERT_NE(pServerConnection, nullptr); ASSERT_NE(pServerConnection, nullptr);
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection. EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection.
EXPECT_TRUE(pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || EXPECT_TRUE(pServerConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); pServerConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
// Establish the client connection // Establish the client connection
sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>();
@@ -560,8 +560,8 @@ Size = 1024000
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server. EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server.
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
appcontrol.SetRunningMode(); appcontrol.SetRunningMode();
// Try send; should succeed, since connected // Try send; should succeed, since connected
@@ -666,7 +666,7 @@ Size = 1024000
ASSERT_NE(pServerConnection, nullptr); ASSERT_NE(pServerConnection, nullptr);
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
EXPECT_TRUE(pServerConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
TRACE("Connection estabished..."); TRACE("Connection estabished...");
appcontrol.SetRunningMode(); appcontrol.SetRunningMode();
@@ -750,7 +750,7 @@ Size = 1024000
ASSERT_NE(pServerConnection, nullptr); ASSERT_NE(pServerConnection, nullptr);
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
EXPECT_TRUE(pServerConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
TRACE("Connection estabished..."); TRACE("Connection estabished...");
appcontrol.SetRunningMode(); appcontrol.SetRunningMode();

View File

@@ -6,8 +6,11 @@
# https://www.apache.org/licenses/LICENSE-2.0 # https://www.apache.org/licenses/LICENSE-2.0
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
#
# Contributors:
# Denisa Ros - initial API and implementation
#******************************************************************************* #*******************************************************************************
if(UNIX)
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
project(UnixSocketCommunicationTests LANGUAGES CXX) project(UnixSocketCommunicationTests LANGUAGES CXX)
@@ -26,11 +29,19 @@ target_include_directories(UnitTest_UnixSocketConnectTests PUBLIC
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_link_libraries(UnitTest_UnixSocketConnectTests target_link_libraries(UnitTest_UnixSocketConnectTests
GTest::GTest ${CMAKE_THREAD_LIBS_INIT} stdc++fs ${CMAKE_DL_LIBS} rt GTest::GTest
${CMAKE_THREAD_LIBS_INIT}
stdc++fs ${CMAKE_DL_LIBS}
rt
uds_unix_sockets
) )
else() else()
target_link_libraries(UnitTest_UnixSocketConnectTests target_link_libraries(UnitTest_UnixSocketConnectTests
GTest::GTest Ws2_32 Winmm Rpcrt4.lib GTest::GTest
Ws2_32
Winmm
Rpcrt4.lib
uds_unix_sockets
) )
endif() endif()
@@ -46,4 +57,6 @@ add_custom_command(TARGET UnitTest_UnixSocketConnectTests POST_BUILD
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixSocketConnectTests.xml --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixSocketConnectTests.xml
VERBATIM VERBATIM
) )
endif()
endif() endif()

View File

@@ -1,12 +1,15 @@
/******************************************************************************** /********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG * Copyright (c) 2025-2026 ZF Friedrichshafen AG
* *
* This program and the accompanying materials are made available under the * This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at * terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0 * https://www.apache.org/licenses/LICENSE-2.0
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
********************************************************************************/ *
* Contributors:
* Denisa Ros - initial API and implementation
********************************************************************************/
#if defined __unix__ #if defined __unix__
@@ -15,8 +18,8 @@
#include <support/app_control.h> #include <support/app_control.h>
#include <interfaces/ipc.h> #include <interfaces/ipc.h>
#include "../../../sdv_services/uds_unix_sockets/channel_mgnt.cpp" #include "../../../sdv_services/uds_unix_sockets/channel_mgnt.h"
#include "../../../sdv_services/uds_unix_sockets/connection.cpp" #include "../../../sdv_services/uds_unix_sockets/connection.h"
#include <thread> #include <thread>
#include <atomic> #include <atomic>
@@ -24,6 +27,7 @@
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
#include <cstring> #include <cstring>
#include <sys/un.h>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
@@ -44,24 +48,24 @@ public:
// don't test data path yet // don't test data path yet
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& /*seqData*/) override {} void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& /*seqData*/) override {}
void SetStatus(sdv::ipc::EConnectStatus s) override { void SetConnectState(sdv::ipc::EConnectState s) override {
{ {
std::lock_guard<std::mutex> lk(m_mtx); std::lock_guard<std::mutex> lk(m_mtx);
m_status = s; m_state = s;
} }
m_cv.notify_all(); m_cv.notify_all();
} }
bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
{ {
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
if (m_status == expected) if (m_state == expected)
return true; return true;
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
while (m_status != expected) while (m_state != expected)
{ {
if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout) if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout)
return false; return false;
@@ -69,13 +73,13 @@ public:
return true; return true;
} }
sdv::ipc::EConnectStatus GetStatus() const { sdv::ipc::EConnectState GetConnectState() const {
std::lock_guard<std::mutex> lk(m_mtx); std::lock_guard<std::mutex> lk(m_mtx);
return m_status; return m_state;
} }
private: private:
sdv::ipc::EConnectStatus m_status { sdv::ipc::EConnectStatus::uninitialized }; sdv::ipc::EConnectState m_state { sdv::ipc::EConnectState::uninitialized };
mutable std::mutex m_mtx; mutable std::mutex m_mtx;
std::condition_variable m_cv; std::condition_variable m_cv;
}; };
@@ -101,24 +105,24 @@ public:
m_cv.notify_all(); m_cv.notify_all();
} }
void SetStatus(sdv::ipc::EConnectStatus s) override { void SetConnectState(sdv::ipc::EConnectState s) override {
{ {
std::lock_guard<std::mutex> lk(m_mtx); std::lock_guard<std::mutex> lk(m_mtx);
m_status = s; m_state = s;
} }
m_cv.notify_all(); m_cv.notify_all();
} }
bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
{ {
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
if (m_status == expected) if (m_state == expected)
return true; return true;
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
while (m_status != expected) while (m_state != expected)
{ {
if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout) if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout)
return false; return false;
@@ -141,13 +145,13 @@ public:
private: private:
mutable std::mutex m_mtx; mutable std::mutex m_mtx;
std::condition_variable m_cv; std::condition_variable m_cv;
sdv::ipc::EConnectStatus m_status{ sdv::ipc::EConnectStatus::uninitialized }; sdv::ipc::EConnectState m_state{ sdv::ipc::EConnectState::uninitialized };
sdv::sequence<sdv::pointer<uint8_t>> m_lastData; sdv::sequence<sdv::pointer<uint8_t>> m_lastData;
bool m_received{ false }; bool m_received{ false };
}; };
// A receiver that intentionally throws from SetStatus(...) to test callback-safety. // A receiver that intentionally throws from SetConnectState(...) to test callback-safety.
class CUDSThrowingReceiver : class CUDSThrowingReceiver :
public sdv::IInterfaceAccess, public sdv::IInterfaceAccess,
public sdv::ipc::IDataReceiveCallback, public sdv::ipc::IDataReceiveCallback,
@@ -161,9 +165,9 @@ public:
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& /*seq*/) override {} void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& /*seq*/) override {}
void SetStatus(sdv::ipc::EConnectStatus s) override void SetConnectState(sdv::ipc::EConnectState s) override
{ {
// Store the last status and then throw to simulate misbehaving user code. // Store the last state and then throw to simulate misbehaving user code.
{ {
std::lock_guard<std::mutex> lk(m_mtx); std::lock_guard<std::mutex> lk(m_mtx);
m_last = s; m_last = s;
@@ -172,7 +176,7 @@ public:
throw std::runtime_error("Intentional user callback failure"); throw std::runtime_error("Intentional user callback failure");
} }
bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
{ {
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
@@ -193,7 +197,7 @@ public:
private: private:
std::mutex m_mtx; std::mutex m_mtx;
std::condition_variable m_cv; std::condition_variable m_cv;
sdv::ipc::EConnectStatus m_last{ sdv::ipc::EConnectStatus::uninitialized }; sdv::ipc::EConnectState m_last{ sdv::ipc::EConnectState::uninitialized };
}; };
@@ -328,7 +332,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect)
if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; } if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; }
if (clientConn->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult = 5; return; } if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; }
// Wait for the server to be conected before disconecting the client // Wait for the server to be conected before disconecting the client
while (!allowClientDisconnect.load()) while (!allowClientDisconnect.load())
@@ -339,7 +343,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect)
// SERVER must also report connected // SERVER must also report connected
EXPECT_TRUE(serverConn->WaitForConnection(5000)); EXPECT_TRUE(serverConn->WaitForConnection(5000));
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
// Allow client to dissconect now, because the Server is connected // Allow client to dissconect now, because the Server is connected
allowClientDisconnect = true; allowClientDisconnect = true;
@@ -348,7 +352,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect)
//DISCONNECT both //DISCONNECT both
serverConn->Disconnect(); serverConn->Disconnect();
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
// Shutdown Manager / Framework // Shutdown Manager / Framework
EXPECT_NO_THROW(mgr.Shutdown()); EXPECT_NO_THROW(mgr.Shutdown());
@@ -403,7 +407,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; } if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; }
if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; } if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; }
if (clientConn->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult = 5; return; } if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; }
//waits for confirmation before disconect //waits for confirmation before disconect
while (!allowClientDisconnect.load()) while (!allowClientDisconnect.load())
@@ -415,7 +419,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
// Server has to be connected (same timeout with client?) // Server has to be connected (same timeout with client?)
EXPECT_TRUE(serverConn->WaitForConnection(5000)) << "Server didn't reach 'connected' in time"; EXPECT_TRUE(serverConn->WaitForConnection(5000)) << "Server didn't reach 'connected' in time";
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
// Allows Client to disconnect and waits for finishing // Allows Client to disconnect and waits for finishing
allowClientDisconnect = true; allowClientDisconnect = true;
@@ -424,7 +428,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
// Disconnect server // Disconnect server
serverConn->Disconnect(); serverConn->Disconnect();
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
// SESSION 2 // SESSION 2
// SERVER // SERVER
@@ -450,7 +454,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
if (!clientConn2->AsyncConnect(&cRcvr2)) { clientResult2 = 3; return; } if (!clientConn2->AsyncConnect(&cRcvr2)) { clientResult2 = 3; return; }
if (!clientConn2->WaitForConnection(5000)) { clientResult2 = 4; return; } if (!clientConn2->WaitForConnection(5000)) { clientResult2 = 4; return; }
if (clientConn2->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult2 = 5; return; } if (clientConn2->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult2 = 5; return; }
//waits for confirmation before disconect //waits for confirmation before disconect
while (!allowClientDisconnect2.load()) while (!allowClientDisconnect2.load())
@@ -463,7 +467,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
// Server has to be connected // Server has to be connected
// if unlink(path) from session 1 worked, bind/listen/accept works again // if unlink(path) from session 1 worked, bind/listen/accept works again
EXPECT_TRUE(serverConn2->WaitForConnection(5000)) << "Server didn't reach 'connected' in time (session 2)"; EXPECT_TRUE(serverConn2->WaitForConnection(5000)) << "Server didn't reach 'connected' in time (session 2)";
EXPECT_EQ(serverConn2->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(serverConn2->GetConnectState(), sdv::ipc::EConnectState::connected);
// Allows Client to disconnect and waits for finishing // Allows Client to disconnect and waits for finishing
allowClientDisconnect2 = true; allowClientDisconnect2 = true;
@@ -472,7 +476,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
// Disconnect server // Disconnect server
serverConn2->Disconnect(); serverConn2->Disconnect();
EXPECT_EQ(serverConn2->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(serverConn2->GetConnectState(), sdv::ipc::EConnectState::disconnected);
//Shutdown manager/framework //Shutdown manager/framework
EXPECT_NO_THROW(mgr.Shutdown()); EXPECT_NO_THROW(mgr.Shutdown());
@@ -550,8 +554,8 @@ TEST(UnixSocketIPC, CreateEndpoint_WithConfigAndPathClamping)
EXPECT_TRUE(serverConn->WaitForConnection(5000)); EXPECT_TRUE(serverConn->WaitForConnection(5000));
EXPECT_TRUE(clientConn->WaitForConnection(5000)); EXPECT_TRUE(clientConn->WaitForConnection(5000));
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::connected);
// Cleanup // Cleanup
clientConn->Disconnect(); clientConn->Disconnect();
@@ -592,8 +596,8 @@ TEST(UnixSocketIPC, Access_DefaultPath_ServerClientConnect)
EXPECT_TRUE(serverConn->WaitForConnection(5000)); EXPECT_TRUE(serverConn->WaitForConnection(5000));
EXPECT_TRUE(clientConn->WaitForConnection(5000)); EXPECT_TRUE(clientConn->WaitForConnection(5000));
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::connected);
clientConn->Disconnect(); clientConn->Disconnect();
serverConn->Disconnect(); serverConn->Disconnect();
@@ -637,7 +641,7 @@ TEST(UnixSocketIPC, WaitForConnection_InfiniteWait_SlowClient)
// INFINITE wait (0xFFFFFFFFu) // INFINITE wait (0xFFFFFFFFu)
EXPECT_TRUE(serverConn->WaitForConnection(0xFFFFFFFFu)); EXPECT_TRUE(serverConn->WaitForConnection(0xFFFFFFFFu));
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
// Cleanup // Cleanup
delayedClient.join(); delayedClient.join();
@@ -691,7 +695,8 @@ TEST(UnixSocketIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter)
TEST(UnixSocketIPC, ClientTimeout_NoServer) TEST(UnixSocketIPC, ClientTimeout_NoServer)
{ {
sdv::app::CAppControl app; sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup("")); ASSERT_TRUE(app.Startup(R"toml([LogHandler]
ViewFilter = "Fatal")toml"));
app.SetRunningMode(); app.SetRunningMode();
CUnixDomainSocketsChannelMgnt mgr; CUnixDomainSocketsChannelMgnt mgr;
@@ -714,7 +719,7 @@ TEST(UnixSocketIPC, ClientTimeout_NoServer)
EXPECT_FALSE(clientConn->WaitForConnection(1500)); EXPECT_FALSE(clientConn->WaitForConnection(1500));
// after another ~1s it should be in connection_error // after another ~1s it should be in connection_error
std::this_thread::sleep_for(std::chrono::milliseconds(800)); std::this_thread::sleep_for(std::chrono::milliseconds(800));
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connection_error); EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::connection_error);
clientConn->Disconnect(); // cleanup (joins threads) clientConn->Disconnect(); // cleanup (joins threads)
EXPECT_NO_THROW(mgr.Shutdown()); EXPECT_NO_THROW(mgr.Shutdown());
@@ -754,9 +759,9 @@ TEST(UnixSocketIPC, ServerDisconnectPropagatesToClient)
serverConn->Disconnect(); serverConn->Disconnect();
// Deterministic wait for client-side transition to 'disconnected' // Deterministic wait for client-side transition to 'disconnected'
EXPECT_TRUE(cRcvr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, /*ms*/ 3000)) << "Client did not observe 'disconnected' after server closed the socket."; EXPECT_TRUE(cRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, /*ms*/ 3000)) << "Client did not observe 'disconnected' after server closed the socket.";
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
clientConn->Disconnect(); clientConn->Disconnect();
EXPECT_NO_THROW(mgr.Shutdown()); EXPECT_NO_THROW(mgr.Shutdown());
@@ -794,7 +799,7 @@ TEST(UnixSocketIPC, ReconnectOnSameServerInstance)
ASSERT_TRUE(clientConn->WaitForConnection(5000)); ASSERT_TRUE(clientConn->WaitForConnection(5000));
clientConn->Disconnect(); clientConn->Disconnect();
serverConn->Disconnect(); serverConn->Disconnect();
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
} }
// Second session on the same serverConn object // Second session on the same serverConn object
@@ -1129,7 +1134,7 @@ TEST(UnixSocketIPC, PeerCloseMidTransfer_ClientSeesDisconnected_AndSendMayFailOr
std::cout << "[Debug] SendData result = " << sendResult.load() << std::endl; std::cout << "[Debug] SendData result = " << sendResult.load() << std::endl;
// But client MUST observe disconnected: // But client MUST observe disconnected:
EXPECT_TRUE(cRcvr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000)); EXPECT_TRUE(cRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, 3000));
clientConn->Disconnect(); clientConn->Disconnect();
EXPECT_NO_THROW(mgr.Shutdown()); EXPECT_NO_THROW(mgr.Shutdown());
@@ -1162,8 +1167,8 @@ TEST(UnixSocketIPC, ClientCancelConnect_NoServer_CleansUpPromptly)
std::this_thread::sleep_for(std::chrono::milliseconds(150)); std::this_thread::sleep_for(std::chrono::milliseconds(150));
clientConn->Disconnect(); clientConn->Disconnect();
// Immediately after disconnect, status should be 'disconnected' (no hangs). // Immediately after disconnect, state should be 'disconnected' (no hangs).
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
EXPECT_NO_THROW(mgr.Shutdown()); app.Shutdown(); EXPECT_NO_THROW(mgr.Shutdown()); app.Shutdown();
} }
@@ -1172,7 +1177,8 @@ TEST(UnixSocketIPC, ClientCancelConnect_NoServer_CleansUpPromptly)
TEST(UnixSocketIPC, ServerStartThenImmediateDisconnect_NoClient) TEST(UnixSocketIPC, ServerStartThenImmediateDisconnect_NoClient)
{ {
sdv::app::CAppControl app; sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup("")); ASSERT_TRUE(app.Startup(R"toml([LogHandler]
ViewFilter = "Fatal")toml"));
app.SetRunningMode(); app.SetRunningMode();
CUnixDomainSocketsChannelMgnt mgr; CUnixDomainSocketsChannelMgnt mgr;
EXPECT_NO_THROW(mgr.Initialize("")); EXPECT_NO_THROW(mgr.Initialize(""));
@@ -1192,15 +1198,15 @@ TEST(UnixSocketIPC, ServerStartThenImmediateDisconnect_NoClient)
// The server may be still listening; ensure we can disconnect cleanly // The server may be still listening; ensure we can disconnect cleanly
std::this_thread::sleep_for(std::chrono::milliseconds(50)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
serverConn->Disconnect(); serverConn->Disconnect();
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
EXPECT_NO_THROW(mgr.Shutdown()); EXPECT_NO_THROW(mgr.Shutdown());
app.Shutdown(); app.Shutdown();
} }
//Callback throws in SetStatus -> transport threads keep running //Callback throws in SetConnectState -> transport threads keep running
TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport) TEST(UnixSocketIPC, CallbackThrowsInSetConnectState_DoesNotCrashTransport)
{ {
sdv::app::CAppControl app; sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup("")); ASSERT_TRUE(app.Startup(""));
@@ -1227,7 +1233,7 @@ TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport)
CUDSThrowingReceiver cRcvr; CUDSThrowingReceiver cRcvr;
ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr));
// Despite exceptions thrown inside SetStatus, the transport should still reach connected. // Despite exceptions thrown inside SetConnectState, the transport should still reach connected.
EXPECT_TRUE(serverConn->WaitForConnection(5000)); EXPECT_TRUE(serverConn->WaitForConnection(5000));
EXPECT_TRUE(clientConn->WaitForConnection(5000)); EXPECT_TRUE(clientConn->WaitForConnection(5000));
@@ -1239,8 +1245,8 @@ TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport)
app.Shutdown(); app.Shutdown();
} }
//RegisterStatusEventCallback: multiple listeners receive status updates //RegisterStateEventCallback: multiple listeners receive state updates
TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus) TEST(UnixSocketIPC, RegisterStateEventCallback_MultipleCallbacksReceiveState)
{ {
sdv::app::CAppControl app; sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup("")); ASSERT_TRUE(app.Startup(""));
@@ -1266,12 +1272,12 @@ TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus)
// --- Callback receiver 1 --- // --- Callback receiver 1 ---
CUDSConnectReceiver recv1; CUDSConnectReceiver recv1;
uint64_t cookie1 = server->RegisterStatusEventCallback(&recv1); uint64_t cookie1 = server->RegisterStateEventCallback(&recv1);
EXPECT_NE(cookie1, 0u); EXPECT_NE(cookie1, 0u);
// --- Callback receiver 2 --- // --- Callback receiver 2 ---
CUDSConnectReceiver recv2; CUDSConnectReceiver recv2;
uint64_t cookie2 = server->RegisterStatusEventCallback(&recv2); uint64_t cookie2 = server->RegisterStateEventCallback(&recv2);
EXPECT_NE(cookie2, 0u); EXPECT_NE(cookie2, 0u);
EXPECT_NE(cookie1, cookie2); EXPECT_NE(cookie1, cookie2);
@@ -1283,22 +1289,22 @@ TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus)
ASSERT_TRUE(client->WaitForConnection(5000)); ASSERT_TRUE(client->WaitForConnection(5000));
// Both receivers should have received 'connected' // Both receivers should have received 'connected'
EXPECT_TRUE(recv1.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)); EXPECT_TRUE(recv1.WaitForState(sdv::ipc::EConnectState::connected, 1000));
EXPECT_TRUE(recv2.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)); EXPECT_TRUE(recv2.WaitForState(sdv::ipc::EConnectState::connected, 1000));
// --- Disconnect --- // --- Disconnect ---
client->Disconnect(); client->Disconnect();
EXPECT_TRUE(recv1.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)); EXPECT_TRUE(recv1.WaitForState(sdv::ipc::EConnectState::disconnected, 1000));
EXPECT_TRUE(recv2.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)); EXPECT_TRUE(recv2.WaitForState(sdv::ipc::EConnectState::disconnected, 1000));
server->Disconnect(); server->Disconnect();
EXPECT_NO_THROW(mgr.Shutdown()); EXPECT_NO_THROW(mgr.Shutdown());
app.Shutdown(); app.Shutdown();
} }
//UnregisterStatusEventCallback: removed listener stops receiving events //UnregisterStateEventCallback: removed listener stops receiving events
TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving) TEST(UnixSocketIPC, UnregisterStateEventCallback_RemovedListenerStopsReceiving)
{ {
// Framework + Manager // Framework + Manager
sdv::app::CAppControl app; sdv::app::CAppControl app;
@@ -1328,13 +1334,13 @@ TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving)
ASSERT_NE(client, nullptr); ASSERT_NE(client, nullptr);
// --------- Two distinct receivers ---------- // --------- Two distinct receivers ----------
// recvReg: used ONLY for the status-callback registry // recvReg: used ONLY for the state-callback registry
// recvConn: used ONLY for AsyncConnect (m_pReceiver / m_pEvent path) // recvConn: used ONLY for AsyncConnect (m_pReceiver / m_pEvent path)
CUDSConnectReceiver recvReg; CUDSConnectReceiver recvReg;
CUDSConnectReceiver recvConn; CUDSConnectReceiver recvConn;
// Register recvReg as a status listener (registry path) // Register recvReg as a state listener (registry path)
uint64_t cookie = server->RegisterStatusEventCallback(&recvReg); uint64_t cookie = server->RegisterStateEventCallback(&recvReg);
ASSERT_NE(cookie, 0u) << "Cookie must be non-zero"; ASSERT_NE(cookie, 0u) << "Cookie must be non-zero";
// Start connections (server uses recvReg only for registry; recvConn is the IConnect/IDataReceive side) // Start connections (server uses recvReg only for registry; recvConn is the IConnect/IDataReceive side)
@@ -1346,21 +1352,21 @@ TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving)
ASSERT_TRUE(client->WaitForConnection(5000)); ASSERT_TRUE(client->WaitForConnection(5000));
// Both the connection receiver (recvConn) and the registry listener (recvReg) should observe 'connected' // Both the connection receiver (recvConn) and the registry listener (recvReg) should observe 'connected'
EXPECT_TRUE(recvConn.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)) << "Connection receiver didn't see 'connected'."; EXPECT_TRUE(recvConn.WaitForState(sdv::ipc::EConnectState::connected, 1000)) << "Connection receiver didn't see 'connected'.";
EXPECT_TRUE(recvReg.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)) << "Registry listener didn't see 'connected'."; EXPECT_TRUE(recvReg.WaitForState(sdv::ipc::EConnectState::connected, 1000)) << "Registry listener didn't see 'connected'.";
// --------- Unregister the registry listener ---------- // --------- Unregister the registry listener ----------
server->UnregisterStatusEventCallback(cookie); server->UnregisterStateEventCallback(cookie);
// Trigger a disconnect on client to force status transitions // Trigger a disconnect on client to force state transitions
client->Disconnect(); client->Disconnect();
// The connection receiver (recvConn) still sees disconnected (normal path) // The connection receiver (recvConn) still sees disconnected (normal path)
EXPECT_TRUE(recvConn.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)) << "Connection receiver didn't see 'disconnected'."; EXPECT_TRUE(recvConn.WaitForState(sdv::ipc::EConnectState::disconnected, 1000)) << "Connection receiver didn't see 'disconnected'.";
// The registry listener (recvReg) MUST NOT receive 'disconnected' anymore // The registry listener (recvReg) MUST NOT receive 'disconnected' anymore
// (wait a short, deterministic interval) // (wait a short, deterministic interval)
EXPECT_FALSE(recvReg.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 300)) << "Registry listener received 'disconnected' after UnregisterStatusEventCallback()."; EXPECT_FALSE(recvReg.WaitForState(sdv::ipc::EConnectState::disconnected, 300)) << "Registry listener received 'disconnected' after UnregisterStateEventCallback().";
// Server cleanup // Server cleanup
server->Disconnect(); server->Disconnect();

View File

@@ -0,0 +1,107 @@
#*******************************************************************************
# 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(UNIX)
cmake_minimum_required(VERSION 3.20)
project(UnixTunnelCommunicationTests LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Executable: GTest for UDS connect
add_executable(UnitTest_UnixTunnelConnectTests
unix_tunnel_connect_tests.cpp
)
target_include_directories(UnitTest_UnixTunnelConnectTests PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_link_libraries(UnitTest_UnixTunnelConnectTests
GTest::GTest
${CMAKE_THREAD_LIBS_INIT}
stdc++fs
${CMAKE_DL_LIBS}
rt
uds_unix_tunnel
uds_unix_sockets
)
else()
target_link_libraries(UnitTest_UnixTunnelConnectTests
GTest::GTest
Ws2_32 Winmm
Rpcrt4.lib
uds_unix_tunnel
uds_unix_sockets
)
endif()
add_test(NAME UnitTest_UnixTunnelConnectTests
COMMAND UnitTest_UnixTunnelConnectTests)
add_dependencies(UnitTest_UnixTunnelConnectTests dependency_sdv_components)
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
add_custom_command(TARGET UnitTest_UnixTunnelConnectTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
"$<TARGET_FILE:UnitTest_UnixTunnelConnectTests>"
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixTunnelConnectTests.xml
VERBATIM
)
endif()
add_executable(UnitTest_UnixTunnelChannelMgntTests
unix_tunnel_channel_mgnt_tests.cpp
)
target_include_directories(UnitTest_UnixTunnelChannelMgntTests PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_link_libraries(UnitTest_UnixTunnelChannelMgntTests
GTest::GTest
${CMAKE_THREAD_LIBS_INIT}
stdc++fs
${CMAKE_DL_LIBS}
rt
uds_unix_tunnel
uds_unix_sockets
)
else()
target_link_libraries(UnitTest_UnixTunnelChannelMgntTests
GTest::GTest
Ws2_32
Winmm
Rpcrt4.lib
uds_unix_tunnel
uds_unix_sockets
)
endif()
add_test(NAME UnitTest_UnixTunnelChannelMgntTests
COMMAND UnitTest_UnixTunnelChannelMgntTests)
add_dependencies(UnitTest_UnixTunnelChannelMgntTests dependency_sdv_components)
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
add_custom_command(TARGET UnitTest_UnixTunnelChannelMgntTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
"$<TARGET_FILE:UnitTest_UnixTunnelChannelMgntTests>"
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixTunnelChannelMgntTests.xml
VERBATIM
)
endif()
endif()

View File

@@ -0,0 +1,240 @@
#if defined(__unix__)
#include "gtest/gtest.h"
#include <support/app_control.h>
#include <interfaces/ipc.h>
#include <support/interface_ptr.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstring>
#include <mutex>
#include <thread>
#include "../sdv_services/uds_unix_tunnel/channel_mgnt.h"
#include "../sdv_services/uds_unix_tunnel/connection.h"
#include "../sdv_services/uds_unix_sockets/connection.h"
// Reuse the same CTunnelTestReceiver from unix_tunnel_connect_tests
class CTunnelMgrTestReceiver :
public sdv::IInterfaceAccess,
public sdv::ipc::IDataReceiveCallback,
public sdv::ipc::IConnectEventCallback
{
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
END_SDV_INTERFACE_MAP()
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& seqData) override
{
{
std::lock_guard<std::mutex> lk(m_mtx);
m_lastData = seqData;
m_received = true;
}
m_cv.notify_all();
}
void SetConnectState(sdv::ipc::EConnectState s) override
{
{
std::lock_guard<std::mutex> lk(m_mtx);
m_state = s;
}
m_cv.notify_all();
}
bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
{
std::unique_lock<std::mutex> lk(m_mtx);
if (m_state == expected)
return true;
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
while (m_state != expected)
{
if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout)
return false;
}
return true;
}
bool WaitForData(uint32_t ms = 2000)
{
std::unique_lock<std::mutex> lk(m_mtx);
return m_cv.wait_for(lk, std::chrono::milliseconds(ms), [&]{ return m_received; });
}
sdv::sequence<sdv::pointer<uint8_t>> GetLastData() const
{
std::lock_guard<std::mutex> lk(m_mtx);
return m_lastData;
}
private:
mutable std::mutex m_mtx;
std::condition_variable m_cv;
sdv::ipc::EConnectState m_state{ sdv::ipc::EConnectState::uninitialized };
sdv::sequence<sdv::pointer<uint8_t>> m_lastData;
bool m_received{ false };
};
//Manager instantiate + lifecycle
TEST(UnixTunnelChannelMgnt, InstantiateAndLifecycle)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
CUnixTunnelChannelMgnt mgr;
EXPECT_NO_THROW(mgr.Initialize(""));
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized);
EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::configuring));
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::configuring);
EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running));
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::running);
EXPECT_NO_THROW(mgr.Shutdown());
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending);
app.Shutdown();
}
// CreateEndpoint -> Access(server/client) -> AsyncConnect -> Wait -> Disconnect
TEST(UnixTunnelChannelMgnt, BasicConnectDisconnect)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CUnixTunnelChannelMgnt mgr;
EXPECT_NO_THROW(mgr.Initialize(""));
EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running));
ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running);
// Create a tunnel endpoint (server)
auto ep = mgr.CreateEndpoint("");
ASSERT_NE(ep.pConnection, nullptr);
ASSERT_FALSE(ep.ssConnectString.empty());
const std::string serverCS = ep.ssConnectString;
// Convert to client by role=client
std::string clientCS = serverCS;
{
const std::string from = "role=server";
const std::string to = "role=client";
auto pos = clientCS.find(from);
if (pos != std::string::npos)
clientCS.replace(pos, from.size(), to);
}
// SERVER
sdv::TObjectPtr serverObj = mgr.Access(serverCS);
ASSERT_TRUE(serverObj);
auto* serverConn = serverObj.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(serverConn, nullptr);
CTunnelMgrTestReceiver sRcvr;
ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr));
// CLIENT (thread)
std::atomic<int> clientResult{0};
std::thread clientThread([&]{
sdv::TObjectPtr clientObj = mgr.Access(clientCS);
if (!clientObj) { clientResult = 1; return; }
auto* clientConn = clientObj.GetInterface<sdv::ipc::IConnect>();
if (!clientConn) { clientResult = 2; return; }
CTunnelMgrTestReceiver cRcvr;
if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; }
if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; }
if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; }
clientConn->Disconnect();
clientResult = 0;
});
EXPECT_TRUE(serverConn->WaitForConnection(5000));
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
clientThread.join();
EXPECT_EQ(clientResult.load(), 0);
serverConn->Disconnect();
EXPECT_NO_THROW(mgr.Shutdown());
app.Shutdown();
}
// Data path: "hello" via channel manager (using proto=tunnel)
TEST(UnixTunnelChannelMgnt, DataPath_SimpleHello_ViaManager)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CUnixTunnelChannelMgnt mgr;
EXPECT_NO_THROW(mgr.Initialize(""));
EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running));
ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running);
auto ep = mgr.CreateEndpoint("");
ASSERT_FALSE(ep.ssConnectString.empty());
const std::string serverCS = ep.ssConnectString;
std::string clientCS = serverCS;
{
const std::string from = "role=server";
const std::string to = "role=client";
auto pos = clientCS.find(from);
if (pos != std::string::npos)
clientCS.replace(pos, from.size(), to);
}
// Server
sdv::TObjectPtr serverObj = mgr.Access(serverCS);
ASSERT_TRUE(serverObj);
auto* serverConn = serverObj.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(serverConn, nullptr);
CTunnelMgrTestReceiver sRcvr;
ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr));
// Client
sdv::TObjectPtr clientObj = mgr.Access(clientCS);
ASSERT_TRUE(clientObj);
auto* clientConn = clientObj.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(clientConn, nullptr);
CTunnelMgrTestReceiver cRcvr;
ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr));
EXPECT_TRUE(serverConn->WaitForConnection(5000));
EXPECT_TRUE(clientConn->WaitForConnection(5000));
// Payload "hello"
sdv::pointer<uint8_t> p;
p.resize(5);
std::memcpy(p.get(), "hello", 5);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(p);
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientConn);
ASSERT_NE(pSend, nullptr);
EXPECT_TRUE(pSend->SendData(seq));
EXPECT_TRUE(sRcvr.WaitForData(3000));
sdv::sequence<sdv::pointer<uint8_t>> recv = sRcvr.GetLastData();
ASSERT_EQ(recv.size(), 1u);
ASSERT_EQ(recv[0].size(), 5u);
EXPECT_EQ(std::memcmp(recv[0].get(), "hello", 5), 0);
clientConn->Disconnect();
serverConn->Disconnect();
EXPECT_NO_THROW(mgr.Shutdown());
app.Shutdown();
}
#endif // defined(__unix__)

View File

@@ -0,0 +1,376 @@
/********************************************************************************
* 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 "gtest/gtest.h"
#include <interfaces/ipc.h>
#include <support/interface_ptr.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstring>
#include <mutex>
#include <random>
#include <sstream>
#include <string>
#include <thread>
// Adjust include paths to your tree layout:
#include "../sdv_services/uds_unix_sockets/connection.h" // CUnixSocketConnection
#include "../sdv_services/uds_unix_tunnel/connection.h" // CUnixTunnelConnection
// ===================== Test helpers =====================
class CTunnelTestReceiver :
public sdv::IInterfaceAccess,
public sdv::ipc::IDataReceiveCallback,
public sdv::ipc::IConnectEventCallback
{
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
END_SDV_INTERFACE_MAP()
// IDataReceiveCallback
void ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) override
{
{
std::lock_guard<std::mutex> lk(m_mtx);
m_lastData = seqData;
m_received = true;
}
m_cv.notify_all();
}
// IConnectEventCallback
void SetConnectState(sdv::ipc::EConnectState s) override
{
{
std::lock_guard<std::mutex> lk(m_mtx);
m_state = s;
}
m_cv.notify_all();
}
bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
{
std::unique_lock<std::mutex> lk(m_mtx);
if (m_state == expected)
return true;
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
while (m_state != expected)
{
if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout)
return false;
}
return true;
}
bool WaitForData(uint32_t ms = 2000)
{
std::unique_lock<std::mutex> lk(m_mtx);
return m_cv.wait_for(lk, std::chrono::milliseconds(ms), [&]{ return m_received; });
}
sdv::sequence<sdv::pointer<uint8_t>> GetLastData() const
{
std::lock_guard<std::mutex> lk(m_mtx);
return m_lastData;
}
sdv::ipc::EConnectState GetConnectState() const
{
std::lock_guard<std::mutex> lk(m_mtx);
return m_state;
}
private:
mutable std::mutex m_mtx;
std::condition_variable m_cv;
sdv::ipc::EConnectState m_state { sdv::ipc::EConnectState::uninitialized };
sdv::sequence<sdv::pointer<uint8_t>> m_lastData;
bool m_received { false };
};
// Small helper similar to MakeRandomSuffix() in uds_connect_tests.cpp
static std::string MakeRandomSuffix()
{
std::mt19937_64 rng{std::random_device{}()};
std::uniform_int_distribution<uint64_t> dist;
std::ostringstream oss;
oss << std::hex << dist(rng);
return oss.str();
}
// ===================== Tests =====================
// BASIC: server + client CUnixSocketConnection wrapped by CUnixTunnelConnection.
// Just test connect + disconnect via tunnel.
TEST(UnixTunnelIPC, BasicConnectDisconnectViaTunnel)
{
const std::string udsPath = std::string("/tmp/sdv_tunnel_") + MakeRandomSuffix() + ".sock";
// --- Physical transports (UDS) ---
auto serverTransport = std::make_shared<CUnixSocketConnection>(
-1, /*preconfiguredFd*/
true, /*acceptConnectionRequired (server)*/
udsPath);
auto clientTransport = std::make_shared<CUnixSocketConnection>(
-1,
false, /*client*/
udsPath);
// --- Tunnel wrappers ---
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(
serverTransport,
/*channelId*/ 1);
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(
clientTransport,
/*channelId*/ 1);
CTunnelTestReceiver serverRcvr;
CTunnelTestReceiver clientRcvr;
// Register state callbacks (optional, but useful)
uint64_t srvCookie = serverTunnel->RegisterStateEventCallback(&serverRcvr);
uint64_t cliCookie = clientTunnel->RegisterStateEventCallback(&clientRcvr);
EXPECT_NE(srvCookie, 0u);
EXPECT_NE(cliCookie, 0u);
// Async connect on both ends
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
// Wait for connections
EXPECT_TRUE(serverTunnel->WaitForConnection(5000));
EXPECT_TRUE(clientTunnel->WaitForConnection(5000));
EXPECT_EQ(serverTunnel->GetConnectState(), sdv::ipc::EConnectState::connected);
EXPECT_EQ(clientTunnel->GetConnectState(), sdv::ipc::EConnectState::connected);
// Disconnect client first
clientTunnel->Disconnect();
EXPECT_TRUE(clientRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, 2000));
// Disconnect server
serverTunnel->Disconnect();
EXPECT_TRUE(serverRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, 2000));
serverTunnel->UnregisterStateEventCallback(srvCookie);
clientTunnel->UnregisterStateEventCallback(cliCookie);
}
// DATA PATH: send "hello" via tunnel and verify it is received on server side.
// For now the tunnel is "pass-through" (no STunnelHeader yet).
TEST(UnixTunnelIPC, DataPath_SimpleHello_ViaTunnel)
{
const std::string udsPath = std::string("/tmp/sdv_tunnel_data_") + MakeRandomSuffix() + ".sock";
// Physical transports
auto serverTransport = std::make_shared<CUnixSocketConnection>(
-1, true, udsPath);
auto clientTransport = std::make_shared<CUnixSocketConnection>(
-1, false, udsPath);
// Tunnel wrappers (same logical channel for both ends)
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(serverTransport, 1);
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(clientTransport, 1);
CTunnelTestReceiver serverRcvr;
CTunnelTestReceiver clientRcvr;
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
ASSERT_TRUE(serverTunnel->WaitForConnection(5000));
ASSERT_TRUE(clientTunnel->WaitForConnection(5000));
// Build "hello" payload
sdv::sequence<sdv::pointer<uint8_t>> seq;
sdv::pointer<uint8_t> p;
const char* msg = "hello";
const size_t len = std::strlen(msg);
p.resize(len);
std::memcpy(p.get(), msg, len);
seq.push_back(p);
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientTunnel.get());
ASSERT_NE(pSend, nullptr);
EXPECT_TRUE(pSend->SendData(seq));
// Wait for server-side data callback
ASSERT_TRUE(serverRcvr.WaitForData(3000));
sdv::sequence<sdv::pointer<uint8_t>> recv = serverRcvr.GetLastData();
ASSERT_EQ(recv.size(), 1u);
ASSERT_EQ(recv[0].size(), len);
EXPECT_EQ(std::memcmp(recv[0].get(), msg, len), 0);
clientTunnel->Disconnect();
serverTunnel->Disconnect();
}
TEST(UnixTunnelIPC, DataPath_MultiChunk_ViaTunnel)
{
const std::string udsPath = std::string("/tmp/sdv_tunnel_multi_") + MakeRandomSuffix() + ".sock";
auto serverTransport = std::make_shared<CUnixSocketConnection>(-1, true, udsPath);
auto clientTransport = std::make_shared<CUnixSocketConnection>(-1, false, udsPath);
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(serverTransport, 1);
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(clientTransport, 1);
CTunnelTestReceiver serverRcvr;
CTunnelTestReceiver clientRcvr;
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
ASSERT_TRUE(serverTunnel->WaitForConnection(5000));
ASSERT_TRUE(clientTunnel->WaitForConnection(5000));
// Two chunks: "sdv" and "framework"
sdv::pointer<uint8_t> p1, p2;
p1.resize(3);
std::memcpy(p1.get(), "sdv", 3);
p2.resize(9);
std::memcpy(p2.get(), "framework", 9);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(p1);
seq.push_back(p2);
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientTunnel.get());
ASSERT_NE(pSend, nullptr);
EXPECT_TRUE(pSend->SendData(seq));
ASSERT_TRUE(serverRcvr.WaitForData(3000));
sdv::sequence<sdv::pointer<uint8_t>> recv = serverRcvr.GetLastData();
ASSERT_EQ(recv.size(), 2u);
EXPECT_EQ(recv[0].size(), 3u);
EXPECT_EQ(recv[1].size(), 9u);
EXPECT_EQ(std::memcmp(recv[0].get(), "sdv", 3), 0);
EXPECT_EQ(std::memcmp(recv[1].get(), "framework", 9), 0);
clientTunnel->Disconnect();
serverTunnel->Disconnect();
}
TEST(UnixTunnelIPC, DataPath_LargePayload_Fragmentation_ViaTunnel)
{
const std::string udsPath = std::string("/tmp/sdv_tunnel_large_") + MakeRandomSuffix() + ".sock";
auto serverTransport = std::make_shared<CUnixSocketConnection>(-1, true, udsPath);
auto clientTransport = std::make_shared<CUnixSocketConnection>(-1, false, udsPath);
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(serverTransport, 1);
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(clientTransport, 1);
CTunnelTestReceiver serverRcvr;
CTunnelTestReceiver clientRcvr;
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
ASSERT_TRUE(serverTunnel->WaitForConnection(5000));
ASSERT_TRUE(clientTunnel->WaitForConnection(5000));
// Build a large payload (e.g. 256 KiB)
const size_t totalBytes = 256 * 1024;
sdv::pointer<uint8_t> big;
big.resize(totalBytes);
for (size_t i = 0; i < totalBytes; ++i)
big.get()[i] = static_cast<uint8_t>(i & 0xFF);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(big);
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientTunnel.get());
ASSERT_NE(pSend, nullptr);
EXPECT_TRUE(pSend->SendData(seq));
ASSERT_TRUE(serverRcvr.WaitForData(5000));
sdv::sequence<sdv::pointer<uint8_t>> recv = serverRcvr.GetLastData();
ASSERT_EQ(recv.size(), 1u);
ASSERT_EQ(recv[0].size(), totalBytes);
EXPECT_EQ(std::memcmp(recv[0].get(), big.get(), totalBytes), 0);
clientTunnel->Disconnect();
serverTunnel->Disconnect();
}
// DATA PATH + HEADER: ensure that the upper receiver sees only the original
// payload (no STunnelHeader bytes in front)
TEST(UnixTunnelIPC, DataPath_HeaderStripped_ForUpperReceiver)
{
const std::string udsPath =
std::string("/tmp/sdv_tunnel_header_") + MakeRandomSuffix() + ".sock";
// Physical transports (UDS)
auto serverTransport = std::make_shared<CUnixSocketConnection>(
-1, /*server*/ true, udsPath);
auto clientTransport = std::make_shared<CUnixSocketConnection>(
-1, /*client*/ false, udsPath);
// Tunnel wrappers
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(serverTransport, /*channelId*/ 42);
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(clientTransport, /*channelId*/ 42);
CTunnelTestReceiver serverRcvr;
CTunnelTestReceiver clientRcvr;
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
ASSERT_TRUE(serverTunnel->WaitForConnection(5000));
ASSERT_TRUE(clientTunnel->WaitForConnection(5000));
// Build payload "HEADER_TEST"
const char* msg = "HEADER_TEST";
const size_t len = std::strlen(msg);
sdv::pointer<uint8_t> p;
p.resize(len);
std::memcpy(p.get(), msg, len);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(p);
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientTunnel.get());
ASSERT_NE(pSend, nullptr);
EXPECT_TRUE(pSend->SendData(seq));
// Wait for server-side data callback
ASSERT_TRUE(serverRcvr.WaitForData(3000));
auto recv = serverRcvr.GetLastData();
// --- Assertions that header was stripped for upper receiver ---
// Upper receiver must see exactly one chunk with the original size, without extra bytes for STunnelHeader
ASSERT_EQ(recv.size(), 1u) << "Upper receiver must see exactly one payload chunk";
ASSERT_EQ(recv[0].size(), len) << "Payload size must equal original size (no header)";
EXPECT_EQ(std::memcmp(recv[0].get(), msg, len), 0)
<< "Payload content must match exactly the original message (no header in front)";
clientTunnel->Disconnect();
serverTunnel->Disconnect();
}
#endif // defined(__unix__)

View File

@@ -7,7 +7,7 @@
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
#******************************************************************************* #*******************************************************************************
if(WIN32)
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
project(WinSocketCommunicationTests LANGUAGES CXX) project(WinSocketCommunicationTests LANGUAGES CXX)
@@ -36,6 +36,7 @@ target_include_directories(UnitTest_WinSocketConnectTests
Ws2_32 Ws2_32
Winmm Winmm
Rpcrt4 Rpcrt4
uds_win_sockets
) )
add_test(NAME UnitTest_WinSocketConnectTests add_test(NAME UnitTest_WinSocketConnectTests
@@ -50,4 +51,6 @@ add_custom_command(TARGET UnitTest_WinSocketConnectTests POST_BUILD
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinSocketConnectTests.xml --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinSocketConnectTests.xml
VERBATIM VERBATIM
) )
endif()
endif() endif()

View File

@@ -15,8 +15,8 @@
#include <support/app_control.h> #include <support/app_control.h>
#include <interfaces/ipc.h> #include <interfaces/ipc.h>
#include "../../../sdv_services/uds_win_sockets/channel_mgnt.cpp" #include "../../../sdv_services/uds_win_sockets/channel_mgnt.h"
#include "../../../sdv_services/uds_win_sockets/connection.cpp" #include "../../../sdv_services/uds_win_sockets/connection.h"
#include <windows.h> #include <windows.h>
#include <afunix.h> #include <afunix.h>
@@ -99,7 +99,7 @@ inline void SpinUntilServerArmed(sdv::ipc::IConnect* server, uint32_t maxWaitMs
using namespace std::chrono; using namespace std::chrono;
auto deadline = steady_clock::now() + milliseconds(maxWaitMs); auto deadline = steady_clock::now() + milliseconds(maxWaitMs);
while (server->GetStatus() == sdv::ipc::EConnectStatus::uninitialized && while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized &&
steady_clock::now() < deadline) steady_clock::now() < deadline)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
@@ -116,7 +116,7 @@ inline void SleepTiny(uint32_t ms = 20)
} // namespace test_utils } // namespace test_utils
// Unified test receiver (status + data) // Unified test receiver (state + data)
class CTestReceiver : class CTestReceiver :
public sdv::IInterfaceAccess, public sdv::IInterfaceAccess,
public sdv::ipc::IConnectEventCallback, public sdv::ipc::IConnectEventCallback,
@@ -128,11 +128,11 @@ public:
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
END_SDV_INTERFACE_MAP() END_SDV_INTERFACE_MAP()
void SetStatus(sdv::ipc::EConnectStatus s) override void SetConnectState(sdv::ipc::EConnectState s) override
{ {
{ {
std::lock_guard<std::mutex> lk(m_mtx); std::lock_guard<std::mutex> lk(m_mtx);
m_status = s; m_state = s;
} }
m_cv.notify_all(); m_cv.notify_all();
} }
@@ -147,11 +147,11 @@ public:
m_cv.notify_all(); m_cv.notify_all();
} }
bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
{ {
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
return m_cv.wait_for(lk, std::chrono::milliseconds(ms), return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
[&]{ return m_status == expected; }); [&]{ return m_state == expected; });
} }
bool WaitForData(uint32_t ms = 2000) bool WaitForData(uint32_t ms = 2000)
@@ -171,7 +171,7 @@ private:
mutable std::mutex m_mtx; mutable std::mutex m_mtx;
std::condition_variable m_cv; std::condition_variable m_cv;
sdv::ipc::EConnectStatus m_status{ sdv::ipc::EConnectStatus::uninitialized }; sdv::ipc::EConnectState m_state{ sdv::ipc::EConnectState::uninitialized };
sdv::sequence<sdv::pointer<uint8_t>> m_data; sdv::sequence<sdv::pointer<uint8_t>> m_data;
bool m_hasData{false}; bool m_hasData{false};
}; };
@@ -344,7 +344,7 @@ TEST(WindowsAFUnixIPC, ServerDisconnectPropagates)
pair.server->Disconnect(); pair.server->Disconnect();
EXPECT_TRUE(cr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000)); EXPECT_TRUE(cr.WaitForState(sdv::ipc::EConnectState::disconnected, 3000));
pair.client->Disconnect(); pair.client->Disconnect();
@@ -713,7 +713,8 @@ TEST(WindowsAFUnixIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter)
TEST(WindowsAFUnixIPC, ClientTimeout_NoServer) TEST(WindowsAFUnixIPC, ClientTimeout_NoServer)
{ {
sdv::app::CAppControl app; sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup("")); ASSERT_TRUE(app.Startup(R"toml([LogHandler]
ViewFilter = "Fatal")toml"));
app.SetRunningMode(); app.SetRunningMode();
CSocketsChannelMgnt mgr; CSocketsChannelMgnt mgr;
@@ -742,7 +743,7 @@ TEST(WindowsAFUnixIPC, ClientTimeout_NoServer)
EXPECT_FALSE(client->WaitForConnection(1500)); EXPECT_FALSE(client->WaitForConnection(1500));
std::this_thread::sleep_for(std::chrono::milliseconds(800)); std::this_thread::sleep_for(std::chrono::milliseconds(800));
EXPECT_EQ(client->GetStatus(), sdv::ipc::EConnectStatus::connection_error); EXPECT_EQ(client->GetConnectState(), sdv::ipc::EConnectState::connection_error);
client->Disconnect(); client->Disconnect();
mgr.Shutdown(); mgr.Shutdown();
@@ -799,7 +800,7 @@ TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect)
t.join(); t.join();
EXPECT_TRUE(cr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000)); EXPECT_TRUE(cr.WaitForState(sdv::ipc::EConnectState::disconnected, 3000));
pair.client->Disconnect(); pair.client->Disconnect();
mgr.Shutdown(); mgr.Shutdown();
@@ -810,7 +811,8 @@ TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect)
TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup) TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup)
{ {
sdv::app::CAppControl app; sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup("")); ASSERT_TRUE(app.Startup(R"toml([LogHandler]
ViewFilter = "Fatal")toml"));
app.SetRunningMode(); app.SetRunningMode();
CSocketsChannelMgnt mgr; CSocketsChannelMgnt mgr;
@@ -838,7 +840,7 @@ TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup)
std::this_thread::sleep_for(std::chrono::milliseconds(150)); std::this_thread::sleep_for(std::chrono::milliseconds(150));
client->Disconnect(); client->Disconnect();
EXPECT_EQ(client->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(client->GetConnectState(), sdv::ipc::EConnectState::disconnected);
mgr.Shutdown(); mgr.Shutdown();
app.Shutdown(); app.Shutdown();
@@ -848,7 +850,8 @@ TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup)
TEST(WindowsAFUnixIPC, ServerStartThenImmediateDisconnect_NoClient) TEST(WindowsAFUnixIPC, ServerStartThenImmediateDisconnect_NoClient)
{ {
sdv::app::CAppControl app; sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup("")); ASSERT_TRUE(app.Startup(R"toml([LogHandler]
ViewFilter = "Fatal")toml"));
app.SetRunningMode(); app.SetRunningMode();
CSocketsChannelMgnt mgr; CSocketsChannelMgnt mgr;
@@ -872,14 +875,14 @@ TEST(WindowsAFUnixIPC, ServerStartThenImmediateDisconnect_NoClient)
std::this_thread::sleep_for(std::chrono::milliseconds(50)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
server->Disconnect(); server->Disconnect();
EXPECT_EQ(server->GetStatus(), sdv::ipc::EConnectStatus::disconnected); EXPECT_EQ(server->GetConnectState(), sdv::ipc::EConnectState::disconnected);
mgr.Shutdown(); mgr.Shutdown();
app.Shutdown(); app.Shutdown();
} }
// UnregisterStatusEventCallback: ensure single-listener semantics // UnregisterStateEventCallback: ensure single-listener semantics
TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics) TEST(WindowsAFUnixIPC, UnregisterStateEventCallback_SingleListenerSemantics)
{ {
sdv::app::CAppControl app; sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup("")); ASSERT_TRUE(app.Startup(""));
@@ -905,7 +908,7 @@ TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics)
ASSERT_NE(client, nullptr); ASSERT_NE(client, nullptr);
CTestReceiver regListener; CTestReceiver regListener;
const uint64_t cookie = server->RegisterStatusEventCallback(&regListener); const uint64_t cookie = server->RegisterStateEventCallback(&regListener);
ASSERT_NE(cookie, 0u); ASSERT_NE(cookie, 0u);
CTestReceiver mainRecv; CTestReceiver mainRecv;
@@ -917,15 +920,15 @@ TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics)
EXPECT_TRUE(server->WaitForConnection(5000)); EXPECT_TRUE(server->WaitForConnection(5000));
EXPECT_TRUE(client->WaitForConnection(5000)); EXPECT_TRUE(client->WaitForConnection(5000));
EXPECT_TRUE(mainRecv.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)); EXPECT_TRUE(mainRecv.WaitForState(sdv::ipc::EConnectState::connected, 1000));
EXPECT_FALSE(regListener.WaitForStatus(sdv::ipc::EConnectStatus::connected, 300)) << "The registry listener should NOT receive events while main receiver is active."; EXPECT_FALSE(regListener.WaitForState(sdv::ipc::EConnectState::connected, 300)) << "The registry listener should NOT receive events while main receiver is active.";
server->UnregisterStatusEventCallback(cookie); server->UnregisterStateEventCallback(cookie);
client->Disconnect(); client->Disconnect();
EXPECT_TRUE(mainRecv.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1500)); EXPECT_TRUE(mainRecv.WaitForState(sdv::ipc::EConnectState::disconnected, 1500));
EXPECT_FALSE(regListener.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 300)); EXPECT_FALSE(regListener.WaitForState(sdv::ipc::EConnectState::disconnected, 300));
server->Disconnect(); server->Disconnect();
mgr.Shutdown(); mgr.Shutdown();

View File

@@ -0,0 +1,134 @@
#*******************************************************************************
# 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(WIN32)
cmake_minimum_required(VERSION 3.20)
project(WinTunnelCommunicationTests LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Executable: GTest for UDS connect
add_executable(UnitTest_WinTunnelConnectTests
win_tunnel_connect_tests.cpp
)
target_include_directories(UnitTest_WinTunnelConnectTests
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/export
${PROJECT_SOURCE_DIR}/export/support
${PROJECT_SOURCE_DIR}/export/interfaces
${PROJECT_SOURCE_DIR}/sdv_services
)
target_link_libraries(UnitTest_WinTunnelConnectTests
PRIVATE
GTest::GTest
Ws2_32
Winmm
Rpcrt4
uds_win_tunnel
uds_win_sockets
)
add_test(NAME UnitTest_WinTunnelConnectTests
COMMAND UnitTest_WinTunnelConnectTests)
add_dependencies(UnitTest_WinTunnelConnectTests dependency_sdv_components)
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
add_custom_command(TARGET UnitTest_WinTunnelConnectTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
"$<TARGET_FILE:UnitTest_WinTunnelConnectTests>"
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelConnectTests.xml
VERBATIM
)
endif()
add_executable(UnitTest_WinTunnelChannelMgntTests
win_tunnel_channel_mgnt_tests.cpp
)
target_include_directories(UnitTest_WinTunnelChannelMgntTests
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/export
${PROJECT_SOURCE_DIR}/export/support
${PROJECT_SOURCE_DIR}/export/interfaces
${PROJECT_SOURCE_DIR}/sdv_services
)
target_link_libraries(UnitTest_WinTunnelChannelMgntTests
PRIVATE
GTest::GTest
Ws2_32
Winmm
Rpcrt4
uds_win_tunnel
uds_win_sockets
)
add_test(NAME UnitTest_WinTunnelChannelMgntTests
COMMAND UnitTest_WinTunnelChannelMgntTests)
add_dependencies(UnitTest_WinTunnelChannelMgntTests dependency_sdv_components)
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
add_custom_command(TARGET UnitTest_WinTunnelChannelMgntTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
"$<TARGET_FILE:UnitTest_WinTunnelChannelMgntTests>"
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelChannelMgntTests.xml
VERBATIM
)
endif()
# Add negative/edge case tests
add_executable(UnitTest_WinTunnelNegativeEdgeTests
win_tunnel_negative_edge_tests.cpp
)
target_include_directories(UnitTest_WinTunnelNegativeEdgeTests
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/export
${PROJECT_SOURCE_DIR}/export/support
${PROJECT_SOURCE_DIR}/export/interfaces
${PROJECT_SOURCE_DIR}/sdv_services
)
target_link_libraries(UnitTest_WinTunnelNegativeEdgeTests
PRIVATE
GTest::GTest
Ws2_32
Winmm
Rpcrt4
uds_win_tunnel
uds_win_sockets
)
add_test(NAME UnitTest_WinTunnelNegativeEdgeTests
COMMAND UnitTest_WinTunnelNegativeEdgeTests)
add_dependencies(UnitTest_WinTunnelNegativeEdgeTests dependency_sdv_components)
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
add_custom_command(TARGET UnitTest_WinTunnelNegativeEdgeTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
"$<TARGET_FILE:UnitTest_WinTunnelNegativeEdgeTests>"
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelNegativeEdgeTests.xml
VERBATIM
)
endif()
endif()

View File

@@ -0,0 +1,442 @@
/********************************************************************************
* 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
********************************************************************************/
#include "gtest/gtest.h"
#include <support/app_control.h>
#include <interfaces/ipc.h>
#include "../../../sdv_services/uds_win_tunnel/channel_mgnt.h"
#include "../../../sdv_services/uds_win_tunnel/connection.h"
// Reuse helpers from uds_win_sockets (path normalization, MakeShortUdsPath)
#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h"
#include "../../../sdv_services/uds_win_sockets/connection.h"
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <random>
#include <sstream>
#include <cstring>
#include <windows.h>
#include <afunix.h>
// Helper namespace
namespace tunnel_utils
{
inline std::string Expand(const std::string& in)
{
if (in.find('%') == std::string::npos)
return in;
char buf[4096] = {};
const DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, sizeof(buf));
return (n > 0 && n < sizeof(buf)) ? std::string(buf) : in;
}
inline void EnsureParentDir(const std::string& full)
{
auto p = full.find_last_of("\\/");
if (p == std::string::npos)
return;
std::string dir = full.substr(0, p);
CreateDirectoryA(dir.c_str(), nullptr);
}
inline std::string MakeShortUdsPath(const char* name)
{
std::string base = R"(%LOCALAPPDATA%\sdv\)";
base = Expand(base);
EnsureParentDir(base);
return base + name;
}
inline std::string RandomHex()
{
std::mt19937_64 r{std::random_device{}()};
std::uniform_int_distribution<uint64_t> d;
std::ostringstream oss;
oss << std::hex << d(r);
return oss.str();
}
inline std::string Unique(const char* prefix)
{
return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str());
}
inline void SpinUntilServerArmed(sdv::ipc::IConnect* server)
{
using namespace std::chrono;
const auto deadline = steady_clock::now() + milliseconds(500);
while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized &&
steady_clock::now() < deadline)
{
std::this_thread::sleep_for(milliseconds(2));
}
}
} // namespace tunnel_utils
using namespace tunnel_utils;
// Unified receiver (state + data)
class CTunnelMgrTestReceiver :
public sdv::IInterfaceAccess,
public sdv::ipc::IDataReceiveCallback,
public sdv::ipc::IConnectEventCallback
{
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
END_SDV_INTERFACE_MAP()
void SetConnectState(sdv::ipc::EConnectState s) override
{
{ std::lock_guard<std::mutex> lk(m_mtx); m_state = s; }
m_cv.notify_all();
}
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& seq) override
{
{ std::lock_guard<std::mutex> lk(m_mtx);
m_last = seq; m_has = true; }
m_cv.notify_all();
}
bool WaitForState(sdv::ipc::EConnectState s, uint32_t ms = 2000)
{
std::unique_lock<std::mutex> lk(m_mtx);
return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
[&]{ return m_state == s; });
}
bool WaitForData(uint32_t ms = 2000)
{
std::unique_lock<std::mutex> lk(m_mtx);
return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
[&]{ return m_has; });
}
sdv::sequence<sdv::pointer<uint8_t>> Data()
{
std::lock_guard<std::mutex> lk(m_mtx);
return m_last;
}
private:
mutable std::mutex m_mtx;
std::condition_variable m_cv;
sdv::ipc::EConnectState m_state{sdv::ipc::EConnectState::uninitialized};
sdv::sequence<sdv::pointer<uint8_t>> m_last;
bool m_has{false};
};
// Helper to create server + client
struct TunnelPair
{
sdv::TObjectPtr serverObj;
sdv::ipc::IConnect* server = nullptr;
sdv::TObjectPtr clientObj;
sdv::ipc::IConnect* client = nullptr;
};
static TunnelPair CreateTunnelPair(CSocketsTunnelChannelMgnt& mgr,
const std::string& cs)
{
TunnelPair out;
out.serverObj = mgr.Access(cs);
out.server = out.serverObj ?
out.serverObj.GetInterface<sdv::ipc::IConnect>() : nullptr;
out.clientObj = mgr.Access(cs);
out.client = out.clientObj ?
out.clientObj.GetInterface<sdv::ipc::IConnect>() : nullptr;
return out;
}
// TESTS
// Manager instantiate + lifecycle
TEST(WinTunnelChannelMgnt, InstantiateAndLifecycle)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
CSocketsTunnelChannelMgnt mgr;
EXPECT_NO_THROW(mgr.Initialize(""));
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized);
mgr.SetOperationMode(sdv::EOperationMode::configuring);
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::configuring);
mgr.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::running);
mgr.Shutdown();
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending);
app.Shutdown();
}
// Basic connect/disconnect using manager (server + client)
TEST(WinTunnelChannelMgnt, BasicConnectDisconnect)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
const std::string uds = Unique("tunnel_mgr_basic");
const std::string cs = "proto=tunnel;path=" + uds + ";";
auto ep = mgr.CreateEndpoint(cs);
ASSERT_FALSE(ep.ssConnectString.empty());
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
ASSERT_NE(pair.server, nullptr);
ASSERT_NE(pair.client, nullptr);
CTunnelMgrTestReceiver sr, cr;
pair.server->AsyncConnect(&sr);
SpinUntilServerArmed(pair.server);
pair.client->AsyncConnect(&cr);
EXPECT_TRUE(pair.server->WaitForConnection(5000));
EXPECT_TRUE(pair.client->WaitForConnection(5000));
pair.client->Disconnect();
pair.server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}
// Simple hello (header stripped)
TEST(WinTunnelChannelMgnt, DataPath_SimpleHello_ViaManager)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
const std::string cs = "proto=tunnel;path=" + Unique("hello_mgr") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
ASSERT_NE(pair.server, nullptr);
ASSERT_NE(pair.client, nullptr);
CTunnelMgrTestReceiver sr, cr;
pair.server->AsyncConnect(&sr);
SpinUntilServerArmed(pair.server);
pair.client->AsyncConnect(&cr);
ASSERT_TRUE(pair.server->WaitForConnection(5000));
ASSERT_TRUE(pair.client->WaitForConnection(5000));
sdv::pointer<uint8_t> msg;
msg.resize(5);
memcpy(msg.get(), "hello", 5);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(msg);
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
ASSERT_NE(sender, nullptr);
EXPECT_TRUE(sender->SendData(seq));
ASSERT_TRUE(sr.WaitForData(3000));
auto recv = sr.Data();
ASSERT_EQ(recv.size(), 1u);
ASSERT_EQ(recv[0].size(), 5u);
EXPECT_EQ(memcmp(recv[0].get(), "hello", 5), 0);
pair.client->Disconnect();
pair.server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}
// Multi-chunk
TEST(WinTunnelChannelMgnt, DataPath_MultiChunk_ViaManager)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
const std::string cs = "proto=tunnel;path=" + Unique("mc_mgr") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
ASSERT_NE(pair.server, nullptr);
ASSERT_NE(pair.client, nullptr);
CTunnelMgrTestReceiver sr, cr;
pair.server->AsyncConnect(&sr);
SpinUntilServerArmed(pair.server);
pair.client->AsyncConnect(&cr);
ASSERT_TRUE(pair.server->WaitForConnection(5000));
ASSERT_TRUE(pair.client->WaitForConnection(5000));
sdv::pointer<uint8_t> a, b;
a.resize(3);
memcpy(a.get(), "sdv", 3);
b.resize(9);
memcpy(b.get(), "framework", 9);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(a);
seq.push_back(b);
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
ASSERT_NE(sender, nullptr);
EXPECT_TRUE(sender->SendData(seq));
ASSERT_TRUE(sr.WaitForData(3000));
auto recv = sr.Data();
ASSERT_EQ(recv.size(), 2u);
EXPECT_EQ(memcmp(recv[0].get(), "sdv", 3), 0);
EXPECT_EQ(memcmp(recv[1].get(), "framework", 9), 0);
pair.client->Disconnect();
pair.server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}
// Header stripping invariant
TEST(WinTunnelChannelMgnt, HeaderStrippedInvariant)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
const std::string cs ="proto=tunnel;path=" + Unique("hdr_mgr") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
ASSERT_NE(pair.server, nullptr);
ASSERT_NE(pair.client, nullptr);
CTunnelMgrTestReceiver sr, cr;
pair.server->AsyncConnect(&sr);
SpinUntilServerArmed(pair.server);
pair.client->AsyncConnect(&cr);
ASSERT_TRUE(pair.server->WaitForConnection(5000));
ASSERT_TRUE(pair.client->WaitForConnection(5000));
const char* msg = "HDR_TEST";
const size_t len = strlen(msg);
sdv::pointer<uint8_t> buf;
buf.resize(len);
memcpy(buf.get(), msg, len);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(buf);
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
ASSERT_NE(sender, nullptr);
EXPECT_TRUE(sender->SendData(seq));
ASSERT_TRUE(sr.WaitForData(3000));
auto recv = sr.Data();
ASSERT_EQ(recv.size(), 1u);
ASSERT_EQ(recv[0].size(), len);
EXPECT_EQ(memcmp(recv[0].get(), msg, len), 0);
pair.client->Disconnect();
pair.server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}
// Long path normalization
TEST(WinTunnelChannelMgnt, CreateEndpoint_LongPath_Normalized)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
std::string veryLong(200, 'A');
const std::string raw ="C:\\Users\\" + veryLong + "\\AppData\\Local\\sdv\\tunnel_long_" + RandomHex() + ".sock";
const std::string cs = "proto=tunnel;path=" + raw + ";";
auto ep = mgr.CreateEndpoint(cs);
ASSERT_FALSE(ep.ssConnectString.empty());
// path must be normalized (basename only)
EXPECT_NE(ep.ssConnectString.find("path=tunnel_long_"), std::string::npos);
sdv::TObjectPtr obj = mgr.Access(ep.ssConnectString);
auto* server = obj.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(server, nullptr);
CTunnelMgrTestReceiver sr;
server->AsyncConnect(&sr);
SpinUntilServerArmed(server);
// boot client
sdv::TObjectPtr cobj = mgr.Access(ep.ssConnectString);
auto* client = cobj.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(client, nullptr);
CTunnelMgrTestReceiver cr;
client->AsyncConnect(&cr);
EXPECT_TRUE(server->WaitForConnection(5000));
EXPECT_TRUE(client->WaitForConnection(5000));
client->Disconnect();
server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}

View File

@@ -0,0 +1,454 @@
/********************************************************************************
* 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
********************************************************************************/
#include "gtest/gtest.h"
#include <support/app_control.h>
#include <interfaces/ipc.h>
// Include transport and tunnel manager
#include "../../../sdv_services/uds_win_tunnel/channel_mgnt.h"
#include "../../../sdv_services/uds_win_tunnel/connection.h"
// Include UDS Windows helpers for path generation
#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h" // ONLY FOR path helpers (MakeShortUdsPath)
#include "../../../sdv_services/uds_win_sockets/connection.h"
#include <windows.h>
#include <afunix.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstring>
#include <mutex>
#include <random>
#include <sstream>
#include <string>
#include <thread>
// Utility namespace from original tests
namespace test_utils
{
inline std::string Expand(const std::string& in)
{
if (in.find('%') == std::string::npos)
return in;
char buf[4096] = {};
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, sizeof(buf));
return (n > 0 && n < sizeof(buf)) ? std::string(buf) : in;
}
inline void EnsureParentDir(const std::string& fullPath)
{
auto pos = fullPath.find_last_of("\\/");
if (pos == std::string::npos)
return;
std::string dir = fullPath.substr(0, pos);
CreateDirectoryA(dir.c_str(), nullptr);
}
inline std::string MakeShortUdsPath(const char* name)
{
std::string base = R"(%LOCALAPPDATA%\sdv\)";
base = Expand(base);
EnsureParentDir(base);
return base + name;
}
inline std::string RandomHex()
{
std::mt19937_64 rng{std::random_device{}()};
std::uniform_int_distribution<uint64_t> dist;
std::ostringstream oss;
oss << std::hex << dist(rng);
return oss.str();
}
inline std::string UniqueUds(const char* prefix)
{
return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str());
}
inline void SpinUntilServerArmed(sdv::ipc::IConnect* server, uint32_t maxWaitMs = 300)
{
using namespace std::chrono;
auto deadline = steady_clock::now() + milliseconds(maxWaitMs);
while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized &&
steady_clock::now() < deadline)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
inline void SleepTiny(uint32_t ms = 20)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
} // namespace test_utils
using namespace test_utils;
// Unified test receiver (status + payload), identical to UDS tests
class CTunnelTestReceiver :
public sdv::IInterfaceAccess,
public sdv::ipc::IConnectEventCallback,
public sdv::ipc::IDataReceiveCallback
{
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
END_SDV_INTERFACE_MAP()
void SetConnectState(sdv::ipc::EConnectState s) override
{
{
std::lock_guard<std::mutex> lk(m_mtx);
m_state = s;
}
m_cv.notify_all();
}
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& seq) override
{
{
std::lock_guard<std::mutex> lk(m_mtx);
m_data = seq;
m_hasData = true;
}
m_cv.notify_all();
}
bool WaitForStatus(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
{
std::unique_lock<std::mutex> lk(m_mtx);
return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
[&]{ return m_state == expected; });
}
bool WaitForData(uint32_t ms = 2000)
{
std::unique_lock<std::mutex> lk(m_mtx);
return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
[&]{ return m_hasData; });
}
sdv::sequence<sdv::pointer<uint8_t>> Data()
{
std::lock_guard<std::mutex> lk(m_mtx);
return m_data;
}
private:
mutable std::mutex m_mtx;
std::condition_variable m_cv;
sdv::ipc::EConnectState m_state{sdv::ipc::EConnectState::uninitialized};
sdv::sequence<sdv::pointer<uint8_t>> m_data;
bool m_hasData{false};
};
// Helper to create server + client via manager
struct TunnelPair
{
sdv::TObjectPtr serverObj;
sdv::ipc::IConnect* server = nullptr;
sdv::TObjectPtr clientObj;
sdv::ipc::IConnect* client = nullptr;
};
static TunnelPair CreateTunnelPair(CSocketsTunnelChannelMgnt& mgr, const std::string& cs)
{
TunnelPair out;
out.serverObj = mgr.Access(cs);
out.server = out.serverObj ? out.serverObj.GetInterface<sdv::ipc::IConnect>() : nullptr;
out.clientObj = mgr.Access(cs);
out.client = out.clientObj ? out.clientObj.GetInterface<sdv::ipc::IConnect>() : nullptr;
return out;
}
// TEST SUITE START
// Instantiate manager
TEST(WinTunnelIPC, InstantiateManager)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
CSocketsTunnelChannelMgnt mgr;
EXPECT_NO_THROW(mgr.Initialize(""));
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized);
EXPECT_NO_THROW(mgr.Shutdown());
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending);
app.Shutdown();
}
// Basic connect/disconnect
TEST(WinTunnelIPC, BasicConnectDisconnect)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
const std::string path = UniqueUds("tunnel_basic");
const std::string cs = "proto=tunnel;path=" + path + ";";
auto ep = mgr.CreateEndpoint(cs);
ASSERT_FALSE(ep.ssConnectString.empty());
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
ASSERT_NE(pair.server, nullptr);
ASSERT_NE(pair.client, nullptr);
CTunnelTestReceiver sr, cr;
pair.server->AsyncConnect(&sr);
SpinUntilServerArmed(pair.server);
pair.client->AsyncConnect(&cr);
EXPECT_TRUE(pair.server->WaitForConnection(5000));
EXPECT_TRUE(pair.client->WaitForConnection(5000));
pair.client->Disconnect();
pair.server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}
// Simple data (hello)
TEST(WinTunnelIPC, DataPath_SimpleHello_Tunnel)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
const std::string cs = "proto=tunnel;path=" + UniqueUds("hello_t") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
ASSERT_NE(pair.server, nullptr);
ASSERT_NE(pair.client, nullptr);
CTunnelTestReceiver sr, cr;
pair.server->AsyncConnect(&sr);
SpinUntilServerArmed(pair.server);
pair.client->AsyncConnect(&cr);
ASSERT_TRUE(pair.server->WaitForConnection(5000));
ASSERT_TRUE(pair.client->WaitForConnection(5000));
// Build payload = "hello"
sdv::pointer<uint8_t> p;
p.resize(5);
memcpy(p.get(), "hello", 5);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(p);
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
ASSERT_NE(sender, nullptr);
EXPECT_TRUE(sender->SendData(seq));
EXPECT_TRUE(sr.WaitForData(3000));
auto recv = sr.Data();
// header eliminated
ASSERT_EQ(recv.size(), 1u);
ASSERT_EQ(recv[0].size(), 5u);
EXPECT_EQ(memcmp(recv[0].get(), "hello", 5), 0);
pair.client->Disconnect();
pair.server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}
// Multi-chunk
TEST(WinTunnelIPC, DataPath_MultiChunk_Tunnel)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
const std::string cs = "proto=tunnel;path=" + UniqueUds("mc_t") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
ASSERT_NE(pair.server, nullptr);
ASSERT_NE(pair.client, nullptr);
CTunnelTestReceiver sr, cr;
pair.server->AsyncConnect(&sr);
SpinUntilServerArmed(pair.server);
pair.client->AsyncConnect(&cr);
ASSERT_TRUE(pair.server->WaitForConnection(5000));
ASSERT_TRUE(pair.client->WaitForConnection(5000));
sdv::pointer<uint8_t> p1, p2;
p1.resize(3);
memcpy(p1.get(), "sdv", 3);
p2.resize(9);
memcpy(p2.get(), "framework", 9);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(p1);
seq.push_back(p2);
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
ASSERT_NE(sender, nullptr);
EXPECT_TRUE(sender->SendData(seq));
ASSERT_TRUE(sr.WaitForData(3000));
auto recv = sr.Data();
ASSERT_EQ(recv.size(), 2u);
EXPECT_EQ(memcmp(recv[0].get(), "sdv", 3), 0);
EXPECT_EQ(memcmp(recv[1].get(), "framework", 9), 0);
pair.client->Disconnect();
pair.server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}
// Large fragmentation
TEST(WinTunnelIPC, DataPath_LargePayload_Tunnel)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
const std::string cs = "proto=tunnel;path=" + UniqueUds("big_t") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
ASSERT_NE(pair.server, nullptr);
ASSERT_NE(pair.client, nullptr);
CTunnelTestReceiver sr, cr;
pair.server->AsyncConnect(&sr);
SpinUntilServerArmed(pair.server);
pair.client->AsyncConnect(&cr);
ASSERT_TRUE(pair.server->WaitForConnection(5000));
ASSERT_TRUE(pair.client->WaitForConnection(5000));
const size_t N = 256 * 1024;
sdv::pointer<uint8_t> payload;
payload.resize(N);
for (size_t i = 0; i < N; ++i)
payload.get()[i] = uint8_t(i & 0xFF);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(payload);
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
ASSERT_NE(sender, nullptr);
EXPECT_TRUE(sender->SendData(seq));
ASSERT_TRUE(sr.WaitForData(5000));
auto recv = sr.Data();
ASSERT_EQ(recv.size(), 1u);
ASSERT_EQ(recv[0].size(), N);
EXPECT_EQ(memcmp(recv[0].get(), payload.get(), N), 0);
pair.client->Disconnect();
pair.server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}
// Header stripping test (tunnel feature)
TEST(WinTunnelIPC, DataPath_HeaderStripped_Tunnel)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
app.SetRunningMode();
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
const std::string cs =
"proto=tunnel;path=" + UniqueUds("headerstrip") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
ASSERT_NE(pair.server, nullptr);
ASSERT_NE(pair.client, nullptr);
CTunnelTestReceiver sr, cr;
pair.server->AsyncConnect(&sr);
SpinUntilServerArmed(pair.server);
pair.client->AsyncConnect(&cr);
ASSERT_TRUE(pair.server->WaitForConnection(5000));
ASSERT_TRUE(pair.client->WaitForConnection(5000));
const char* msg = "HEADER_TEST";
const size_t len = strlen(msg);
sdv::pointer<uint8_t> p;
p.resize(len);
memcpy(p.get(), msg, len);
sdv::sequence<sdv::pointer<uint8_t>> seq;
seq.push_back(p);
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
ASSERT_NE(sender, nullptr);
EXPECT_TRUE(sender->SendData(seq));
ASSERT_TRUE(sr.WaitForData(3000));
auto recv = sr.Data();
// check header stripped
ASSERT_EQ(recv.size(), 1u);
ASSERT_EQ(recv[0].size(), len);
EXPECT_EQ(memcmp(recv[0].get(), msg, len), 0);
pair.client->Disconnect();
pair.server->Disconnect();
mgr.Shutdown();
app.Shutdown();
}

View File

@@ -0,0 +1,164 @@
/********************************************************************************
* 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
********************************************************************************/
#include "gtest/gtest.h"
#include <support/app_control.h>
#include <interfaces/ipc.h>
#include "../../../sdv_services/uds_win_tunnel/channel_mgnt.h"
#include "../../../sdv_services/uds_win_tunnel/connection.h"
// Include UDS Windows helpers for path generation
#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h" // ONLY FOR path helpers (MakeShortUdsPath)
#include "../../../sdv_services/uds_win_sockets/connection.h"
// Helper for unique UDS path
template<typename T=void>
static std::string UniqueUds(const char* prefix) {
char buf[64];
sprintf(buf, "%s_%08x.sock", prefix, rand());
return std::string("%LOCALAPPDATA%/sdv/") + buf;
}
// Negative: invalid connect string
TEST(WinTunnelNegative, InvalidConnectString)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
ViewFilter = "Fatal")toml"));
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
// Missing proto, missing path
auto obj = mgr.Access("role=server;");
EXPECT_EQ(obj, nullptr);
app.Shutdown();
}
// Negative: connect to non-existent server
TEST(WinTunnelNegative, ConnectToNonExistentServer)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
ViewFilter = "Fatal")toml"));
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
std::string cs = "proto=tunnel;path=" + UniqueUds("no_server") + ";";
auto obj = mgr.Access(cs);
if (!obj) {
SUCCEED() << "Client object is nullptr as expected when server does not exist";
} else {
auto* client = obj->GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(client, nullptr);
EXPECT_FALSE(client->WaitForConnection(200));
}
app.Shutdown();
}
// Edge: double disconnect
TEST(WinTunnelEdge, DoubleDisconnect)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
std::string cs = "proto=tunnel;path=" + UniqueUds("double_disc") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto obj = mgr.Access(ep.ssConnectString);
auto* conn = obj->GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(conn, nullptr);
conn->Disconnect();
// Should not crash or throw
EXPECT_NO_THROW(conn->Disconnect());
app.Shutdown();
}
// Edge: repeated connect/disconnect cycles (recreate endpoint each time)
TEST(WinTunnelEdge, RepeatedConnectDisconnect)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
ViewFilter = "Fatal")toml"));
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
std::string cs = "proto=tunnel;path=" + UniqueUds("repeat") + ";";
for (int i = 0; i < 3; ++i) {
auto ep = mgr.CreateEndpoint(cs); // recreate endpoint every time
sdv::TObjectPtr obj = mgr.Access(ep.ssConnectString);
auto* conn = obj.GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(conn, nullptr);
conn->AsyncConnect(nullptr);
conn->WaitForConnection(200);
conn->Disconnect();
// obj goes out of scope and cleans up
}
app.Shutdown();
}
// Edge: simultaneous multiple clients
TEST(WinTunnelEdge, MultipleClients)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(""));
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
std::string cs = "proto=tunnel;path=" + UniqueUds("multi_client") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto obj1 = mgr.Access(ep.ssConnectString);
auto obj2 = mgr.Access(ep.ssConnectString);
auto* c1 = obj1->GetInterface<sdv::ipc::IConnect>();
auto* c2 = obj2->GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(c1, nullptr);
ASSERT_NE(c2, nullptr);
c1->AsyncConnect(nullptr);
c2->AsyncConnect(nullptr);
c1->WaitForConnection(500);
c2->WaitForConnection(500);
c1->Disconnect();
c2->Disconnect();
app.Shutdown();
}
// Edge: callback throws exception (should not crash)
class ThrowingReceiver : public sdv::IInterfaceAccess, public sdv::ipc::IDataReceiveCallback, public sdv::ipc::IConnectEventCallback {
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
END_SDV_INTERFACE_MAP()
void SetConnectState(sdv::ipc::EConnectState) override { throw std::runtime_error("SetConnectState fail"); }
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>&) override { throw std::runtime_error("ReceiveData fail"); }
};
TEST(WinTunnelEdge, CallbackThrows)
{
sdv::app::CAppControl app;
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
ViewFilter = "Fatal")toml"));
CSocketsTunnelChannelMgnt mgr;
mgr.Initialize("");
mgr.SetOperationMode(sdv::EOperationMode::running);
std::string cs = "proto=tunnel;path=" + UniqueUds("cb_throw") + ";";
auto ep = mgr.CreateEndpoint(cs);
auto obj = mgr.Access(ep.ssConnectString);
auto* conn = obj->GetInterface<sdv::ipc::IConnect>();
ASSERT_NE(conn, nullptr);
ThrowingReceiver rcv;
// Should not crash even if callback throws
EXPECT_NO_THROW(conn->AsyncConnect(&rcv));
conn->Disconnect();
app.Shutdown();
}