Precommit (#1)

* first commit

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

View File

@@ -0,0 +1,334 @@
#include "console.h"
#ifdef _WIN32
#include <conio.h> // Needed for _kbhit
#else
#include <fcntl.h>
#endif
#include "vss_vehiclechassisrearaxlerowwheel_bs_tx.h"
#include "vss_vehiclechassissteeringwheelangle_bs_rx.h"
#include "vss_vehiclesoftwareapplicationisactivecounter_bs_tx.h"
#include "vss_vehiclespeed_bs_rx.h"
/**
* @brief Key hit check. Windows uses the _kbhit function; POSIX emulates this.
* @return Returns whether a key has been pressed.
*/
inline bool KeyHit()
{
#ifdef _WIN32
return _kbhit();
#elif __unix__
int ch = getchar();
if (ch != EOF) {
ungetc(ch, stdin);
return true;
}
return false;
#endif
}
/**
* @brief Get the character from the keyboard buffer if pressed.
* @return Returns the character from the keyboard buffer.
*/
char GetChar()
{
#ifdef _WIN32
return static_cast<char>(_getch());
#else
return getchar();
#endif
}
const CConsole::SConsolePos g_sTitle{ 1, 1 };
const CConsole::SConsolePos g_sSeparator1{ 2, 1 };
const CConsole::SConsolePos g_sDLDescription{ 4, 1 };
const CConsole::SConsolePos g_sDLSteeringWheel{ 6, 3 };
const CConsole::SConsolePos g_sDLVehicleSpeed{ 7, 3 };
const CConsole::SConsolePos g_sDLRearAxle{ 6, 41 };
const CConsole::SConsolePos g_sDLAliveCounter{ 7, 41 };
const CConsole::SConsolePos g_sSeparator2{ 9, 1 };
const CConsole::SConsolePos g_sBSDescription{ 11, 1 };
const CConsole::SConsolePos g_sBSSteeringWheel{ 13, 3 };
const CConsole::SConsolePos g_sBSVehicleSpeed{ 14, 3 };
const CConsole::SConsolePos g_sSeparator3{ 16, 1 };
const CConsole::SConsolePos g_sCSDescription{ 18, 1 };
const CConsole::SConsolePos g_sCSActivated{ 20, 3 };
const CConsole::SConsolePos g_sCSActive{ 21, 3 };
const CConsole::SConsolePos g_sCSRearAxle{ 20, 41 };
const CConsole::SConsolePos g_sSeparator4{ 23, 1 };
const CConsole::SConsolePos g_sControlDescription{ 25, 1 };
const CConsole::SConsolePos g_sCursor{ 26, 1 };
CConsole::CConsole(bool bMonitorDatalink) : m_bMonitorDatalink(bMonitorDatalink)
{
#ifdef _WIN32
// Enable ANSI escape codes
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut != INVALID_HANDLE_VALUE && GetConsoleMode(hStdOut, &m_dwConsoleOutMode))
SetConsoleMode(hStdOut, m_dwConsoleOutMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (hStdIn != INVALID_HANDLE_VALUE && GetConsoleMode(hStdIn, &m_dwConsoleInMode))
SetConsoleMode(hStdIn, m_dwConsoleInMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT));
#elif defined __unix__
// Disable echo
tcgetattr(STDIN_FILENO, &m_sTermAttr);
struct termios sTermAttrTemp = m_sTermAttr;
sTermAttrTemp.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &sTermAttrTemp);
m_iFileStatus = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, m_iFileStatus | O_NONBLOCK);
#else
#error The OS is not supported!
#endif
}
CConsole::~CConsole()
{
SetCursorPos(g_sCursor);
#ifdef _WIN32
// Return to the stored console mode
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut != INVALID_HANDLE_VALUE)
SetConsoleMode(hStdOut, m_dwConsoleOutMode);
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (hStdIn != INVALID_HANDLE_VALUE)
SetConsoleMode(hStdIn, m_dwConsoleInMode);
#elif defined __unix__
// Return the previous file status flags.
fcntl(STDIN_FILENO, F_SETFL, m_iFileStatus);
// Return to previous terminal state
tcsetattr(STDIN_FILENO, TCSANOW, &m_sTermAttr);
#endif
}
void CConsole::PrintHeader(const bool bServer, const bool bSimulate)
{
// Clear the screen...
std::cout << "\x1b[2J";
// Create titles
std::string title = "System demo example";
std::string dataLinkTitle = "Data link signal values:";
if (bServer)
title.append(" - Connected to running server");
else
{
if (bSimulate)
{
title.append(" - run as standalone application, simulation mode");
dataLinkTitle = "Dispatch service signal values:";
}
else
{
title.append(" - run as standalone application including data link");
}
}
// Print the titles
PrintText(g_sTitle, title);
PrintText(g_sSeparator1, "============================================================================");
PrintText(g_sDLDescription, dataLinkTitle.c_str());
PrintText(g_sSeparator2, "----------------------------------------------------------------------------");
PrintText(g_sBSDescription, "Basic services event values:");
PrintText(g_sSeparator3, "----------------------------------------------------------------------------");
PrintText(g_sCSDescription, "Counter steering example service values:");
PrintText(g_sSeparator4, "============================================================================");
PrintText(g_sControlDescription, "Press 'X' to quit; 'T' to toggle the service activity...");
}
bool CConsole::PrepareDataConsumers()
{
//////////////////////////////////////
// DATA LINK
// Request the data link signals from the dispatch service. This only works for standalone applications, since the data dispatch
// service needs to be accessible from within the same process (no IPC marshalling is provided). For server based applications,
// the data link layer is inacessible by any application (and complex service), hence data cannot be received if connected to a
// server. The m_bMonitorDataLink flag determines whether data from the data link should can be received or not.
// NOTE: registering signals and timer for data link data can only occur during configuration time. During the execution, no new
// signals can be registered.
if (m_bMonitorDatalink)
{
sdv::core::CDispatchService dispatch;
m_signalRearAxleAngle = dispatch.RegisterTxSignal(demo::dsAxleAngle, 0);
m_signalCounter = dispatch.RegisterTxSignal(demo::dsLiveCounter, 0);
m_signalSteeringWheel = dispatch.Subscribe(demo::dsWheelAngle, [&](sdv::any_t value) { DataLinkCallbackSteeringWheelAngle(value); });
m_signalSpeed = dispatch.Subscribe(demo::dsVehicleSpeed, [&](sdv::any_t value) { DataLinkCallbackVehicleSpeed(value); });
if (!m_signalRearAxleAngle || !m_signalCounter || !m_signalSteeringWheel || !m_signalSpeed)
{
std::cerr << "Console ERROR: TX register and RX subscription failed" << std::endl;
return false;
}
}
//////////////////////////////////////
// BASIC SERVICES
// Request the basic service for the steering wheel.
auto pSteeringWheelSvc = sdv::core::GetObject("Vehicle.Chassis.SteeringWheel.Angle_Service").GetInterface<vss::Vehicle::Chassis::SteeringWheel::AngleService::IVSS_GetSteeringWheel>();
if (!pSteeringWheelSvc)
{
std::cerr << "Console ERROR: Could not get basic service interface 'IVSS_SetSteeringAngle'" << std::endl;
return false;
}
// Request the basic service for the vehicle speed.
auto pVehSpeedSvc = sdv::core::GetObject("Vehicle.Speed_Service").GetInterface<vss::Vehicle::SpeedService::IVSS_GetSpeed>();
if (!pVehSpeedSvc)
{
std::cerr << "Console ERROR: Could not get basic service interface 'IVSS_SetSpeed'" << std::endl;
return false;
}
// Register steering wheel change event handler.
pSteeringWheelSvc->RegisterOnSignalChangeOfWheelAngle(static_cast<vss::Vehicle::Chassis::SteeringWheel::AngleService::IVSS_SetSteeringWheel_Event*> (this));
// Register vehicle speed change event handler.
pVehSpeedSvc->RegisterOnSignalChangeOfVehicleSpeed(static_cast<vss::Vehicle::SpeedService::IVSS_SetSpeed_Event*> (this));
//////////////////////////////////////
// COMPLEX SERVICE
m_pCounterSteeringSvc = sdv::core::GetObject("Counter Steering Example Service").GetInterface<ICounterSteeringService>();
if (!m_pCounterSteeringSvc)
{
std::cerr << "Console ERROR: Could not get complex service interface 'ICounterSteeringService'" << std::endl;
return false;
}
return true;
}
void CConsole::RunUntilBreak()
{
// Run until break
bool bRunning = true;
while (bRunning)
{
// Update and display the data from data link, basic services and complex service.
UpdateData();
// Check for a key
if (!KeyHit())
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
// Get a keyboard value (if there is any).
char c = GetChar();
switch (c)
{
case 't':
case 'T':
if (m_pCounterSteeringSvc) m_pCounterSteeringSvc->ActivateService(!m_pCounterSteeringSvc->IsActivated());
break;
case 'x':
case 'X':
bRunning = false;
break;
default:
break;
}
}
// Set the cursor position at the end
SetCursorPos(g_sCursor);
// Unregister the data link signalss
if (m_signalSteeringWheel) m_signalSteeringWheel.Reset();
if (m_signalSpeed) m_signalSpeed.Reset();
if (m_signalRearAxleAngle) m_signalRearAxleAngle.Reset();
if (m_signalCounter) m_signalCounter.Reset();
}
void CConsole::DataLinkCallbackSteeringWheelAngle(sdv::any_t value)
{
m_fDLSteeringWheelAngle = value.get<float>();
}
void CConsole::DataLinkCallbackVehicleSpeed(sdv::any_t value)
{
m_fDLVehicleSpeed = value.get<float>();
}
void CConsole::UpdateData()
{
// Print data link data
if (m_bMonitorDatalink)
{
PrintValue(g_sDLSteeringWheel, "Steering Angle RX", m_fDLSteeringWheelAngle, "rad");
PrintValue(g_sDLVehicleSpeed, "Vehicle Speed RX", m_fDLVehicleSpeed, "m/s");
PrintValue(g_sDLRearAxle, "Rear axle angle TX", m_signalRearAxleAngle.Read().get<float>(), "deg");
PrintValue(g_sDLAliveCounter, "Alive counter TX", m_signalCounter.Read().get<float>(), "");
}
else
PrintText(g_sDLSteeringWheel, "Data link signals are unavailable!");
// Print basic service event values
PrintValue(g_sBSSteeringWheel, "Steering Angle", m_fSteeringWheelAngle * 57.296f , "deg");
PrintValue(g_sBSVehicleSpeed, "Vehicle Speed RX", m_fVehicleSpeed * 3.6f , "km/h");
// Get complex service information
if (m_pCounterSteeringSvc)
{
PrintValue(g_sCSActivated, "Service activated", m_pCounterSteeringSvc->IsActivated(), "");
PrintValue(g_sCSActive, "Counter steering active", m_pCounterSteeringSvc->CounterSteeringActive(), "");
PrintValue(g_sCSRearAxle, "Rear axle angle", m_pCounterSteeringSvc->RearAxleAngle(), "deg");
}
}
void CConsole::SetSteeringWheel(float value)
{
m_fSteeringWheelAngle = value;
}
void CConsole::SetSpeed(float value)
{
m_fVehicleSpeed = value;
}
CConsole::SConsolePos CConsole::GetCursorPos() const
{
SConsolePos sPos{};
std::cout << "\033[6n";
char buff[128];
int indx = 0;
for(;;) {
int cc = std::cin.get();
buff[indx] = (char)cc;
indx++;
if(cc == 'R') {
buff[indx + 1] = '\0';
break;
}
}
int iRow = 0, iCol = 0;
sscanf(buff, "\x1b[%d;%dR", &iRow, &iCol);
sPos.uiRow = static_cast<uint32_t>(iRow);
sPos.uiCol = static_cast<uint32_t>(iCol);
fseek(stdin, 0, SEEK_END);
return sPos;
}
void CConsole::SetCursorPos(SConsolePos sPos)
{
std::cout << "\033[" << sPos.uiRow << ";" << sPos.uiCol << "H";
}
void CConsole::PrintText(SConsolePos sPos, const std::string& rssText)
{
std::lock_guard<std::mutex> lock(m_mtxPrintToConsole);
SetCursorPos(sPos);
std::cout << rssText;
}

