Precommit (#1)

* first commit

* cleanup
This commit is contained in:
tompzf
2025-11-04 13:28:06 +01:00
committed by GitHub
parent dba45dc636
commit 6ed4b1534e
898 changed files with 256340 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
[Configuration]
Version = 100
[[Component]]
Path = "task_timer.sdv"
Class = "TaskTimerService"
[[Component]]
Path = "data_dispatch_service.sdv"
Class = "DataDispatchService"
[[Component]]
Path = "can_com_silkit.sdv"
Class = "CAN_Com_SilKit"
DebugInfo = true
SyncMode = true
SilKitParticipantName = "CAN1"
CanSilKitNetwork = "PrivateCAN"
RegistryURI = "silkit://localhost:8500"
SilKitConfig = """{
"Logging": {
"Sinks": [ { "Type": "Stdout", "Level": "Info" } ]
},
}"""
[[Component]]
Path = "can_datalink.sdv"
Class = "CAN_data_link"

View File

@@ -0,0 +1,55 @@
# Only build on MSVC/Windows or 64-bit Linux (not ARM)
if((WIN32 AND NOT MSVC) OR (UNIX AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64"))
project(ManualTestCanComSilKit)
FetchContent_MakeAvailable(SilKit)
set(SilKit_DIR "${silkit_SOURCE_DIR}/SilKit/lib/cmake/SilKit")
find_package(SilKit REQUIRED CONFIG)
if(CMAKE_COMPILER_IS_GNUCXX)
add_compile_options(-Wno-shadow)
endif()
add_executable(ManualTest_CANComSilKit
src/can_com_manual_test_silkit.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../../sdv_services/can_communication_silkit/can_com_silkit.cpp
)
add_executable(can_writer
src/can_writer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../../sdv_services/can_communication_silkit/can_com_silkit.cpp
)
add_executable(can_reader
src/can_reader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../../sdv_services/can_communication_silkit/can_com_silkit.cpp
)
# Copy the config files
if(UNIX)
file(COPY ${PROJECT_SOURCE_DIR}/run_silkit_util.sh DESTINATION ${CMAKE_BINARY_DIR}/tests/manual_tests/)
elseif(WIN32)
file(COPY ${PROJECT_SOURCE_DIR}/run_silkit_util.bat DESTINATION ${CMAKE_BINARY_DIR}/tests/manual_tests/)
endif()
target_link_libraries(ManualTest_CANComSilKit ${CMAKE_DL_LIBS} GTest::GTest SilKit::SilKit)
target_link_libraries(can_writer ${CMAKE_DL_LIBS} GTest::GTest SilKit::SilKit )
target_link_libraries(can_reader ${CMAKE_DL_LIBS} GTest::GTest SilKit::SilKit)
add_test(NAME ManualTest_CANComSilKit COMMAND ManualTest_CANComSilKit)
# add_custom_command(TARGET ManualTest_CANComSilKit POST_BUILD
# COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:ManualTest_CANComSilKit>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ManualTest_CANComSilKit.xml
# VERBATIM
# )
# Build dependencies
add_dependencies(ManualTest_CANComSilKit dependency_sdv_components can_com_silkit)
add_dependencies(can_reader dependency_sdv_components can_com_silkit)
add_dependencies(can_writer dependency_sdv_components can_com_silkit)
endif() # Only build on MSVC/Windows or 64-bit Linux (not ARM)

View File

@@ -0,0 +1,128 @@
# SilKit CAN Communication Tests
This guide provides instructions on how to execute the manual tests for the SilKit CAN communication system, including the `can_reader` and `can_writer` components.
## Prerequisites
Before running the tests, ensure the following:
- Operating System: Ubuntu (or a Linux distribution with gnome-terminal installed) **or Windows**.
- Dependencies:
- `sil-kit-registry` and `sil-kit-system-controller` binaries are available in the directory: `<...>/vapi-cpp-vehicle-api-platform/build/<compiler_version>/_deps/silkit-src/SilKit/bin/`
- gnome-terminal is installed on your system (Linux only).
- Build: The project has been built successfully, and the binaries for can_reader and can_writer are available in the directory: `<...>/vapi-cpp-vehicle-api-platform/build/<compiler_version>/tests/bin/`
## Test Components
The tests involve the following components:
- `sil-kit-registry`: Manages the SilKit network.
- `sil-kit-system-controller`: Controls the lifecycle of SilKit participants.
- `can_writer`: Sends CAN messages to the network.
- `can_reader`: Receives CAN messages from the network.
## Instructions
### Linux
1. **Start the SilKit Utilities**
- Navigate to the directory containing the `run_silkit_util.sh` script:
```bash
cd <...>/vapi-cpp-vehicle-api-platform/build/<compiler_version>/tests/manual_tests/
```
- Make the script executable (if not already):
```bash
chmod +x run_silkit_util.sh
```
- Run the script to start the SilKit utilities:
```bash
./run_silkit_util.sh
```
This will open two new terminal windows:
- One running `sil-kit-registry`.
- Another running `sil-kit-system-controller` with the arguments `can_reader can_writer`.
2. **Run the CAN Writer**
In a new terminal, navigate to the directory containing the can_writer binary:
```bash
cd <...>/vapi-cpp-vehicle-api-platform/build/<compiler_version>/tests/bin/
```
- Run the can_writer binary:
```bash
./can_writer
```
The `can_writer` will send different CAN messages to the network. Each message will be logged in the terminal.
3. **Run the CAN Reader**
In another terminal, navigate to the directory containing the can_reader binary:
```bash
cd <...>/vapi-cpp-vehicle-api-platform/build/<compiler_version>/tests/bin/
```
- Run the `can_reader` binary:
```bash
./can_reader
```
The `can_reader` will receive and log the CAN messages sent by the `can_writer`. Each message will be logged in the terminal.
### Windows
1. **Start the SilKit Utilities**
- Before running the executables, copy the `SilKit.dll` and `SilKitd.dll` DLLs from the SilKit source directory `<..>\_deps\silkit-src\SilKit\bin` to the `<..>\test\bin` directory.
- Open PowerShell or Command Prompt and navigate to the directory containing the `run_silkit_util.bat` script (if available):
```bat
cd <...>\vapi-cpp-vehicle-api-platform\build\<compiler_version>\tests\manual_tests
```
- Run the script to start the SilKit utilities:
```bat
run_silkit_util.bat
```
This will open two new windows:
- One running `sil-kit-registry`.
- Another running `sil-kit-system-controller` with the arguments `can_reader can_writer`.
**If the script is not available, you can start the utilities manually:**
- Open two Command Prompt or PowerShell windows:
- In the first window, navigate to the SilKit bin directory and run:
```bat
cd <...>\vapi-cpp-vehicle-api-platform\build\<compiler_version>\_deps\silkit-src\SilKit\bin
sil-kit-registry.exe
```
- In the second window, run:
```bat
sil-kit-system-controller.exe can_reader can_writer
```
1. **Run the CAN Writer**
- Open a new Command Prompt or PowerShell window and navigate to the directory containing the can_writer binary:
```bat
cd <...>\vapi-cpp-vehicle-api-platform\build\<compiler_version>\tests\bin
can_writer.exe
```
The `can_writer` will send different CAN messages to the network. Each message will be logged in the terminal.
3. **Run the CAN Reader**
- Open another Command Prompt or PowerShell window and navigate to the directory containing the can_reader binary:
```bat
cd <...>\vapi-cpp-vehicle-api-platform\build\<compiler_version>\tests\bin
can_reader.exe
```
The `can_reader` will receive and log the CAN messages sent by the `can_writer`. Each message will be logged in the terminal.
## Expected Output
- **CAN Writer:** Logs the messages being sent, including the CAN ID, data length, and data content in the terminal.
- **CAN Reader:** Logs the messages being received, including the CAN ID, data length, and data content in the terminal.
## Troubleshooting
- Binaries Not Found: Ensure the `sil-kit-registry`, `sil-kit-system-controller`, `can_writer`, and `can_reader` binaries are built and located in the correct directories.
- **Terminal Emulator: The script uses gnome-terminal on Linux. On Windows, PowerShell is used. If you use a different terminal emulator, modify the scripts accordingly.**
- Network Issues: Ensure the RegistryURI in the configuration matches the address of the sil-kit-registry.
## Additional Notes
- The `can_writer` and `can_reader` configurations are hardcoded in their respective source files. If you need to modify the configurations (e.g., participant names, network names, or registry URI), update the source code and rebuild the binaries.
- The tests are designed to run in synchronous mode (SyncMode = true). Ensure this setting is consistent across all components.

View File

@@ -0,0 +1,217 @@
#ifndef TEST_CAN_SILKIT_HELPER_H
#define TEST_CAN_SILKIT_HELPER_H
#include <iostream>
#include <mutex>
#include <deque>
#include <thread>
#include <gtest/gtest.h>
#include <interfaces/can.h>
#include <support/app_control.h>
#include <support/toml.h>
#include <support/component_impl.h>
#include "../sdv_services/core/toml_parser/parser_toml.h"
#include "../../../../sdv_services/can_communication_silkit/can_com_silkit.h"
#include "../../../../export/support/app_control.h"
class CTestCanSilKit : public CCANSilKit
{
public:
virtual void Initialize(const sdv::u8string& ssObjectConfig) override
{
return CCANSilKit::Initialize(ssObjectConfig);
}
virtual sdv::EObjectStatus GetStatus() const override
{
return CCANSilKit::GetStatus();
}
virtual void Shutdown() override
{
return CCANSilKit::Shutdown();
}
virtual void RegisterReceiver(/*in*/ sdv::can::IReceive* pReceiver) override
{
return CCANSilKit::RegisterReceiver(pReceiver);
}
virtual void UnregisterReceiver(/*in*/ sdv::can::IReceive* pReceiver) override
{
return CCANSilKit::UnregisterReceiver(pReceiver);
}
virtual void Send(/*in*/ const sdv::can::SMessage& sMsg, /*in*/ uint32_t uiIfcIndex) override
{
return CCANSilKit::Send(sMsg, uiIfcIndex);
}
virtual sdv::sequence<sdv::u8string> GetInterfaces() const override
{
return CCANSilKit::GetInterfaces();
}
};
sdv::can::SMessage CreateCanMsgData(uint8_t init, uint8_t dataSize)
{
uint8_t value = init;
sdv::can::SMessage testMsg;
testMsg.uiID = value;
testMsg.bCanFd = false;
testMsg.bExtended = false;
testMsg.seqData.resize(dataSize);
for (uint8_t i = 0; i < dataSize; i++)
{
testMsg.seqData[i] = value;
value += 2;
}
return testMsg;
}
class CanReceiver : public sdv::can::IReceive
{
public:
void Receive(const sdv::can::SMessage& sMessage, uint32_t) override
{
vecReceivedMessages.push_back(sMessage);
}
void Error(const sdv::can::SErrorFrame&, uint32_t) override {}
std::vector<sdv::can::SMessage> GetReceivedMessages()
{
return vecReceivedMessages;
}
void ClearReceivedMessages()
{
vecReceivedMessages.clear();
}
private:
std::vector<sdv::can::SMessage> vecReceivedMessages;
};
class CComTestHelper
{
public:
struct PcloseDeleter
{
void operator()(FILE* file) const
{
#ifdef _WIN32
_pclose(file);
#else
pclose(file);
#endif
}
};
std::string exec(const char* cmd)
{
std::array<char, 128> buffer;
std::string result;
#ifdef _WIN32
std::unique_ptr<FILE, PcloseDeleter> pipe(_popen(cmd, "r"));
#else
std::unique_ptr<FILE, PcloseDeleter> pipe(popen(cmd, "r"));
#endif
if (!pipe)
{
return "";
}
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) != nullptr)
{
result += buffer.data();
}
return result;
}
sdv::can::SMessage CreateTestData(uint8_t init, uint8_t dataSize)
{
uint8_t value = 65;
// uint8_t value = init;
sdv::can::SMessage testMsg;
testMsg.uiID = init;
testMsg.bCanFd = false;
testMsg.bExtended = false;
testMsg.seqData.resize(dataSize);
for (uint8_t i = 0; i < dataSize; i++)
{
testMsg.seqData[i] = value;
value += 2;
}
return testMsg;
}
bool ValidateInterfaces(const sdv::sequence<sdv::u8string>& seqExpectedInterfaces, const sdv::sequence<sdv::u8string>& seqInterfaces)
{
bool success = true;
if (seqExpectedInterfaces.size() != seqInterfaces.size())
{
std::cout << "Interfaces failure, expected: " << std::to_string(seqExpectedInterfaces.size()) << " got: " << std::to_string(seqInterfaces.size()) << std::endl;
success = false;
}
else
{
for (size_t nIndex = 0; nIndex < seqInterfaces.size(); nIndex++)
{
if (seqExpectedInterfaces[nIndex].compare(seqInterfaces[nIndex]) != 0)
{
success = false,
std::cout << "Interface mismatch, expected: " << seqExpectedInterfaces[nIndex].c_str() << " got: " << seqInterfaces[nIndex].c_str() << std::endl;
}
}
}
return success;
}
bool ValidateReceivedMessages(const std::deque<std::pair<std::string, sdv::can::SMessage>>& received,
const sdv::can::SMessage& testMessage, const std::string& interfaceName, size_t expectedSize)
{
uint32_t error = 0;
if (expectedSize == 0)
error++;
if (received.size() != expectedSize)
error++;
for (auto message : received)
{
if (interfaceName.compare(message.first) != 0)
error++;
if (message.second.uiID != testMessage.uiID)
error++;
if (message.second.seqData.size() != testMessage.seqData.size())
error++;
}
if (error)
{
std::cout << "Got " << std::to_string(error) << " errors from validation of "
<< interfaceName << ", expect " << std::to_string(expectedSize)
<< " messages, got " << std::to_string(received.size()) << std::endl;
return false;
}
return true;
}
std::string NormalizeWhitespace(const std::string& str)
{
std::string trimmed = str;
trimmed.erase(trimmed.find_last_not_of(" \t\n\r") + 1); // Trim trailing whitespace
trimmed.erase(0, trimmed.find_first_not_of(" \t\n\r")); // Trim leading whitespace
std::string result;
std::unique_copy(trimmed.begin(), trimmed.end(), std::back_inserter(result),
[](char a, char b) { return std::isspace(a) && std::isspace(b); });
return result;
}
};
#endif // ! defined TEST_CAN_SILKIT_HELPER_H

View File

@@ -0,0 +1,6 @@
@echo off
REM Run sil-kit-registry in a new Command Prompt window
start cmd /k "..\..\_deps\silkit-src\SilKit\bin\sil-kit-registry.exe"
REM Run sil-kit-system-controller with arguments in another new Command Prompt window
start cmd /k "..\..\_deps\silkit-src\SilKit\bin\sil-kit-system-controller.exe can_reader can_writer"

View File

@@ -0,0 +1,7 @@
#!/bin/bash
# Run sil-kit-registry in a new terminal window
gnome-terminal -- bash -c "./../../_deps/silkit-src/SilKit/bin/sil-kit-registry; exec bash"
# Run sil-kit-system-controller with arguments in another new terminal window
gnome-terminal -- bash -c "./../../_deps/silkit-src/SilKit/bin/sil-kit-system-controller can_reader can_writer; exec bash"

View File

@@ -0,0 +1,270 @@
#include "../../../../global/process_watchdog.h"
#include "../include/can_com_test_helper.h"
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
#endif
class CANSilkitTest : public ::testing::Test
{
public:
virtual void SetUp() override
{
}
virtual void TearDown() override
{
}
};
class CTestCANSilkit : public CCANSilKit
{
public:
virtual void Initialize(const sdv::u8string& ssObjectConfig) override
{
CCANSilKit::Initialize(ssObjectConfig);
}
virtual void Shutdown() override
{
CCANSilKit::Shutdown();
}
virtual void Send(const sdv::can::SMessage& sMsg, uint32_t uiIfcIndex) override
{
CCANSilKit::Send(sMsg, uiIfcIndex);
m_MessagesSent++;
}
virtual void RegisterReceiver(sdv::can::IReceive* pReceiver) override
{
CCANSilKit::RegisterReceiver(pReceiver);
}
virtual void UnregisterReceiver(sdv::can::IReceive* pReceiver) override
{
CCANSilKit::UnregisterReceiver(pReceiver);
}
virtual sdv::sequence<sdv::u8string> GetInterfaces() const override
{
return CCANSilKit::GetInterfaces();
}
sdv::EObjectStatus GetTestStatus() const
{
return CCANSilKit::GetStatus();
}
uint64_t GetMessagesSent() const
{
return m_MessagesSent;
}
uint64_t m_MessagesSent = 0;
};
class MockCANReceiver : public sdv::can::IReceive
{
public:
void Receive(const sdv::can::SMessage& msg, uint32_t ifIndex) override
{
std::lock_guard<std::mutex> lock(m_mutex);
m_receivedMessages.push_back(std::make_pair(ifIndex, msg));
}
void Error(const sdv::can::SErrorFrame&, uint32_t) override {}
std::deque<std::pair<uint32_t, sdv::can::SMessage>>GetReceivedMessages() const
{
return m_receivedMessages;
}
private:
std::deque<std::pair<uint32_t, sdv::can::SMessage>> m_receivedMessages; ///< Storing index and received messages
std::mutex m_mutex;
};
bool InitializeAppControl(sdv::app::CAppControl* appcontrol, const std::string& configFileName)
{
auto bResult = appcontrol->AddModuleSearchDir("../../bin");
bResult &= appcontrol->Startup("");
appcontrol->SetConfigMode();
bResult &= appcontrol->AddConfigSearchDir("../../tests/bin/config");
if (!configFileName.empty())
{
bResult &= appcontrol->LoadConfig(configFileName.c_str()) == sdv::core::EConfigProcessResult::successful;
}
return bResult;
}
void InitializeCanComObject(CTestCANSilkit& canComObj, const std::string config, MockCANReceiver& mockRcv)
{
ASSERT_NO_THROW(canComObj.Initialize(config.c_str()));
EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized);
ASSERT_NO_THROW(canComObj.SetOperationMode(sdv::EOperationMode::configuring));
ASSERT_NO_THROW(canComObj.RegisterReceiver(&mockRcv));
EXPECT_NO_THROW(canComObj.SetOperationMode(sdv::EOperationMode::running));
}
void ShutDownCanComObject(CTestCANSilkit& canComObj, MockCANReceiver& mockRcv)
{
EXPECT_NO_THROW(canComObj.UnregisterReceiver(&mockRcv));
ASSERT_NO_THROW(canComObj.Shutdown());
}
void SendThread(bool& stop, CTestCANSilkit& canComObj, sdv::can::SMessage& testData)
{
while (!stop)
{
EXPECT_NO_THROW(canComObj.Send(testData, 1)); // Send to second configuration
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
}
TEST_F(CANSilkitTest, ValidConfigString)
{
sdv::can::SMessage testMsg {1, 0, 0, {0x11, 0x22, 0x33, 0x44}};
sdv::app::CAppControl appControl;
appControl.Startup("");
sdv::u8string ssObjectConfig =
R"(
DebugInfo = true
SyncMode = true
SilKitParticipantName = "can_writer"
CanSilKitNetwork = "PrivateCAN"
RegistryURI = "silkit://localhost:8500"
SilKitConfig = """{
"Logging": {
"Sinks": [ { "Type": "Stdout", "Level": "Info" } ]
},
}"""
)";
CTestCANSilkit canComObj;
ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str()));
ASSERT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized);
ASSERT_NO_THROW(canComObj.Send(testMsg, 0));
ASSERT_NO_THROW(canComObj.Shutdown());
ASSERT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::shutdown_in_progress);
}
TEST_F(CANSilkitTest, InvalidConfigIdentifier)
{
sdv::app::CAppControl appControl;
appControl.Startup("");
sdv::u8string ssObjectConfig =
R"(
DebugInfo = true
SyncMode = true
InvalidSilKitParticipantName = "can_writer"
CanSilKitNetwork = "PrivateCAN"
RegistryURI = "silkit://localhost:8500"
SilKitConfig = """{
"Logging": {
"Sinks": [ { "Type": "Stdout", "Level": "Info" } ]
},
}"""
)";
CTestCANSilkit canComObj;
ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str()));
EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialization_failure);
ASSERT_NO_THROW(canComObj.Shutdown());
EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::shutdown_in_progress);
}
TEST_F(CANSilkitTest, SendReceiveTest)
{
sdv::app::CAppControl appControl;
appControl.Startup("");
sdv::u8string ssConfig1 =
R"(
DebugInfo = true
SyncMode = true
SilKitParticipantName = "can_writer"
CanSilKitNetwork = "PrivateCAN"
RegistryURI = "silkit://localhost:8500"
SilKitConfig = """{
"Logging": {
"Sinks": [ { "Type": "Stdout", "Level": "Info" } ]
},
}"""
)";
sdv::u8string ssConfig2 =
R"(
DebugInfo = true
SyncMode = true
SilKitParticipantName = "can_reader"
CanSilKitNetwork = "PrivateCAN"
RegistryURI = "silkit://localhost:8500"
SilKitConfig = """{
"Logging": {
"Sinks": [ { "Type": "Stdout", "Level": "Info" } ]
},
}"""
)";
CTestCANSilkit canComObj1;
CTestCANSilkit canComObj2;
MockCANReceiver mockRcv;
canComObj1.Initialize(ssConfig1.c_str());
ASSERT_EQ(canComObj1.GetStatus(), sdv::EObjectStatus::initialized);
canComObj2.Initialize(ssConfig2.c_str());
ASSERT_EQ(canComObj2.GetStatus(), sdv::EObjectStatus::initialized);
ASSERT_NO_THROW(canComObj1.SetOperationMode(sdv::EOperationMode::running));
ASSERT_NO_THROW(canComObj2.SetOperationMode(sdv::EOperationMode::configuring));
ASSERT_NO_THROW(canComObj2.RegisterReceiver(&mockRcv));
ASSERT_NO_THROW(canComObj2.SetOperationMode(sdv::EOperationMode::running));
uint8_t msgId = 102;
uint8_t dataSize = 1;
uint32_t expectToBeSend = 8;
uint32_t msgToBeSend = 8;
CComTestHelper testHelper;
while (dataSize <= msgToBeSend)
{
auto testData = testHelper.CreateTestData(msgId, dataSize);
EXPECT_NO_THROW(canComObj1.Send(testData, 0)); // Send to CAN1
std::this_thread::sleep_for(std::chrono::milliseconds(10));
dataSize++;
}
auto receivedMessages = mockRcv.GetReceivedMessages();
if (expectToBeSend != receivedMessages.size())
{
std::stringstream sstream;
sstream << " Failed, expected " << std::to_string(expectToBeSend ) << " messages, got from CAN1 to CAN2: "
<< std::to_string(receivedMessages.size());
FAIL() << sstream.str();
}
else
{
dataSize = 1;
for(auto rcvMsg : receivedMessages)
{
auto testData = testHelper.CreateTestData(msgId, dataSize);
EXPECT_EQ(msgId, rcvMsg.second.uiID);
EXPECT_EQ(dataSize++, rcvMsg.second.seqData.size());
EXPECT_EQ(testData.seqData, rcvMsg.second.seqData);
std::cout << "Expected " << std::to_string(expectToBeSend ) << " messages, got from CAN1 to CAN2: "
<< std::to_string(receivedMessages.size()) << std::endl;
}
}
ASSERT_NO_THROW(canComObj2.UnregisterReceiver(&mockRcv));
canComObj1.Shutdown();
canComObj2.Shutdown();
}

