mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-08-30 12:15:12 +00:00
connection between 2 systems and bug fixes (#14)
This commit is contained in:
471
sdv_executables/sdv_control/connection_config.cpp
Normal file
471
sdv_executables/sdv_control/connection_config.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user