View File

@@ -0,0 +1,202 @@
#ifndef CONSOLE_OUTPUT_H
#define CONSOLE_OUTPUT_H
#include <iostream>
#include <string>
#include <functional>
#include <support/signal_support.h>
#include <support/app_control.h>
#include <support/component_impl.h>
#include <support/timer.h>
#include "signal_names.h"
#ifdef __unix__
#include <termios.h> // Needed for tcgetattr and fcntl
#include <unistd.h>
#endif
// VSS interfaces - located in ../generated/vss_files/include
#include "vss_vehiclechassisrearaxlerowwheel_bs_tx.h"
#include "vss_vehiclechassissteeringwheelangle_bs_rx.h"
#include "vss_vehiclesoftwareapplicationisactivecounter_bs_tx.h"
#include "vss_vehiclespeed_bs_rx.h"
// Complex service counter steering interface - located in ../generated/example_service
#include "countersteering.h"
/**
* @brief Console operation class.
* @details This class retrieves data from the data link, basix services and complex service and presents it in a regular interval.
* Furthermore, it runs in a loop and allows interaction with the complex service.
*/
class CConsole :
public vss::Vehicle::Chassis::SteeringWheel::AngleService::IVSS_SetSteeringWheel_Event, // Basic service interface
public vss::Vehicle::SpeedService::IVSS_SetSpeed_Event // Basic service interface
{
public:
/**
* @brief Screen position structure
*/
struct SConsolePos
{
uint32_t uiRow; ///< Row position (starts at 1)
uint32_t uiCol; ///< Column position (starts at 1)
};
/**
* @brief Constructor
* @attention Monitoring the data link can only occur when running as standalone. When connecting to a server, the datalink is
* not accessible from the application due to safety and security reasons.
* @param[in] bMonitorDatalink When set, register the signals to monitor the datalink.
*/
CConsole(bool bMonitorDatalink);
/**
* @brief Destructor
*/
~CConsole();
/**
* @brief Print the header.
* @param[in] bServer if true onnected to a running server, otherwise the application is running standalone
* @param[in] bSimulate if true signals are simulated, otherwise the signal came from data link
*/
void PrintHeader(const bool bServer, const bool bSimulate);
/**
* @brief Prepare the data consumers..
* @details Normally the application communicates through the complex services. As an example of how it could work, three levels
* of data consumers are prepared (as an example of how this could work): data link (if the monitor-data-link flag is set),
* basic service and complex service.
* @return Returns whether the preparation of the data consumers was successful or not.
*/
bool PrepareDataConsumers();
/**
* @brief Block this thread until CTRL+C is pressed.
*/
void RunUntilBreak();
private:
/**
* @brief Callback function when steering wheel signal has changed in disptach service.
* @remarks Only called when m_bMonitorDatalink is enabled (when running as standalone).
* @param[in] value The value of the signal to update.
*/
void DataLinkCallbackSteeringWheelAngle(sdv::any_t value);
/**
* @brief Callback function when speed signal has changed in disptach service.
* @remarks Only called when m_bMonitorDatalink is enabled (when running as standalone).
* @param[in] value The value of the signal to update.
*/
void DataLinkCallbackVehicleSpeed(sdv::any_t value);
/**
* @brief Read the data link TX signals, the basic service event values and the complex service values and print them into the
* console.
* @remarks Data link data is only shown when m_bMonitorDatalink is enabled (when running as standalone).
*/
void UpdateData();
/**
* @brief Set steering angle event. Overload of
* vss::Vehicle::Chassis::SteeringWheel::AngleService::IVSS_SetSteeringWheel_Event::SetSteeringWheel.
* @remarks This is an event function of the steering wheel basic service.
* @param[in] value Steering wheel angle in radials (-16...16 rad)
*/
virtual void SetSteeringWheel(float value) override;
/**
* @brief Speed event. Overload of vss::Vehicle::SpeedService::IVSS_SetSpeed_Event::SetSpeed.
* @remarks This is an event function of the vehicle speed service.
* @param[in] value Vehicle speed in m/s (0... 128 m/s)
*/
virtual void SetSpeed(float value) override;
/**
* @brief Get the cursor position of the console.
* @return The cursor position.
*/
SConsolePos GetCursorPos() const;
/**
* @brief Set the current cursor position for the console.
* @param[in] sPos Console position to place the current cursor at.
*/
void SetCursorPos(SConsolePos sPos);
/**
* @brief Print text at a specific location.
* @param[in] sPos The location to print text at.
* @param[in] rssText Reference to the text to print.
*/
void PrintText(SConsolePos sPos, const std::string& rssText);
/**
* @brief Print a value string at a specific location.
* @tparam TValue Type of value.
* @param[in] sPos The location to print the value at.
* @param[in] rssName Reference to the value.
* @param[in] tValue The value.
* @param[in] rssUnits Units the value is in.
*/
template <typename TValue>
void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssUnits);
/**
* @brief Align string between name and value.
* @param[in] message Reference to the message to align.
* @param[in] desiredLength The desired length or 0 when no length is specified.
* @return The aligned string.
*/
std::string AlignString(const std::string& message, uint32_t desiredLength = 0);
mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message
bool m_bMonitorDatalink = false; ///< When set, the console output monitors datalink data.
sdv::core::CSignal m_signalSteeringWheel; ///< steering wheel angle (input) - datalink monitoring
sdv::core::CSignal m_signalSpeed; ///< speed (input) - datalink monitoring
sdv::core::CSignal m_signalRearAxleAngle; ///< rear angle (output) - datalink monitoring
sdv::core::CSignal m_signalCounter; ///< simple counter (output) - datalink monitoring
float m_fDLSteeringWheelAngle = 0.0f; ///< default value (input signal) - datalink monitoring
float m_fDLVehicleSpeed = 0.0f; ///< default value (input signal) - datalink monitoring
float m_fSteeringWheelAngle = 0.0f; ///< Steering wheel angle - basic service event value
float m_fVehicleSpeed = 0.0f; ///< Vehicle speed - basic service event value
ICounterSteeringService* m_pCounterSteeringSvc = nullptr; ///< Counter steering service interface pointer.
#ifdef _WIN32
DWORD m_dwConsoleOutMode = 0u; ///< The console mode before switching on ANSI support.
DWORD m_dwConsoleInMode = 0u; ///< The console mode before switching on ANSI support.
#elif defined __unix__
struct termios m_sTermAttr{}; ///< The terminal attributes before disabling echo.
int m_iFileStatus = 0; ///< The file status flags for STDIN.
#else
#error The OS is not supported!
#endif
};
template <typename TValue>
inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssUnits)
{
const size_t nValueNameLen = 26;
std::stringstream sstreamValueText;
sstreamValueText << rssName <<
std::string(nValueNameLen - std::min(rssName.size(), static_cast<size_t>(nValueNameLen - 1)) - 1, '.') <<
" " << std::fixed << std::setprecision(2) << tValue << " " << rssUnits << " ";
std::lock_guard<std::mutex> lock(m_mtxPrintToConsole);
SetCursorPos(sPos);
std::cout << sstreamValueText.str();
}
template <>
inline void CConsole::PrintValue<bool>(SConsolePos sPos, const std::string& rssName, bool bValue, const std::string& rssUnits)
{
PrintValue(sPos, rssName, bValue ? "yes" : "no", rssUnits);
}
#endif // !define CONSOLE_OUTPUT_H

