connection between 2 systems and bug fixes (#14)

This commit is contained in:
tompzf
2026-07-03 14:53:48 +02:00
committed by GitHub
parent 0fb5660d27
commit 46842200f1
284 changed files with 35200 additions and 8265 deletions

View File

@@ -25,7 +25,7 @@ add_executable(sdv_control
"startup_shutdown.h"
"startup_shutdown.cpp"
"context.h"
"print_table.h" "start_stop_service.cpp" "start_stop_service.h" "installation.h" "installation.cpp")
"print_table.h" "start_stop_service.cpp" "start_stop_service.h" "installation.h" "installation.cpp" "connection_config.h" "connection_config.cpp")
target_link_libraries(sdv_control ${CMAKE_DL_LIBS})

View File

@@ -0,0 +1,471 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include "connection_config.h"
#include "../../global/cmdlnparser/cmdlnparser.h"
#include <support/toml.h>
#include <interfaces/app.h>
#include "../error_msg.h"
#include <charconv>
void ListenerHelp(const SContext& rsContext)
{
// First argument should be "LISTENER"
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing listener command..." << std::endl;
return;
}
if (iequals(rsContext.seqCmdLine[0], "LISTENER"))
{
CCommandLine::PrintHelpText(std::cout,
"Usage: sdv_control LISTENER ADD <name> <provider> <parameters...> [options...]\n"
" sdv_control LISTENER REMOVE <name> [options...]\n"
" sdv_control LISTENER SHOW <name> [options...]\n\n"
"Configure the system to add (or update) or remove a listener. The command doesn't check for valid parameters. A "
"server restart is required for the new listener settings to take effect.\n\n"
"A list of actively configured listeners can be requested by executing \"sdv_control LIST LISTENERS\"\n\n"
"Note: if not explicitly added, a default listener will added to the list for the communication between isolated "
"applications and the core system.\n\n");
return;
}
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid listener command..." << std::endl;
}
void ConnectionHelp(const SContext& rsContext)
{
// First argument should be "CONNECTION"
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing connection command..." << std::endl;
return;
}
if (iequals(rsContext.seqCmdLine[0], "CONNECTION"))
{
CCommandLine::PrintHelpText(std::cout,
"Usage: sdv_control CONNECTION ADD <name> <provider> <parameters...> [options...]\n"
" sdv_control CONNECTION REMOVE <name> [options...]\n"
" sdv_control CONNECTION SHOW <name> [options...]\n\n"
"Configure the system to add (or update) or remove a connection. The command doesn't check for valid parameters. A "
"server restart is required for the new connection settings to take effect.\n\n"
"A list of actively configured connections can be requested by executing \"sdv_control LIST CONNECTIONS\"\n\n"
"Note: if not explicitly added, a default connetion will be added to the list for the communication between external "
"applications and the core system.\n\n"
"Options:\n"
" --insert_before<string> With ADD, Insert the connection before an existing connection with the supplied name.\n\n");
return;
}
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid connection command..." << std::endl;
}
int ConfigureListener(const SContext& rsContext)
{
// First argument should be "CONNECTION"
if (rsContext.seqCmdLine.size() < 2)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listener command... " << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "LISTENER"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command: " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Which list command is requested
enum class ECommand { unknown, add, remove, show } eCommand = ECommand::unknown;
if (iequals(rsContext.seqCmdLine[1], "ADD")) eCommand = ECommand::add;
else if (iequals(rsContext.seqCmdLine[1], "REMOVE")) eCommand = ECommand::remove;
else if (iequals(rsContext.seqCmdLine[1], "SHOW")) eCommand = ECommand::show;
else
{
if (!rsContext.bSilent)
{
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid listener command." << std::endl;
ListenerHelp(rsContext);
}
return CMDLN_ARG_ERR;
}
if (rsContext.seqCmdLine.size() < 3)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listener name... " << std::endl;
return CMDLN_ARG_ERR;
}
// Get the name
std::string ssListener = rsContext.seqCmdLine[2];
switch (eCommand)
{
case ECommand::remove:
return RemoveListener(ssListener, rsContext);
case ECommand::show:
return ShowListenerConfig(ssListener, rsContext);
case ECommand::add:
default:
break;
}
// For add, check for provider and parameters
if (rsContext.seqCmdLine.size() < 4)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listener provider... " << std::endl;
return CMDLN_ARG_ERR;
}
std::string ssProvider = rsContext.seqCmdLine[3];
sdv::toml::CTOMLParser parser;
parser.AddValue("Provider.Name", ssProvider);
// Check for parameters
// Parameters have the format "param=value". It is possible that parameters have additional sub-tables. Then the format is:
// "table.param=value".
sdv::toml::CNodeCollection ipc_channel_table = parser.AddTable("IpcChannel");
for (size_t nIndex = 4; nIndex < rsContext.seqCmdLine.size(); nIndex++)
{
std::string ssRawParam = rsContext.seqCmdLine[nIndex];
// Search for the equal sign
size_t nEqualPos = ssRawParam.find_first_of('=');
std::string ssParamName = ssRawParam.substr(0, nEqualPos);
while (!ssParamName.empty() && std::isspace(ssParamName.back()))
ssParamName.pop_back();
if (ssParamName.empty() || nEqualPos == std::string::npos)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid listener parameter: " << ssRawParam << std::endl;
return CMDLN_ARG_ERR;
}
sdv::any_t anyValue;
size_t nValuePos = nEqualPos + 1;
while (nValuePos < ssRawParam.size() && std::isspace(ssRawParam[nValuePos]))
nValuePos++;
if (nValuePos < ssRawParam.size())
{
std::string ssValue = ssRawParam.substr(nValuePos);
if (!ssValue.empty() && ssValue.find_first_not_of("-0123456789") == std::string::npos)
{
int64_t iValue = 0;
if (std::from_chars(ssValue.data(), ssValue.data() + ssValue.size(), iValue).ec == std::errc())
anyValue = iValue;
else
anyValue = ssValue;
}
else
anyValue = ssValue;
}
ipc_channel_table.AddValue(ssParamName, anyValue);
}
return AddListener(ssListener, parser.GetTOML(), rsContext);
}
int AddListener(const std::string& rssListener, const std::string& rssConfig, const SContext& rsContext)
{
// Load the settings.
auto ptrAppService = sdv::core::GetObject("AppSettingsService");
sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface<sdv::app::IAppSettingsPersist>();
sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface<sdv::app::IAppConnections>();
if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl;
return CANNOT_LOAD_SETTINGS;
}
// Add the listener
if (!pAppConnections->AddListenerConfig(rssListener, rssConfig))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << ADD_LISTENER_ERROR_MSG << std::endl;
return ADD_LISTENER_ERROR;
}
// Save the settings.
if (!pAppSettingsPersist->SaveSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_SAVE_SETTINGS_MSG << std::endl;
return CANNOT_SAVE_SETTINGS;
}
return NO_ERROR;
}
int RemoveListener(const std::string& rssListener, const SContext& rsContext)
{
// Load the settings.
auto ptrAppService = sdv::core::GetObject("AppSettingsService");
sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface<sdv::app::IAppSettingsPersist>();
sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface<sdv::app::IAppConnections>();
if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl;
return CANNOT_LOAD_SETTINGS;
}
// Remove the listener
if (!pAppConnections->RemoveListenerConfig(rssListener))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REMOVE_LISTENER_ERROR_MSG << std::endl;
return REMOVE_LISTENER_ERROR;
}
// Save the settings.
if (!pAppSettingsPersist->SaveSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_SAVE_SETTINGS_MSG << std::endl;
return CANNOT_SAVE_SETTINGS;
}
return NO_ERROR;
}
int ShowListenerConfig(const std::string& rssListener, const SContext& rsContext)
{
// Load the settings.
auto ptrAppService = sdv::core::GetObject("AppSettingsService");
sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface<sdv::app::IAppSettingsPersist>();
sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface<sdv::app::IAppConnections>();
if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl;
return CANNOT_LOAD_SETTINGS;
}
// Remove the listener
std::string ssConfig = pAppConnections->GetListenerConfig(rssListener);
if (ssConfig.empty())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << SHOW_LISTENER_ERROR_MSG << std::endl;
return SHOW_LISTENER_ERROR;
}
std::cout << ssConfig << std::endl;
return NO_ERROR;
}
int ConfigureConnection(const SContext& rsContext)
{
// First argument should be "CONNECTION"
if (rsContext.seqCmdLine.size() < 2)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing connection command... " << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "CONNECTION"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command: " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Which list command is requested
enum class ECommand { unknown, add, remove, show } eCommand = ECommand::unknown;
if (iequals(rsContext.seqCmdLine[1], "ADD")) eCommand = ECommand::add;
else if (iequals(rsContext.seqCmdLine[1], "REMOVE")) eCommand = ECommand::remove;
else if (iequals(rsContext.seqCmdLine[1], "SHOW")) eCommand = ECommand::show;
else
{
if (!rsContext.bSilent)
{
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid connection command." << std::endl;
ConnectionHelp(rsContext);
}
return CMDLN_ARG_ERR;
}
if (rsContext.seqCmdLine.size() < 3)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing connection name... " << std::endl;
return CMDLN_ARG_ERR;
}
// Get the name
std::string ssConnection = rsContext.seqCmdLine[2];
switch (eCommand)
{
case ECommand::remove:
return RemoveConnection(ssConnection, rsContext);
case ECommand::show:
return ShowConnectionConfig(ssConnection, rsContext);
case ECommand::add:
default:
break;
}
// For add, check for provider and parameters
if (rsContext.seqCmdLine.size() < 4)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing connection provider... " << std::endl;
return CMDLN_ARG_ERR;
}
std::string ssProvider = rsContext.seqCmdLine[3];
sdv::toml::CTOMLParser parser;
parser.AddValue("Provider.Name", ssProvider);
// Check for parameters
// Parameters have the format "param=value". It is possible that parameters have additional sub-tables. Then the format is:
// "table.param=value".
sdv::toml::CNodeCollection ipc_channel_table = parser.AddTable("IpcChannel");
for (size_t nIndex = 4; nIndex < rsContext.seqCmdLine.size(); nIndex++)
{
std::string ssRawParam = rsContext.seqCmdLine[nIndex];
// Search for the equal sign
size_t nEqualPos = ssRawParam.find_first_of('=');
std::string ssParamName = ssRawParam.substr(0, nEqualPos);
while (!ssParamName.empty() && std::isspace(ssParamName.back()))
ssParamName.pop_back();
if (ssParamName.empty() || nEqualPos == std::string::npos)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid listener parameter: " << ssRawParam << std::endl;
return CMDLN_ARG_ERR;
}
sdv::any_t anyValue;
size_t nValuePos = nEqualPos + 1;
while (nValuePos < ssRawParam.size() && std::isspace(ssRawParam[nValuePos]))
nValuePos++;
if (nValuePos < ssRawParam.size())
{
std::string ssValue = ssRawParam.substr(nValuePos);
if (!ssValue.empty() && ssValue.find_first_not_of("-0123456789") == std::string::npos)
{
int64_t iValue = 0;
if (std::from_chars(ssValue.data(), ssValue.data() + ssValue.size(), iValue).ec == std::errc())
anyValue = iValue;
else
anyValue = ssValue;
}
else
anyValue = ssValue;
}
ipc_channel_table.AddValue(ssParamName, anyValue);
}
return AddConnection(ssConnection, parser.GetTOML(), rsContext);
}
int AddConnection(const std::string& rssConnection, const std::string& rssConfig, const SContext& rsContext)
{
// Load the settings.
auto ptrAppService = sdv::core::GetObject("AppSettingsService");
sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface<sdv::app::IAppSettingsPersist>();
sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface<sdv::app::IAppConnections>();
if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl;
return CANNOT_LOAD_SETTINGS;
}
// Add the connection
if (!pAppConnections->AddConnectionConfig(rssConnection, rssConfig, rsContext.ssInsertBeforeConnection))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << ADD_CONNECTION_ERROR_MSG << std::endl;
return ADD_CONNECTION_ERROR;
}
// Save the settings.
if (!pAppSettingsPersist->SaveSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_SAVE_SETTINGS_MSG << std::endl;
return CANNOT_SAVE_SETTINGS;
}
return NO_ERROR;
}
int RemoveConnection(const std::string& rssConnection, const SContext& rsContext)
{
// Load the settings.
auto ptrAppService = sdv::core::GetObject("AppSettingsService");
sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface<sdv::app::IAppSettingsPersist>();
sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface<sdv::app::IAppConnections>();
if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl;
return CANNOT_LOAD_SETTINGS;
}
// Remove the connection
if (!pAppConnections->RemoveConnectionConfig(rssConnection))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REMOVE_CONNECTION_ERROR_MSG << std::endl;
return REMOVE_CONNECTION_ERROR;
}
// Save the settings.
if (!pAppSettingsPersist->SaveSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_SAVE_SETTINGS_MSG << std::endl;
return CANNOT_SAVE_SETTINGS;
}
return NO_ERROR;
}
int ShowConnectionConfig(const std::string& rssConnection, const SContext& rsContext)
{
// Load the settings.
auto ptrAppService = sdv::core::GetObject("AppSettingsService");
sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface<sdv::app::IAppSettingsPersist>();
sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface<sdv::app::IAppConnections>();
if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl;
return CANNOT_LOAD_SETTINGS;
}
// Remove the connection
std::string ssConfig = pAppConnections->GetConnectionConfig(rssConnection);
if (ssConfig.empty())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << SHOW_CONNECTION_ERROR_MSG << std::endl;
return SHOW_CONNECTION_ERROR;
}
std::cout << ssConfig << std::endl;
return NO_ERROR;
}

