mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-07-02 05:35:11 +00:00
update parser (#5)
This commit is contained in:
@@ -53,16 +53,11 @@ endif()
|
||||
# Add the communication unittest
|
||||
add_test(NAME UnitTest_InprocMemTests COMMAND UnitTest_InprocMemTests)
|
||||
|
||||
#TODO Shared memory tests during complete rebuild fail on Windows when compiling with MINGW. This is due to race conditions occuring
|
||||
# only when the system is under heavy load (like during a complete rebuild). The tests have been disabled for the moment and a
|
||||
# bug report is filed here: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608134
|
||||
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
|
||||
# Execute the test
|
||||
add_custom_command(TARGET UnitTest_InprocMemTests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_InprocMemTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_InprocMemTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# Shared mem buffer test
|
||||
add_executable(UnitTest_SharedMemBufferTests
|
||||
@@ -85,16 +80,11 @@ endif()
|
||||
# Add the communication unittest
|
||||
add_test(NAME UnitTest_SharedMemBufferTests COMMAND UnitTest_SharedMemBufferTests)
|
||||
|
||||
#TODO Shared memory tests during complete rebuild fail on Windows when compiling with MINGW. This is due to race conditions occuring
|
||||
# only when the system is under heavy load (like during a complete rebuild). The tests have been disabled for the moment and a
|
||||
# bug report is filed here: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608134
|
||||
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
|
||||
# Execute the test
|
||||
add_custom_command(TARGET UnitTest_SharedMemBufferTests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_SharedMemBufferTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SharedMemBufferTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# Shared mem connection test
|
||||
add_executable(UnitTest_SharedMemConnectTests
|
||||
@@ -115,16 +105,11 @@ endif()
|
||||
# Add the communication unittest
|
||||
add_test(NAME UnitTest_SharedMemConnectTests COMMAND UnitTest_SharedMemConnectTests)
|
||||
|
||||
#TODO Shared memory tests during complete rebuild fail on Windows when compiling with MINGW. This is due to race conditions occuring
|
||||
# only when the system is under heavy load (like during a complete rebuild). The tests have been disabled for the moment and a
|
||||
# bug report is filed here: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608134
|
||||
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
|
||||
# Execute the test
|
||||
add_custom_command(TARGET UnitTest_SharedMemConnectTests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_SharedMemConnectTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SharedMemConnectTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
# Shared mem large data test
|
||||
@@ -146,16 +131,11 @@ endif()
|
||||
# Add the communication unittest
|
||||
add_test(NAME UnitTest_SharedMemLargeDataTests COMMAND UnitTest_SharedMemLargeDataTests)
|
||||
|
||||
#TODO Shared memory tests during complete rebuild fail on Windows when compiling with MINGW. This is due to race conditions occuring
|
||||
# only when the system is under heavy load (like during a complete rebuild). The tests have been disabled for the moment and a
|
||||
# bug report is filed here: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608134
|
||||
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
|
||||
# Execute the test
|
||||
add_custom_command(TARGET UnitTest_SharedMemLargeDataTests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_SharedMemLargeDataTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SharedMemLargeDataTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(UnitTest_SharedMemTests_App_Repeater core_services)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#define TIME_TRACKING
|
||||
#include "../../../sdv_services/ipc_shared_mem/channel_mgnt.cpp"
|
||||
#include "../../../sdv_services/ipc_shared_mem/connection.cpp"
|
||||
#include "../../../sdv_services/ipc_shared_mem/connection.cpp" // Tracing is enabled/disabled in the connection.h file
|
||||
#include "../../../sdv_services/ipc_shared_mem/watchdog.cpp"
|
||||
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp"
|
||||
#include <sstream>
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <unistd.h>
|
||||
@@ -67,6 +68,8 @@ public:
|
||||
{
|
||||
// Send the same data back again (if needed).
|
||||
std::unique_lock<std::mutex> lock(m_mtxData);
|
||||
m_nReceiveCallCnt++;
|
||||
m_nPackageReceiveCnt += seqData.size();
|
||||
m_queueSendData.push(seqData);
|
||||
lock.unlock();
|
||||
|
||||
@@ -93,7 +96,10 @@ public:
|
||||
|
||||
// Send the data back to the sender
|
||||
if (m_pSend)
|
||||
{
|
||||
m_pSend->SendData(seqData);
|
||||
m_nSendCallCnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +190,33 @@ public:
|
||||
m_threadSender.join();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the receive call count.
|
||||
* @return The amount of receive calls that has been made.
|
||||
*/
|
||||
size_t GetReceiveCallCount() const
|
||||
{
|
||||
return m_nReceiveCallCnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the package call count.
|
||||
* @return The amount of packages that have been received.
|
||||
*/
|
||||
size_t GetPackageReceiveCount() const
|
||||
{
|
||||
return m_nPackageReceiveCnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the send call count.
|
||||
* @return The amount of send calls that has been made.
|
||||
*/
|
||||
size_t GetSendCallCount() const
|
||||
{
|
||||
return m_nSendCallCnt;
|
||||
}
|
||||
|
||||
private:
|
||||
sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function.
|
||||
mutable std::mutex m_mtxData; ///< Protect data access.
|
||||
@@ -191,9 +224,12 @@ private:
|
||||
std::condition_variable m_cvDisconnect; ///< Disconnect event.
|
||||
std::condition_variable m_cvReceived; ///< Receive event.
|
||||
std::thread m_threadSender; ///< Thread to send data.
|
||||
bool m_bConnected = false; ///< Set when connected was triggered.
|
||||
bool m_bDisconnect = false; ///< Set when shutdown was triggered.
|
||||
bool m_bShutdown = false; ///< Set when shutdown is processed.
|
||||
std::atomic_bool m_bConnected = false; ///< Set when connected was triggered.
|
||||
std::atomic_bool m_bDisconnect = false; ///< Set when shutdown was triggered.
|
||||
std::atomic_bool m_bShutdown = false; ///< Set when shutdown is processed.
|
||||
std::atomic_size_t m_nReceiveCallCnt = 0; ///< Receive call counter.
|
||||
std::atomic_size_t m_nPackageReceiveCnt = 0; ///< Package receive counter.
|
||||
std::atomic_size_t m_nSendCallCnt = 0; ///< Send call counter.
|
||||
};
|
||||
|
||||
|
||||
@@ -280,15 +316,18 @@ extern "C" int main(int argc, char* argv[])
|
||||
|
||||
// Open the control channel endpoint
|
||||
sdv::TObjectPtr ptrControlConnection;
|
||||
sdv::ipc::IConnect* pControlConnect = nullptr;
|
||||
CReceiver receiverControl;
|
||||
uint64_t uiControlEventCookie = 0;
|
||||
if (!ssControlConnectString.empty())
|
||||
{
|
||||
TRACE(bServer ? "Server" : "Client", ": Start of control channel connection...");
|
||||
ptrControlConnection = mgntControlMgntChannel.Access(ssControlConnectString);
|
||||
if (!ptrControlConnection) return -12;
|
||||
sdv::ipc::IConnect* pControlConnect = ptrControlConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
pControlConnect = ptrControlConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
if (!pControlConnect) return -13;
|
||||
if (!pControlConnect->RegisterStatusEventCallback(&receiverControl)) return -20;
|
||||
uiControlEventCookie = pControlConnect->RegisterStatusEventCallback(&receiverControl);
|
||||
if (!uiControlEventCookie) return -20;
|
||||
if (!pControlConnect->AsyncConnect(&receiverControl)) return -14;
|
||||
if (!pControlConnect->WaitForConnection(250)) return -5; // Note: Connection should be possible within 250ms.
|
||||
if (pControlConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -15;
|
||||
@@ -339,8 +378,10 @@ Size = 1024000
|
||||
|
||||
// Establish the connection
|
||||
sdv::ipc::IConnect* pDataConnect = ptrDataConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
uint64_t uiDataEventCookie = 0;
|
||||
if (!pDataConnect) return -3;
|
||||
if (!pDataConnect->RegisterStatusEventCallback(&receiverData)) return -21;
|
||||
uiDataEventCookie = pDataConnect->RegisterStatusEventCallback(&receiverData);
|
||||
if (!uiDataEventCookie) return -21;
|
||||
if (!pDataConnect->AsyncConnect(&receiverData)) return -4;
|
||||
if (!pDataConnect->WaitForConnection(10000)) return -5; // Note: Connection should be possible within 10000ms.
|
||||
if (pDataConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -5;
|
||||
@@ -358,12 +399,17 @@ Size = 1024000
|
||||
// Repeat data until disconnect occurrs (differentiate between forced and not forced to allow two apps to start at the
|
||||
// same time).
|
||||
receiverData.WaitUntilDisconnect(bForceTerminate ? 800 : 1600);
|
||||
std::cout << "App " << (bServer ? "server" : "client") << " connect process disconnecting..." << std::endl;
|
||||
TRACE("App ", bServer ? "server" : "client", " connect process disconnecting...");
|
||||
}
|
||||
|
||||
// Statistics
|
||||
TRACE("Receive was called ", receiverData.GetReceiveCallCount(), " times (", receiverData.GetPackageReceiveCount(),
|
||||
" packages).");
|
||||
TRACE("Send was called ", receiverData.GetSendCallCount(), " times.");
|
||||
|
||||
if (bForceTerminate)
|
||||
{
|
||||
std::cout << "Forced termination of app " << (bServer ? "server" : "client") << " connect process..." << std::endl;
|
||||
TRACE("Forced termination of app ", bServer ? "server" : "client", " connect process...");
|
||||
#ifdef _MSC_VER
|
||||
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
||||
#endif
|
||||
@@ -371,9 +417,11 @@ Size = 1024000
|
||||
}
|
||||
|
||||
// Initiate shutdown
|
||||
if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStatusEventCallback(uiDataEventCookie);
|
||||
ptrDataConnection.Clear();
|
||||
mgntDataMgntChannel.Shutdown();
|
||||
if (mgntDataMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -6;
|
||||
if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStatusEventCallback(uiControlEventCookie);
|
||||
ptrControlConnection.Clear();
|
||||
mgntControlMgntChannel.Shutdown();
|
||||
if (mgntControlMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -16;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "../../../sdv_services/ipc_shared_mem/in_process_mem_buffer.h"
|
||||
#include <support/app_control.h>
|
||||
#include "pattern_gen.h"
|
||||
#include <atomic>
|
||||
|
||||
TEST(InProcessMemoryBufferTest, Instantiate)
|
||||
{
|
||||
@@ -26,7 +27,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestRx)
|
||||
CInProcMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
size_t nCorrectCnt = 0;
|
||||
std::condition_variable cvStart;
|
||||
std::mutex mtxStart;
|
||||
@@ -75,7 +76,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestTx)
|
||||
CInProcMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
size_t nCorrectCnt = 0;
|
||||
std::condition_variable cvStart;
|
||||
std::mutex mtxStart;
|
||||
@@ -124,7 +125,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestRxTx)
|
||||
CInProcMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
size_t nCorrectCnt = 0;
|
||||
std::condition_variable cvSenderStart, cvReceiverStart;
|
||||
std::mutex mtxReceiverStart;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../../../sdv_services/ipc_shared_mem/channel_mgnt.cpp"
|
||||
#include "../../../sdv_services/ipc_shared_mem/watchdog.cpp"
|
||||
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp"
|
||||
#include <cstring>
|
||||
|
||||
/**
|
||||
* @brief Load support modules to publish the needed services.
|
||||
@@ -28,8 +29,30 @@ extern "C" int wmain(int argc, wchar_t* argv[])
|
||||
extern "C" int main(int argc, char* argv[])
|
||||
#endif
|
||||
{
|
||||
CProcessWatchdog watchdog;
|
||||
// Check for the --gtest_repeat option.
|
||||
bool bRepeatEnabled = false;
|
||||
for (int iIndex = 0; iIndex < argc; iIndex++)
|
||||
{
|
||||
if (!argv[iIndex])
|
||||
continue;
|
||||
#if defined(_WIN32) && defined(_UNICODE)
|
||||
bRepeatEnabled |= std::wcsncmp(argv[iIndex], L"--gtest_repeat", 14) == 0;
|
||||
#else
|
||||
bRepeatEnabled |= std::strncmp(argv[iIndex], "--gtest_repeat", 14) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
// When repeat is enabled, do not enable the watchdog.
|
||||
if (bRepeatEnabled)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
else
|
||||
{
|
||||
CProcessWatchdog watchdog;
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include "pattern_gen.h"
|
||||
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.h"
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define PATTERN_GEN_H
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.h"
|
||||
|
||||
/**
|
||||
@@ -43,8 +44,8 @@ public:
|
||||
private:
|
||||
CMemBufferAccessorTx& m_raccessorOut; //!< Reference to the output accessor
|
||||
std::thread m_thread; //!< Processing thread
|
||||
bool m_bStarted = false; //!< Set by the thread when started.
|
||||
bool m_bShutdown = false; //!< When set, shutdown the thread.
|
||||
std::atomic_bool m_bStarted = false; //!< Set by the thread when started.
|
||||
std::atomic_bool m_bShutdown = false; //!< When set, shutdown the thread.
|
||||
uint32_t m_uiDelayMs = 0u; //!< Delay (in ms) to insert while processing.
|
||||
uint32_t m_uiCycleCnt = 0u; //!< Amount of packets
|
||||
uint32_t m_uiPacketCnt = 0u; //!< Amount of packets
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <atomic>
|
||||
#include "gtest/gtest.h"
|
||||
#include "pattern_gen.h"
|
||||
#include "../../../sdv_services/ipc_shared_mem/shared_mem_buffer_posix.h"
|
||||
@@ -28,10 +29,14 @@ TEST(SharedMemoryBufferTest, CreateBuffer)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
appcontrol.Shutdown();
|
||||
}
|
||||
@@ -42,12 +47,16 @@ TEST(SharedMemoryBufferTest, TriggerTestRx)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
size_t nCorrectCnt = 0;
|
||||
std::condition_variable cvStart;
|
||||
std::mutex mtxStart;
|
||||
@@ -91,12 +100,16 @@ TEST(SharedMemoryBufferTest, TriggerTestTx)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
size_t nCorrectCnt = 0;
|
||||
std::condition_variable cvStart;
|
||||
std::mutex mtxStart;
|
||||
@@ -140,16 +153,21 @@ TEST(SharedMemoryBufferTest, TriggerTestRxTx)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
size_t nCorrectCnt = 0;
|
||||
std::condition_variable cvSenderStart, cvReceiverStart;
|
||||
std::mutex mtxReceiverStart;
|
||||
std::mutex mtxSenderStart;
|
||||
|
||||
auto fnWaitForTriggerReceiver = [&]()
|
||||
{
|
||||
std::unique_lock<std::mutex> lockReceiver(mtxReceiverStart);
|
||||
@@ -166,9 +184,6 @@ TEST(SharedMemoryBufferTest, TriggerTestRxTx)
|
||||
std::unique_lock<std::mutex> lockSender(mtxSenderStart);
|
||||
lockSender.unlock();
|
||||
cvSenderStart.notify_all();
|
||||
std::unique_lock<std::mutex> lockReceiver(mtxReceiverStart);
|
||||
cvReceiverStart.wait(lockReceiver);
|
||||
lockReceiver.unlock();
|
||||
while (!bShutdown)
|
||||
{
|
||||
bool bResult = sender.WaitForFreeSpace(200);
|
||||
@@ -179,14 +194,15 @@ TEST(SharedMemoryBufferTest, TriggerTestRxTx)
|
||||
};
|
||||
|
||||
std::unique_lock<std::mutex> lockStartSender(mtxSenderStart);
|
||||
std::unique_lock<std::mutex> lockStartReceiver(mtxReceiverStart);
|
||||
std::thread threadSender(fnWaitForTriggerSender);
|
||||
std::thread threadReceiver(fnWaitForTriggerReceiver);
|
||||
cvSenderStart.wait(lockStartSender);
|
||||
lockStartSender.unlock();
|
||||
std::unique_lock<std::mutex> lockStartReceiver(mtxReceiverStart);
|
||||
std::thread threadReceiver(fnWaitForTriggerReceiver);
|
||||
//CHECKPOINT();
|
||||
cvReceiverStart.wait(lockStartReceiver);
|
||||
lockStartReceiver.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Needed for the threads to enter their loop.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Needed for the threads to enter their loop.
|
||||
for (size_t n = 0; n < 200; n++)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
@@ -210,10 +226,14 @@ TEST(SharedMemoryBufferTest, SimpleSynchronousWriteRead)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
EXPECT_TRUE(sender.TryWrite("HELLO", 6));
|
||||
auto optPacket = receiver.TryRead();
|
||||
@@ -229,10 +249,14 @@ TEST(SharedMemoryBufferTest, ReadWithoutSending)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
auto optPacket = receiver.TryRead();
|
||||
EXPECT_FALSE(optPacket);
|
||||
@@ -246,10 +270,14 @@ TEST(SharedMemoryBufferTest, RequestReadPacketSize)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
EXPECT_TRUE(sender.TryWrite("HELLO", 5));
|
||||
|
||||
@@ -266,10 +294,14 @@ TEST(SharedMemoryBufferTest, FragmentRead)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
EXPECT_TRUE(sender.TryWrite("HELLO", 6));
|
||||
EXPECT_TRUE(sender.TryWrite("HELLO2", 7));
|
||||
@@ -296,10 +328,14 @@ TEST(SharedMemoryBufferTest, BufferBoundary)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender(256);
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
// The buffer header has 16 bytes
|
||||
// Each allocation is 8 bytes header, 6 bytes data and 2 bytes alignment
|
||||
@@ -353,10 +389,14 @@ TEST(SharedMemoryBufferTest, ReserveCommitAccessReleaseNonChronologicalOrder)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender(256);
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
// Reserve buffers for strings
|
||||
// The buffer header has 16 bytes
|
||||
@@ -464,10 +504,14 @@ TEST(SharedMemoryBufferTest, SendReceivePattern)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
CPatternReceiver pattern_inspector(receiver);
|
||||
CPatternSender pattern_generator(sender);
|
||||
@@ -501,10 +545,14 @@ TEST(SharedMemoryBufferTest, DelayedSendReceivePattern)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
CPatternReceiver pattern_inspector(receiver);
|
||||
CPatternSender pattern_generator(sender, 10);
|
||||
@@ -538,10 +586,14 @@ TEST(SharedMemoryBufferTest, SendDelayedReceivePattern)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx sender;
|
||||
EXPECT_TRUE(sender.IsValid());
|
||||
if (!sender.IsValid())
|
||||
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
|
||||
ASSERT_TRUE(sender.IsValid());
|
||||
|
||||
CSharedMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
if (!receiver.IsValid())
|
||||
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
|
||||
ASSERT_TRUE(receiver.IsValid());
|
||||
|
||||
CPatternReceiver pattern_inspector(receiver, 10);
|
||||
CPatternSender pattern_generator(sender);
|
||||
@@ -575,9 +627,13 @@ TEST(SharedMemoryBufferTest, SendRepeatReceivePattern)
|
||||
ASSERT_TRUE(appcontrol.Startup(""));
|
||||
|
||||
CSharedMemBufferTx bufferTX;
|
||||
EXPECT_TRUE(bufferTX.IsValid());
|
||||
if (!bufferTX.IsValid())
|
||||
std::cout << "ERROR TX: " << bufferTX.GetError() << std::endl;
|
||||
ASSERT_TRUE(bufferTX.IsValid());
|
||||
CSharedMemBufferRx bufferRX;
|
||||
EXPECT_TRUE(bufferRX.IsValid());
|
||||
if (!bufferTX.IsValid())
|
||||
std::cout << "ERROR RX: " << bufferTX.GetError() << std::endl;
|
||||
ASSERT_TRUE(bufferRX.IsValid());
|
||||
|
||||
// The connection string containing the RX and TX strings for the repeater
|
||||
std::string ssConnectionString = bufferTX.GetConnectionString() + "\n" + bufferRX.GetConnectionString();
|
||||
@@ -630,9 +686,13 @@ Mode = "Essential")code"));
|
||||
LoadSupportServices();
|
||||
|
||||
CSharedMemBufferTx bufferTX;
|
||||
EXPECT_TRUE(bufferTX.IsValid());
|
||||
if (!bufferTX.IsValid())
|
||||
std::cout << "ERROR TX: " << bufferTX.GetError() << std::endl;
|
||||
ASSERT_TRUE(bufferTX.IsValid());
|
||||
CSharedMemBufferRx bufferRX;
|
||||
EXPECT_TRUE(bufferRX.IsValid());
|
||||
if (!bufferTX.IsValid())
|
||||
std::cout << "ERROR RX: " << bufferTX.GetError() << std::endl;
|
||||
ASSERT_TRUE(bufferRX.IsValid());
|
||||
|
||||
// Start process
|
||||
sdv::process::IProcessControl* pProcessControl = sdv::core::GetObject<sdv::process::IProcessControl>("ProcessControlService");
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
|
||||
/**
|
||||
* @brief Receiver helper class.
|
||||
@@ -21,8 +22,7 @@ public:
|
||||
* @brief Constructor
|
||||
* @param[in] bEnableEvent When set, enable the connection event callback interface.
|
||||
*/
|
||||
CConnectReceiver(bool bEnableEvent = false) : m_bEnableEvent(bEnableEvent),
|
||||
m_threadDecoupledSend(&CConnectReceiver::DecoupledSendThread, this)
|
||||
CConnectReceiver(bool bEnableEvent = false) : m_bEnableEvent(bEnableEvent)
|
||||
{}
|
||||
|
||||
/**
|
||||
@@ -31,6 +31,8 @@ public:
|
||||
~CConnectReceiver()
|
||||
{
|
||||
m_bShutdown = true;
|
||||
std::unique_lock<std::mutex> lock(m_mtxData);
|
||||
lock.unlock();
|
||||
if (m_threadDecoupledSend.joinable())
|
||||
m_threadDecoupledSend.join();
|
||||
}
|
||||
@@ -64,6 +66,10 @@ public:
|
||||
// Copy the data
|
||||
m_seqDataCopy = seqData;
|
||||
|
||||
// Start the processing thread if needed
|
||||
if (!m_threadDecoupledSend.joinable()) m_threadDecoupledSend =
|
||||
std::thread(&CConnectReceiver::DecoupledSendThread, this);
|
||||
|
||||
// Store data into the queue for sending.
|
||||
m_queueDecoupledSend.push(std::move(seqData));
|
||||
m_cvDecoupledSend.notify_all();
|
||||
@@ -170,7 +176,7 @@ private:
|
||||
std::thread m_threadDecoupledSend; ///< Decoupled send thread.
|
||||
std::queue<sdv::sequence<sdv::pointer<uint8_t>>> m_queueDecoupledSend; ///< Data queue for sending.
|
||||
std::condition_variable m_cvDecoupledSend; ///< Trigger decoupled sending.
|
||||
bool m_bShutdown = false; ///< Shutdown send thread.
|
||||
std::atomic_bool m_bShutdown = false; ///< Shutdown send thread.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
#include <../global/base64.h>
|
||||
#include <support/sdv_core.h>
|
||||
#include <support/app_control.h>
|
||||
#include <support/sdv_test_macro.h>
|
||||
#include "../../include/sdv_test_macro.h"
|
||||
#include <interfaces/ipc.h>
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
|
||||
/**
|
||||
* @brief Load support modules to publish the needed services.
|
||||
@@ -27,8 +28,7 @@ public:
|
||||
* @brief Constructor
|
||||
* @param[in] bEnableEvent When set, enable the connection event callback interface.
|
||||
*/
|
||||
CLargeDataReceiver(bool bEnableEvent = false) : m_bEnableEvent(bEnableEvent),
|
||||
m_threadDecoupledSend(&CLargeDataReceiver::DecoupledSendThread, this)
|
||||
CLargeDataReceiver(bool bEnableEvent = false) : m_bEnableEvent(bEnableEvent)
|
||||
{}
|
||||
|
||||
/**
|
||||
@@ -37,6 +37,8 @@ public:
|
||||
~CLargeDataReceiver()
|
||||
{
|
||||
m_bShutdown = true;
|
||||
std::unique_lock<std::mutex> lock(m_mtxData);
|
||||
lock.unlock();
|
||||
if (m_threadDecoupledSend.joinable())
|
||||
m_threadDecoupledSend.join();
|
||||
}
|
||||
@@ -73,6 +75,10 @@ public:
|
||||
for (const sdv::pointer<uint8_t>& rptrData : seqData)
|
||||
m_queueDataCopy.push(rptrData);
|
||||
|
||||
// Start the processing thread if needed
|
||||
if (!m_threadDecoupledSend.joinable())
|
||||
m_threadDecoupledSend = std::thread(&CLargeDataReceiver::DecoupledSendThread, this);
|
||||
|
||||
// Store data into the queue for sending.
|
||||
m_queueDecoupledSend.push(std::move(seqData));
|
||||
m_cvDecoupledSend.notify_all();
|
||||
@@ -87,7 +93,7 @@ public:
|
||||
/**
|
||||
* @brief Wait until the caller hasn't sent anything anymore for 1 second.
|
||||
*/
|
||||
void WaitForNoActivity(sdv::IInterfaceAccess* pSender, size_t nCount = 1, uint32_t uiTimeoutMs = 20000)
|
||||
void WaitForNoActivity(sdv::IInterfaceAccess* pSender, [[maybe_unused]] size_t nCount = 1, uint32_t uiTimeoutMs = 1000)
|
||||
{
|
||||
CConnection* pConnection = dynamic_cast<CConnection*>(pSender);
|
||||
double dTimeout = static_cast<double>(uiTimeoutMs) / 1000.0;
|
||||
@@ -237,16 +243,16 @@ private:
|
||||
sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function.
|
||||
mutable std::mutex m_mtxData; ///< Protect data access.
|
||||
std::queue<sdv::pointer<uint8_t>> m_queueDataCopy; ///< Copy of the data.
|
||||
sdv::ipc::EConnectStatus m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current received status.
|
||||
std::atomic<sdv::ipc::EConnectStatus> m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current received status.
|
||||
bool m_bConnectError = false; ///< Connection error ocurred.
|
||||
bool m_bCommError = false; ///< Communication error occurred.
|
||||
bool m_bForcedDisconnect = false; ///< Force disconnect.
|
||||
size_t m_nCount = 0; ///< Receive counter.
|
||||
std::atomic_size_t m_nCount = 0; ///< Receive counter.
|
||||
std::condition_variable m_cvReceived; ///< Receive event.
|
||||
std::thread m_threadDecoupledSend; ///< Decoupled send thread.
|
||||
std::queue<sdv::sequence<sdv::pointer<uint8_t>>> m_queueDecoupledSend; ///< Data queue for sending.
|
||||
std::condition_variable m_cvDecoupledSend; ///< Trigger decoupled sending.
|
||||
bool m_bShutdown = false; ///< Shutdown send thread.
|
||||
std::atomic_bool m_bShutdown = false; ///< Shutdown send thread.
|
||||
};
|
||||
|
||||
TEST(SharedMemChannelService, CommunicateOneLargeBlock)
|
||||
@@ -341,6 +347,7 @@ Size = 1024000
|
||||
EXPECT_NO_THROW(ptrServerConnection.Clear());
|
||||
|
||||
EXPECT_NO_THROW(mgntServer.Shutdown());
|
||||
|
||||
EXPECT_NO_THROW(mgntClient.Shutdown());
|
||||
|
||||
EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending);
|
||||
@@ -371,6 +378,7 @@ Size = 1024000
|
||||
EXPECT_NE(sChannelEndpoint.pConnection, nullptr);
|
||||
EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty());
|
||||
|
||||
|
||||
sdv::TObjectPtr ptrServerConnection(sChannelEndpoint.pConnection);
|
||||
sdv::TObjectPtr ptrClientConnection = mgntClient.Access(sChannelEndpoint.ssConnectString);
|
||||
EXPECT_TRUE(ptrServerConnection);
|
||||
@@ -439,7 +447,7 @@ Size = 1024000
|
||||
}
|
||||
EXPECT_EQ(nCnt, 30);
|
||||
|
||||
nCnt = 0;
|
||||
nCnt = 0;
|
||||
bCorrect = true;
|
||||
EXPECT_EQ(receiverClient.GetReceiveCount(), 30u);
|
||||
while (bCorrect)
|
||||
@@ -467,6 +475,7 @@ Size = 1024000
|
||||
EXPECT_NO_THROW(ptrServerConnection.Clear());
|
||||
|
||||
EXPECT_NO_THROW(mgntServer.Shutdown());
|
||||
|
||||
EXPECT_NO_THROW(mgntClient.Shutdown());
|
||||
|
||||
EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending);
|
||||
@@ -582,6 +591,7 @@ Size = 1024000
|
||||
EXPECT_NO_THROW(ptrServerConnection.Clear());
|
||||
|
||||
EXPECT_NO_THROW(mgntServer.Shutdown());
|
||||
|
||||
EXPECT_NO_THROW(mgntClient.Shutdown());
|
||||
|
||||
EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending);
|
||||
@@ -650,6 +660,7 @@ Size = 1024000
|
||||
// Try send; should succeed, since connected
|
||||
EXPECT_TRUE(pServerSend->SendData(seqPattern));
|
||||
receiverServer.WaitForNoActivity(ptrServerConnection);
|
||||
EXPECT_EQ(receiverServer.GetReceiveCount(), 1u);
|
||||
auto ptrServerPattern = receiverServer.GetData();
|
||||
ASSERT_EQ(ptrServerPattern.size(), nCount * sizeof(uint32_t));
|
||||
pData = reinterpret_cast<uint32_t*>(ptrServerPattern.get());
|
||||
@@ -730,13 +741,15 @@ Size = 1024000
|
||||
TRACE("Connection estabished...");
|
||||
appcontrol.SetRunningMode();
|
||||
|
||||
EXPECT_EQ(receiverServer.GetReceiveCount(), 0u);
|
||||
|
||||
// Try send; should succeed, since connected
|
||||
for (size_t nCnt = 0; nCnt < 30; nCnt++)
|
||||
{
|
||||
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
|
||||
SDV_TIMING_EXPECT_EQ(pServerSend->SendData(seqPattern), true, sdv::TEST::WARNING_REDUCED);
|
||||
SDV_EXPECT_EQ_WARN(pServerSend->SendData(seqPattern), true, sdv_test::WARNING_REDUCED);
|
||||
else
|
||||
SDV_TIMING_EXPECT_EQ(pServerSend->SendData(seqPattern), true, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_EQ_WARN(pServerSend->SendData(seqPattern), true, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
receiverServer.WaitForNoActivity(ptrServerConnection, 30);
|
||||
|
||||
@@ -744,9 +757,9 @@ Size = 1024000
|
||||
size_t nCnt = 0;
|
||||
bool bCorrect = true;
|
||||
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
|
||||
SDV_TIMING_EXPECT_EQ(receiverServer.GetReceiveCount(), 30u, sdv::TEST::WARNING_REDUCED);
|
||||
SDV_EXPECT_EQ_WARN(receiverServer.GetReceiveCount(), 30u, sdv_test::WARNING_REDUCED);
|
||||
else
|
||||
SDV_TIMING_EXPECT_EQ(receiverServer.GetReceiveCount(), 30u, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_EQ_WARN(receiverServer.GetReceiveCount(), 30u, sdv_test::WARNING_ENABLED);
|
||||
while (bCorrect)
|
||||
{
|
||||
auto ptrServerPattern = receiverServer.GetData();
|
||||
@@ -765,9 +778,9 @@ Size = 1024000
|
||||
}
|
||||
}
|
||||
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
|
||||
SDV_TIMING_EXPECT_EQ(nCnt, 30, sdv::TEST::WARNING_REDUCED);
|
||||
SDV_EXPECT_EQ_WARN(nCnt, 30u, sdv_test::WARNING_REDUCED);
|
||||
else
|
||||
SDV_TIMING_EXPECT_EQ(nCnt, 30, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_EQ_WARN(nCnt, 30u, sdv_test::WARNING_ENABLED);
|
||||
|
||||
appcontrol.SetConfigMode();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user