View File

@@ -0,0 +1,222 @@
#include "control.h"
CExampleControl::~CExampleControl()
{
Shutdown();
}
bool CExampleControl::Initialize()
{
if (m_bInitialized) return true;
// Set the SDV V-API framework directory.
if (!m_pathFramework.empty())
m_appcontrol.SetFrameworkRuntimeDirectory(m_pathFramework);
// Start the framework
std::stringstream sstreamAppConfig;
sstreamAppConfig << "[Application]" << std::endl;
sstreamAppConfig << "Mode=\"" << (RunAsStandaloneApp() ? "Standalone" : "External") << "\"" << std::endl;
sstreamAppConfig << "Instance=" << m_uiInstance << std::endl;
sstreamAppConfig << "Retries=" << 6 << std::endl;
sstreamAppConfig << "[Console]" << std::endl;
sstreamAppConfig << "Report=";
switch (m_eReporting)
{
case EAppControlReporting::normal:
sstreamAppConfig << "\"Normal\"";
break;
case EAppControlReporting::verbose:
sstreamAppConfig << "\"Verbose\"";
break;
default:
sstreamAppConfig << "\"Silent\"";
break;
}
sstreamAppConfig << std::endl;
if (!m_appcontrol.Startup(sstreamAppConfig.str())) return false;
// Local configurations only when running as standalone.
if (RunAsStandaloneApp())
{
// Start in configuration mode.
m_appcontrol.SetConfigMode();
if (!m_appcontrol.AddConfigSearchDir("config"))
{
m_appcontrol.Shutdown();
return false;
}
}
m_bInitialized = true;
return true;
}
void CExampleControl::Shutdown()
{
if (!m_bInitialized)
m_appcontrol.Shutdown();
m_bInitialized = false;
}
bool CExampleControl::IsInitialized() const
{
return m_bInitialized;
}
bool CExampleControl::LoadConfigFile(const std::string& inputMsg, const std::string& configFileName)
{
if (!m_bInitialized && m_eRunAs == ERunAs::standalone) return false;
std::string msg = inputMsg;
if (m_appcontrol.LoadConfig(configFileName) == sdv::core::EConfigProcessResult::successful)
{
msg.append("ok\n");
std::cout << msg.c_str();
return true;
}
msg.append("FAILED.\n");
std::cout << msg.c_str();
return false;
}
void CExampleControl::StartTestRun()
{
if (!m_bInitialized) return;
// Switch to running mode.
m_appcontrol.SetRunningMode();
// Should the datalink be simulated?
if (m_eRunAs != ERunAs::standalone_simulated) return;
// Start the simulated datalink
m_bRunning = true;
m_threadSimulateDatalink = std::thread(&CExampleControl::SimulateDatalinkThreadFunc, this);
}
void CExampleControl::StopTestRun()
{
// Stop running and wait for any thread to finalize
m_bRunning = false;
if (m_threadSimulateDatalink.joinable())
m_threadSimulateDatalink.join();
}
bool CExampleControl::HasCommandLineError() const
{
return m_bCmdLnError;
}
bool CExampleControl::HasRequestedCommandLineHelp() const
{
return m_bCmdLnHelp;
}
CExampleControl::ERunAs CExampleControl::GetAppOperation() const
{
return m_eRunAs;
}
bool CExampleControl::IsSimulationMode() const
{
return m_eRunAs == ERunAs::standalone_simulated;
}
bool CExampleControl::RunAsStandaloneApp() const
{
return m_eRunAs == ERunAs::standalone || m_eRunAs == ERunAs::standalone_simulated;
}
bool CExampleControl::RunAsServerApp() const
{
return m_eRunAs == ERunAs::server_connect;
}
bool CExampleControl::RegisterSignalsSimDatalink()
{
if (!m_bInitialized) return false;
if (m_eRunAs != ERunAs::standalone_simulated) return true; // Nothing to do...
std::string msg = "Register all signals: ";
sdv::core::CDispatchService dispatch;
m_signalSteeringWheel = dispatch.RegisterRxSignal(demo::dsWheelAngle);
m_signalSpeed = dispatch.RegisterRxSignal(demo::dsVehicleSpeed);
m_signalRearAngle = dispatch.RegisterTxSignal(demo::dsAxleAngle, 0);
m_signalCounter = dispatch.RegisterTxSignal(demo::dsLiveCounter, 0);
if (m_signalSteeringWheel && m_signalSpeed && m_signalRearAngle && m_signalCounter)
std::cout << "Registration was successful\n";
else
std::cout << "ATTENTION! Registration failed\n";
auto allSignals = dispatch.GetRegisteredSignals();
msg.append("(number of signals == ");
msg.append(std::to_string(allSignals.size()));
msg.append(") ok\n");
std::cout << msg.c_str();
return true;
}
void CExampleControl::ResetSignalsSimDatalink()
{
if (m_eRunAs != ERunAs::standalone_simulated) return; // Nothing to do...
if (m_signalSteeringWheel)
m_signalSteeringWheel.Reset();
if (m_signalSpeed)
m_signalSpeed.Reset();
if (m_signalRearAngle)
m_signalRearAngle.Reset();
if (m_signalCounter)
m_signalCounter.Reset();
}
void CExampleControl::SimulateDatalinkThreadFunc()
{
if (m_eRunAs != ERunAs::standalone_simulated) return; // Nothing to do...
// Send fSteeringWheelAngle wheel angel from -16 to 16 radians and vice versa
// Send fVehicleSpeed from 0 to 12 m/s (43.2 km/h) and vice versa
float fSteeringWheelAngle = 0.0f;
float fVehicleSpeed = 0.0f;
m_signalSpeed.Write(fVehicleSpeed);
m_signalSteeringWheel.Write(fSteeringWheelAngle);
float fDeltaSteering = 0.1f;
float fDeltaSpeed = 0.1f;
while (m_bRunning)
{
fSteeringWheelAngle += fDeltaSteering;
if (fSteeringWheelAngle >= 15.999f)
{
fSteeringWheelAngle = 16.0f;
fDeltaSteering = -0.1f;
}
else if (fSteeringWheelAngle <= -16.0f)
{
fSteeringWheelAngle = -16.0f;
fDeltaSteering = 0.1f;
}
fVehicleSpeed += fDeltaSpeed;
if (fVehicleSpeed >= 12.0f)
{
fVehicleSpeed = 12.0f;
fDeltaSpeed = -1.0f;
}
else if (fVehicleSpeed <= 0.0f)
{
fVehicleSpeed = 0.0f;
fDeltaSpeed = 0.1f;
}
m_signalSteeringWheel.Write(fSteeringWheelAngle);
m_signalSpeed.Write(fVehicleSpeed);
std::this_thread::sleep_for(std::chrono::milliseconds(300));
}
}