View File

@@ -0,0 +1,421 @@
#include <iostream>
#include <set>
#include <mutex>
#include <thread>
#include <atomic>
#include <vector>
#include <condition_variable>
#include <gtest/gtest.h>
#include <interfaces/can.h>
#include <support/app_control.h>
#include <support/toml.h>
#include <support/component_impl.h>
#include "../../../global/process_watchdog.h"
#include "../sdv_services/core/toml_parser/parser_toml.h"
/**
* @brief Dummy CAN Component to be able to load data link for testing purposes.
* Sends and receives CAN messages with multiple signals.
*/
class CDummyCANSilKit
: public sdv::CSdvObject
, public sdv::IObjectControl
, public sdv::can::IRegisterReceiver
, public sdv::can::ISend
{
public:
// Interface map
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
SDV_INTERFACE_ENTRY(sdv::can::IRegisterReceiver)
SDV_INTERFACE_ENTRY(sdv::can::ISend)
END_SDV_INTERFACE_MAP()
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device)
DECLARE_OBJECT_CLASS_NAME("Dummy_CAN_Sockets")
DECLARE_DEFAULT_OBJECT_NAME("CAN_Communication_Object")
DECLARE_OBJECT_SINGLETON()
virtual void Initialize(const sdv::u8string& ) override
{
m_StopThread = false;
//m_thSend2DatalinkThread = std::thread(&CDummyCANSockets::Send2DatalinkThread, this);
m_status = sdv::EObjectStatus::initialized;
}
virtual sdv::EObjectStatus GetStatus() const override
{
return m_status;
}
void SetOperationMode(sdv::EOperationMode eMode) override
{
switch (eMode)
{
case sdv::EOperationMode::configuring:
if (m_status == sdv::EObjectStatus::running || m_status == sdv::EObjectStatus::initialized)
m_status = sdv::EObjectStatus::configuring;
break;
case sdv::EOperationMode::running:
if (m_status == sdv::EObjectStatus::configuring || m_status == sdv::EObjectStatus::initialized)
m_status = sdv::EObjectStatus::running;
break;
default:
break;
}
}
virtual void Shutdown() override
{
m_StopThread = true;
if (m_thSend2DatalinkThread.joinable())
{
m_thSend2DatalinkThread.join();
}
}
virtual void RegisterReceiver(sdv::can::IReceive* pReceiver) override
{
if (!pReceiver)
{
return;
}
std::cout << "Registering Dummy CAN communication receiver...\n";
std::unique_lock<std::mutex> lock(m_mtxReceivers);
m_setReceivers.insert(pReceiver);
}
virtual void UnregisterReceiver(sdv::can::IReceive* pReceiver) override
{
if (!pReceiver)
{
return;
}
std::cout << "Unregistering Dummy CAN communication receiver...\n";
std::unique_lock<std::mutex> lock(m_mtxReceivers);
m_setReceivers.erase(pReceiver);
}
virtual void Send(const sdv::can::SMessage& sMsg, uint32_t uiIfcIndex) override
{
{
std::unique_lock<std::mutex> lockReceivers(m_mtxReceivers);
for (sdv::can::IReceive* pReceiver : m_setReceivers)
{
pReceiver->Receive(sMsg, uiIfcIndex);
}
}
{
std::lock_guard<std::mutex> lock(m_mtx);
m_messageSent = true;
}
m_cv.notify_one();
}
// Synchronization members
std::condition_variable m_cv;
std::mutex m_mtx;
bool m_messageSent = false;
private:
bool m_StopThread = false;
std::thread m_thSend2DatalinkThread;
mutable std::mutex m_mtxReceivers;
std::set<sdv::can::IReceive*> m_setReceivers;
std::atomic<sdv::EObjectStatus> m_status = { sdv::EObjectStatus::initialization_pending };
};
class MockCANReceiver : public sdv::can::IReceive
{
public:
std::vector<sdv::can::SMessage> vecReceivedMessages;
void Receive(const sdv::can::SMessage& sMessage, uint32_t) override
{
vecReceivedMessages.push_back(sMessage);
}
void Error(const sdv::can::SErrorFrame&, uint32_t) override {}
};
bool InitializeAppControl(sdv::app::CAppControl* appcontrol, const std::string& configFileName)
{
auto bResult = appcontrol->AddModuleSearchDir("../../bin");
bResult &= appcontrol->Startup("");
appcontrol->SetConfigMode();
bResult &= appcontrol->AddConfigSearchDir("../../tests/bin/config");
if (!configFileName.empty())
{
bResult &= appcontrol->LoadConfig(configFileName.c_str()) == sdv::core::EConfigProcessResult::successful;
}
return bResult;
}
std::string ReplaceWhitespaceWithSingleSpaceAndEscapeQuotes(const std::string& input)
{
std::stringstream result;
bool inWhitespace = false;
for (uint8_t singleChar : input)
{
if (std::isspace(static_cast<uint8_t>(singleChar)))
{
if (!inWhitespace)
{
result << ' '; // Add a single space for whitespace
inWhitespace = true;
}
}
else if (singleChar == '"')
{
// Escape the quote character
result << "\\\"";
}
else
{
result << singleChar; // Add the character as is
inWhitespace = false;
}
}
return result.str();
}
bool StringToBool(const std::string& value) {
return value == "true" || value == "1"; // Return true for "true" or "1", false otherwise
}
std::string ExtractValue(const std::string& content, const std::string& key)
{
std::size_t startPos = content.find(key + " = ");
if (startPos == std::string::npos) {
return ""; // Key not found
}
startPos += key.length() + 3; // Move past the key and " = "
// If the value is multi-line (enclosed by triple quotes), find the start and end
std::size_t endPos;
if (content.substr(startPos, 3) == "\"\"\"") { // Check for starting triple quotes
startPos += 3; // Skip over the triple quotes
endPos = content.find("\"\"\"", startPos); // Look for the closing triple quotes
if (endPos == std::string::npos) {
return ""; // End of value not found, invalid format
}
} else {
// Otherwise, find the first newline or end of the string
endPos = content.find_first_of("\n", startPos);
if (endPos == std::string::npos) {
endPos = content.length();
}
}
// Extract the value between startPos and endPos
std::string value = content.substr(startPos, endPos - startPos);
// Remove surrounding whitespace and newline characters if present
value.erase(std::remove(value.begin(), value.end(), '\"'), value.end());
value.erase(std::remove(value.begin(), value.end(), '\n'), value.end());
value.erase(std::remove(value.begin(), value.end(), '\r'), value.end());
return value;
}
class CANCommunicationTest : public ::testing::Test
{
void SetUp() override {}
void TearDown() override {}
};
TEST_F(CANCommunicationTest, BasicSendAndReceiveMessageTest)
{
// Dummy test message
sdv::can::SMessage testMsg;
testMsg.uiID = 0x1234;
testMsg.bCanFd = false;
testMsg.bExtended = false;
testMsg.seqData = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x77, 0x88};
// Configuration content as a string
std::string testConfigFileContent = R"(
[Configuration]
Version = 100
[[Component]]
Path = "task_timer.sdv"
Class = "TaskTimerService"
[[Component]]
Path = "data_dispatch_service.sdv"
Class = "DataDispatchService"
[[Component]]
Path = "can_com_silkit.sdv"
Class = "CAN_Com_SilKit"
DebugInfo = true
SyncMode = true
CanSilKitChannel = "CAN1"
CanSilKitNetwork = "PrivateCAN"
RegistryURI = "silkit://localhost:8500"
SilKitConfig = """{
"Logging": {
"Sinks": [ { "Type": "Stdout", "Level": "Info" } ]
},
}"""
[[Component]]
Path = "can_datalink.sdv"
Class = "CAN_data_link"
"""
)";
sdv::app::CAppControl appControl;
auto bResult = InitializeAppControl(&appControl, "test_manual_can_com_silkit.toml");
EXPECT_EQ(bResult, true);
std::string SilKitChannel = ExtractValue(testConfigFileContent, "CanSilKitChannel");
EXPECT_EQ(SilKitChannel, "CAN1");
// Check RegistryURI
std::string SilKitRegistryUri = ExtractValue(testConfigFileContent, "RegistryURI");
EXPECT_EQ(SilKitRegistryUri, "silkit://localhost:8500");
// Check CanSilKitNetwork
std::string SilKitNetwork = ExtractValue(testConfigFileContent, "CanSilKitNetwork");
EXPECT_EQ(SilKitNetwork, "PrivateCAN");
// Check SyncMode
std::string SyncModeString = ExtractValue(testConfigFileContent, "SyncMode");
// Convert the extracted value to a bool
bool SilKitSyncMode = StringToBool(SyncModeString);
EXPECT_EQ(SilKitSyncMode, true);
// Check SilKitConfig
std::string SilKitJSONConfigContent = ExtractValue(testConfigFileContent, "SilKitConfig");
std::string ExpectedConfigContent = R"({
Logging: {
Sinks: [ { Type: Stdout, Level: Info } ]
},
})";
EXPECT_EQ(ReplaceWhitespaceWithSingleSpaceAndEscapeQuotes(SilKitJSONConfigContent), ReplaceWhitespaceWithSingleSpaceAndEscapeQuotes(ExpectedConfigContent));
// Create an object of the CDummyCANSilKit class
CDummyCANSilKit canComObj;
canComObj.Initialize("");
sdv::can::ISend* pCanSend = &canComObj;
EXPECT_NE(pCanSend, nullptr);
sdv::can::IRegisterReceiver* pCanRegRcvr = &canComObj;
EXPECT_NE(pCanRegRcvr, nullptr);
MockCANReceiver mockRcv;
EXPECT_NO_THROW(pCanRegRcvr->RegisterReceiver(&mockRcv));
appControl.SetRunningMode();
for (int i = 0; i < 5; ++i)
{
// Send a message
EXPECT_NO_THROW(pCanSend->Send(testMsg, 0));
// Wait for the message to be received
std::unique_lock<std::mutex> lock(canComObj.m_mtx);
canComObj.m_cv.wait(lock, [&] { return canComObj.m_messageSent; });
// Verify received messages
ASSERT_FALSE(mockRcv.vecReceivedMessages.empty());
auto rcvMsg = mockRcv.vecReceivedMessages.front();
EXPECT_EQ(rcvMsg.uiID, testMsg.uiID);
EXPECT_EQ(rcvMsg.seqData, testMsg.seqData);
// Reset the flag for the next iteration
canComObj.m_messageSent = false;
mockRcv.vecReceivedMessages.clear();
}
EXPECT_NO_THROW(pCanRegRcvr->UnregisterReceiver(&mockRcv));
// Ensure proper shutdown
canComObj.Shutdown();
}
TEST_F(CANCommunicationTest, ExtractAndCompareSignals)
{
// Dummy test message
sdv::can::SMessage testMsg;
testMsg.uiID = 0x1234;
testMsg.bCanFd = false;
testMsg.bExtended = false;
testMsg.seqData = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x77, 0x88};
sdv::app::CAppControl appControl;
auto bResult = InitializeAppControl(&appControl, "test_manual_can_com_silkit.toml");
EXPECT_EQ(bResult, true);
// Create a normal object of the CDummyCANSilKit class
CDummyCANSilKit canComObj;
canComObj.Initialize("");
sdv::can::ISend* pCanSend = &canComObj;
EXPECT_NE(pCanSend, nullptr);
sdv::can::IRegisterReceiver* pCanRegRcvr = &canComObj;
EXPECT_NE(pCanRegRcvr, nullptr);
MockCANReceiver mockRcv;
EXPECT_NO_THROW(pCanRegRcvr->RegisterReceiver(&mockRcv));
appControl.SetRunningMode();
for (int i = 0; i < 5; ++i)
{
// Send a message
EXPECT_NO_THROW(pCanSend->Send(testMsg, 0));
// Wait for the message to be received
std::unique_lock<std::mutex> lock(canComObj.m_mtx);
canComObj.m_cv.wait(lock, [&] { return canComObj.m_messageSent; });
// Verify received messages
ASSERT_FALSE(mockRcv.vecReceivedMessages.empty());
auto rcvMsg = mockRcv.vecReceivedMessages.front();
EXPECT_EQ(rcvMsg.uiID, testMsg.uiID);
EXPECT_EQ(rcvMsg.seqData, testMsg.seqData);
// Extract and compare signals
uint32_t sentSpeed = (testMsg.seqData[6] << 8) | testMsg.seqData[7];
uint32_t receivedSpeed = (rcvMsg.seqData[6] << 8) | rcvMsg.seqData[7];
EXPECT_EQ(sentSpeed, receivedSpeed);
int32_t sentSteeringWheel = (testMsg.seqData[4] << 8) | testMsg.seqData[5];
int32_t receivedSteeringWheel = (rcvMsg.seqData[4] << 8) | rcvMsg.seqData[5];
EXPECT_EQ(sentSteeringWheel, receivedSteeringWheel);
// Reset the flag for the next iteration
canComObj.m_messageSent = false;
mockRcv.vecReceivedMessages.clear();
}
EXPECT_NO_THROW(pCanRegRcvr->UnregisterReceiver(&mockRcv));
// Ensure proper shutdown
canComObj.Shutdown();
}
extern "C" int main(int argc, char* argv[])
{
CProcessWatchdog watchdog;
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,67 @@
#include "../include/can_com_test_helper.h"
void ReceiveData()
{
sdv::app::CAppControl appControl;
appControl.Startup("");
sdv::u8string config = R"(
DebugInfo = true
SyncMode = true
SilKitParticipantName = "can_reader"
CanSilKitNetwork = "PrivateCAN"
RegistryURI = "silkit://localhost:8500"
SilKitConfig = """{
"Logging": {
"Sinks": [ { "Type": "Stdout", "Level": "Info" } ]
},
}"""
)";
CTestCanSilKit canComObj;
canComObj.Initialize(config.c_str());
canComObj.SetOperationMode(sdv::EOperationMode::configuring);
CanReceiver mockRcv;
canComObj.RegisterReceiver(&mockRcv);
canComObj.SetOperationMode(sdv::EOperationMode::running);
while(true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
auto receivedMessages = mockRcv.GetReceivedMessages();
if(receivedMessages.size() != 0)
{
for(auto rcvdMsg : receivedMessages)
{
std::stringstream buffer;
buffer << "\n[RX] CAN message being received [CANID=";
buffer << rcvdMsg.uiID; buffer << ", Length=" ;
buffer << unsigned(rcvdMsg.seqData.size()); buffer << "]" ;
buffer << " Data=";
for(uint16_t i = 0; i < rcvdMsg.seqData.size(); i++)
{
buffer << "[" ; buffer << std::hex << unsigned(rcvdMsg.seqData[i]); buffer << "] ";
}
std::cout << buffer.str().c_str();
mockRcv.ClearReceivedMessages();
}
}
else
{
std::cout << "[RX] Received Message size is " << receivedMessages.size() << std::endl;
}
}
canComObj.UnregisterReceiver(&mockRcv);
std::cout << "CAN has read all messages\n";
canComObj.Shutdown();
}
int main()
{
ReceiveData();
return 0;
}

View File

@@ -0,0 +1,59 @@
#include "../include/can_com_test_helper.h"
int main()
{
sdv::app::CAppControl appControl;
appControl.Startup("");
sdv::u8string config = R"(
DebugInfo = true
SyncMode = true
SilKitParticipantName = "can_writer"
CanSilKitNetwork = "PrivateCAN"
RegistryURI = "silkit://localhost:8500"
SilKitConfig = """{
"Logging": {
"Sinks": [ { "Type": "Stdout", "Level": "Info" } ]
},
}"""
)";
CTestCanSilKit canComObj;
canComObj.Initialize(config.c_str());
canComObj.SetOperationMode(sdv::EOperationMode::running);
uint8_t msgId = 65;
uint8_t dataSize = 1;
uint16_t i = 0;
std::cout << "Can writes 30 different datas:\n";
CComTestHelper testHelper;
while(i < 30) //Sending 30 messages
{
auto testData = testHelper.CreateTestData(msgId, dataSize);
canComObj.Send(testData, 0);
if(testData.seqData.size() != 0)
{
std::stringstream buffer;
buffer << "\n[TX] CAN message being sent [CANID=";
buffer << testData.uiID; buffer << ", Length=" ;
buffer << unsigned(testData.seqData.size()); buffer << "]" ;
buffer << " Data=";
for(uint16_t j = 0; j < testData.seqData.size(); j++)
{
buffer << "[" ; buffer << std::hex << unsigned(testData.seqData[j]); buffer << "] ";
}
std::cout << buffer.str().c_str();
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
msgId++;
i++;
dataSize++;
if(dataSize > 8)
dataSize = 1;
}
canComObj.Shutdown();
return 0;
}