mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-04-10 15:40:24 +00:00
tunnel component & update vehicle abstraction example (#8)
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|
||||||
@@ -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")
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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")
|
||||||
|
|||||||
@@ -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':
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|
||||||
123
examples/vehicle_abstraction_example/CMakeLists.txt
Normal file
123
examples/vehicle_abstraction_example/CMakeLists.txt
Normal 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)
|
||||||
|
|
||||||
@@ -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"
|
||||||
@@ -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"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[Configuration]
|
||||||
|
Version = 100
|
||||||
|
|
||||||
|
[[Component]]
|
||||||
|
Path = "data_dispatch_service.sdv"
|
||||||
|
Class = "DataDispatchService"
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[Configuration]
|
||||||
|
Version = 100
|
||||||
|
|
||||||
|
[[Component]]
|
||||||
|
Path = "can_dl_abstraction.sdv"
|
||||||
|
Class = "CAN_data_link"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[Configuration]
|
||||||
|
Version = 100
|
||||||
|
|
||||||
|
[[Component]]
|
||||||
|
Path = "task_timer.sdv"
|
||||||
|
Class = "TaskTimerService"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[Configuration]
|
||||||
|
Version = 100
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[Component]]
|
||||||
|
Path = "abstraction_bs_vehiclespeed_rx.sdv"
|
||||||
|
Class = "Vehicle.Speed_Service"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[Configuration]
|
||||||
|
Version = 100
|
||||||
|
|
||||||
|
[[Component]]
|
||||||
|
Path = "abstraction_vd_vehiclespeedkmh_rx.sdv"
|
||||||
|
Class = "Vehicle.Speed_Device"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[Configuration]
|
||||||
|
Version = 100
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[Component]]
|
||||||
|
Path = "abstraction_vd_vehiclespeedms_rx.sdv"
|
||||||
|
Class = "Vehicle.Speed_Device"
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
@@ -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";
|
||||||
|
|
||||||
@@ -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;"
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
33
sdv_services/uds_unix_tunnel/CMakeLists.txt
Normal file
33
sdv_services/uds_unix_tunnel/CMakeLists.txt
Normal 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()
|
||||||
178
sdv_services/uds_unix_tunnel/channel_mgnt.cpp
Normal file
178
sdv_services/uds_unix_tunnel/channel_mgnt.cpp
Normal 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__)
|
||||||
140
sdv_services/uds_unix_tunnel/channel_mgnt.h
Normal file
140
sdv_services/uds_unix_tunnel/channel_mgnt.h
Normal 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__)
|
||||||
230
sdv_services/uds_unix_tunnel/connection.cpp
Normal file
230
sdv_services/uds_unix_tunnel/connection.cpp
Normal 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__)
|
||||||
191
sdv_services/uds_unix_tunnel/connection.h
Normal file
191
sdv_services/uds_unix_tunnel/connection.h
Normal 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__)
|
||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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=<udsPath>
|
* 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=<udsPath>
|
* 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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
40
sdv_services/uds_win_tunnel/CMakeLists.txt
Normal file
40
sdv_services/uds_win_tunnel/CMakeLists.txt
Normal 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)
|
||||||
414
sdv_services/uds_win_tunnel/channel_mgnt.cpp
Normal file
414
sdv_services/uds_win_tunnel/channel_mgnt.cpp
Normal 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
|
||||||
106
sdv_services/uds_win_tunnel/channel_mgnt.h
Normal file
106
sdv_services/uds_win_tunnel/channel_mgnt.h
Normal 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
|
||||||
226
sdv_services/uds_win_tunnel/connection.cpp
Normal file
226
sdv_services/uds_win_tunnel/connection.cpp
Normal 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
|
||||||
168
sdv_services/uds_win_tunnel/connection.h
Normal file
168
sdv_services/uds_win_tunnel/connection.h
Normal 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
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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();
|
||||||
|
|||||||
107
tests/unit_tests/unix_tunnel/CMakeLists.txt
Normal file
107
tests/unit_tests/unix_tunnel/CMakeLists.txt
Normal 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()
|
||||||
240
tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp
Normal file
240
tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp
Normal 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__)
|
||||||
376
tests/unit_tests/unix_tunnel/unix_tunnel_connect_tests.cpp
Normal file
376
tests/unit_tests/unix_tunnel/unix_tunnel_connect_tests.cpp
Normal 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__)
|
||||||
@@ -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()
|
||||||
@@ -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(®Listener);
|
const uint64_t cookie = server->RegisterStateEventCallback(®Listener);
|
||||||
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();
|
||||||
|
|||||||
134
tests/unit_tests/win_tunnel/CMakeLists.txt
Normal file
134
tests/unit_tests/win_tunnel/CMakeLists.txt
Normal 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()
|
||||||
442
tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp
Normal file
442
tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp
Normal 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();
|
||||||
|
}
|
||||||
454
tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp
Normal file
454
tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
164
tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp
Normal file
164
tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp
Normal 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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user