View File

@@ -0,0 +1,98 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#ifndef CONNECTION_CONFIG_H
#define CONNECTION_CONFIG_H
#include "context.h"
#include <support/sequence.h>
#include <support/string.h>
/**
* @brief Help for configuring listeners.
* @param[in] rsContext Reference to the context.
*/
void ListenerHelp(const SContext& rsContext);
/**
* @brief Help for configuring connections.
* @param[in] rsContext Reference to the context.
*/
void ConnectionHelp(const SContext& rsContext);
/**
* @brief Configure the listener.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int ConfigureListener(const SContext& rsContext);
/**
* @brief Add a listener to the system settings.
* @param[in] rssListener Reference to the string containing the listener configuration name.
* @param[in] rssConfig Reference to the string containing the listener configuration.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int AddListener(const std::string& rssListener, const std::string& rssConfig, const SContext& rsContext);
/**
* @brief Remove a listener from the system settings.
* @param[in] rssListener Reference to the string containing the listener configuration name.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int RemoveListener(const std::string& rssListener, const SContext& rsContext);
/**
* @brief Show a listener configuration.
* @param[in] rssListener Reference to the string containing the listener configuration name.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int ShowListenerConfig(const std::string& rssListener, const SContext& rsContext);
/**
* @brief Configure the connection.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int ConfigureConnection(const SContext& rsContext);
/**
* @brief Add a connection to the system settings.
* @param[in] rssConnection Reference to the string containing the connection configuration name.
* @param[in] rssConfig Reference to the string containing the connection configuration.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int AddConnection(const std::string& rssConnection, const std::string& rssConfig, const SContext& rsContext);
/**
* @brief Remove a connection from the system settings.
* @param[in] rssConnection Reference to the string containing the connection configuration name.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int RemoveConnection(const std::string& rssConnection, const SContext& rsContext);
/**
* @brief Show a connection configuration.
* @param[in] rssConnection Reference to the string containing the connection configuration name.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int ShowConnectionConfig(const std::string& rssConnection, const SContext& rsContext);
#endif // !defined CONNECTION_CONFIG_H

View File

@@ -33,6 +33,7 @@ struct SContext
bool bListShort = false; ///< Print only a shortened list with one column.
sdv::sequence<sdv::u8string> seqCmdLine; ///< The commands provided on the command line.
std::filesystem::path pathInstallDir; ///< Optional installation directory.
std::string ssInsertBeforeConnection; ///< Insert before the provided connection
};
#include <cctype> // std::tolower

View File

@@ -88,7 +88,7 @@ int Install(const SContext& rsContext)
ssName = rsContext.seqCmdLine[1];
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository();
if (!ptrRepository)
{
if (!rsContext.bSilent)

View File

@@ -14,8 +14,10 @@
#include "list_elements.h"
#include "print_table.h"
#include <interfaces/config.h>
#include <interfaces/app.h>
#include <support/sdv_core.h>
#include <support/local_service_access.h>
#include <support/toml.h>
#include "../../global/cmdlnparser/cmdlnparser.h"
#include "../../global/exec_dir_helper.h"
#include "../../global/flags.h"
@@ -38,13 +40,14 @@ void ListHelp(const SContext& rsContext)
}
// Which list command help is requested
enum class EListCommand { unknown, modules, classes, components, installations, connections } eListCommand = EListCommand::unknown;
enum class EListCommand { unknown, modules, classes, components, installations, listeners, connections } eListCommand = EListCommand::unknown;
if (rsContext.seqCmdLine.size() >= 2)
{
if (iequals(rsContext.seqCmdLine[1], "MODULES")) eListCommand = EListCommand::modules;
else if (iequals(rsContext.seqCmdLine[1], "CLASSES")) eListCommand = EListCommand::classes;
else if (iequals(rsContext.seqCmdLine[1], "COMPONENTS")) eListCommand = EListCommand::components;
else if (iequals(rsContext.seqCmdLine[1], "INSTALLATIONS")) eListCommand = EListCommand::installations;
else if (iequals(rsContext.seqCmdLine[1], "LISTENERS")) eListCommand = EListCommand::listeners;
else if (iequals(rsContext.seqCmdLine[1], "CONNECTIONS")) eListCommand = EListCommand::connections;
}
@@ -65,20 +68,24 @@ void ListHelp(const SContext& rsContext)
case EListCommand::installations:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST INSTALLATIONS [options...]\n\nShow a list of all installations.\n");
break;
case EListCommand::listeners:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST LISTENERS [options...]\n\nShow a list of all configured listeners.\n");
break;
case EListCommand::connections:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST CONNECTIONS [options...]\n\nShow a list of all connected applications.\n");
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST CONNECTIONS [options...]\n\nShow a list of all configured connections.\n");
break;
default:
CCommandLine::PrintHelpText(std::cout, R"code(Usage: sdv_control LIST <list_command> [options...]
CCommandLine::PrintHelpText(std::cout, R"text(Usage: sdv_control LIST <list_command> [options...]
Supported listing commands:
LIST MODULES Show a list of loaded server modules.
LIST CLASSES Show a list of available component classes.
LIST COMPONENTS Show a list of instantiated server components
LIST INSTALLATIONS Show a list of installations.
LIST LISTENERS Show a list of current listeners.
LIST CONNECTIONS Show a list of current connections.
)code");
)text");
break;
}
std::cout << "Options:\n";
@@ -103,33 +110,39 @@ int ListElements(const SContext& rsContext, std::ostream& rstream /*= std::cout*
return CMDLN_ARG_ERR;
}
// Which list command help is requested
enum class EListCommand { unknown, modules, classes, components, installations, connections } eListCommand = EListCommand::unknown;
// Which list command is requested
enum class EListCommand { unknown, modules, classes, components, installations, listeners, connections } eListCommand = EListCommand::unknown;
if (rsContext.seqCmdLine.size() >= 2)
{
if (iequals(rsContext.seqCmdLine[1], "MODULES")) eListCommand = EListCommand::modules;
else if (iequals(rsContext.seqCmdLine[1], "CLASSES")) eListCommand = EListCommand::classes;
else if (iequals(rsContext.seqCmdLine[1], "COMPONENTS")) eListCommand = EListCommand::components;
else if (iequals(rsContext.seqCmdLine[1], "INSTALLATIONS")) eListCommand = EListCommand::installations;
else if (iequals(rsContext.seqCmdLine[1], "LISTENERS")) eListCommand = EListCommand::listeners;
else if (iequals(rsContext.seqCmdLine[1], "CONNECTIONS")) eListCommand = EListCommand::connections;
}
else
{
if (!rsContext.bSilent)
{
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listing command.." << std::endl << std::endl;
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listing command." << std::endl;
ListHelp(rsContext);
}
return CMDLN_ARG_ERR;
}
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
// Try to connect (except when listing the listeners and connections... they can be read from the settings).
sdv::TObjectPtr ptrRepository;
if (eListCommand != EListCommand::listeners && eListCommand != EListCommand::connections)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << "." << std::endl;
return CONNECT_SDV_SERVER_ERROR;
ptrRepository = sdv::com::ConnectToLocalServerRepository();
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << "."
<< std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
}
int iRet = -100;
@@ -147,12 +160,16 @@ int ListElements(const SContext& rsContext, std::ostream& rstream /*= std::cout*
case EListCommand::installations:
iRet = ListInstallations(rsContext, ptrRepository, rstream);
break;
case EListCommand::listeners:
iRet = ListListeners(rsContext, rstream);
break;
case EListCommand::connections:
iRet = ListConnections(rsContext, ptrRepository, rstream);
iRet = ListConnections(rsContext, rstream);
break;
default:
break;
}
rstream << std::endl;
return iRet;
}
@@ -388,8 +405,107 @@ int ListInstallations(const SContext& rsContext, const sdv::TObjectPtr& rptrRepo
return NO_ERROR;
}
int ListConnections(const SContext& /*rsContext*/, const sdv::TObjectPtr& /*rptrRepository*/, std::ostream& /*rstream = std::cout */ )
std::string BuildParameters(const sdv::toml::CNodeCollection& rtable, const std::string& ssPrefix = std::string())
{
std::cout << "ERROR: " << NOT_IMPLEMENTED_MSG << " :-(" << std::endl;
return NOT_IMPLEMENTED;
std::string ssParams;
for (uint32_t uiIndex = 0; uiIndex < rtable.GetCount(); uiIndex++)
{
sdv::toml::CNode node = rtable.Get(uiIndex);
if (!node) continue;
std::string ssNodeText;
if (node.GetType() == sdv::toml::ENodeType::node_table)
{
sdv::toml::CNodeCollection tableChild(node);
if (!tableChild) continue;
ssNodeText = BuildParameters(tableChild, static_cast<std::string>(tableChild.GetName()) + ".");
}
else if (node.GetType() == sdv::toml::ENodeType::node_array)
{
// TODO...
}
else
ssNodeText = ssPrefix + node.GetName() + "=" + node.GetValueAsString();
// Add the text
if (!ssParams.empty())
ssParams += ", ";
ssParams += ssNodeText;
}
return ssParams;
}
int ListListeners(const SContext& rsContext, std::ostream& rstream /*= std::cout */ )
{
std::vector<std::array<std::string, 3>> vecListenerList;
vecListenerList.push_back({"Listener", "Provider", "Parameters"});
// Request the settings.
auto ptrAppService = sdv::core::GetObject("AppSettingsService");
sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface<sdv::app::IAppSettingsPersist>();
sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface<sdv::app::IAppConnections>();
if (!pAppSettingsPersist || !pAppConnections)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl;
return CANNOT_LOAD_SETTINGS;
}
auto seqListeners = pAppConnections->GetListeners();
for (const sdv::u8string& rssListener : seqListeners)
{
std::string ssListener = rssListener;
auto ssConfig = pAppConnections->GetListenerConfig(rssListener);
sdv::toml::CTOMLParser parser(ssConfig);
if (!parser)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << PROCESS_LISTENER_CONFIG_ERROR_MSG << std::endl;
return PROCESS_LISTENER_CONFIG_ERROR;
}
std::string ssProvider = parser.GetDirect("Provider.Name").GetValueAsString();
std::string ssParams = BuildParameters(parser.GetDirect("IpcChannel"));
vecListenerList.push_back({ssListener, ssProvider, ssParams});
}
PrintTable(vecListenerList, rstream, rsContext.bListNoHdr);
if (vecListenerList.size() < 2)
rstream << " No listeners configured!" << std::endl;
return NO_ERROR;
}
int ListConnections(const SContext& rsContext, std::ostream& rstream /*= std::cout */ )
{
std::vector<std::array<std::string, 3>> vecConnectionList;
vecConnectionList.push_back({"Connection", "Provider", "Parameters"});
// Request the settings.
auto ptrAppService = sdv::core::GetObject("AppSettingsService");
sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface<sdv::app::IAppSettingsPersist>();
sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface<sdv::app::IAppConnections>();
if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings())
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl;
return CANNOT_LOAD_SETTINGS;
}
auto seqConnections = pAppConnections->GetConnections();
for (const sdv::u8string& rssConnection : seqConnections)
{
std::string ssConnection = rssConnection;
auto ssConfig = pAppConnections->GetConnectionConfig(rssConnection);
sdv::toml::CTOMLParser parser(ssConfig);
if (!parser)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << PROCESS_CONNECTION_CONFIG_ERROR_MSG << std::endl;
return PROCESS_CONNECTION_CONFIG_ERROR;
}
std::string ssProvider = parser.GetDirect("Provider.Name").GetValueAsString();
std::string ssParams = BuildParameters(parser.GetDirect("IpcChannel"));
vecConnectionList.push_back({ssConnection, ssProvider, ssParams});
}
PrintTable(vecConnectionList, rstream, rsContext.bListNoHdr);
if (vecConnectionList.size() < 2)
rstream << " No connections configured!" << std::endl;
return NO_ERROR;
}