View File

@@ -0,0 +1,330 @@
#ifndef EXMAPLE_UTILITY_H
#define EXMAPLE_UTILITY_H
#include <string>
#include <support/app_control.h>
#include <support/signal_support.h>
#ifdef _WIN32
#include <codecvt>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif
#include "signal_names.h"
/**
* @brief Utility handler for example demos
*/
class CExampleControl
{
public:
/**
* @brief Constructor with the command line arguments.
* @tparam TCharType Character type of the command line arguments.
* @param[in] iArgs Amount of arguments.
* @param[in] rgszArgs Array of argument strings. The first argument represents the application name.
*/
template <typename TCharType>
CExampleControl(int iArgs, const TCharType rgszArgs[]);
/**
* @brief Destructor.
*/
~CExampleControl();
/**
* @brief Initialize application control based on the command line arguments provided through the constructor.
* @return Return true on success; false when not.
*/
bool Initialize();
/**
* @brief Shutdown the system.
*/
void Shutdown();
/**
* @brief Is the control initialized?
* @return Returns whether control is initialized.
*/
bool IsInitialized() const;
/**
* @brief Register signals, required by the vehicle devices.
* @remarks Only valid for standalone applications.
* @pre Initialization must be done before.
* @param[in] inputMsg message string to be printed on console in case of success and failure
* @param[in] configFileName config toml file name
* @return Return true on success otherwise false
*/
bool LoadConfigFile(const std::string& inputMsg, const std::string& configFileName);
/**
* @brief Start the test run if not running before.
* @details Write the input signals. In case of data link the signals are written from a asc file by the can_com_sim.sdv component.
* Otherwise the signals are create and can be written directly.
*/
void StartTestRun();
/**
* @brief Stops a test run if currently running.
*/
void StopTestRun();
/**
* @brief Did a command line error occur?
* @return Returns whether a command line error occurred.
*/
bool HasCommandLineError() const;
/**
* @brief Was command line help requested?
* @return Returns whether a command line help was requested.
*/
bool HasRequestedCommandLineHelp() const;
/**
* @brief Application operation.
*/
enum class ERunAs
{
standalone, ///< This application runs as standalone using data link and can_com_sim.sdv which reads the CAN messages from an asc file.
standalone_simulated, ///< This application runs as standalone simulating data read/write without datalink.
server_connect, ///< Connect to a server and run as a client application.
};
/**
* @brief Get the current application operation (retrieved from command line).
* @return The application operation.
*/
ERunAs GetAppOperation() const;
/**
* @brief Is the simulation mode activated
* @return Returns whether simulation or data link is active.
*/
bool IsSimulationMode() const;
/**
* @brief Does the application run as a standalone application?
* @return Returns whether the application runs as a standalone application.
*/
bool RunAsStandaloneApp() const;
/**
* @brief Does the application connect to a server and run on the server?
* @return Returns whether the application runs on the server.
*/
bool RunAsServerApp() const;
/**
* @brief Register signals for datalink simulation; required by the vehicle devices.
* @return Returns 'true' on success; 'false' on failure.
*/
bool RegisterSignalsSimDatalink();
/**
* @brief Reset signals for datalink simulation; needed for a proper shutdown.
*/
void ResetSignalsSimDatalink();
private:
/**
* @brief Provide simulated signals until m_bRunning is disabled.
*/
void SimulateDatalinkThreadFunc();
/**
* @brief Reporting by the SDV V-API application control.
*/
enum class EAppControlReporting
{
silent, ///< No reporting by application control (default)
normal, ///< Normal reporting by application control
verbose, ///< Extensive reporting by application control
};
bool m_bInitialized = false; ///< Set when initialized.
bool m_bCmdLnError = false; ///< Command line error occurred.
bool m_bCmdLnHelp = false; ///< Command line help provided.
bool m_bRunning = false; ///< When set, the application is running.
ERunAs m_eRunAs = ERunAs::standalone; ///< Application operation.
EAppControlReporting m_eReporting = EAppControlReporting::silent; ///< Application control reporting.
uint32_t m_uiInstance = 1000u; ///< Server instance to connect to.
std::filesystem::path m_pathFramework; ///< Path to the SDV V-API framework.
sdv::app::CAppControl m_appcontrol; ///< App-control of SDV V-API.
std::thread m_threadSimulateDatalink; ///< Simulation datalink thread.
sdv::core::CSignal m_signalSteeringWheel; ///< Steering wheel angle signal (input) - simulated datalink
sdv::core::CSignal m_signalSpeed; ///< Speed signal (input) - simulated datalink
sdv::core::CSignal m_signalRearAngle; ///< Rear angle signal (output) - simulated datalink
sdv::core::CSignal m_signalCounter; ///< Simple counter signal (output) - simulated datalink
};
template <typename TCharType>
CExampleControl::CExampleControl(int iArgs, const TCharType rgszArgs[])
{
bool bStandalone = false;
bool bStandaloneSimulated = false;
bool bServerConnect = false;
bool bSilent = false;
bool bNormal = false;
bool bVerbose = false;
for (int i = 1; i < iArgs; i++)
{
#ifdef _WIN32
std::wstring wide_string = rgszArgs[i];
size_t size_needed = std::wcstombs(nullptr, wide_string.c_str(), 0);
std::string ssArg(size_needed, 0);
std::wcstombs(&ssArg[0], wide_string.c_str(), size_needed);
#else
std::string ssArg = rgszArgs[i];
#endif
#ifdef _WIN32
if (ssArg == "/?" || ssArg == "--help" || ssArg == "-?")
#else
if (ssArg == "--help" || ssArg == "-?")
#endif
{
m_bCmdLnHelp = true;
continue;
}
if (ssArg.substr(0, 10) == "--instance")
{
m_uiInstance = std::stoul(ssArg.substr(10));
continue;
}
#ifdef _WIN32
if (ssArg.substr(0, 2) == "/f" || ssArg.substr(0, 2) == "-f")
#else
if (ssArg.substr(0, 2) == "-f")
#endif
{
m_pathFramework = std::filesystem::u8path(ssArg.substr(2));
continue;
}
if (ssArg == "--standalone")
{
bStandalone = true;
continue;
}
if (ssArg == "--connect")
{
bServerConnect = true;
continue;
}
if (ssArg == "--simulate")
{
bStandaloneSimulated = true;
continue;
}
#ifdef _WIN32
if (ssArg == "/s" || ssArg == "-s" || ssArg == "--silent")
#else
if (ssArg == "-s" || ssArg == "--silent")
#endif
{
bSilent = true;
continue;
}
#ifdef _WIN32
if (ssArg == "/n" || ssArg == "-n" || ssArg == "--normal")
#else
if (ssArg == "-n" || ssArg == "--normal")
#endif
{
bSilent = true;
continue;
}
#ifdef _WIN32
if (ssArg == "/v" || ssArg == "-v" || ssArg == "--verbose")
#else
if (ssArg == "-v" || ssArg == "--verbose")
#endif
{
bVerbose = true;
continue;
}
// Argument not known
std::cerr << "ERROR: unknown argument '" << ssArg << "'" << std::endl;
m_bCmdLnError = true;
}
// Check for an invalid flag.
if (!m_bCmdLnError && (bStandalone || bStandaloneSimulated) && bServerConnect)
{
std::cerr << "ERROR: the application can run either as standalone application of connect to a server.";
m_bCmdLnError = true;
}
// Set to server connect if requested.
if (bServerConnect)
m_eRunAs = ERunAs::server_connect;
if (bStandaloneSimulated)
m_eRunAs = ERunAs::standalone_simulated;
// Check console output flags
if ((bSilent && bNormal) ||
(bSilent && bVerbose) ||
(bNormal && bSilent))
{
std::cerr << "ERROR: multiple settings for the SDV V-API application control console output.";
m_bCmdLnError = true;
}
// Set control app reporting mode if requested.
if (bNormal)
m_eReporting = EAppControlReporting::normal;
if (bVerbose)
m_eReporting = EAppControlReporting::verbose;
// Check for the framework runtime location
if (!m_bCmdLnError && m_pathFramework.empty())
{
const char* szSDVFramework = std::getenv("SDV_FRAMEWORK_RUNTIME");
if (szSDVFramework)
m_pathFramework = std::filesystem::u8path(szSDVFramework);
else
{
std::cerr << "ERROR: Missing path to SDV V-API framework. Use commandline argument -f or set "
"SDV_FRAMEWORK_RUNTIME environment variable." << std::endl;
m_bCmdLnError = true;
}
}
if (!m_bCmdLnError && !std::filesystem::is_directory(m_pathFramework) && !std::filesystem::exists(m_pathFramework / "core_services.sdv"))
{
std::cerr << "ERROR: Invalid path to SDV V-API framework." << std::endl;
m_bCmdLnError = true;
}
// Print help or error information
if (m_bCmdLnHelp || m_bCmdLnError)
{
std::cout << R"text(
Usage: system_demo_example <options>
--help, -? Show help
--instance<no> Set instance number for server connection (default = 1000).
-f<framework_location> Location of SDV V-API framework if SDV_FRAMEWORK_RUNTIME hasn't been set.
--standalone Run as standalone application (data link layer through can_com_sim.sdv which reads the CAN messages from an asc file).
--simulate Run as standalone application and simulate the data link layer.
--connect Connect to running server.
--silent, -s Start the SDV application control with console output in silent mode (default).
--normal, -n Start the SDV application control with console output in normal mode.
--verbose, -v Start the SDV application control with console output in verbose mode.
)text";
}}
#endif // ! defined EXMAPLE_UTILITY_H

