Files
openvehicle-api/tests/component_tests/dbc_util/dbc_util_test.cpp

3525 lines
140 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include <iostream>
#include <ostream>
#include <fstream>
#include <mutex>
#include <thread>
#include <utility>
#include <atomic>
#include <chrono>
#include <support/signal_support.h>
#include <interfaces/dispatch.h>
#include <support/app_control.h>
#include <support/timer.h>
2026-01-16 11:40:02 +01:00
#include "../../include/sdv_test_macro.h"
#include "../../../global/process_watchdog.h"
#include "../global/ascformat/ascreader.cpp"
#include "../global/ascformat/ascwriter.cpp"
#include "../global/exec_dir_helper.h"
#define CDataLink CAscReaderDataLink
#define SObjectClassInstance_CDataLink SObjectClassInstance_CAscReaderDataLink
#define g_CDataLink g_CAscReaderDataLink
#undef CDataLink
#undef SObjectClassInstance_CDataLink
#undef g_CDataLink
#define CDataLink CDbcStructDataLink
#define SObjectClassInstance_CDataLink SObjectClassInstance_CDbcStructDataLink
#define g_CDataLink g_CSbcStructDataLink
#include "generated/dbc_struct_test/can_dl/datalink.cpp"
#undef CDataLink
#undef SObjectClassInstance_CDataLink
#undef g_CDataLink
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int argc, wchar_t* argv[])
#else
extern "C" int main(int argc, char* argv[])
#endif
{
CProcessWatchdog watchdog;
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
TEST(DbcUtilCanDLTest, Prerequisitives)
{
try
{
std::filesystem::remove(GetExecDirectory() / "asc_writer_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "asc_writer_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_asc.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
sdv::TInterfaceAccessPtr ptrDispatchService = sdv::core::GetObject("DataDispatchService");
EXPECT_TRUE(ptrDispatchService);
sdv::TInterfaceAccessPtr ptrCanComObj = sdv::core::GetObject("CAN_Communication_Object");
EXPECT_TRUE(ptrCanComObj);
appcontrol.Shutdown();
}
TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalBE1 = dispatch.AddPublisher("TestBE.SignalBE1");
EXPECT_TRUE(signalBE1);
sdv::core::CSignal signalBE2 = dispatch.AddPublisher("TestBE.SignalBE2");
EXPECT_TRUE(signalBE2);
sdv::core::CSignal signalBE3 = dispatch.AddPublisher("TestBE.SignalBE3");
EXPECT_TRUE(signalBE3);
sdv::core::CSignal signalBE4 = dispatch.AddPublisher("TestBE.SignalBE4");
EXPECT_TRUE(signalBE4);
sdv::core::CSignal signalBE5 = dispatch.AddPublisher("TestBE.SignalBE5");
EXPECT_TRUE(signalBE5);
sdv::core::CSignal signalBE6 = dispatch.AddPublisher("TestBE.SignalBE6");
EXPECT_TRUE(signalBE6);
appcontrol.SetRunningMode();
EXPECT_NO_THROW(signalBE1.Write(0xa));
EXPECT_NO_THROW(signalBE2.Write(0x5d));
EXPECT_NO_THROW(signalBE3.Write(0x31));
EXPECT_NO_THROW(signalBE4.Write(0x2d));
EXPECT_NO_THROW(signalBE5.Write(0x96));
EXPECT_NO_THROW(signalBE6.Write(0x33cc55aa));
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalBE1.Reset();
signalBE2.Reset();
signalBE3.Reset();
signalBE4.Reset();
signalBE5.Reset();
signalBE6.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0x6, {0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xDC, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xDC, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xDC, 0x6D, 0x96, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xDC, 0x6D, 0x96, 0x33, 0xCC, 0x55, 0xAA}}
};
// Compare with test data.
for (const STestData& sData : rgsData)
{
EXPECT_FALSE(reader.IsEOF());
std::pair<asc::SCanMessage, bool> prMsg;
do {
prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
} while (!reader.IsEOF() && prMsg.first.uiId != sData.uiID); // Skip other messages
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, sData.uiID);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.uiLength, 8ul);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.rguiData[0], sData.rguiData[0]);
EXPECT_EQ(prMsg.first.rguiData[1], sData.rguiData[1]);
EXPECT_EQ(prMsg.first.rguiData[2], sData.rguiData[2]);
EXPECT_EQ(prMsg.first.rguiData[3], sData.rguiData[3]);
EXPECT_EQ(prMsg.first.rguiData[4], sData.rguiData[4]);
EXPECT_EQ(prMsg.first.rguiData[5], sData.rguiData[5]);
EXPECT_EQ(prMsg.first.rguiData[6], sData.rguiData[6]);
EXPECT_EQ(prMsg.first.rguiData[7], sData.rguiData[7]);
}
}
TEST(DbcUtilCanDLTest, SpontaneousTransmit64BitBigEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalInt64BE = dispatch.AddPublisher("TestInt64BE.SignalInt64BE");
EXPECT_TRUE(signalInt64BE);
sdv::core::CSignal signalUint64BE = dispatch.AddPublisher("TestUint64BE.SignalUint64BE");
EXPECT_TRUE(signalUint64BE);
sdv::core::CSignal signalScaleInt64BE = dispatch.AddPublisher("TestScale64BE.SignalScaleInt64BE");
EXPECT_TRUE(signalScaleInt64BE);
appcontrol.SetRunningMode();
EXPECT_NO_THROW(signalInt64BE.Write(-6144092013047381999ll)); // 0xaabbccddeeff0011
EXPECT_NO_THROW(signalUint64BE.Write(0xaabbccddeeff0011));
EXPECT_NO_THROW(signalScaleInt64BE.Write(987.654321012)); // 0xE5F4C8F374
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalInt64BE.Reset();
signalUint64BE.Reset();
signalScaleInt64BE.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0xCB, {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11}},
{ 0x67, {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11}},
{ 0x1F7, {0x00, 0x00, 0x00, 0xE5, 0xF4, 0xC8, 0xF3, 0x74}}
};
// Compare with test data.
for (const STestData& sData : rgsData)
{
EXPECT_FALSE(reader.IsEOF());
std::pair<asc::SCanMessage, bool> prMsg;
do {
prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
} while (!reader.IsEOF() && prMsg.first.uiId != sData.uiID); // Skip other messages
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, sData.uiID);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.uiLength, 8ul);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.rguiData[0], sData.rguiData[0]);
EXPECT_EQ(prMsg.first.rguiData[1], sData.rguiData[1]);
EXPECT_EQ(prMsg.first.rguiData[2], sData.rguiData[2]);
EXPECT_EQ(prMsg.first.rguiData[3], sData.rguiData[3]);
EXPECT_EQ(prMsg.first.rguiData[4], sData.rguiData[4]);
EXPECT_EQ(prMsg.first.rguiData[5], sData.rguiData[5]);
EXPECT_EQ(prMsg.first.rguiData[6], sData.rguiData[6]);
EXPECT_EQ(prMsg.first.rguiData[7], sData.rguiData[7]);
}
}
TEST(DbcUtilCanDLTest, ReceiveBigEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0x6, {0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xDC, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xDC, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xDC, 0x6D, 0x96, 0x00, 0x00, 0x00, 0x00}},
{ 0x6, {0xA5, 0xDC, 0x6D, 0x96, 0x33, 0xCC, 0x55, 0xAA}}
};
// Create ASC file with a delay of 100 (to allow the system to initialize)
asc::CAscWriter writer;
double dTimestamp = 0.100;
for (const STestData& sData : rgsData)
{
asc::SCanMessage sMsg{};
sMsg.dTimestamp = dTimestamp;
dTimestamp += 0.001;
sMsg.uiChannel = 1ul;
sMsg.uiId = sData.uiID;
sMsg.bExtended = false;
sMsg.bCanFd = false;
sMsg.uiLength = 8ul;
sMsg.eDirection = asc::SCanMessage::EDirection::rx;
sMsg.rguiData[0] = sData.rguiData[0];
sMsg.rguiData[1] = sData.rguiData[1];
sMsg.rguiData[2] = sData.rguiData[2];
sMsg.rguiData[3] = sData.rguiData[3];
sMsg.rguiData[4] = sData.rguiData[4];
sMsg.rguiData[5] = sData.rguiData[5];
sMsg.rguiData[6] = sData.rguiData[6];
sMsg.rguiData[7] = sData.rguiData[7];
writer.AddSample(sMsg);
}
// Add a dummy message with a big timegap to prevent the repetition function to jump in.
asc::SCanMessage sMsgDummy{};
sMsgDummy.dTimestamp = 100.0;
sMsgDummy.uiChannel = 1ul;
sMsgDummy.uiId = 999;
writer.AddSample(sMsgDummy);
// Write the ASC file
EXPECT_TRUE(writer.Write(GetExecDirectory() / "receiver_test.asc"));
writer.Clear();
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_rx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
uint32_t rguiVal[6] = {};
size_t nCnt = 0;
sdv::core::CSignal signalBE1 = dispatch.Subscribe("TestBE.SignalBE1", [&](sdv::any_t tVal) {rguiVal[0] = tVal; nCnt++; });
EXPECT_TRUE(signalBE1);
sdv::core::CSignal signalBE2 = dispatch.Subscribe("TestBE.SignalBE2", [&](sdv::any_t tVal) {rguiVal[1] = tVal; nCnt++; });
EXPECT_TRUE(signalBE2);
sdv::core::CSignal signalBE3 = dispatch.Subscribe("TestBE.SignalBE3", [&](sdv::any_t tVal) {rguiVal[2] = tVal; nCnt++; });
EXPECT_TRUE(signalBE3);
sdv::core::CSignal signalBE4 = dispatch.Subscribe("TestBE.SignalBE4", [&](sdv::any_t tVal) {rguiVal[3] = tVal; nCnt++; });
EXPECT_TRUE(signalBE4);
sdv::core::CSignal signalBE5 = dispatch.Subscribe("TestBE.SignalBE5", [&](sdv::any_t tVal) {rguiVal[4] = tVal; nCnt++; });
EXPECT_TRUE(signalBE5);
sdv::core::CSignal signalBE6 = dispatch.Subscribe("TestBE.SignalBE6", [&](sdv::any_t tVal) {rguiVal[5] = tVal; nCnt++; });
EXPECT_TRUE(signalBE6);
appcontrol.SetRunningMode();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
EXPECT_EQ(nCnt, 36u);
EXPECT_EQ(rguiVal[0], 0xau);
EXPECT_EQ(rguiVal[1], 0x5du);
EXPECT_EQ(rguiVal[2], 0x31u);
EXPECT_EQ(rguiVal[3], 0x2du);
EXPECT_EQ(rguiVal[4], 0x96u);
EXPECT_EQ(rguiVal[5], 0x33cc55aau);
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalBE1.Reset();
signalBE2.Reset();
signalBE3.Reset();
signalBE4.Reset();
signalBE5.Reset();
signalBE6.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
}
TEST(DbcUtilCanDLTest, Receive64BitBigEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0xCB, {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11}},
{ 0x67, {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11}},
{ 0x1F7, {0x00, 0x00, 0x00, 0xE5, 0xF4, 0xC8, 0xF3, 0x74}}
};
// Create ASC file with a delay of 100 (to allow the system to initialize)
asc::CAscWriter writer;
double dTimestamp = 0.100;
for (const STestData& sData : rgsData)
{
asc::SCanMessage sMsg{};
sMsg.dTimestamp = dTimestamp;
dTimestamp += 0.001;
sMsg.uiChannel = 1ul;
sMsg.uiId = sData.uiID;
sMsg.bExtended = false;
sMsg.bCanFd = false;
sMsg.uiLength = 8ul;
sMsg.eDirection = asc::SCanMessage::EDirection::rx;
sMsg.rguiData[0] = sData.rguiData[0];
sMsg.rguiData[1] = sData.rguiData[1];
sMsg.rguiData[2] = sData.rguiData[2];
sMsg.rguiData[3] = sData.rguiData[3];
sMsg.rguiData[4] = sData.rguiData[4];
sMsg.rguiData[5] = sData.rguiData[5];
sMsg.rguiData[6] = sData.rguiData[6];
sMsg.rguiData[7] = sData.rguiData[7];
writer.AddSample(sMsg);
}
// Add a dummy message with a big timegap to prevent the repetition function to jump in.
asc::SCanMessage sMsgDummy{};
sMsgDummy.dTimestamp = 100.0;
sMsgDummy.uiChannel = 1ul;
sMsgDummy.uiId = 999;
writer.AddSample(sMsgDummy);
// Write the ASC file
EXPECT_TRUE(writer.Write(GetExecDirectory() / "receiver_test.asc"));
writer.Clear();
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_rx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
int64_t iInt64Val = 0;
uint64_t uiUint64Val = 0u;
double dScaleInt64Val = 0;
sdv::core::CSignal signalInt64BE = dispatch.Subscribe("TestInt64BE.SignalInt64BE", [&](sdv::any_t tVal) {iInt64Val = tVal; });
EXPECT_TRUE(signalInt64BE);
sdv::core::CSignal signalUint64BE = dispatch.Subscribe("TestUint64BE.SignalUint64BE", [&](sdv::any_t tVal) {uiUint64Val = tVal; });
EXPECT_TRUE(signalUint64BE);
sdv::core::CSignal signalScaleInt64BE = dispatch.Subscribe("TestScale64BE.SignalScaleInt64BE", [&](sdv::any_t tVal) {dScaleInt64Val = tVal; });
EXPECT_TRUE(signalScaleInt64BE);
appcontrol.SetRunningMode();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
EXPECT_EQ(iInt64Val, -6144092013047381999ll);
EXPECT_EQ(uiUint64Val, 0xaabbccddeeff0011);
EXPECT_EQ(round(dScaleInt64Val * 1e8), round(987.654321012 * 1e8));
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalInt64BE.Reset();
signalUint64BE.Reset();
signalScaleInt64BE.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
}
TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalLE1 = dispatch.AddPublisher("TestLE.SignalLE1");
EXPECT_TRUE(signalLE1);
sdv::core::CSignal signalLE2 = dispatch.AddPublisher("TestLE.SignalLE2");
EXPECT_TRUE(signalLE2);
sdv::core::CSignal signalLE3 = dispatch.AddPublisher("TestLE.SignalLE3");
EXPECT_TRUE(signalLE3);
sdv::core::CSignal signalLE4 = dispatch.AddPublisher("TestLE.SignalLE4");
EXPECT_TRUE(signalLE4);
sdv::core::CSignal signalLE5 = dispatch.AddPublisher("TestLE.SignalLE5");
EXPECT_TRUE(signalLE5);
sdv::core::CSignal signalLE6 = dispatch.AddPublisher("TestLE.SignalLE6");
EXPECT_TRUE(signalLE6);
appcontrol.SetRunningMode();
EXPECT_NO_THROW(signalLE1.Write(0xa));
EXPECT_NO_THROW(signalLE2.Write(0x5d));
EXPECT_NO_THROW(signalLE3.Write(0x31));
EXPECT_NO_THROW(signalLE4.Write(0x2d));
EXPECT_NO_THROW(signalLE5.Write(0x96));
EXPECT_NO_THROW(signalLE6.Write(0x33cc55aa));
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalLE1.Reset();
signalLE2.Reset();
signalLE3.Reset();
signalLE4.Reset();
signalLE5.Reset();
signalLE6.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0x7, {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x15, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x15, 0xB7, 0x96, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x15, 0xB7, 0x96, 0xAA, 0x55, 0xCC, 0x33}}
};
// Compare with test data.
for (const STestData& sData : rgsData)
{
EXPECT_FALSE(reader.IsEOF());
std::pair<asc::SCanMessage, bool> prMsg;
do {
prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
} while (!reader.IsEOF() && prMsg.first.uiId != sData.uiID); // Skip other messages
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, sData.uiID);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.uiLength, 8ul);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.rguiData[0], sData.rguiData[0]);
EXPECT_EQ(prMsg.first.rguiData[1], sData.rguiData[1]);
EXPECT_EQ(prMsg.first.rguiData[2], sData.rguiData[2]);
EXPECT_EQ(prMsg.first.rguiData[3], sData.rguiData[3]);
EXPECT_EQ(prMsg.first.rguiData[4], sData.rguiData[4]);
EXPECT_EQ(prMsg.first.rguiData[5], sData.rguiData[5]);
EXPECT_EQ(prMsg.first.rguiData[6], sData.rguiData[6]);
EXPECT_EQ(prMsg.first.rguiData[7], sData.rguiData[7]);
}
}
TEST(DbcUtilCanDLTest, SpontaneousTransmit64BitLittleEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalInt64LE = dispatch.AddPublisher("TestInt64LE.SignalInt64LE");
EXPECT_TRUE(signalInt64LE);
sdv::core::CSignal signalUint64LE = dispatch.AddPublisher("TestUint64LE.SignalUint64LE");
EXPECT_TRUE(signalUint64LE);
sdv::core::CSignal signalScaleInt64LE = dispatch.AddPublisher("TestScale64LE.SignalScaleInt64LE");
EXPECT_TRUE(signalScaleInt64LE);
appcontrol.SetRunningMode();
EXPECT_NO_THROW(signalInt64LE.Write(-6144092013047381999ll)); // 0xaabbccddeeff0011
EXPECT_NO_THROW(signalUint64LE.Write(0xaabbccddeeff0011));
EXPECT_NO_THROW(signalScaleInt64LE.Write(987.654321012)); // 0xE5F4C8F374
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalInt64LE.Reset();
signalUint64LE.Reset();
signalScaleInt64LE.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0xCA, {0x11, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA}},
{ 0x66, {0x11, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA}},
{ 0x1F6, {0x74, 0xF3, 0xC8, 0xF4, 0xE5, 0x00, 0x00, 0x00}}
};
// Compare with test data.
for (const STestData& sData : rgsData)
{
EXPECT_FALSE(reader.IsEOF());
std::pair<asc::SCanMessage, bool> prMsg;
do {
prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
} while (!reader.IsEOF() && prMsg.first.uiId != sData.uiID); // Skip other messages
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, sData.uiID);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.uiLength, 8ul);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.rguiData[0], sData.rguiData[0]);
EXPECT_EQ(prMsg.first.rguiData[1], sData.rguiData[1]);
EXPECT_EQ(prMsg.first.rguiData[2], sData.rguiData[2]);
EXPECT_EQ(prMsg.first.rguiData[3], sData.rguiData[3]);
EXPECT_EQ(prMsg.first.rguiData[4], sData.rguiData[4]);
EXPECT_EQ(prMsg.first.rguiData[5], sData.rguiData[5]);
EXPECT_EQ(prMsg.first.rguiData[6], sData.rguiData[6]);
EXPECT_EQ(prMsg.first.rguiData[7], sData.rguiData[7]);
}
}
TEST(DbcUtilCanDLTest, ReceiveLittleEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0x7, {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x15, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x15, 0xB7, 0x96, 0x00, 0x00, 0x00, 0x00}},
{ 0x7, {0xDA, 0x15, 0xB7, 0x96, 0xAA, 0x55, 0xCC, 0x33}}
};
// Create ASC file with a delay of 100 (to allow the system to initialize)
asc::CAscWriter writer;
double dTimestamp = 0.100;
for (const STestData& sData : rgsData)
{
asc::SCanMessage sMsg{};
sMsg.dTimestamp = dTimestamp;
dTimestamp += 0.001;
sMsg.uiChannel = 1ul;
sMsg.uiId = sData.uiID;
sMsg.bExtended = false;
sMsg.bCanFd = false;
sMsg.uiLength = 8ul;
sMsg.eDirection = asc::SCanMessage::EDirection::rx;
sMsg.rguiData[0] = sData.rguiData[0];
sMsg.rguiData[1] = sData.rguiData[1];
sMsg.rguiData[2] = sData.rguiData[2];
sMsg.rguiData[3] = sData.rguiData[3];
sMsg.rguiData[4] = sData.rguiData[4];
sMsg.rguiData[5] = sData.rguiData[5];
sMsg.rguiData[6] = sData.rguiData[6];
sMsg.rguiData[7] = sData.rguiData[7];
writer.AddSample(sMsg);
}
// Add a dummy message with a big timegap to prevent the repetition function to jump in.
asc::SCanMessage sMsgDummy{};
sMsgDummy.dTimestamp = 100.0;
sMsgDummy.uiChannel = 1ul;
sMsgDummy.uiId = 999;
writer.AddSample(sMsgDummy);
// Write the ASC file
EXPECT_TRUE(writer.Write(GetExecDirectory() / "receiver_test.asc"));
writer.Clear();
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_rx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
uint32_t rguiVal[6] = {};
size_t nCnt = 0;
sdv::core::CSignal signalLE1 = dispatch.Subscribe("TestLE.SignalLE1", [&](sdv::any_t tVal) {rguiVal[0] = tVal; nCnt++; });
EXPECT_TRUE(signalLE1);
sdv::core::CSignal signalLE2 = dispatch.Subscribe("TestLE.SignalLE2", [&](sdv::any_t tVal) {rguiVal[1] = tVal; nCnt++; });
EXPECT_TRUE(signalLE2);
sdv::core::CSignal signalLE3 = dispatch.Subscribe("TestLE.SignalLE3", [&](sdv::any_t tVal) {rguiVal[2] = tVal; nCnt++; });
EXPECT_TRUE(signalLE3);
sdv::core::CSignal signalLE4 = dispatch.Subscribe("TestLE.SignalLE4", [&](sdv::any_t tVal) {rguiVal[3] = tVal; nCnt++; });
EXPECT_TRUE(signalLE4);
sdv::core::CSignal signalLE5 = dispatch.Subscribe("TestLE.SignalLE5", [&](sdv::any_t tVal) {rguiVal[4] = tVal; nCnt++; });
EXPECT_TRUE(signalLE5);
sdv::core::CSignal signalLE6 = dispatch.Subscribe("TestLE.SignalLE6", [&](sdv::any_t tVal) {rguiVal[5] = tVal; nCnt++; });
EXPECT_TRUE(signalLE6);
appcontrol.SetRunningMode();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
EXPECT_EQ(nCnt, 36u);
EXPECT_EQ(rguiVal[0], 0xau);
EXPECT_EQ(rguiVal[1], 0x5du);
EXPECT_EQ(rguiVal[2], 0x31u);
EXPECT_EQ(rguiVal[3], 0x2du);
EXPECT_EQ(rguiVal[4], 0x96u);
EXPECT_EQ(rguiVal[5], 0x33cc55aau);
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalLE1.Reset();
signalLE2.Reset();
signalLE3.Reset();
signalLE4.Reset();
signalLE5.Reset();
signalLE6.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
}
TEST(DbcUtilCanDLTest, Receive64BitLittleEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0xCA, {0x11, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA}},
{ 0x66, {0x11, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA}},
{ 0x1F6, {0x74, 0xF3, 0xC8, 0xF4, 0xE5, 0x00, 0x00, 0x00}}
};
// Create ASC file with a delay of 100 (to allow the system to initialize)
asc::CAscWriter writer;
double dTimestamp = 0.100;
for (const STestData& sData : rgsData)
{
asc::SCanMessage sMsg{};
sMsg.dTimestamp = dTimestamp;
dTimestamp += 0.001;
sMsg.uiChannel = 1ul;
sMsg.uiId = sData.uiID;
sMsg.bExtended = false;
sMsg.bCanFd = false;
sMsg.uiLength = 8ul;
sMsg.eDirection = asc::SCanMessage::EDirection::rx;
sMsg.rguiData[0] = sData.rguiData[0];
sMsg.rguiData[1] = sData.rguiData[1];
sMsg.rguiData[2] = sData.rguiData[2];
sMsg.rguiData[3] = sData.rguiData[3];
sMsg.rguiData[4] = sData.rguiData[4];
sMsg.rguiData[5] = sData.rguiData[5];
sMsg.rguiData[6] = sData.rguiData[6];
sMsg.rguiData[7] = sData.rguiData[7];
writer.AddSample(sMsg);
}
// Add a dummy message with a big timegap to prevent the repetition function to jump in.
asc::SCanMessage sMsgDummy{};
sMsgDummy.dTimestamp = 100.0;
sMsgDummy.uiChannel = 1ul;
sMsgDummy.uiId = 999;
writer.AddSample(sMsgDummy);
// Write the ASC file
EXPECT_TRUE(writer.Write(GetExecDirectory() / "receiver_test.asc"));
writer.Clear();
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_rx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
int64_t iInt64Val = 0;
uint64_t uiUint64Val = 0u;
double dScaleInt64Val = 0;
sdv::core::CSignal signalInt64LE = dispatch.Subscribe("TestInt64LE.SignalInt64LE", [&](sdv::any_t tVal) {iInt64Val = tVal; });
EXPECT_TRUE(signalInt64LE);
sdv::core::CSignal signalUint64LE = dispatch.Subscribe("TestUint64LE.SignalUint64LE", [&](sdv::any_t tVal) {uiUint64Val = tVal; });
EXPECT_TRUE(signalUint64LE);
sdv::core::CSignal signalScaleInt64LE = dispatch.Subscribe("TestScale64LE.SignalScaleInt64LE", [&](sdv::any_t tVal) {dScaleInt64Val = tVal; });
EXPECT_TRUE(signalScaleInt64LE);
appcontrol.SetRunningMode();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
EXPECT_EQ(iInt64Val, -6144092013047381999ll);
EXPECT_EQ(uiUint64Val, 0xaabbccddeeff0011);
EXPECT_EQ(round(dScaleInt64Val * 1e8), round(987.654321012 * 1e8));
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalInt64LE.Reset();
signalUint64LE.Reset();
signalScaleInt64LE.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
}
TEST(DbcUtilCanDLTest, SpontaneousTransactionalTransmitBigEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalBE1 = dispatch.AddPublisher("TestBE.SignalBE1");
EXPECT_TRUE(signalBE1);
sdv::core::CSignal signalBE2 = dispatch.AddPublisher("TestBE.SignalBE2");
EXPECT_TRUE(signalBE2);
sdv::core::CSignal signalBE3 = dispatch.AddPublisher("TestBE.SignalBE3");
EXPECT_TRUE(signalBE3);
sdv::core::CSignal signalBE4 = dispatch.AddPublisher("TestBE.SignalBE4");
EXPECT_TRUE(signalBE4);
sdv::core::CSignal signalBE5 = dispatch.AddPublisher("TestBE.SignalBE5");
EXPECT_TRUE(signalBE5);
sdv::core::CSignal signalBE6 = dispatch.AddPublisher("TestBE.SignalBE6");
EXPECT_TRUE(signalBE6);
appcontrol.SetRunningMode();
sdv::core::CTransaction transaction = dispatch.CreateTransaction();
EXPECT_TRUE(transaction);
EXPECT_NO_THROW(signalBE1.Write(0xa, transaction));
EXPECT_NO_THROW(signalBE2.Write(0x5d, transaction));
EXPECT_NO_THROW(signalBE3.Write(0x31, transaction));
EXPECT_NO_THROW(signalBE4.Write(0x2d, transaction));
EXPECT_NO_THROW(signalBE5.Write(0x96, transaction));
EXPECT_NO_THROW(signalBE6.Write(0x33cc55aa, transaction));
transaction.Finish();
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalBE1.Reset();
signalBE2.Reset();
signalBE3.Reset();
signalBE4.Reset();
signalBE5.Reset();
signalBE6.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
// Get first message
std::pair<asc::SCanMessage, bool> prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, 6ul);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.uiLength, 8ul);
EXPECT_EQ(prMsg.first.rguiData[0], 0xa5);
EXPECT_EQ(prMsg.first.rguiData[1], 0xdC);
EXPECT_EQ(prMsg.first.rguiData[2], 0x6d);
EXPECT_EQ(prMsg.first.rguiData[3], 0x96);
EXPECT_EQ(prMsg.first.rguiData[4], 0x33);
EXPECT_EQ(prMsg.first.rguiData[5], 0xcc);
EXPECT_EQ(prMsg.first.rguiData[6], 0x55);
EXPECT_EQ(prMsg.first.rguiData[7], 0xaa);
}
TEST(DbcUtilCanDLTest, SpontaneousTransactionalTransmitLittleEndian)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalLE1 = dispatch.AddPublisher("TestLE.SignalLE1");
EXPECT_TRUE(signalLE1);
sdv::core::CSignal signalLE2 = dispatch.AddPublisher("TestLE.SignalLE2");
EXPECT_TRUE(signalLE2);
sdv::core::CSignal signalLE3 = dispatch.AddPublisher("TestLE.SignalLE3");
EXPECT_TRUE(signalLE3);
sdv::core::CSignal signalLE4 = dispatch.AddPublisher("TestLE.SignalLE4");
EXPECT_TRUE(signalLE4);
sdv::core::CSignal signalLE5 = dispatch.AddPublisher("TestLE.SignalLE5");
EXPECT_TRUE(signalLE5);
sdv::core::CSignal signalLE6 = dispatch.AddPublisher("TestLE.SignalLE6");
EXPECT_TRUE(signalLE6);
appcontrol.SetRunningMode();
sdv::core::CTransaction transaction = dispatch.CreateTransaction();
EXPECT_TRUE(transaction);
EXPECT_NO_THROW(signalLE1.Write(0xa, transaction));
EXPECT_NO_THROW(signalLE2.Write(0x5d, transaction));
EXPECT_NO_THROW(signalLE3.Write(0x31, transaction));
EXPECT_NO_THROW(signalLE4.Write(0x2d, transaction));
EXPECT_NO_THROW(signalLE5.Write(0x96, transaction));
EXPECT_NO_THROW(signalLE6.Write(0x33cc55aa, transaction));
transaction.Finish();
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalLE1.Reset();
signalLE2.Reset();
signalLE3.Reset();
signalLE4.Reset();
signalLE5.Reset();
signalLE6.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
// Get first message
std::pair<asc::SCanMessage, bool> prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, 7ul);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.uiLength, 8ul);
EXPECT_EQ(prMsg.first.rguiData[0], 0xda);
EXPECT_EQ(prMsg.first.rguiData[1], 0x15);
EXPECT_EQ(prMsg.first.rguiData[2], 0xb7);
EXPECT_EQ(prMsg.first.rguiData[3], 0x96);
EXPECT_EQ(prMsg.first.rguiData[4], 0xaa);
EXPECT_EQ(prMsg.first.rguiData[5], 0x55);
EXPECT_EQ(prMsg.first.rguiData[6], 0xcc);
EXPECT_EQ(prMsg.first.rguiData[7], 0x33);
}
TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndianAllDataTypes)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to integer based signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalIntBE1 = dispatch.AddPublisher("TestIntBE.SignalIntBE1");
EXPECT_TRUE(signalIntBE1);
sdv::core::CSignal signalIntBE2 = dispatch.AddPublisher("TestIntBE.SignalIntBE2");
EXPECT_TRUE(signalIntBE2);
sdv::core::CSignal signalIntBE3 = dispatch.AddPublisher("TestIntBE.SignalIntBE3");
EXPECT_TRUE(signalIntBE3);
sdv::core::CSignal signalIntBE4 = dispatch.AddPublisher("TestIntBE.SignalIntBE4");
EXPECT_TRUE(signalIntBE4);
sdv::core::CSignal signalIntBE5 = dispatch.AddPublisher("TestIntBE.SignalIntBE5");
EXPECT_TRUE(signalIntBE5);
sdv::core::CSignal signalIntBE6 = dispatch.AddPublisher("TestIntBE.SignalIntBE6");
EXPECT_TRUE(signalIntBE6);
// Subscribe to unsigned integer based signals
sdv::core::CSignal signalUintBE1 = dispatch.AddPublisher("TestUintBE.SignalUintBE1");
EXPECT_TRUE(signalUintBE1);
sdv::core::CSignal signalUintBE2 = dispatch.AddPublisher("TestUintBE.SignalUintBE2");
EXPECT_TRUE(signalUintBE2);
sdv::core::CSignal signalUintBE3 = dispatch.AddPublisher("TestUintBE.SignalUintBE3");
EXPECT_TRUE(signalUintBE3);
sdv::core::CSignal signalUintBE4 = dispatch.AddPublisher("TestUintBE.SignalUintBE4");
EXPECT_TRUE(signalUintBE4);
sdv::core::CSignal signalUintBE5 = dispatch.AddPublisher("TestUintBE.SignalUintBE5");
EXPECT_TRUE(signalUintBE5);
sdv::core::CSignal signalUintBE6 = dispatch.AddPublisher("TestUintBE.SignalUintBE6");
EXPECT_TRUE(signalUintBE6);
// Subscribe to floating point based signals
sdv::core::CSignal signalFloatBE1 = dispatch.AddPublisher("TestFloatBE.SignalFloatBE1");
EXPECT_TRUE(signalFloatBE1);
sdv::core::CSignal signalFloatBE2 = dispatch.AddPublisher("TestFloatBE.SignalFloatBE2");
EXPECT_TRUE(signalFloatBE2);
// Subscribe to double precision floating point based signals
sdv::core::CSignal signalDoubleBE1 = dispatch.AddPublisher("TestDoubleBE.SignalDoubleBE1");
EXPECT_TRUE(signalDoubleBE1);
appcontrol.SetRunningMode();
for (size_t n = 10; n < 20; n++)
{
sdv::core::CTransaction transaction = dispatch.CreateTransaction();
signalIntBE1.Write((static_cast<int32_t>(n) & 0b1) * (n & 1 ? -1 : 1), transaction); // 2 bits incl. sign
signalIntBE2.Write((static_cast<int32_t>(n) & 0b1111) * (n & 1 ? -1 : 1), transaction); // 5 bits incl. sign
signalIntBE3.Write((static_cast<int32_t>(n) & 0b11111) * (n & 1 ? -1 : 1), transaction); // 6 bits incl. sign
signalIntBE4.Write((static_cast<int32_t>(n) & 0b11) * (n & 1 ? -1 : 1), transaction); // 3 bits incl. sign
signalIntBE5.Write((static_cast<int32_t>(n)) * (n & 1 ? -1 : 1), transaction); // 18 bits incl. sign
signalIntBE6.Write((static_cast<int32_t>(n)) * (n & 1 ? -1 : 1), transaction); // 30 bits incl. sign
transaction.Finish();
transaction = dispatch.CreateTransaction();
signalUintBE1.Write(static_cast<uint32_t>(n) & 0b1, transaction); // 1 bit
signalUintBE2.Write(static_cast<uint32_t>(n) & 0b11, transaction); // 2 bits
signalUintBE3.Write(static_cast<uint32_t>(n) & 0b111111, transaction); // 6 bits
signalUintBE4.Write(static_cast<uint32_t>(n) & 0b1111111, transaction); // 7 bits
signalUintBE5.Write(static_cast<uint32_t>(n), transaction); // 18 bits
signalUintBE6.Write(static_cast<uint32_t>(n), transaction); // 30 bits
transaction.Finish();
transaction = dispatch.CreateTransaction();
signalFloatBE1.Write(static_cast<float>(n) * 1.111f, transaction);
signalFloatBE2.Write(static_cast<float>(n) * 2.222f, transaction);
transaction.Finish();
transaction = dispatch.CreateTransaction();
signalDoubleBE1.Write(static_cast<double>(n) * 3.333, transaction);
transaction.Finish();
}
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalIntBE1.Reset();
signalIntBE2.Reset();
signalIntBE3.Reset();
signalIntBE4.Reset();
signalIntBE5.Reset();
signalIntBE6.Reset();
signalUintBE1.Reset();
signalUintBE2.Reset();
signalUintBE3.Reset();
signalUintBE4.Reset();
signalUintBE5.Reset();
signalUintBE6.Reset();
signalFloatBE1.Reset();
signalFloatBE2.Reset();
signalDoubleBE1.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0xC9 , {0x14, 0x52, 0x00, 0x02, 0x80, 0x00, 0x00, 0x0A}},
{ 0x65 , {0x45, 0x0A, 0x00, 0x02, 0x80, 0x00, 0x00, 0x0A}},
{ 0x12D, {0x41, 0x31, 0xC2, 0x8F, 0x41, 0xB1, 0xC2, 0x8F}},
{ 0x191, {0x40, 0x40, 0xAA, 0x3D, 0x70, 0xA3, 0xD7, 0x0A}},
{ 0xC9 , {0xEB, 0xAD, 0xFF, 0xFD, 0x7F, 0xFF, 0xFF, 0xF5}},
{ 0x65 , {0xE5, 0x8B, 0x00, 0x02, 0xC0, 0x00, 0x00, 0x0B}},
{ 0x12D, {0x41, 0x43, 0x89, 0x37, 0x41, 0xC3, 0x89, 0x37}},
{ 0x191, {0x40, 0x42, 0x54, 0xDD, 0x2F, 0x1A, 0x9F, 0xBF}},
{ 0xC9 , {0x18, 0x60, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C}},
{ 0x65 , {0x06, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C}},
{ 0x12D, {0x41, 0x55, 0x4F, 0xDE, 0x41, 0xD5, 0x4F, 0xDE}},
{ 0x191, {0x40, 0x43, 0xFF, 0x7C, 0xED, 0x91, 0x68, 0x73}},
{ 0xC9 , {0xE7, 0x9F, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF3}},
{ 0x65 , {0xA6, 0x8D, 0x00, 0x03, 0x40, 0x00, 0x00, 0x0D}},
{ 0x12D, {0x41, 0x67, 0x16, 0x86, 0x41, 0xE7, 0x16, 0x86}},
{ 0x191, {0x40, 0x45, 0xAA, 0x1C, 0xAC, 0x08, 0x31, 0x27}},
{ 0xC9 , {0x1C, 0x72, 0x00, 0x03, 0x80, 0x00, 0x00, 0x0E}},
{ 0x65 , {0x47, 0x0E, 0x00, 0x03, 0x80, 0x00, 0x00, 0x0E}},
{ 0x12D, {0x41, 0x78, 0xDD, 0x2E, 0x41, 0xF8, 0xDD, 0x2E}},
{ 0x191, {0x40, 0x47, 0x54, 0xBC, 0x6A, 0x7E, 0xF9, 0xDC}},
{ 0xC9 , {0xE3, 0x8D, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xF1}},
{ 0x65 , {0xE7, 0x8F, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x0F}},
{ 0x12D, {0x41, 0x85, 0x51, 0xEB, 0x42, 0x05, 0x51, 0xEB}},
{ 0x191, {0x40, 0x48, 0xFF, 0x5C, 0x28, 0xF5, 0xC2, 0x90}},
{ 0xC9 , {0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10}},
{ 0x65 , {0x08, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10}},
{ 0x12D, {0x41, 0x8E, 0x35, 0x3F, 0x42, 0x0E, 0x35, 0x3F}},
{ 0x191, {0x40, 0x4A, 0xA9, 0xFB, 0xE7, 0x6C, 0x8B, 0x44}},
{ 0xC9 , {0xFF, 0x7F, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xEF}},
{ 0x65 , {0xA8, 0x91, 0x00, 0x04, 0x40, 0x00, 0x00, 0x11}},
{ 0x12D, {0x41, 0x97, 0x18, 0x93, 0x42, 0x17, 0x18, 0x93}},
{ 0x191, {0x40, 0x4C, 0x54, 0x9B, 0xA5, 0xE3, 0x53, 0xF8}},
{ 0xC9 , {0x04, 0x92, 0x00, 0x04, 0x80, 0x00, 0x00, 0x12}},
{ 0x65 , {0x49, 0x12, 0x00, 0x04, 0x80, 0x00, 0x00, 0x12}},
{ 0x12D, {0x41, 0x9F, 0xFB, 0xE7, 0x42, 0x1F, 0xFB, 0xE7}},
{ 0x191, {0x40, 0x4D, 0xFF, 0x3B, 0x64, 0x5A, 0x1C, 0xAC}},
{ 0xC9 , {0xFB, 0x6D, 0xFF, 0xFB, 0x7F, 0xFF, 0xFF, 0xED}},
{ 0x65 , {0xE9, 0x93, 0x00, 0x04, 0xC0, 0x00, 0x00, 0x13}},
{ 0x12D, {0x41, 0xA8, 0xDF, 0x3B, 0x42, 0x28, 0xDF, 0x3B}},
{ 0x191, {0x40, 0x4F, 0xA9, 0xDB, 0x22, 0xD0, 0xE5, 0x61}}
};
// Compare with test data.
for (const STestData& sData : rgsData)
{
EXPECT_FALSE(reader.IsEOF());
std::pair<asc::SCanMessage, bool> prMsg;
do {
prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
} while (!reader.IsEOF() && prMsg.first.uiId != sData.uiID); // Skip other messages
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, sData.uiID);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.uiLength, 8ul);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.rguiData[0], sData.rguiData[0]);
EXPECT_EQ(prMsg.first.rguiData[1], sData.rguiData[1]);
EXPECT_EQ(prMsg.first.rguiData[2], sData.rguiData[2]);
EXPECT_EQ(prMsg.first.rguiData[3], sData.rguiData[3]);
EXPECT_EQ(prMsg.first.rguiData[4], sData.rguiData[4]);
EXPECT_EQ(prMsg.first.rguiData[5], sData.rguiData[5]);
EXPECT_EQ(prMsg.first.rguiData[6], sData.rguiData[6]);
EXPECT_EQ(prMsg.first.rguiData[7], sData.rguiData[7]);
}
}
TEST(DbcUtilCanDLTest, ReceiveBigEndianAllDataTypes)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0xC9 , {0x14, 0x52, 0x00, 0x02, 0x80, 0x00, 0x00, 0x0A}},
{ 0x65 , {0x45, 0x0A, 0x00, 0x02, 0x80, 0x00, 0x00, 0x0A}},
{ 0x12D, {0x41, 0x31, 0xC2, 0x8F, 0x41, 0xB1, 0xC2, 0x8F}},
{ 0x191, {0x40, 0x40, 0xAA, 0x3D, 0x70, 0xA3, 0xD7, 0x0A}},
{ 0xC9 , {0xEB, 0xAD, 0xFF, 0xFD, 0x7F, 0xFF, 0xFF, 0xF5}},
{ 0x65 , {0xE5, 0x8B, 0x00, 0x02, 0xC0, 0x00, 0x00, 0x0B}},
{ 0x12D, {0x41, 0x43, 0x89, 0x37, 0x41, 0xC3, 0x89, 0x37}},
{ 0x191, {0x40, 0x42, 0x54, 0xDD, 0x2F, 0x1A, 0x9F, 0xBF}},
{ 0xC9 , {0x18, 0x60, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C}},
{ 0x65 , {0x06, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C}},
{ 0x12D, {0x41, 0x55, 0x4F, 0xDE, 0x41, 0xD5, 0x4F, 0xDE}},
{ 0x191, {0x40, 0x43, 0xFF, 0x7C, 0xED, 0x91, 0x68, 0x73}},
{ 0xC9 , {0xE7, 0x9F, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF3}},
{ 0x65 , {0xA6, 0x8D, 0x00, 0x03, 0x40, 0x00, 0x00, 0x0D}},
{ 0x12D, {0x41, 0x67, 0x16, 0x86, 0x41, 0xE7, 0x16, 0x86}},
{ 0x191, {0x40, 0x45, 0xAA, 0x1C, 0xAC, 0x08, 0x31, 0x27}},
{ 0xC9 , {0x1C, 0x72, 0x00, 0x03, 0x80, 0x00, 0x00, 0x0E}},
{ 0x65 , {0x47, 0x0E, 0x00, 0x03, 0x80, 0x00, 0x00, 0x0E}},
{ 0x12D, {0x41, 0x78, 0xDD, 0x2E, 0x41, 0xF8, 0xDD, 0x2E}},
{ 0x191, {0x40, 0x47, 0x54, 0xBC, 0x6A, 0x7E, 0xF9, 0xDC}},
{ 0xC9 , {0xE3, 0x8D, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xF1}},
{ 0x65 , {0xE7, 0x8F, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x0F}},
{ 0x12D, {0x41, 0x85, 0x51, 0xEB, 0x42, 0x05, 0x51, 0xEB}},
{ 0x191, {0x40, 0x48, 0xFF, 0x5C, 0x28, 0xF5, 0xC2, 0x90}},
{ 0xC9 , {0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10}},
{ 0x65 , {0x08, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10}},
{ 0x12D, {0x41, 0x8E, 0x35, 0x3F, 0x42, 0x0E, 0x35, 0x3F}},
{ 0x191, {0x40, 0x4A, 0xA9, 0xFB, 0xE7, 0x6C, 0x8B, 0x44}},
{ 0xC9 , {0xFF, 0x7F, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xEF}},
{ 0x65 , {0xA8, 0x91, 0x00, 0x04, 0x40, 0x00, 0x00, 0x11}},
{ 0x12D, {0x41, 0x97, 0x18, 0x93, 0x42, 0x17, 0x18, 0x93}},
{ 0x191, {0x40, 0x4C, 0x54, 0x9B, 0xA5, 0xE3, 0x53, 0xF8}},
{ 0xC9 , {0x04, 0x92, 0x00, 0x04, 0x80, 0x00, 0x00, 0x12}},
{ 0x65 , {0x49, 0x12, 0x00, 0x04, 0x80, 0x00, 0x00, 0x12}},
{ 0x12D, {0x41, 0x9F, 0xFB, 0xE7, 0x42, 0x1F, 0xFB, 0xE7}},
{ 0x191, {0x40, 0x4D, 0xFF, 0x3B, 0x64, 0x5A, 0x1C, 0xAC}},
{ 0xC9 , {0xFB, 0x6D, 0xFF, 0xFB, 0x7F, 0xFF, 0xFF, 0xED}},
{ 0x65 , {0xE9, 0x93, 0x00, 0x04, 0xC0, 0x00, 0x00, 0x13}},
{ 0x12D, {0x41, 0xA8, 0xDF, 0x3B, 0x42, 0x28, 0xDF, 0x3B}},
{ 0x191, {0x40, 0x4F, 0xA9, 0xDB, 0x22, 0xD0, 0xE5, 0x61}}
};
// Create ASC file with a delay of 100 (to allow the system to initialize)
asc::CAscWriter writer;
double dTimestamp = 0.100;
for (const STestData& sData : rgsData)
{
asc::SCanMessage sMsg{};
sMsg.dTimestamp = dTimestamp;
dTimestamp += 0.001;
sMsg.uiChannel = 1ul;
sMsg.uiId = sData.uiID;
sMsg.bExtended = false;
sMsg.bCanFd = false;
sMsg.uiLength = 8ul;
sMsg.eDirection = asc::SCanMessage::EDirection::rx;
sMsg.rguiData[0] = sData.rguiData[0];
sMsg.rguiData[1] = sData.rguiData[1];
sMsg.rguiData[2] = sData.rguiData[2];
sMsg.rguiData[3] = sData.rguiData[3];
sMsg.rguiData[4] = sData.rguiData[4];
sMsg.rguiData[5] = sData.rguiData[5];
sMsg.rguiData[6] = sData.rguiData[6];
sMsg.rguiData[7] = sData.rguiData[7];
writer.AddSample(sMsg);
}
// Add a dummy message with a big timegap to prevent the repetition function to jump in.
asc::SCanMessage sMsgDummy{};
sMsgDummy.dTimestamp = 100.0;
sMsgDummy.uiChannel = 1ul;
sMsgDummy.uiId = 999;
writer.AddSample(sMsgDummy);
// Write the ASC file
EXPECT_TRUE(writer.Write(GetExecDirectory() / "receiver_test.asc"));
writer.Clear();
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_rx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
int32_t rgiIntCnt[6] = {10, 10, 10, 10, 10, 10};
sdv::core::CSignal signalIntBE1 = dispatch.Subscribe("TestIntBE.SignalIntBE1", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[0]++;
int32_t iCalculated = (static_cast<int32_t>(i) & 0b1) * (i & 1 ? -1 : 1); // 2 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntBE1);
sdv::core::CSignal signalIntBE2 = dispatch.Subscribe("TestIntBE.SignalIntBE2", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[1]++;
int32_t iCalculated = (static_cast<int32_t>(i) & 0b1111) * (i & 1 ? -1 : 1); // 5 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntBE2);
sdv::core::CSignal signalIntBE3 = dispatch.Subscribe("TestIntBE.SignalIntBE3", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[2]++;
int32_t iCalculated = (static_cast<int32_t>(i) & 0b11111) * (i & 1 ? -1 : 1); // 6 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntBE3);
sdv::core::CSignal signalIntBE4 = dispatch.Subscribe("TestIntBE.SignalIntBE4", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[3]++;
int32_t iCalculated = (static_cast<int32_t>(i) & 0b11) * (i & 1 ? -1 : 1); // 3 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntBE4);
sdv::core::CSignal signalIntBE5 = dispatch.Subscribe("TestIntBE.SignalIntBE5", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[4]++;
int32_t iCalculated = (static_cast<int32_t>(i)) * (i & 1 ? -1 : 1); // 18 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntBE5);
sdv::core::CSignal signalIntBE6 = dispatch.Subscribe("TestIntBE.SignalIntBE6", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[5]++;
int32_t iCalculated = (static_cast<int32_t>(i)) * (i & 1 ? -1 : 1); // 30 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntBE6);
// Subscribe to unsigned integer based signals
uint32_t rguiUintCnt[6] = {10u, 10u, 10u, 10u, 10u, 10u};
sdv::core::CSignal signalUintBE1 = dispatch.Subscribe("TestUintBE.SignalUintBE1", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[0]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui) & 0b1; // 1 bit
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintBE1);
sdv::core::CSignal signalUintBE2 = dispatch.Subscribe("TestUintBE.SignalUintBE2", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[1]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui) & 0b11; // 2 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintBE2);
sdv::core::CSignal signalUintBE3 = dispatch.Subscribe("TestUintBE.SignalUintBE3", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[2]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui) & 0b111111; // 6 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintBE3);
sdv::core::CSignal signalUintBE4 = dispatch.Subscribe("TestUintBE.SignalUintBE4", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[3]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui) & 0b1111111; // 7 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintBE4);
sdv::core::CSignal signalUintBE5 = dispatch.Subscribe("TestUintBE.SignalUintBE5", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[4]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui); // 18 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintBE5);
sdv::core::CSignal signalUintBE6 = dispatch.Subscribe("TestUintBE.SignalUintBE6", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[5]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui); // 30 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintBE6);
// Subscribe to floating point based signals
uint32_t rguiFloatCnt[2] = {10u, 10u};
sdv::core::CSignal signalFloatBE1 = dispatch.Subscribe("TestFloatBE.SignalFloatBE1", [&](sdv::any_t tVal)
{
uint32_t ui = rguiFloatCnt[0]++;
float fCalculated = static_cast<float>(ui) * 1.111f;
EXPECT_EQ(fCalculated, static_cast<float>(tVal));
});
EXPECT_TRUE(signalFloatBE1);
sdv::core::CSignal signalFloatBE2 = dispatch.Subscribe("TestFloatBE.SignalFloatBE2", [&](sdv::any_t tVal)
{
uint32_t ui = rguiFloatCnt[1]++;
float fCalculated = static_cast<float>(ui) * 2.222f;
EXPECT_EQ(fCalculated, static_cast<float>(tVal));
});
EXPECT_TRUE(signalFloatBE2);
// Subscribe to double precision floating point based signals
uint32_t uiDoubleCnt = 10u;
sdv::core::CSignal signalDoubleBE1 = dispatch.Subscribe("TestDoubleBE.SignalDoubleBE1", [&](sdv::any_t tVal)
{
uint32_t ui = uiDoubleCnt++;
double dCalculated = static_cast<double>(ui) * 3.333;
EXPECT_EQ(dCalculated, static_cast<double>(tVal));
});
EXPECT_TRUE(signalDoubleBE1);
appcontrol.SetRunningMode();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
// Check for correct counters
for (int32_t i : rgiIntCnt)
EXPECT_EQ(i, 20);
for (uint32_t ui : rguiUintCnt)
EXPECT_EQ(ui, 20u);
for (uint32_t ui : rguiFloatCnt)
EXPECT_EQ(ui, 20u);
EXPECT_EQ(uiDoubleCnt, 20u);
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalIntBE1.Reset();
signalIntBE2.Reset();
signalIntBE3.Reset();
signalIntBE4.Reset();
signalIntBE5.Reset();
signalIntBE6.Reset();
signalUintBE1.Reset();
signalUintBE2.Reset();
signalUintBE3.Reset();
signalUintBE4.Reset();
signalUintBE5.Reset();
signalUintBE6.Reset();
signalFloatBE1.Reset();
signalFloatBE2.Reset();
signalDoubleBE1.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
}
TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndianAllDataTypes)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to integer based signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalIntLE1 = dispatch.AddPublisher("TestIntLE.SignalIntLE1");
EXPECT_TRUE(signalIntLE1);
sdv::core::CSignal signalIntLE2 = dispatch.AddPublisher("TestIntLE.SignalIntLE2");
EXPECT_TRUE(signalIntLE2);
sdv::core::CSignal signalIntLE3 = dispatch.AddPublisher("TestIntLE.SignalIntLE3");
EXPECT_TRUE(signalIntLE3);
sdv::core::CSignal signalIntLE4 = dispatch.AddPublisher("TestIntLE.SignalIntLE4");
EXPECT_TRUE(signalIntLE4);
sdv::core::CSignal signalIntLE5 = dispatch.AddPublisher("TestIntLE.SignalIntLE5");
EXPECT_TRUE(signalIntLE5);
sdv::core::CSignal signalIntLE6 = dispatch.AddPublisher("TestIntLE.SignalIntLE6");
EXPECT_TRUE(signalIntLE6);
// Subscribe to unsigned integer based signals
sdv::core::CSignal signalUintLE1 = dispatch.AddPublisher("TestUintLE.SignalUintLE1");
EXPECT_TRUE(signalUintLE1);
sdv::core::CSignal signalUintLE2 = dispatch.AddPublisher("TestUintLE.SignalUintLE2");
EXPECT_TRUE(signalUintLE2);
sdv::core::CSignal signalUintLE3 = dispatch.AddPublisher("TestUintLE.SignalUintLE3");
EXPECT_TRUE(signalUintLE3);
sdv::core::CSignal signalUintLE4 = dispatch.AddPublisher("TestUintLE.SignalUintLE4");
EXPECT_TRUE(signalUintLE4);
sdv::core::CSignal signalUintLE5 = dispatch.AddPublisher("TestUintLE.SignalUintLE5");
EXPECT_TRUE(signalUintLE5);
sdv::core::CSignal signalUintLE6 = dispatch.AddPublisher("TestUintLE.SignalUintLE6");
EXPECT_TRUE(signalUintLE6);
// Subscribe to floating point based signals
sdv::core::CSignal signalFloatLE1 = dispatch.AddPublisher("TestFloatLE.SignalFloatLE1");
EXPECT_TRUE(signalFloatLE1);
sdv::core::CSignal signalFloatLE2 = dispatch.AddPublisher("TestFloatLE.SignalFloatLE2");
EXPECT_TRUE(signalFloatLE2);
// Subscribe to double precision floating point based signals
sdv::core::CSignal signalDoubleLE1 = dispatch.AddPublisher("TestDoubleLE.SignalDoubleLE1");
EXPECT_TRUE(signalDoubleLE1);
appcontrol.SetRunningMode();
for (size_t n = 10; n < 20; n++)
{
sdv::core::CTransaction transaction = dispatch.CreateTransaction();
signalIntLE1.Write((static_cast<int32_t>(n) & 0b1) * (n & 1 ? -1 : 1), transaction); // 2 bits incl. sign
signalIntLE2.Write((static_cast<int32_t>(n) & 0b1111) * (n & 1 ? -1 : 1), transaction); // 5 bits incl. sign
signalIntLE3.Write((static_cast<int32_t>(n) & 0b11111) * (n & 1 ? -1 : 1), transaction); // 6 bits incl. sign
signalIntLE4.Write((static_cast<int32_t>(n) & 0b11) * (n & 1 ? -1 : 1), transaction); // 3 bits incl. sign
signalIntLE5.Write((static_cast<int32_t>(n)) * (n & 1 ? -1 : 1), transaction); // 18 bits incl. sign
signalIntLE6.Write((static_cast<int32_t>(n)) * (n & 1 ? -1 : 1), transaction); // 30 bits incl. sign
transaction.Finish();
transaction = dispatch.CreateTransaction();
signalUintLE1.Write(static_cast<uint32_t>(n) & 0b1, transaction); // 1 bit
signalUintLE2.Write(static_cast<uint32_t>(n) & 0b11, transaction); // 2 bits
signalUintLE3.Write(static_cast<uint32_t>(n) & 0b111111, transaction); // 6 bits
signalUintLE4.Write(static_cast<uint32_t>(n) & 0b1111111, transaction); // 7 bits
signalUintLE5.Write(static_cast<uint32_t>(n), transaction); // 18 bits
signalUintLE6.Write(static_cast<uint32_t>(n), transaction); // 30 bits
transaction.Finish();
transaction = dispatch.CreateTransaction();
signalFloatLE1.Write(static_cast<float>(n) * 1.111f, transaction);
signalFloatLE2.Write(static_cast<float>(n) * 2.222f, transaction);
transaction.Finish();
transaction = dispatch.CreateTransaction();
signalDoubleLE1.Write(static_cast<double>(n) * 3.333, transaction);
transaction.Finish();
}
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalIntLE1.Reset();
signalIntLE2.Reset();
signalIntLE3.Reset();
signalIntLE4.Reset();
signalIntLE5.Reset();
signalIntLE6.Reset();
signalUintLE1.Reset();
signalUintLE2.Reset();
signalUintLE3.Reset();
signalUintLE4.Reset();
signalUintLE5.Reset();
signalUintLE6.Reset();
signalFloatLE1.Reset();
signalFloatLE2.Reset();
signalDoubleLE1.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0xC8 , {0x28, 0x45, 0x0A, 0x00, 0x28, 0x00, 0x00, 0x00}},
{ 0x64 , {0x54, 0x14, 0x0A, 0x00, 0x28, 0x00, 0x00, 0x00}},
{ 0x12C, {0x8F, 0xC2, 0x31, 0x41, 0x8F, 0xC2, 0xB1, 0x41}},
{ 0x190, {0x0A, 0xD7, 0xA3, 0x70, 0x3D, 0xAA, 0x40, 0x40}},
{ 0xC8 , {0xD7, 0xBA, 0xF5, 0xFF, 0xD7, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x5F, 0x16, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00}},
{ 0x12C, {0x37, 0x89, 0x43, 0x41, 0x37, 0x89, 0xC3, 0x41}},
{ 0x190, {0xBF, 0x9F, 0x1A, 0x2F, 0xDD, 0x54, 0x42, 0x40}},
{ 0xC8 , {0x30, 0x06, 0x0C, 0x00, 0x30, 0x00, 0x00, 0x00}},
{ 0x64 , {0x60, 0x18, 0x0C, 0x00, 0x30, 0x00, 0x00, 0x00}},
{ 0x12C, {0xDE, 0x4F, 0x55, 0x41, 0xDE, 0x4F, 0xD5, 0x41}},
{ 0x190, {0x73, 0x68, 0x91, 0xED, 0x7C, 0xFF, 0x43, 0x40}},
{ 0xC8 , {0xCF, 0xF9, 0xF3, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x6B, 0x1A, 0x0D, 0x00, 0x34, 0x00, 0x00, 0x00}},
{ 0x12C, {0x86, 0x16, 0x67, 0x41, 0x86, 0x16, 0xE7, 0x41}},
{ 0x190, {0x27, 0x31, 0x08, 0xAC, 0x1C, 0xAA, 0x45, 0x40}},
{ 0xC8 , {0x38, 0x47, 0x0E, 0x00, 0x38, 0x00, 0x00, 0x00}},
{ 0x64 , {0x74, 0x1C, 0x0E, 0x00, 0x38, 0x00, 0x00, 0x00}},
{ 0x12C, {0x2E, 0xDD, 0x78, 0x41, 0x2E, 0xDD, 0xF8, 0x41}},
{ 0x190, {0xDC, 0xF9, 0x7E, 0x6A, 0xBC, 0x54, 0x47, 0x40}},
{ 0xC8 , {0xC7, 0xB8, 0xF1, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x7F, 0x1E, 0x0F, 0x00, 0x3C, 0x00, 0x00, 0x00}},
{ 0x12C, {0xEB, 0x51, 0x85, 0x41, 0xEB, 0x51, 0x05, 0x42}},
{ 0x190, {0x90, 0xC2, 0xF5, 0x28, 0x5C, 0xFF, 0x48, 0x40}},
{ 0xC8 , {0x00, 0x08, 0x10, 0x00, 0x40, 0x00, 0x00, 0x00}},
{ 0x64 , {0x80, 0x20, 0x10, 0x00, 0x40, 0x00, 0x00, 0x00}},
{ 0x12C, {0x3F, 0x35, 0x8E, 0x41, 0x3F, 0x35, 0x0E, 0x42}},
{ 0x190, {0x44, 0x8B, 0x6C, 0xE7, 0xFB, 0xA9, 0x4A, 0x40}},
{ 0xC8 , {0xFF, 0xF7, 0xEF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x8B, 0x22, 0x11, 0x00, 0x44, 0x00, 0x00, 0x00}},
{ 0x12C, {0x93, 0x18, 0x97, 0x41, 0x93, 0x18, 0x17, 0x42}},
{ 0x190, {0xF8, 0x53, 0xE3, 0xA5, 0x9B, 0x54, 0x4C, 0x40}},
{ 0xC8 , {0x08, 0x49, 0x12, 0x00, 0x48, 0x00, 0x00, 0x00}},
{ 0x64 , {0x94, 0x24, 0x12, 0x00, 0x48, 0x00, 0x00, 0x00}},
{ 0x12C, {0xE7, 0xFB, 0x9F, 0x41, 0xE7, 0xFB, 0x1F, 0x42}},
{ 0x190, {0xAC, 0x1C, 0x5A, 0x64, 0x3B, 0xFF, 0x4D, 0x40}},
{ 0xC8 , {0xF7, 0xB6, 0xED, 0xFF, 0xB7, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x9F, 0x26, 0x13, 0x00, 0x4C, 0x00, 0x00, 0x00}},
{ 0x12C, {0x3B, 0xDF, 0xA8, 0x41, 0x3B, 0xDF, 0x28, 0x42}},
{ 0x190, {0x61, 0xE5, 0xD0, 0x22, 0xDB, 0xA9, 0x4F, 0x40}}
};
// Compare with test data.
for (const STestData& sData : rgsData)
{
EXPECT_FALSE(reader.IsEOF());
std::pair<asc::SCanMessage, bool> prMsg;
do {
prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
} while (!reader.IsEOF() && prMsg.first.uiId != sData.uiID); // Skip other messages
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, sData.uiID);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.uiLength, 8ul);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.rguiData[0], sData.rguiData[0]);
EXPECT_EQ(prMsg.first.rguiData[1], sData.rguiData[1]);
EXPECT_EQ(prMsg.first.rguiData[2], sData.rguiData[2]);
EXPECT_EQ(prMsg.first.rguiData[3], sData.rguiData[3]);
EXPECT_EQ(prMsg.first.rguiData[4], sData.rguiData[4]);
EXPECT_EQ(prMsg.first.rguiData[5], sData.rguiData[5]);
EXPECT_EQ(prMsg.first.rguiData[6], sData.rguiData[6]);
EXPECT_EQ(prMsg.first.rguiData[7], sData.rguiData[7]);
}
}
TEST(DbcUtilCanDLTest, ReceiveLittleEndianAllDataTypes)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[8];
} rgsData[] = {
{ 0xC8 , {0x28, 0x45, 0x0A, 0x00, 0x28, 0x00, 0x00, 0x00}},
{ 0x64 , {0x54, 0x14, 0x0A, 0x00, 0x28, 0x00, 0x00, 0x00}},
{ 0x12C, {0x8F, 0xC2, 0x31, 0x41, 0x8F, 0xC2, 0xB1, 0x41}},
{ 0x190, {0x0A, 0xD7, 0xA3, 0x70, 0x3D, 0xAA, 0x40, 0x40}},
{ 0xC8 , {0xD7, 0xBA, 0xF5, 0xFF, 0xD7, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x5F, 0x16, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00}},
{ 0x12C, {0x37, 0x89, 0x43, 0x41, 0x37, 0x89, 0xC3, 0x41}},
{ 0x190, {0xBF, 0x9F, 0x1A, 0x2F, 0xDD, 0x54, 0x42, 0x40}},
{ 0xC8 , {0x30, 0x06, 0x0C, 0x00, 0x30, 0x00, 0x00, 0x00}},
{ 0x64 , {0x60, 0x18, 0x0C, 0x00, 0x30, 0x00, 0x00, 0x00}},
{ 0x12C, {0xDE, 0x4F, 0x55, 0x41, 0xDE, 0x4F, 0xD5, 0x41}},
{ 0x190, {0x73, 0x68, 0x91, 0xED, 0x7C, 0xFF, 0x43, 0x40}},
{ 0xC8 , {0xCF, 0xF9, 0xF3, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x6B, 0x1A, 0x0D, 0x00, 0x34, 0x00, 0x00, 0x00}},
{ 0x12C, {0x86, 0x16, 0x67, 0x41, 0x86, 0x16, 0xE7, 0x41}},
{ 0x190, {0x27, 0x31, 0x08, 0xAC, 0x1C, 0xAA, 0x45, 0x40}},
{ 0xC8 , {0x38, 0x47, 0x0E, 0x00, 0x38, 0x00, 0x00, 0x00}},
{ 0x64 , {0x74, 0x1C, 0x0E, 0x00, 0x38, 0x00, 0x00, 0x00}},
{ 0x12C, {0x2E, 0xDD, 0x78, 0x41, 0x2E, 0xDD, 0xF8, 0x41}},
{ 0x190, {0xDC, 0xF9, 0x7E, 0x6A, 0xBC, 0x54, 0x47, 0x40}},
{ 0xC8 , {0xC7, 0xB8, 0xF1, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x7F, 0x1E, 0x0F, 0x00, 0x3C, 0x00, 0x00, 0x00}},
{ 0x12C, {0xEB, 0x51, 0x85, 0x41, 0xEB, 0x51, 0x05, 0x42}},
{ 0x190, {0x90, 0xC2, 0xF5, 0x28, 0x5C, 0xFF, 0x48, 0x40}},
{ 0xC8 , {0x00, 0x08, 0x10, 0x00, 0x40, 0x00, 0x00, 0x00}},
{ 0x64 , {0x80, 0x20, 0x10, 0x00, 0x40, 0x00, 0x00, 0x00}},
{ 0x12C, {0x3F, 0x35, 0x8E, 0x41, 0x3F, 0x35, 0x0E, 0x42}},
{ 0x190, {0x44, 0x8B, 0x6C, 0xE7, 0xFB, 0xA9, 0x4A, 0x40}},
{ 0xC8 , {0xFF, 0xF7, 0xEF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x8B, 0x22, 0x11, 0x00, 0x44, 0x00, 0x00, 0x00}},
{ 0x12C, {0x93, 0x18, 0x97, 0x41, 0x93, 0x18, 0x17, 0x42}},
{ 0x190, {0xF8, 0x53, 0xE3, 0xA5, 0x9B, 0x54, 0x4C, 0x40}},
{ 0xC8 , {0x08, 0x49, 0x12, 0x00, 0x48, 0x00, 0x00, 0x00}},
{ 0x64 , {0x94, 0x24, 0x12, 0x00, 0x48, 0x00, 0x00, 0x00}},
{ 0x12C, {0xE7, 0xFB, 0x9F, 0x41, 0xE7, 0xFB, 0x1F, 0x42}},
{ 0x190, {0xAC, 0x1C, 0x5A, 0x64, 0x3B, 0xFF, 0x4D, 0x40}},
{ 0xC8 , {0xF7, 0xB6, 0xED, 0xFF, 0xB7, 0xFF, 0xFF, 0xFF}},
{ 0x64 , {0x9F, 0x26, 0x13, 0x00, 0x4C, 0x00, 0x00, 0x00}},
{ 0x12C, {0x3B, 0xDF, 0xA8, 0x41, 0x3B, 0xDF, 0x28, 0x42}},
{ 0x190, {0x61, 0xE5, 0xD0, 0x22, 0xDB, 0xA9, 0x4F, 0x40}}
};
// Create ASC file with a delay of 100 (to allow the system to initialize)
asc::CAscWriter writer;
double dTimestamp = 0.100;
for (const STestData& sData : rgsData)
{
asc::SCanMessage sMsg{};
sMsg.dTimestamp = dTimestamp;
dTimestamp += 0.001;
sMsg.uiChannel = 1ul;
sMsg.uiId = sData.uiID;
sMsg.bExtended = false;
sMsg.bCanFd = false;
sMsg.uiLength = 8ul;
sMsg.eDirection = asc::SCanMessage::EDirection::rx;
sMsg.rguiData[0] = sData.rguiData[0];
sMsg.rguiData[1] = sData.rguiData[1];
sMsg.rguiData[2] = sData.rguiData[2];
sMsg.rguiData[3] = sData.rguiData[3];
sMsg.rguiData[4] = sData.rguiData[4];
sMsg.rguiData[5] = sData.rguiData[5];
sMsg.rguiData[6] = sData.rguiData[6];
sMsg.rguiData[7] = sData.rguiData[7];
writer.AddSample(sMsg);
}
// Add a dummy message with a big timegap to prevent the repetition function to jump in.
asc::SCanMessage sMsgDummy{};
sMsgDummy.dTimestamp = 100.0;
sMsgDummy.uiChannel = 1ul;
sMsgDummy.uiId = 999;
writer.AddSample(sMsgDummy);
// Write the ASC file
EXPECT_TRUE(writer.Write(GetExecDirectory() / "receiver_test.asc"));
writer.Clear();
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_rx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
int32_t rgiIntCnt[6] = {10, 10, 10, 10, 10, 10};
sdv::core::CSignal signalIntLE1 = dispatch.Subscribe("TestIntLE.SignalIntLE1", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[0]++;
int32_t iCalculated = (static_cast<int32_t>(i) & 0b1) * (i & 1 ? -1 : 1); // 2 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntLE1);
sdv::core::CSignal signalIntLE2 = dispatch.Subscribe("TestIntLE.SignalIntLE2", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[1]++;
int32_t iCalculated = (static_cast<int32_t>(i) & 0b1111) * (i & 1 ? -1 : 1); // 5 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntLE2);
sdv::core::CSignal signalIntLE3 = dispatch.Subscribe("TestIntLE.SignalIntLE3", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[2]++;
int32_t iCalculated = (static_cast<int32_t>(i) & 0b11111) * (i & 1 ? -1 : 1); // 6 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntLE3);
sdv::core::CSignal signalIntLE4 = dispatch.Subscribe("TestIntLE.SignalIntLE4", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[3]++;
int32_t iCalculated = (static_cast<int32_t>(i) & 0b11) * (i & 1 ? -1 : 1); // 3 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntLE4);
sdv::core::CSignal signalIntLE5 = dispatch.Subscribe("TestIntLE.SignalIntLE5", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[4]++;
int32_t iCalculated = (static_cast<int32_t>(i)) * (i & 1 ? -1 : 1); // 18 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntLE5);
sdv::core::CSignal signalIntLE6 = dispatch.Subscribe("TestIntLE.SignalIntLE6", [&](sdv::any_t tVal)
{
int32_t i = rgiIntCnt[5]++;
int32_t iCalculated = (static_cast<int32_t>(i)) * (i & 1 ? -1 : 1); // 30 bits incl. sign
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalIntLE6);
// Subscribe to unsigned integer based signals
uint32_t rguiUintCnt[6] = {10u, 10u, 10u, 10u, 10u, 10u};
sdv::core::CSignal signalUintLE1 = dispatch.Subscribe("TestUintLE.SignalUintLE1", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[0]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui) & 0b1; // 1 bit
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintLE1);
sdv::core::CSignal signalUintLE2 = dispatch.Subscribe("TestUintLE.SignalUintLE2", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[1]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui) & 0b11; // 2 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintLE2);
sdv::core::CSignal signalUintLE3 = dispatch.Subscribe("TestUintLE.SignalUintLE3", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[2]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui) & 0b111111; // 6 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintLE3);
sdv::core::CSignal signalUintLE4 = dispatch.Subscribe("TestUintLE.SignalUintLE4", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[3]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui) & 0b1111111; // 7 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintLE4);
sdv::core::CSignal signalUintLE5 = dispatch.Subscribe("TestUintLE.SignalUintLE5", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[4]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui); // 18 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintLE5);
sdv::core::CSignal signalUintLE6 = dispatch.Subscribe("TestUintLE.SignalUintLE6", [&](sdv::any_t tVal)
{
uint32_t ui = rguiUintCnt[5]++;
uint32_t uiCalculated = static_cast<uint32_t>(ui); // 30 bits
EXPECT_EQ(uiCalculated, static_cast<uint32_t>(tVal));
});
EXPECT_TRUE(signalUintLE6);
// Subscribe to floating point based signals
uint32_t rguiFloatCnt[2] = {10u, 10u};
sdv::core::CSignal signalFloatLE1 = dispatch.Subscribe("TestFloatLE.SignalFloatLE1", [&](sdv::any_t tVal)
{
uint32_t ui = rguiFloatCnt[0]++;
float fCalculated = static_cast<float>(ui) * 1.111f;
EXPECT_EQ(fCalculated, static_cast<float>(tVal));
});
EXPECT_TRUE(signalFloatLE1);
sdv::core::CSignal signalFloatLE2 = dispatch.Subscribe("TestFloatLE.SignalFloatLE2", [&](sdv::any_t tVal)
{
uint32_t ui = rguiFloatCnt[1]++;
float fCalculated = static_cast<float>(ui) * 2.222f;
EXPECT_EQ(fCalculated, static_cast<float>(tVal));
});
EXPECT_TRUE(signalFloatLE2);
// Subscribe to double precision floating point based signals
uint32_t uiDoubleCnt = 10u;
sdv::core::CSignal signalDoubleLE1 = dispatch.Subscribe("TestDoubleLE.SignalDoubleLE1", [&](sdv::any_t tVal)
{
uint32_t ui = uiDoubleCnt++;
double dCalculated = static_cast<double>(ui) * 3.333;
EXPECT_EQ(dCalculated, static_cast<double>(tVal));
});
EXPECT_TRUE(signalDoubleLE1);
appcontrol.SetRunningMode();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
// Check for correct counters
for (int32_t i : rgiIntCnt)
EXPECT_EQ(i, 20);
for (uint32_t ui : rguiUintCnt)
EXPECT_EQ(ui, 20u);
for (uint32_t ui : rguiFloatCnt)
EXPECT_EQ(ui, 20u);
EXPECT_EQ(uiDoubleCnt, 20u);
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalIntLE1.Reset();
signalIntLE2.Reset();
signalIntLE3.Reset();
signalIntLE4.Reset();
signalIntLE5.Reset();
signalIntLE6.Reset();
signalUintLE1.Reset();
signalUintLE2.Reset();
signalUintLE3.Reset();
signalUintLE4.Reset();
signalUintLE5.Reset();
signalUintLE6.Reset();
signalFloatLE1.Reset();
signalFloatLE2.Reset();
signalDoubleLE1.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
}
TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndianScaledDataTypes)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to integer based signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalUintBE1 = dispatch.AddPublisher("TestScaleBE.SignalScaleUintBE1");
EXPECT_TRUE(signalUintBE1);
sdv::core::CSignal signalUintBE2 = dispatch.AddPublisher("TestScaleBE.SignalScaleUintBE2");
EXPECT_TRUE(signalUintBE2);
sdv::core::CSignal signalUintBE3 = dispatch.AddPublisher("TestScaleBE.SignalScaleUintBE3");
EXPECT_TRUE(signalUintBE3);
sdv::core::CSignal signalUintBE4 = dispatch.AddPublisher("TestScaleBE.SignalScaleUintBE4");
EXPECT_TRUE(signalUintBE4);
sdv::core::CSignal signalIntBE5 = dispatch.AddPublisher("TestScaleBE.SignalScaleIntBE5");
EXPECT_TRUE(signalIntBE5);
sdv::core::CSignal signalIntBE6 = dispatch.AddPublisher("TestScaleBE.SignalScaleIntBE6");
EXPECT_TRUE(signalIntBE6);
sdv::core::CSignal signalFloatBE7 = dispatch.AddPublisher("TestScaleBE.SignalScaleFloatBE7");
EXPECT_TRUE(signalFloatBE7);
appcontrol.SetRunningMode();
for (size_t n = 4; n < 12; n++)
{
sdv::core::CTransaction transaction = dispatch.CreateTransaction();
signalUintBE1.Write(static_cast<double>(n) / 1000.0, transaction); // 4 bits - factor 0.001
signalUintBE2.Write(static_cast<int32_t>(n) - 5, transaction); // 4 bits - offset -3.5
signalUintBE3.Write(static_cast<int32_t>(n) * -1, transaction); // 4 bits - factor -1
signalUintBE4.Write(static_cast<int32_t>(n) - 1, transaction); // 4 bits - offset -2
signalIntBE5.Write(static_cast<float>(n) / 1000.0f, transaction); // 4 bits incl. sign - factor 0.001, offset 0.008
signalIntBE6.Write(static_cast<int32_t>(n) - 10, transaction); // 4 bits incl. sign - offset -3.5
signalFloatBE7.Write(static_cast<float>(n) / 10000.0f, transaction); // 32 bits - factor 0.001, offset 100
transaction.Finish();
}
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalUintBE1.Reset();
signalUintBE2.Reset();
signalUintBE3.Reset();
signalUintBE4.Reset();
signalIntBE5.Reset();
signalIntBE6.Reset();
signalFloatBE7.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[7];
} rgsData[] = {
{ 0x1F5, {0x43, 0x45, 0xCD, 0xC7, 0xC3, 0x4F, 0xCD}},
{ 0x1F5, {0x54, 0x56, 0xDE, 0xC7, 0xC3, 0x4F, 0xBF}},
{ 0x1F5, {0x65, 0x67, 0xEF, 0xC7, 0xC3, 0x4F, 0xB2}},
{ 0x1F5, {0x76, 0x78, 0xF1, 0xC7, 0xC3, 0x4F, 0xA6}},
{ 0x1F5, {0x87, 0x89, 0x02, 0xC7, 0xC3, 0x4F, 0x99}},
{ 0x1F5, {0x98, 0x9A, 0x13, 0xC7, 0xC3, 0x4F, 0x8C}},
{ 0x1F5, {0xA9, 0xAB, 0x24, 0xC7, 0xC3, 0x4F, 0x7F}},
{ 0x1F5, {0xBA, 0xBC, 0x35, 0xC7, 0xC3, 0x4F, 0x73}}
};
// Compare with test data.
for (const STestData& sData : rgsData)
{
EXPECT_FALSE(reader.IsEOF());
std::pair<asc::SCanMessage, bool> prMsg;
do {
prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
} while (!reader.IsEOF() && prMsg.first.uiId != sData.uiID); // Skip other messages
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, sData.uiID);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.uiLength, 7ul);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.rguiData[0], sData.rguiData[0]);
EXPECT_EQ(prMsg.first.rguiData[1], sData.rguiData[1]);
EXPECT_EQ(prMsg.first.rguiData[2], sData.rguiData[2]);
EXPECT_EQ(prMsg.first.rguiData[3], sData.rguiData[3]);
EXPECT_EQ(prMsg.first.rguiData[4], sData.rguiData[4]);
EXPECT_EQ(prMsg.first.rguiData[5], sData.rguiData[5]);
EXPECT_EQ(prMsg.first.rguiData[6], sData.rguiData[6]);
}
}
TEST(DbcUtilCanDLTest, ReceiveBigEndianScaledDataType)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[7];
} rgsData[] = {
{ 0x1F5, {0x43, 0x45, 0xCD, 0xC7, 0xC3, 0x4F, 0xCD}},
{ 0x1F5, {0x54, 0x56, 0xDE, 0xC7, 0xC3, 0x4F, 0xBF}},
{ 0x1F5, {0x65, 0x67, 0xEF, 0xC7, 0xC3, 0x4F, 0xB2}},
{ 0x1F5, {0x76, 0x78, 0xF1, 0xC7, 0xC3, 0x4F, 0xA6}},
{ 0x1F5, {0x87, 0x89, 0x02, 0xC7, 0xC3, 0x4F, 0x99}},
{ 0x1F5, {0x98, 0x9A, 0x13, 0xC7, 0xC3, 0x4F, 0x8C}},
{ 0x1F5, {0xA9, 0xAB, 0x24, 0xC7, 0xC3, 0x4F, 0x7F}},
{ 0x1F5, {0xBA, 0xBC, 0x35, 0xC7, 0xC3, 0x4F, 0x73}}
};
// Create ASC file with a delay of 100 (to allow the system to initialize)
asc::CAscWriter writer;
double dTimestamp = 0.100;
for (const STestData& sData : rgsData)
{
asc::SCanMessage sMsg{};
sMsg.dTimestamp = dTimestamp;
dTimestamp += 0.001;
sMsg.uiChannel = 1ul;
sMsg.uiId = sData.uiID;
sMsg.bExtended = false;
sMsg.bCanFd = false;
sMsg.uiLength = 7ul;
sMsg.eDirection = asc::SCanMessage::EDirection::rx;
sMsg.rguiData[0] = sData.rguiData[0];
sMsg.rguiData[1] = sData.rguiData[1];
sMsg.rguiData[2] = sData.rguiData[2];
sMsg.rguiData[3] = sData.rguiData[3];
sMsg.rguiData[4] = sData.rguiData[4];
sMsg.rguiData[5] = sData.rguiData[5];
sMsg.rguiData[6] = sData.rguiData[6];
writer.AddSample(sMsg);
}
// Add a dummy message with a big timegap to prevent the repetition function to jump in.
asc::SCanMessage sMsgDummy{};
sMsgDummy.dTimestamp = 100.0;
sMsgDummy.uiChannel = 1ul;
sMsgDummy.uiId = 999;
writer.AddSample(sMsgDummy);
// Write the ASC file
EXPECT_TRUE(writer.Write(GetExecDirectory() / "receiver_test.asc"));
writer.Clear();
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_rx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
uint32_t rguiCnt[7] = {4u, 4u, 4u, 4u, 4u, 4u, 4u};
sdv::core::CSignal signalUintBE1 = dispatch.Subscribe("TestScaleBE.SignalScaleUintBE1", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[0]++;
double dCalculated = static_cast<double>(ui) / 1000.0; // 4 bits - factor 0.001
EXPECT_EQ(std::round(dCalculated * 1000.0), std::round(static_cast<double>(tVal) * 1000.0));
});
EXPECT_TRUE(signalUintBE1);
sdv::core::CSignal signalUintBE2 = dispatch.Subscribe("TestScaleBE.SignalScaleUintBE2", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[1]++;
// Since the offset is a floating point value, but the data type is a unsigned integer, the value is being rounded.
double dCalculated = static_cast<int32_t>(std::round(static_cast<int32_t>(ui) - 5.0 + 3.5)) - 3.5; // 4 bits - offset -3.5
EXPECT_EQ(dCalculated, static_cast<double>(tVal));
});
EXPECT_TRUE(signalUintBE2);
sdv::core::CSignal signalUintBE3 = dispatch.Subscribe("TestScaleBE.SignalScaleUintBE3", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[2]++;
int32_t iCalculated = static_cast<int32_t>(ui) * -1; // 4 bits - factor -1
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalUintBE3);
sdv::core::CSignal signalUintBE4 = dispatch.Subscribe("TestScaleBE.SignalScaleUintBE4", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[3]++;
int32_t iCalculated = static_cast<int32_t>(ui) - 1; // 4 bits - offset -2
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalUintBE4);
sdv::core::CSignal signalIntBE5 = dispatch.Subscribe("TestScaleBE.SignalScaleIntBE5", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[4]++;
float fCalculated = static_cast<float>(ui) / 1000.0f; // 4 bits incl. sign - factor 0.001, offset 0.008
EXPECT_EQ(fCalculated, static_cast<float>(tVal));
});
EXPECT_TRUE(signalIntBE5);
sdv::core::CSignal signalIntBE6 = dispatch.Subscribe("TestScaleBE.SignalScaleIntBE6", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[5]++;
// Since the offset is a floating point value, but the data type is a integer, the value is being rounded.
double dCalculated = static_cast<int32_t>(std::round(static_cast<int32_t>(ui) - 10.0 + 3.5)) - 3.5; // 4 bits incl. sign - offset -3.5
EXPECT_EQ(dCalculated, static_cast<double>(tVal));
});
EXPECT_TRUE(signalIntBE6);
sdv::core::CSignal signalFloatBE7 = dispatch.Subscribe("TestScaleBE.SignalScaleFloatBE7", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[6]++;
float fCalculated = static_cast<float>(ui) / 10000.0f; // 32 bits - factor 0.001, offset 100
EXPECT_EQ(std::round(fCalculated * 1000.f), std::round(static_cast<float>(tVal) * 1000.f));
});
EXPECT_TRUE(signalFloatBE7);
appcontrol.SetRunningMode();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
// Check for correct counters
for (uint32_t ui : rguiCnt)
EXPECT_EQ(ui, 12u);
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalUintBE1.Reset();
signalUintBE2.Reset();
signalUintBE3.Reset();
signalUintBE4.Reset();
signalIntBE5.Reset();
signalIntBE6.Reset();
signalFloatBE7.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
}
TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndianScaledDataTypes)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to integer based signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalUintLE1 = dispatch.AddPublisher("TestScaleLE.SignalScaleUintLE1");
EXPECT_TRUE(signalUintLE1);
sdv::core::CSignal signalUintLE2 = dispatch.AddPublisher("TestScaleLE.SignalScaleUintLE2");
EXPECT_TRUE(signalUintLE2);
sdv::core::CSignal signalUintLE3 = dispatch.AddPublisher("TestScaleLE.SignalScaleUintLE3");
EXPECT_TRUE(signalUintLE3);
sdv::core::CSignal signalUintLE4 = dispatch.AddPublisher("TestScaleLE.SignalScaleUintLE4");
EXPECT_TRUE(signalUintLE4);
sdv::core::CSignal signalIntLE5 = dispatch.AddPublisher("TestScaleLE.SignalScaleIntLE5");
EXPECT_TRUE(signalIntLE5);
sdv::core::CSignal signalIntLE6 = dispatch.AddPublisher("TestScaleLE.SignalScaleIntLE6");
EXPECT_TRUE(signalIntLE6);
sdv::core::CSignal signalFloatLE7 = dispatch.AddPublisher("TestScaleLE.SignalScaleFloatLE7");
EXPECT_TRUE(signalFloatLE7);
appcontrol.SetRunningMode();
for (size_t n = 4; n < 12; n++)
{
sdv::core::CTransaction transaction = dispatch.CreateTransaction();
signalUintLE1.Write(static_cast<double>(n) / 1000.0, transaction); // 4 bits - factor 0.001
signalUintLE2.Write(static_cast<int32_t>(n) - 5, transaction); // 4 bits - offset -3.5
signalUintLE3.Write(static_cast<int32_t>(n) * -1, transaction); // 4 bits - factor -1
signalUintLE4.Write(static_cast<int32_t>(n) - 1, transaction); // 4 bits - offset -2
signalIntLE5.Write(static_cast<float>(n) / 1000.0f, transaction); // 4 bits incl. sign - factor 0.001, offset 0.008
signalIntLE6.Write(static_cast<int32_t>(n) - 10, transaction); // 4 bits incl. sign - offset -3.5
signalFloatLE7.Write(static_cast<float>(n) / 10000.0f, transaction); // 32 bits - factor 0.001, offset 100
transaction.Finish();
}
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalUintLE1.Reset();
signalUintLE2.Reset();
signalUintLE3.Reset();
signalUintLE4.Reset();
signalIntLE5.Reset();
signalIntLE6.Reset();
signalFloatLE7.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[7];
} rgsData[] = {
{ 0x1F4, {0x34, 0x54, 0xDC, 0xCD, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x45, 0x65, 0xED, 0xBF, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x56, 0x76, 0xFE, 0xB2, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x67, 0x87, 0x1F, 0xA6, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x78, 0x98, 0x20, 0x99, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x89, 0xA9, 0x31, 0x8C, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x9A, 0xBA, 0x42, 0x7F, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0xAB, 0xCB, 0x53, 0x73, 0x4F, 0xC3, 0xC7}}
};
// Compare with test data.
for (const STestData& sData : rgsData)
{
EXPECT_FALSE(reader.IsEOF());
std::pair<asc::SCanMessage, bool> prMsg;
do {
prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
} while (!reader.IsEOF() && prMsg.first.uiId != sData.uiID); // Skip other messages
EXPECT_EQ(prMsg.first.uiChannel, 1ul);
EXPECT_EQ(prMsg.first.uiId, sData.uiID);
EXPECT_FALSE(prMsg.first.bExtended);
EXPECT_FALSE(prMsg.first.bCanFd);
EXPECT_EQ(prMsg.first.uiLength, 7ul);
EXPECT_EQ(prMsg.first.eDirection, asc::SCanMessage::EDirection::tx);
EXPECT_EQ(prMsg.first.rguiData[0], sData.rguiData[0]);
EXPECT_EQ(prMsg.first.rguiData[1], sData.rguiData[1]);
EXPECT_EQ(prMsg.first.rguiData[2], sData.rguiData[2]);
EXPECT_EQ(prMsg.first.rguiData[3], sData.rguiData[3]);
EXPECT_EQ(prMsg.first.rguiData[4], sData.rguiData[4]);
EXPECT_EQ(prMsg.first.rguiData[5], sData.rguiData[5]);
EXPECT_EQ(prMsg.first.rguiData[6], sData.rguiData[6]);
}
}
TEST(DbcUtilCanDLTest, ReceiveLittleEndianScaledDataType)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
struct STestData
{
uint32_t uiID;
uint8_t rguiData[7];
} rgsData[] = {
{ 0x1F4, {0x34, 0x54, 0xDC, 0xCD, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x45, 0x65, 0xED, 0xBF, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x56, 0x76, 0xFE, 0xB2, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x67, 0x87, 0x1F, 0xA6, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x78, 0x98, 0x20, 0x99, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x89, 0xA9, 0x31, 0x8C, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0x9A, 0xBA, 0x42, 0x7F, 0x4F, 0xC3, 0xC7}},
{ 0x1F4, {0xAB, 0xCB, 0x53, 0x73, 0x4F, 0xC3, 0xC7}}
};
// Create ASC file with a delay of 100 (to allow the system to initialize)
asc::CAscWriter writer;
double dTimestamp = 0.100;
for (const STestData& sData : rgsData)
{
asc::SCanMessage sMsg{};
sMsg.dTimestamp = dTimestamp;
dTimestamp += 0.001;
sMsg.uiChannel = 1ul;
sMsg.uiId = sData.uiID;
sMsg.bExtended = false;
sMsg.bCanFd = false;
sMsg.uiLength = 7ul;
sMsg.eDirection = asc::SCanMessage::EDirection::rx;
sMsg.rguiData[0] = sData.rguiData[0];
sMsg.rguiData[1] = sData.rguiData[1];
sMsg.rguiData[2] = sData.rguiData[2];
sMsg.rguiData[3] = sData.rguiData[3];
sMsg.rguiData[4] = sData.rguiData[4];
sMsg.rguiData[5] = sData.rguiData[5];
sMsg.rguiData[6] = sData.rguiData[6];
writer.AddSample(sMsg);
}
// Add a dummy message with a big timegap to prevent the repetition function to jump in.
asc::SCanMessage sMsgDummy{};
sMsgDummy.dTimestamp = 100.0;
sMsgDummy.uiChannel = 1ul;
sMsgDummy.uiId = 999;
writer.AddSample(sMsgDummy);
// Write the ASC file
EXPECT_TRUE(writer.Write(GetExecDirectory() / "receiver_test.asc"));
writer.Clear();
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_rx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
uint32_t rguiCnt[7] = {4u, 4u, 4u, 4u, 4u, 4u, 4u};
sdv::core::CSignal signalUintLE1 = dispatch.Subscribe("TestScaleLE.SignalScaleUintLE1", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[0]++;
double dCalculated = static_cast<double>(ui) / 1000.0; // 4 bits - factor 0.001
EXPECT_EQ(std::round(dCalculated * 1000.0), std::round(static_cast<double>(tVal) * 1000.0));
});
EXPECT_TRUE(signalUintLE1);
sdv::core::CSignal signalUintLE2 = dispatch.Subscribe("TestScaleLE.SignalScaleUintLE2", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[1]++;
// Since the offset is a floating point value, but the data type is a unsigned integer, the value is being rounded.
double dCalculated = static_cast<int32_t>(std::round(static_cast<int32_t>(ui) - 5.0 + 3.5)) - 3.5; // 4 bits - offset -3.5
EXPECT_EQ(dCalculated, static_cast<double>(tVal));
});
EXPECT_TRUE(signalUintLE2);
sdv::core::CSignal signalUintLE3 = dispatch.Subscribe("TestScaleLE.SignalScaleUintLE3", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[2]++;
int32_t iCalculated = static_cast<int32_t>(ui) * -1; // 4 bits - factor -1
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalUintLE3);
sdv::core::CSignal signalUintLE4 = dispatch.Subscribe("TestScaleLE.SignalScaleUintLE4", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[3]++;
int32_t iCalculated = static_cast<int32_t>(ui) - 1; // 4 bits - offset -2
EXPECT_EQ(iCalculated, static_cast<int32_t>(tVal));
});
EXPECT_TRUE(signalUintLE4);
sdv::core::CSignal signalIntLE5 = dispatch.Subscribe("TestScaleLE.SignalScaleIntLE5", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[4]++;
float fCalculated = static_cast<float>(ui) / 1000.0f; // 4 bits incl. sign - factor 0.001, offset 0.008
EXPECT_EQ(fCalculated, static_cast<float>(tVal));
});
EXPECT_TRUE(signalIntLE5);
sdv::core::CSignal signalIntLE6 = dispatch.Subscribe("TestScaleLE.SignalScaleIntLE6", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[5]++;
// Since the offset is a floating point value, but the data type is a integer, the value is being rounded.
double dCalculated = static_cast<int32_t>(std::round(static_cast<int32_t>(ui) - 10.0 + 3.5)) - 3.5; // 4 bits incl. sign - offset -3.5
EXPECT_EQ(dCalculated, static_cast<double>(tVal));
});
EXPECT_TRUE(signalIntLE6);
sdv::core::CSignal signalFloatLE7 = dispatch.Subscribe("TestScaleLE.SignalScaleFloatLE7", [&](sdv::any_t tVal)
{
uint32_t ui = rguiCnt[6]++;
float fCalculated = static_cast<float>(ui) / 10000.0f; // 32 bits - factor 0.001, offset 100
EXPECT_EQ(std::round(fCalculated * 1000.f), std::round(static_cast<float>(tVal) * 1000.f));
});
EXPECT_TRUE(signalFloatLE7);
appcontrol.SetRunningMode();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
// Check for correct counters
for (uint32_t ui : rguiCnt)
EXPECT_EQ(ui, 12u);
// Shutdown
appcontrol.SetConfigMode();
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalUintLE1.Reset();
signalUintLE2.Reset();
signalUintLE3.Reset();
signalUintLE4.Reset();
signalIntLE5.Reset();
signalIntLE6.Reset();
signalFloatLE7.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
}
TEST(DbcUtilCanDLTest, CyclicTransmit)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalCounter = dispatch.AddPublisher("TestCyclic.Counter");
EXPECT_TRUE(signalCounter);
// The cycle time is 10 ms. Write the counter 5 times with 50ms difference
size_t n = 0;
std::mutex mtx;
std::condition_variable cv;
sdv::core::CTaskTimer timer(50, [&]()
{
if (n == 5)
cv.notify_all();
else
signalCounter.Write(n++);
});
appcontrol.SetRunningMode();
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock);
timer.Reset();
lock.unlock();
appcontrol.SetConfigMode();
// Shutdown
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalCounter.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
std::vector<size_t> vecStat(5);
std::vector<double> vecTime(5);
int32_t iCnt = -1;
size_t nSignalCnt = 0;
double dLastTime = 0;
while (!reader.IsEOF())
{
std::pair<asc::SCanMessage, bool> prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
dLastTime = prMsg.first.dTimestamp;
// Only look at the cyclic message
if (prMsg.first.uiId != 0x8) continue;
nSignalCnt++;
int32_t iCntTemp = static_cast<int32_t>(prMsg.first.rguiData[0] >> 5);
EXPECT_TRUE(iCntTemp == iCnt || iCntTemp == iCnt + 1);
iCnt = iCntTemp;
EXPECT_LT(iCnt, 5);
if (iCnt < 5)
{
vecStat[iCnt]++;
if (iCnt && !vecTime[iCnt - 1]) vecTime[iCnt - 1] = prMsg.first.dTimestamp;
}
}
if (!vecTime[4]) vecTime[4] = dLastTime;
if (iCnt < 4)
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
}
for (n = 0; n < 5; n++)
{
if (n != 0)
{
2026-01-16 11:40:02 +01:00
if (vecStat[n] > 6u) // In the unlucky case, 6 triggers might have occurred (during startup, there might be many more...).
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_ENABLED);
}
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
}
if (n == 4)
{
if (dPeriod < 0.019) // Min 19ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
}
}
else
{
if (dPeriod < 0.039) // Min 39ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
}
}
}
if (vecStat[n] < 4u) // At least 4 triggers.
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(vecStat[n], 4u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(vecStat[n], 4u, sdv_test::WARNING_ENABLED);
}
}
}
TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalCounter = dispatch.AddPublisher("TestCyclicIfActive.Counter");
EXPECT_TRUE(signalCounter);
// The counter value 2 is the default value and therefore determines the active state.
// The cycle time is 10 ms. Write the counter 5 times with 50ms difference
size_t n = 0;
std::mutex mtx;
std::condition_variable cv;
sdv::core::CTaskTimer timer(50, [&]()
{
if (n == 5)
cv.notify_all();
else
signalCounter.Write(n++);
});
appcontrol.SetRunningMode();
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock);
appcontrol.SetConfigMode();
timer.Reset();
// Shutdown
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalCounter.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
std::vector<size_t> vecStat(5);
std::vector<double> vecTime(5);
int32_t iCnt = -1;
size_t nSignalCnt = 0;
double dLastTime = 0;
bool bInit = false;
while (!reader.IsEOF())
{
std::pair<asc::SCanMessage, bool> prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
dLastTime = prMsg.first.dTimestamp;
// Only look at the cyclic message
if (prMsg.first.uiId != 0xa) continue;
nSignalCnt++;
iCnt = static_cast<int32_t>(prMsg.first.rguiData[0] >> 5);
EXPECT_LT(iCnt, 5);
if (!bInit)
{
// Expected iCnt to be the default value...
if (iCnt != 0 && iCnt != 2)
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_ENABLED);
}
bInit = true;
}
else if (iCnt < 5)
{
vecStat[iCnt]++;
if (iCnt && !vecTime[iCnt - 1]) vecTime[iCnt - 1] = prMsg.first.dTimestamp;
}
}
if (!vecTime[4]) vecTime[4] = dLastTime;
if (iCnt < 4)
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
}
for (n = 0; n < 5; n++)
{
if (n != 0)
{
if (n == 2)
{
2026-01-16 11:40:02 +01:00
if (vecStat[n] != 1u) // One trigger should have occurred due to default value
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_ENABLED);
}
}
else
{
2026-01-16 11:40:02 +01:00
if (vecStat[n] > 6u) // In the unlucky case, 6 triggers might have occurred (during startup, there might be many more...).
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_ENABLED);
}
}
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
}
if (n == 4)
{
if (dPeriod < 0.019) // Min 19ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
}
}
else
{
if (dPeriod < 0.039) // Min 39ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
}
}
}
}
}
TEST(DbcUtilCanDLTest, CyclicAndSpontaneousTransmit)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalCounter = dispatch.AddPublisher("TestCyclicAndSpontaneous.Counter");
EXPECT_TRUE(signalCounter);
// The cycle time is 50 ms. Write a value every 50ms. This should result in a spontaneous and a cyclic trigger.
size_t n = 0;
std::mutex mtx;
std::condition_variable cv;
sdv::core::CTaskTimer timer(50, [&]()
{
if (n == 5)
cv.notify_all();
else
signalCounter.Write(n++);
});
appcontrol.SetRunningMode();
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock);
appcontrol.SetConfigMode();
timer.Reset();
// Shutdown
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalCounter.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
std::vector<size_t> vecStat(5);
std::vector<double> vecTime(5);
int32_t iCnt = -1;
size_t nSignalCnt = 0;
double dLastTime = 0;
while (!reader.IsEOF())
{
std::pair<asc::SCanMessage, bool> prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
dLastTime = prMsg.first.dTimestamp;
// Only look at the cyclic message
if (prMsg.first.uiId != 0x9) continue;
nSignalCnt++;
int32_t iCntTemp = static_cast<int32_t>(prMsg.first.rguiData[0] >> 5);
EXPECT_TRUE(iCntTemp == iCnt || iCntTemp == iCnt + 1);
iCnt = iCntTemp;
EXPECT_LT(iCnt, 5);
if (iCnt < 5)
{
vecStat[iCnt]++;
if (iCnt && !vecTime[iCnt - 1]) vecTime[iCnt - 1] = prMsg.first.dTimestamp;
}
}
if (!vecTime[4]) vecTime[4] = dLastTime;
if (iCnt < 4)
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
}
for (n = 0; n < 5; n++)
{
if (n != 0)
{
2026-01-16 11:40:02 +01:00
if (vecStat[n] != 2u) // One trigger and one cycle.
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_ENABLED);
}
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
}
if (n == 4)
{
if (dPeriod < 0.019) // Min 19ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
}
}
else
{
if (dPeriod < 0.039) // Min 39ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
}
}
}
}
}
TEST(DbcUtilCanDLTest, SpontaneousDelayTransmit)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalCounter = dispatch.AddPublisher("TestSpontaneousDelay.Counter");
EXPECT_TRUE(signalCounter);
// The delay is 20ms. Write the counter 5 times every 10ms.
size_t n = 0;
std::mutex mtx;
std::condition_variable cv;
size_t nCycle = 0;
sdv::core::CTaskTimer timer(10, [&]()
{
if (n < 5) signalCounter.Write(n);
if (nCycle++ == 5)
{
nCycle = 0;
if (n == 5) cv.notify_all();
n++;
}
});
appcontrol.SetRunningMode();
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock);
appcontrol.SetConfigMode();
timer.Reset();
// Shutdown
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalCounter.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
std::vector<size_t> vecStat(5);
std::vector<double> vecFirstTime(5);
std::vector<double> vecLastTime(5);
int32_t iCnt = -1;
size_t nSignalCnt = 0;
while (!reader.IsEOF())
{
std::pair<asc::SCanMessage, bool> prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
// Only look at the cyclic message
if (prMsg.first.uiId != 0xc) continue;
nSignalCnt++;
int32_t iCntTemp = static_cast<int32_t>(prMsg.first.rguiData[0] >> 5);
EXPECT_TRUE(iCntTemp == iCnt || iCntTemp == iCnt + 1);
iCnt = iCntTemp;
EXPECT_LT(iCnt, 5);
if (iCnt < 5)
{
vecStat[iCnt]++;
if (!vecFirstTime[iCnt]) vecFirstTime[iCnt] = prMsg.first.dTimestamp;
vecLastTime[iCnt] = prMsg.first.dTimestamp;
}
}
if (iCnt < 4)
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
}
for (n = 0; n < 5; n++)
{
if (n != 0)
{
2026-01-16 11:40:02 +01:00
if (vecStat[n] > 4u) // Max 4 times
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(vecStat[n], 4u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(vecStat[n], 4u, sdv_test::WARNING_ENABLED);
}
2026-01-16 11:40:02 +01:00
if (vecStat[n] < 3u) // Min 3 times
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(vecStat[n], 3u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(vecStat[n], 3u, sdv_test::WARNING_ENABLED);
}
double dPeriod = std::round((vecLastTime[n] - vecFirstTime[n]) * 1000.0) / 1000.0;
if (dPeriod > 0.061) // Max 61ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.061, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.061, sdv_test::WARNING_ENABLED);
}
if (dPeriod < 0.039) // Min 39ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
}
}
}
}
TEST(DbcUtilCanDLTest, CyclicAndSpontaneousDelayTransmit)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalCounter = dispatch.AddPublisher("TestCyclicAndSpontaneousDelay.Counter");
EXPECT_TRUE(signalCounter);
// The cycle time is 50ms. The delay is 25ms. Write the counter 2 times every 25ms.
size_t n = 0;
std::mutex mtx;
std::condition_variable cv;
sdv::core::CTaskTimer timer(25, [&]()
{
if (n == 10)
cv.notify_all();
else
{
signalCounter.Write(n / 2);
n++;
}
});
appcontrol.SetRunningMode();
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock);
appcontrol.SetConfigMode();
timer.Reset();
// Shutdown
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalCounter.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
std::vector<size_t> vecStat(5);
std::vector<double> vecTime(5);
int32_t iCnt = -1;
size_t nSignalCnt = 0;
double dLastTime = 0;
while (!reader.IsEOF())
{
std::pair<asc::SCanMessage, bool> prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
dLastTime = prMsg.first.dTimestamp;
// Only look at the cyclic message
if (prMsg.first.uiId != 0xd) continue;
nSignalCnt++;
int32_t iCntTemp = static_cast<int32_t>(prMsg.first.rguiData[0] >> 5);
EXPECT_TRUE(iCntTemp == iCnt || iCntTemp == iCnt + 1);
iCnt = iCntTemp;
EXPECT_LT(iCnt, 5);
if (iCnt < 5)
{
vecStat[iCnt]++;
if (iCnt && !vecTime[iCnt - 1]) vecTime[iCnt - 1] = prMsg.first.dTimestamp;
}
}
if (!vecTime[4]) vecTime[4] = dLastTime;
if (iCnt < 4)
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
}
for (n = 0; n < 5; n++)
{
if (n != 0)
{
2026-01-16 11:40:02 +01:00
if (vecStat[n] != 2u) // Two trigger and/or cycle.
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_ENABLED);
}
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
}
if (n == 4)
{
if (dPeriod < 0.019) // Min 19ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
}
}
else
{
if (dPeriod < 0.049) // Min 49ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.049, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.049, sdv_test::WARNING_ENABLED);
}
}
}
}
}
TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit)
{
try
{
std::filesystem::remove(GetExecDirectory() / "receiver_test.asc");
std::filesystem::remove(GetExecDirectory() / "transmitter_test.asc");
} catch (const std::filesystem::filesystem_error&)
{}
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "receiver_test.asc"));
EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "transmitter_test.asc"));
std::filesystem::current_path(GetExecDirectory());
sdv::app::CAppControl appcontrol;
bool bResult = appcontrol.Startup("");
EXPECT_TRUE(bResult);
appcontrol.SetConfigMode();
sdv::core::EConfigProcessResult eResult = appcontrol.LoadConfig("test_dbc_util_config_tx.toml");
EXPECT_EQ(eResult, sdv::core::EConfigProcessResult::successful);
// Start the data link
CDbcStructDataLink dl;
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending);
dl.Initialize("");
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized);
dl.SetOperationMode(sdv::EOperationMode::running);
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running);
// Subscribe to several signals
sdv::core::CDispatchService dispatch;
sdv::core::CSignal signalCounter = dispatch.AddPublisher("TestCyclicIfActiveAndSpontaneous.Counter");
EXPECT_TRUE(signalCounter);
// The cycle time is 50 ms. Write a value every 50ms. This should result in a spontaneous and a cyclic trigger.
size_t n = 0;
std::mutex mtx;
std::condition_variable cv;
sdv::core::CTaskTimer timer(50, [&]()
{
if (n == 5)
cv.notify_all();
else
signalCounter.Write(n++);
});
appcontrol.SetRunningMode();
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock);
appcontrol.SetConfigMode();
timer.Reset();
// Shutdown
dl.Shutdown();
EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending);
signalCounter.Reset();
// Shutdown. This will close the CAN recording.
appcontrol.Shutdown();
// Read the CAN recording.
asc::CAscReader reader;
EXPECT_TRUE(reader.Read(GetExecDirectory() / "transmitter_test.asc"));
std::vector<size_t> vecStat(5);
std::vector<double> vecTime(5);
int32_t iCnt = -1;
size_t nSignalCnt = 0;
double dLastTime = 0;
bool bInit = false;
while (!reader.IsEOF())
{
std::pair<asc::SCanMessage, bool> prMsg = reader.Get();
EXPECT_TRUE(prMsg.second);
++reader;
dLastTime = prMsg.first.dTimestamp;
// Only look at the cyclic message
if (prMsg.first.uiId != 0xb) continue;
nSignalCnt++;
iCnt = static_cast<int32_t>(prMsg.first.rguiData[0] >> 5);
EXPECT_LT(iCnt, 5);
if (!bInit)
{
// Expected iCnt to be the default value...
if (iCnt != 0 && iCnt != 2)
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_ENABLED);
}
bInit = true;
}
else if (iCnt < 5)
{
vecStat[iCnt]++;
if (iCnt && !vecTime[iCnt - 1]) vecTime[iCnt - 1] = prMsg.first.dTimestamp;
}
}
if (!vecTime[4]) vecTime[4] = dLastTime;
if (iCnt < 4)
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
}
for (n = 0; n < 5; n++)
{
if (n != 0)
{
if (n == 2)
{
2026-01-16 11:40:02 +01:00
if (vecStat[n] != 1u) // One trigger no cycle.
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_ENABLED);
}
}
else
{
2026-01-16 11:40:02 +01:00
if (vecStat[n] != 2u) // One trigger and one cycle.
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_ENABLED);
}
}
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
}
if (n == 4)
{
if (dPeriod < 0.019) // Min 19ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
}
}
else
{
if (dPeriod < 0.039) // Min 39ms
{
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else
2026-01-16 11:40:02 +01:00
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
}
}
}
}
}