View File

@@ -71,12 +71,19 @@ int ListComponents(const SContext& rsContext, const sdv::TObjectPtr& rptrReposit
int ListInstallations(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
/**
* @brief List the current connections.
* @brief List the current configured listeners.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListConnections(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
int ListListeners(const SContext& rsContext, std::ostream& rstream);
/**
* @brief List the current connections.
* @param[in] rsContext Reference to the context.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListConnections(const SContext& rsContext, std::ostream& rstream);
#endif // !defined LIST_ELEMENTS_H

View File

@@ -27,6 +27,7 @@
#include "list_elements.h"
#include "start_stop_service.h"
#include "installation.h"
#include "connection_config.h"
#include "../error_msg.h"
/**
@@ -53,12 +54,6 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
// NOTE EVE 27.05.2025: This task has been taken over by the process watchdog.
CProcessWatchdog watchdog;
// If not set, set the runtime location to the EXE directory.
if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty())
sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory());
if (sdv::app::CAppControl::GetComponentInstallDirectory().empty())
sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory());
CCommandLine cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character));
SContext sContext;
bool bError = false;
@@ -67,24 +62,40 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
std::string ssArgError;
try
{
auto& rArgHelpDef = cmdln.DefineOption("?", bHelp, "Show help. Use <COMMAND> --help for specific help on the command.");
// Argument groups 1 - 10:
// STARTUP
// SHUTDOWN
// LIST
// INSTALL
// UPDATE
// UNINSTALL
// START
// STOP
// LISTENER
// CONNECTION
auto& rArgHelpDef =
cmdln.DefineOption("?", bHelp, "Show help. Use <COMMAND> --help for specific help on the command.");
rArgHelpDef.AddSubOptionName("help");
auto& rArgSilentDef = cmdln.DefineOption("s", sContext.bSilent, "Do not show any information on std::cout. Not compatible with 'verbose'.");
auto& rArgSilentDef = cmdln.DefineOption("s", sContext.bSilent, "Do not show any information on std::cout. Not compatible "
"with 'verbose'.");
rArgSilentDef.AddSubOptionName("silent");
auto& rArgVerboseDef = cmdln.DefineOption("v", sContext.bVerbose, "Provide verbose information. Not compatible with 'silent'.");
auto& rArgVerboseDef = cmdln.DefineOption("v", sContext.bVerbose, "Provide verbose information. Not compatible with "
"'silent'.");
rArgVerboseDef.AddSubOptionName("verbose");
cmdln.DefineSubOption("version", bVersion, "Show version information.");
cmdln.DefineSubOption("instance", sContext.uiInstanceID, "The instance ID of the SDV instance (default ID is 1000).");
cmdln.DefineSubOption("server_silent", sContext.bServerSilent, "Only used with STARTUP command: Server is started using "
"silent option. Not compatible with 'server_verbose'.");
"silent option. Not compatible with 'server_verbose'.", true, 1);
cmdln.DefineSubOption("server_verbose", sContext.bServerVerbose, "Only used with STARTUP command: Server is started using "
"verbose option. Not compatible with 'server_silent'.");
"verbose option. Not compatible with 'server_silent'.", true, 1);
cmdln.DefineSubOption("install_dir", sContext.pathInstallDir, "Only used with STARTUP command: Installation directory "
"(absolute or relative to the sdv_core executable).");
"(absolute or relative to the sdv_core executable).", true, 1);
cmdln.DefineSubOption("no_header", sContext.bListNoHdr, "Only used with LIST command: Do not print a header for the "
"listing table.");
"listing table.", true, 3);
cmdln.DefineSubOption("short", sContext.bListShort, "Only used with LIST command: Print only the most essential "
"information as one column.");
"information as one column.", true, 3);
cmdln.DefineSubOption(
"insert_before", sContext.ssInsertBeforeConnection, "Name of the connection to insert before.", true, 10);
cmdln.DefineDefaultArgument(sContext.seqCmdLine, "COMMAND");
cmdln.Parse(static_cast<size_t>(iArgc), rgszArgv);
@@ -98,7 +109,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
if (!sContext.bSilent)
{
std::cout << "SDV Server Control Utility" << std::endl;
std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl;
std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl;
std::cout << "Author: Erik Verhoeven" << std::endl << std::endl;
}
@@ -113,7 +124,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
bError = true;
}
enum class ECommand { unknown, startup, shutdown, list, install, update, uninstall, start, stop } eCommand = ECommand::unknown;
enum class ECommand { unknown, startup, shutdown, list, install, update, uninstall, start, stop, listener, connection } eCommand = ECommand::unknown;
if (!sContext.seqCmdLine.empty())
{
if (iequals(sContext.seqCmdLine[0], "STARTUP")) eCommand = ECommand::startup;
@@ -124,6 +135,8 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
else if (iequals(sContext.seqCmdLine[0], "UNINSTALL")) eCommand = ECommand::uninstall;
else if (iequals(sContext.seqCmdLine[0], "START")) eCommand = ECommand::start;
else if (iequals(sContext.seqCmdLine[0], "STOP")) eCommand = ECommand::stop;
else if (iequals(sContext.seqCmdLine[0], "LISTENER")) eCommand = ECommand::listener;
else if (iequals(sContext.seqCmdLine[0], "CONNECTION")) eCommand = ECommand::connection;
else
{
if (!sContext.bSilent)
@@ -157,17 +170,25 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
case ECommand::uninstall:
InstallationHelp(sContext);
break;
case ECommand::listener:
ListenerHelp(sContext);
break;
case ECommand::connection:
ConnectionHelp(sContext);
break;
default:
cmdln.PrintHelp(std::cout, R"code(Supported commands:
STARTUP Start the core application server
SHUTDOWN Stop the core application server
LIST List module/classes/component/installation/connection information.
INSTALL Install a new application or service.
UPDATE Update an existing installation.
UNINSTALL Uninstall an installed application or service.
START Start a service (complex services only).
STOP Stop a service (complex services only).
)code");
cmdln.PrintHelp(std::cout, R"text(Supported commands:
STARTUP Start the core application server
SHUTDOWN Stop the core application server
LIST List information about modules/classes/components/installations/listeners/connections.
INSTALL Install a new application or service.
UPDATE Update an existing installation.
UNINSTALL Uninstall an installed application or service.
START Start a service (complex services and vehicle functions only).
STOP Stop a service (complex services and vehicle functions only).
LISTENER Add or remove a communication listener configuration.
CONNECTION Add or remove a connection configuration.
)text");
break;
}
}
@@ -185,11 +206,11 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
if (sContext.bVerbose)
std::cout << "Starting local application control.." << std::endl;
sdv::app::CAppControl appcontrol;
std::string ssAppConfig = std::string(R"code(
std::string ssAppConfig = std::string(R"toml(
[Application]
Mode = "Maintenance"
Instance = )code") + std::to_string(sContext.uiInstanceID) + R"code(
)code";
Instance = )toml") + std::to_string(sContext.uiInstanceID) + R"toml(
)toml";
if (!appcontrol.Startup(ssAppConfig))
{
if (!sContext.bSilent)
@@ -224,6 +245,12 @@ Instance = )code") + std::to_string(sContext.uiInstanceID) + R"code(
case ECommand::uninstall:
iRet = Uninstall(sContext);
break;
case ECommand::listener:
iRet = ConfigureListener(sContext);
break;
case ECommand::connection:
iRet = ConfigureConnection(sContext);
break;
default:
std::cout << "Command missing :-(" << std::endl;
break;

View File

@@ -35,8 +35,8 @@ void StartStopServiceHelp(const SContext& rsContext)
" sdv_control START <class> <name> [options...]\n\n"
"Start a complex service with the supplied object name as is defined in the configuration (first usage) or the "
"supplied class name and with object name assignment and add to the configuration (second usage).\n"
"Only complex services can be started. If the service depends on not running other services, these are started as "
"well.\n\n");
"Only vehicle functions and complex services can be started. If the service depends on not running other services, "
"these are started as well.\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "STOP"))
@@ -47,15 +47,14 @@ void StartStopServiceHelp(const SContext& rsContext)
" sdv_control STOP <module> [options...]\n\n"
"Stop the complex service(s) with the supplied object name (first usage), with supplied object ID (second usage), with "
"supplied class name (third usage) or contained in the module with the supplied module name (fourth usage).\n"
"Only complex services can be stopped. Dependent services are stopped as well. If one of object to be stopped is not a "
"complex service, the command fails.\n\n");
"Only vehicle functions and complex services can be stopped. Dependent services are stopped as well. If one of object "
"to be stopped is not a vehicle function or a complex service, the command fails.\n\n");
return;
}
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid start/stop command..." << std::endl;
}
int StartService(const SContext& rsContext)
{
// First argument should be "START"
@@ -88,7 +87,7 @@ int StartService(const SContext& rsContext)
ssName = rsContext.seqCmdLine[1];
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository();
if (!ptrRepository)
{
if (!rsContext.bSilent)
@@ -154,7 +153,7 @@ int StopService(const SContext& rsContext)
ssName = rsContext.seqCmdLine[1];
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository();
if (!ptrRepository)
{
if (!rsContext.bSilent)

View File

@@ -76,9 +76,9 @@ void StartupShutdownHelp(const SContext& rsContext)
"Start the SDV server with the supplied instance ID. If no instance ID is supplied, use the default instance "
"ID #1000.\n\n"
"Options:\n"
" --server_silent Server is started using silent option. Not compatible with 'server_verbose'.\n"
" --server_verbose Server is started using verbose option. Not compatible with 'server_silent'.\n"
" --install_dir Installation directory (absolute or relative to the sdv_core executable).\n\n");
" --server_silent Server is started using silent option. Not compatible with 'server_verbose'.\n"
" --server_verbose Server is started using verbose option. Not compatible with 'server_silent'.\n"
" --install_dir<path> Installation directory (absolute or relative to the sdv_core executable).\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "SHUTDOWN"))
@@ -156,7 +156,7 @@ int StartupSDVServer(const SContext& rsContext)
}
// Try to connect
auto ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
auto ptrRepository = sdv::com::ConnectToLocalServerRepository();
if (!ptrRepository)
{
if (!rsContext.bSilent)
@@ -183,7 +183,7 @@ int ShutdownSDVServer(const SContext& rsContext)
std::cout << "Connecting to the SDV #" << rsContext.uiInstanceID << " server..." << std::endl;
// Try to connect
auto ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
auto ptrRepository = sdv::com::ConnectToLocalServerRepository();
if (!ptrRepository)
{
if (!rsContext.bSilent)