View File

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

View File

@@ -0,0 +1,180 @@
#ifdef __unix__
#include <semaphore.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#endif
#include <iostream>
#include <string>
#include <filesystem>
#include <cstdlib>
#include "control.h"
#include "console.h"
#ifdef _WIN32
// Some old MinGW/CYGWIN distributions don't define this:
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
static HANDLE stdoutHandle, stdinHandle;
static DWORD outModeInit, inModeInit;
void setupConsole(void) {
DWORD outMode = 0, inMode = 0;
stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
if(stdoutHandle == INVALID_HANDLE_VALUE || stdinHandle == INVALID_HANDLE_VALUE) {
exit(GetLastError());
}
if(!GetConsoleMode(stdoutHandle, &outMode) || !GetConsoleMode(stdinHandle, &inMode)) {
exit(GetLastError());
}
outModeInit = outMode;
inModeInit = inMode;
// Enable ANSI escape codes
outMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
// Set stdin as no echo and unbuffered
inMode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
if(!SetConsoleMode(stdoutHandle, outMode) || !SetConsoleMode(stdinHandle, inMode)) {
exit(GetLastError());
}
}
void restoreConsole(void) {
// Reset colors
printf("\x1b[0m");
// Reset console mode
if(!SetConsoleMode(stdoutHandle, outModeInit) || !SetConsoleMode(stdinHandle, inModeInit)) {
exit(GetLastError());
}
}
#else
static struct termios orig_term;
static struct termios new_term;
void setupConsole(void) {
tcgetattr(STDIN_FILENO, &orig_term);
new_term = orig_term;
new_term.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &new_term);
}
void restoreConsole(void) {
// Reset colors
printf("\x1b[0m");
// Reset console mode
tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
}
#endif
void getCursorPosition(int *row, int *col) {
printf("\x1b[6n");
char buff[128];
int indx = 0;
for(;;) {
int cc = getchar();
buff[indx] = (char)cc;
indx++;
if(cc == 'R') {
buff[indx + 1] = '\0';
break;
}
}
sscanf(buff, "\x1b[%d;%dR", row, col);
fseek(stdin, 0, SEEK_END);
}
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int argc, wchar_t* argv[])
{
#else
extern "C" int main(int argc, char* argv[])
{
#endif
std::cout << "System demo example" << std::endl;
std::cout << "----------------------------------------------------------------------------" << std::endl;
// Start application control
CExampleControl control(argc, argv);
if (control.HasCommandLineError()) return -1; // Command line error occurred; cannot continue.
if (control.HasRequestedCommandLineHelp()) return 0; // Command line help requested; job done.
if (!control.Initialize())
{
std::cerr << "ERROR: Failed to initialize application control." << std::endl;
return -2;
}
// The console provides output.
CConsole console(control.RunAsStandaloneApp());
// Load configuration files when running as standalone
if (control.RunAsStandaloneApp())
{
bool bResult = control.LoadConfigFile("Load dispatch example: ", "data_dispatch_example.toml");
bResult &= control.LoadConfigFile("Load task timer: ", "task_timer_example.toml");
if (control.GetAppOperation() == CExampleControl::ERunAs::standalone)
{
std::cout << "Datalink enabled, load CAN Simulation device and datalink component." << std::endl;
bResult &= control.LoadConfigFile("Load can_com_simulation: ", "can_com_simulation.toml");
bResult &= control.LoadConfigFile("Load data link: ", "data_link_example.toml");
}
else
{
std::cout << "Datalink disabled, register the required signals for a simulated datalink." << std::endl;
control.RegisterSignalsSimDatalink();
}
bResult &= control.LoadConfigFile("Load vehicle_devices_basic_services_example: ", "vehicle_devices_basic_services_example.toml");
bResult &= control.LoadConfigFile("Load complex_service_example: ", "complex_service_example.toml");
if (!bResult)
{
std::cerr << std::endl << "ERROR: One or more configurations were not able to load. Cannot continue." << std::endl;
control.Shutdown();
return -3;
}
}
// Print the application header
console.PrintHeader(control.RunAsServerApp(), control.IsSimulationMode());
// Prepare for data consumption.
if (!console.PrepareDataConsumers())
{
std::cerr << std::endl << "ERROR: Cannot instantiate the data consumers. Cannot continue." << std::endl;
control.Shutdown();
return -4;
}
// Start the test run
control.StartTestRun();
// Run until break.
console.RunUntilBreak();
// Finish test run
control.StopTestRun();
// Reset the signals for a simulated datalink
if (control.GetAppOperation() == CExampleControl::ERunAs::standalone_simulated)
control.ResetSignalsSimDatalink();
// Shutdown the example control
control.Shutdown();
return 0;
}

View File

@@ -0,0 +1,62 @@
#include <iostream>
#include <string>
#include <filesystem>
#include "control.h"
#include "countersteering.h"
/**
* @brief check if SDV_FRAMEWORK_RUNTIME environment variable exists
* @return Return true if environment variable is found otherwise false
*/
bool IsSDVFrameworkEnvironmentSet()
{
const char* envVariable = std::getenv("SDV_FRAMEWORK_RUNTIME");
if (envVariable)
{
return true;
}
return false;
}
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain()
#else
extern "C" int main()
#endif
{
uint32_t uiInstance = 3001;
sdv::app::CAppControl appcontrol;
if (!IsSDVFrameworkEnvironmentSet())
{
// if SDV_FRAMEWORK_RUNTIME environment variable is not set we need to set the Framework Runtime directory
appcontrol.SetFrameworkRuntimeDirectory("../../bin");
}
appcontrol.AddModuleSearchDir("../../bin");
std::stringstream sstreamAppConfig;
sstreamAppConfig << "[Application]" << std::endl;
sstreamAppConfig << "Mode=\"External\"" << std::endl;
sstreamAppConfig << "Instance=" << uiInstance << std::endl;
sstreamAppConfig << "Retries=" << 10 << std::endl;
sstreamAppConfig << "[Console]" << std::endl;
sstreamAppConfig << "Report=\"Verbose\"" << std::endl;
// Start the framework as external application, which wants to connect to instance 3001
if (!appcontrol.Startup(sstreamAppConfig.str()))
{
std::cout << "appcontrol.Startup() failed." << std::endl;
return false;
}
auto pCounterSteeringSvc = sdv::core::GetObject("Counter Steering Example Service").GetInterface<ICounterSteeringService>();
if (!pCounterSteeringSvc)
{
std::cout << "\nERROR: Could not get 'ICounterSteeringService', connection to " << std::to_string (uiInstance) << "probably failed.\n" << std::endl;
return false;
}
std::cout << "\nSUCCESS: Got 'ICounterSteeringService', could connect to instance." << std::to_string (uiInstance) << "\n" << std::endl;
return true;
}