mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-04-15 01:38:15 +00:00
Update sdv_packager (#6)
This commit is contained in:
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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 cross-compilation toolchain file
|
||||
include(../cross-compile-tools.cmake)
|
||||
|
||||
@@ -141,6 +154,14 @@ add_custom_command(
|
||||
COMMAND sdv_idl_compiler ${INTERFACE_DIR}/process.idl -O${INTERFACE_DIR} --no_ps
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${INTERFACE_DIR}/param.h
|
||||
DEPENDS sdv_idl_compiler
|
||||
MAIN_DEPENDENCY ${INTERFACE_DIR}/param.idl
|
||||
COMMENT "Compiling param.idl"
|
||||
COMMAND sdv_idl_compiler ${INTERFACE_DIR}/param.idl -O${INTERFACE_DIR} --no_ps
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${INTERFACE_DIR}/repository.h
|
||||
DEPENDS sdv_idl_compiler
|
||||
@@ -175,6 +196,7 @@ add_custom_target(CompileCoreIDL
|
||||
${INTERFACE_DIR}/mem.h
|
||||
${INTERFACE_DIR}/module.h
|
||||
${INTERFACE_DIR}/process.h
|
||||
${INTERFACE_DIR}/param.h
|
||||
${INTERFACE_DIR}/repository.h
|
||||
${INTERFACE_DIR}/timer.h
|
||||
)
|
||||
@@ -190,7 +212,8 @@ add_subdirectory(task_timer)
|
||||
add_subdirectory(ipc_com)
|
||||
add_subdirectory(ipc_connect)
|
||||
add_subdirectory(ipc_shared_mem)
|
||||
add_subdirectory(ipc_sockets)
|
||||
add_subdirectory(uds_win_sockets)
|
||||
add_subdirectory(uds_unix_sockets)
|
||||
add_subdirectory(process_control)
|
||||
add_subdirectory(hardware_ident)
|
||||
add_subdirectory(manifest_util)
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
#*******************************************************************************
|
||||
# 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:
|
||||
# Sudipta Durjoy - initial API and implementation
|
||||
#*******************************************************************************
|
||||
|
||||
# Only build on MSVC/Windows or 64-bit Linux (not ARM)
|
||||
if((WIN32 AND NOT MSVC) OR (UNIX AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64"))
|
||||
if((WIN32 AND MSVC) OR (UNIX AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64"))
|
||||
|
||||
# Define project
|
||||
project(can_com_silkit VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
@@ -1,80 +1,47 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Sudipta Durjoy - initial API and implementation
|
||||
* Thomas Pfleiderer - refactored and finalized
|
||||
********************************************************************************/
|
||||
|
||||
#include "can_com_silkit.h"
|
||||
#include <support/toml.h>
|
||||
#include <bitset>
|
||||
|
||||
void CCANSilKit::Initialize(const sdv::u8string& rssObjectConfig)
|
||||
bool CCANSilKit::OnInitialize()
|
||||
{
|
||||
std::string silKitNetwork = "";
|
||||
std::string silKitJSONConfigContent = "";
|
||||
std::string silKitRegistryUri = "";
|
||||
try
|
||||
{
|
||||
sdv::toml::CTOMLParser config(rssObjectConfig.c_str());
|
||||
sdv::toml::CNode nodeSilKitConfig = config.GetDirect("SilKitConfig");
|
||||
if (nodeSilKitConfig.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
silKitJSONConfigContent = static_cast<std::string>(nodeSilKitConfig.GetValue());
|
||||
}
|
||||
|
||||
sdv::toml::CNode nodeSilKitParticipant = config.GetDirect("SilKitParticipantName");
|
||||
if (nodeSilKitParticipant.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
m_SilKitParticipantName = static_cast<std::string>(nodeSilKitParticipant.GetValue());
|
||||
}
|
||||
|
||||
sdv::toml::CNode nodeSilKitNetwork = config.GetDirect("CanSilKitNetwork");
|
||||
if (nodeSilKitNetwork.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
silKitNetwork = static_cast<std::string>(nodeSilKitNetwork.GetValue());
|
||||
}
|
||||
|
||||
sdv::toml::CNode nodeSilKitRegistryURI = config.GetDirect("RegistryURI");
|
||||
if (nodeSilKitRegistryURI.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
silKitRegistryUri = static_cast<std::string>(nodeSilKitRegistryURI.GetValue());
|
||||
}
|
||||
|
||||
sdv::toml::CNode nodeSilKitSyncMode = config.GetDirect("SyncMode");
|
||||
if (nodeSilKitSyncMode.GetType() == sdv::toml::ENodeType::node_boolean)
|
||||
{
|
||||
m_SilKitIsSynchronousMode = static_cast<bool>(nodeSilKitSyncMode.GetValue());
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& e)
|
||||
{
|
||||
SDV_LOG_ERROR("Configuration could not be read: ", e.what());
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
}
|
||||
|
||||
m_TimerSimulationStep = sdv::core::GetObject<sdv::core::ITimerSimulationStep>("SimulationTaskTimerService");
|
||||
|
||||
if (!ValidateConfiguration(silKitJSONConfigContent, silKitNetwork, silKitRegistryUri))
|
||||
if (!ValidateConfiguration())
|
||||
{
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateSilKitConnection(silKitJSONConfigContent, silKitNetwork, silKitRegistryUri))
|
||||
if (!CreateSilKitConnection())
|
||||
{
|
||||
SDV_LOG_ERROR("Error createing SilKit connection, probably invalid JSON content");
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update status only if initialization was successful
|
||||
m_eStatus = sdv::EObjectStatus::initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCANSilKit::ValidateConfiguration(const std::string& ssSilKitJSONConfigContent, const std::string& ssSilKitNetwork, const std::string& ssSilKitRegistryUri)
|
||||
bool CCANSilKit::ValidateConfiguration()
|
||||
{
|
||||
bool success = true;
|
||||
SDV_LOG_INFO("SilKit connecting to network: ", ssSilKitNetwork.c_str());
|
||||
SDV_LOG_INFO("SilKit registry URI: ", ssSilKitRegistryUri.c_str());
|
||||
SDV_LOG_INFO("SilKit connecting to network: ", m_SilKitNetwork.c_str());
|
||||
SDV_LOG_INFO("SilKit registry URI: ", m_SilKitRegistryUri.c_str());
|
||||
m_SilKitIsSynchronousMode ? SDV_LOG_INFO("SilKit is running in synchronous mode.") : SDV_LOG_INFO("SilKit is running in asynchronous mode.");
|
||||
m_TimerSimulationStep ? SDV_LOG_WARNING("Run simulation with simulation timer service.") : SDV_LOG_WARNING("Run simulation with real time service.");
|
||||
|
||||
if (ssSilKitJSONConfigContent.empty())
|
||||
if (m_SilKitJSONConfigContent.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("Error reading config file content for SilKit, missing 'SilKitConfig'.");
|
||||
success = false;
|
||||
@@ -84,23 +51,23 @@ bool CCANSilKit::ValidateConfiguration(const std::string& ssSilKitJSONConfigCont
|
||||
SDV_LOG_ERROR("SilKit CAN participant is not found in configuration, missing 'SilKitParticipantName'.");
|
||||
success = false;
|
||||
}
|
||||
if (ssSilKitNetwork.empty())
|
||||
if (m_SilKitNetwork.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("Error reading SilKit network name, missing 'CanSilKitNetwork'.");
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SDV_LOG_INFO("SilKit connecting to network: ", ssSilKitNetwork.c_str());
|
||||
SDV_LOG_INFO("SilKit connecting to network: ", m_SilKitNetwork.c_str());
|
||||
}
|
||||
if (ssSilKitRegistryUri.empty())
|
||||
if (m_SilKitRegistryUri.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("Error reading SilKit registry URI, missing 'RegistryURI'.");
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SDV_LOG_INFO("SilKit registry URI: ", ssSilKitRegistryUri.c_str());
|
||||
SDV_LOG_INFO("SilKit registry URI: ", m_SilKitRegistryUri.c_str());
|
||||
}
|
||||
|
||||
if (!m_TimerSimulationStep)
|
||||
@@ -120,68 +87,41 @@ bool CCANSilKit::ValidateConfiguration(const std::string& ssSilKitJSONConfigCont
|
||||
return success;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CCANSilKit::GetStatus() const
|
||||
void CCANSilKit::OnShutdown()
|
||||
{
|
||||
return m_eStatus;
|
||||
}
|
||||
|
||||
void CCANSilKit::SetOperationMode(sdv::EOperationMode eMode)
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eStatus == sdv::EObjectStatus::running || m_eStatus == sdv::EObjectStatus::initialized)
|
||||
m_eStatus = sdv::EObjectStatus::configuring;
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eStatus == sdv::EObjectStatus::configuring || m_eStatus == sdv::EObjectStatus::initialized)
|
||||
m_eStatus = sdv::EObjectStatus::running;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CCANSilKit::Shutdown()
|
||||
{
|
||||
SDV_LOG_INFO("Initiating shutdown process...");
|
||||
|
||||
m_eStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
|
||||
try
|
||||
{
|
||||
if (m_SilKitLifeCycleService)
|
||||
{
|
||||
SDV_LOG_INFO("Stopping SilKit Lifecycle Service...");
|
||||
m_SilKitLifeCycleService->Stop("Shutdown requested.");
|
||||
m_SilKitLifeCycleService = nullptr;
|
||||
}
|
||||
|
||||
if (m_SilKitCanController)
|
||||
{
|
||||
SDV_LOG_INFO("Stopping SilKit CAN Controller...");
|
||||
m_SilKitCanController->Stop();
|
||||
m_SilKitCanController = nullptr;
|
||||
}
|
||||
|
||||
if (m_SilKitParticipant)
|
||||
{
|
||||
SDV_LOG_INFO("Resetting SilKit Participant...");
|
||||
m_SilKitParticipant.reset();
|
||||
m_SilKitParticipant = nullptr;
|
||||
}
|
||||
}
|
||||
catch (const SilKit::SilKitError& e)
|
||||
{
|
||||
SDV_LOG_ERROR("SilKit exception occurred during shutdown: ", e.what());
|
||||
m_eStatus = sdv::EObjectStatus::runtime_error;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
SDV_LOG_ERROR("Unknown exception occurred during shutdown: ", e.what());
|
||||
m_eStatus = sdv::EObjectStatus::runtime_error;
|
||||
}
|
||||
|
||||
m_SilKitLifeCycleService = nullptr;
|
||||
m_SilKitCanController = nullptr;
|
||||
m_SilKitParticipant = nullptr;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_QueueMutex);
|
||||
while (!m_MessageQueue.empty())
|
||||
{
|
||||
@@ -191,12 +131,13 @@ void CCANSilKit::Shutdown()
|
||||
|
||||
void CCANSilKit::RegisterReceiver(/*in*/ sdv::can::IReceive* pReceiver)
|
||||
{
|
||||
if (m_eStatus != sdv::EObjectStatus::configuring)
|
||||
if (GetObjectState() != sdv::EObjectState::configuring)
|
||||
return;
|
||||
|
||||
if (!pReceiver)
|
||||
{
|
||||
SDV_LOG_ERROR("No CAN receiver available.");
|
||||
SetObjectIntoConfigErrorState();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -234,7 +175,7 @@ void CCANSilKit::UnregisterReceiver(/*in*/ sdv::can::IReceive* pReceiver)
|
||||
sdv::sequence<sdv::u8string> CCANSilKit::GetInterfaces() const
|
||||
{
|
||||
sdv::sequence<sdv::u8string> seqIfcNames;
|
||||
if (m_eStatus != sdv::EObjectStatus::running) return seqIfcNames;
|
||||
if (GetObjectState() != sdv::EObjectState::running) return seqIfcNames;
|
||||
|
||||
seqIfcNames.push_back(m_SilKitParticipantName);
|
||||
|
||||
@@ -249,6 +190,7 @@ std::shared_ptr<SilKit::Config::IParticipantConfiguration> CCANSilKit::GetSilKit
|
||||
if (silKitParticipantCconfig == nullptr)
|
||||
{
|
||||
SDV_LOG_ERROR("Error parsing the SilKit config content: ", ssSilKitJSONConfigContent.c_str());
|
||||
SetObjectIntoConfigErrorState();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -261,6 +203,7 @@ std::unique_ptr<SilKit::IParticipant> CCANSilKit::CreateParticipantFromJSONConfi
|
||||
if (silKitParticipantConfig == nullptr)
|
||||
{
|
||||
SDV_LOG_ERROR("The SilKit configuaration file could not be opened.");
|
||||
SetObjectIntoConfigErrorState();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -322,23 +265,24 @@ bool CCANSilKit::SetHandlerFunctions(SilKit::Services::Orchestration::ILifecycle
|
||||
try
|
||||
{
|
||||
// Set a SilKit Stop Handler
|
||||
silKitLifeCycleService->SetStopHandler([this]()
|
||||
silKitLifeCycleService->SetStopHandler(
|
||||
[this]()
|
||||
{
|
||||
m_eStatus = sdv::EObjectStatus::runtime_error;
|
||||
SetObjectIntoRuntimeErrorState();
|
||||
SDV_LOG_INFO("SilKit StopHandlerhandler called");
|
||||
});
|
||||
|
||||
// Set a Shutdown Handler
|
||||
silKitLifeCycleService->SetShutdownHandler([this]()
|
||||
{
|
||||
m_eStatus = sdv::EObjectStatus::runtime_error;
|
||||
SetObjectIntoRuntimeErrorState();
|
||||
SDV_LOG_INFO("SilKit Shutdown handler called");
|
||||
});
|
||||
|
||||
// Set a Shutdown Handler
|
||||
silKitLifeCycleService->SetAbortHandler([this](auto /*participantState*/)
|
||||
{
|
||||
m_eStatus = sdv::EObjectStatus::runtime_error;
|
||||
SetObjectIntoRuntimeErrorState();
|
||||
SDV_LOG_INFO("SilKit Abort handler called");
|
||||
});
|
||||
|
||||
@@ -363,18 +307,18 @@ bool CCANSilKit::SetHandlerFunctions(SilKit::Services::Orchestration::ILifecycle
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCANSilKit::CreateSilKitConnection(const std::string& ssSilKitJSONConfigContent, const std::string& ssSilKitNetwork, const std::string& ssSilKitRegistryUri)
|
||||
bool CCANSilKit::CreateSilKitConnection()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_SilKitParticipant = CreateParticipantFromJSONConfig(ssSilKitJSONConfigContent, ssSilKitRegistryUri);
|
||||
m_SilKitParticipant = CreateParticipantFromJSONConfig(m_SilKitJSONConfigContent, m_SilKitRegistryUri);
|
||||
if (m_SilKitParticipant == nullptr)
|
||||
{
|
||||
SDV_LOG_ERROR("SilKit COM adapter is not available.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_SilKitCanController = CreateController(ssSilKitNetwork);
|
||||
m_SilKitCanController = CreateController(m_SilKitNetwork);
|
||||
if (m_SilKitCanController == nullptr)
|
||||
{
|
||||
SDV_LOG_ERROR("SilKit CAN controller is not available.");
|
||||
@@ -416,7 +360,7 @@ bool CCANSilKit::CreateSilKitConnection(const std::string& ssSilKitJSONConfigCon
|
||||
|
||||
void CCANSilKit::SilKitReceiveMessageHandler(const SilKit::Services::Can::CanFrame& rsSilKitCanFrame)
|
||||
{
|
||||
if (m_eStatus != sdv::EObjectStatus::running)
|
||||
if (GetObjectState() != sdv::EObjectState::running)
|
||||
return;
|
||||
|
||||
if (rsSilKitCanFrame.dlc > m_maxCanDataLength)
|
||||
@@ -461,7 +405,7 @@ void CCANSilKit::SilKitTransmitAcknowledgeHandler(const SilKit::Services::Can::C
|
||||
|
||||
void CCANSilKit::Send(/*in*/ const sdv::can::SMessage& sSDVCanMessage, /*in*/ uint32_t)
|
||||
{
|
||||
if (m_eStatus != sdv::EObjectStatus::running)
|
||||
if (GetObjectState() != sdv::EObjectState::running)
|
||||
return;
|
||||
|
||||
if(sSDVCanMessage.bCanFd)
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Sudipta Durjoy - initial API and implementation
|
||||
* Thomas Pfleiderer - refactored and finalized
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef CAN_COM_SILKIT_H
|
||||
#define CAN_COM_SILKIT_H
|
||||
|
||||
@@ -18,46 +32,54 @@
|
||||
/**
|
||||
* @brief Component to establish Socket CAN communication between VAPI and external application
|
||||
*/
|
||||
class CCANSilKit : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::can::IRegisterReceiver,
|
||||
public sdv::can::ISend, sdv::can::IInformation
|
||||
class CCANSilKit : public sdv::CSdvObject, public sdv::can::IRegisterReceiver, public sdv::can::ISend, sdv::can::IInformation
|
||||
{
|
||||
public:
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
|
||||
SDV_INTERFACE_ENTRY(sdv::can::IRegisterReceiver)
|
||||
SDV_INTERFACE_ENTRY(sdv::can::ISend)
|
||||
SDV_INTERFACE_ENTRY(sdv::can::IInformation)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_bus)
|
||||
DECLARE_OBJECT_CLASS_NAME("CAN_Com_SilKit")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("CAN_Communication_Object")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
*/
|
||||
virtual void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
BEGIN_SDV_PARAM_MAP()
|
||||
SDV_PARAM_ENABLE_LOCKING()
|
||||
SDV_PARAM_ENTRY(m_SilKitJSONConfigContent, "SilKitConfig", "", "", "SilKit Config JSON.")
|
||||
SDV_PARAM_ENTRY(m_SilKitParticipantName, "SilKitParticipantName", "", "", "Name of the participant.")
|
||||
SDV_PARAM_ENTRY(m_SilKitNetwork, "CanSilKitNetwork", "", "", "Declaration of SilKit CAN network.")
|
||||
SDV_PARAM_ENTRY(m_SilKitRegistryUri, "RegistryURI", "", "", "SilKit Registry URI.")
|
||||
SDV_PARAM_ENTRY(m_SilKitIsSynchronousMode, "SyncMode", "", "", "Enable synchronization mode.")
|
||||
SDV_PARAM_ENTRY(m_SilKitDebugInfo, "DebugInfo", "", "", "Enable debug information.")
|
||||
END_SDV_PARAM_MAP()
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize.
|
||||
* @details The CAN_Com_SilKit uses the following configuration:
|
||||
* @code
|
||||
* DebugInfo = true
|
||||
* SyncMode = true
|
||||
* SilKitParticipantName = "can_writer"
|
||||
* CanSilKitNetwork = "PrivateCAN"
|
||||
* RegistryURI = "silkit://localhost:8500"
|
||||
* SilKitConfig = """{
|
||||
* "Logging": {
|
||||
* "Sinks": [ { "Type": "Stdout", "Level": "Info" } ]
|
||||
* },
|
||||
* }"""
|
||||
* @endcode
|
||||
* @return Returns 'true' when the initialization was successful, 'false' when not.
|
||||
*/
|
||||
virtual sdv::EObjectStatus GetStatus() const override;
|
||||
virtual bool OnInitialize() override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
* @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
*/
|
||||
virtual void Shutdown() override;
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Register a CAN message receiver. Overload of sdv::can::IRegisterReceiver::RegisterReceiver.
|
||||
@@ -136,16 +158,13 @@ private:
|
||||
* @param[in] ssSilKitNetwork SilKit network.
|
||||
* @return SilKit::Services::Can::ICanController, nullptr on failure.
|
||||
*/
|
||||
SilKit::Services::Can::ICanController* CreateController(const std::string& ssSilKitNetwork);
|
||||
SilKit::Services::Can::ICanController* CreateController(const std::string& ssSilKitNetwork);
|
||||
|
||||
/**
|
||||
* @brief Validate if the configuration includes all required settings
|
||||
* @param[in] ssSilKitJSONConfigContent SilKit JSON config file.
|
||||
* @param[in] ssSilKitNetwork Declaration of SilKit CAN network.
|
||||
* @param[in] ssSilKitRegistryUri SilKit Registry URI.
|
||||
* @return Return true if required settings are available
|
||||
*/
|
||||
bool ValidateConfiguration(const std::string& ssSilKitJSONConfigContent, const std::string& ssSilKitNetwork, const std::string& ssSilKitRegistryUri);
|
||||
bool ValidateConfiguration();
|
||||
|
||||
/**
|
||||
* @brief Function for SilKit Timesyncservice creation and to set simulation step handler.
|
||||
@@ -154,12 +173,9 @@ private:
|
||||
|
||||
/**
|
||||
* @brief Function to setup CAN interfaces.
|
||||
* @param[in] ssSilKitJSONConfigContent SilKit JSON config file.
|
||||
* @param[in] ssSilKitNetwork Declaration of SilKit CAN network.
|
||||
* @param[in] ssSilKitRegistryUri SilKit Registry URI.
|
||||
* @return Return true if CAN interfaces are setup successfully
|
||||
*/
|
||||
bool CreateSilKitConnection(const std::string& ssSilKitJSONConfigContent, const std::string& ssSilKitNetwork, const std::string& ssSilKitRegistryUri);
|
||||
bool CreateSilKitConnection();
|
||||
|
||||
/**
|
||||
* @brief Create lifecycle service.
|
||||
@@ -185,21 +201,24 @@ private:
|
||||
*/
|
||||
void SilKitTransmitAcknowledgeHandler(const SilKit::Services::Can::CanFrameTransmitEvent& rsSilKitTransmitAcknowledge);
|
||||
|
||||
std::mutex m_ReceiversMtx; ///< Protect the receiver set.
|
||||
std::set<sdv::can::IReceive*> m_SetReceivers; ///< Set with receiver interfaces.
|
||||
std::mutex m_ReceiversMtx; ///< Protect the receiver set.
|
||||
std::set<sdv::can::IReceive*> m_SetReceivers; ///< Set with receiver interfaces.
|
||||
|
||||
std::queue<sdv::can::SMessage> m_MessageQueue; ///< Map of the messages to be sent on SilKit.
|
||||
std::mutex m_QueueMutex; ///< Protection for message map.
|
||||
std::queue<sdv::can::SMessage> m_MessageQueue; ///< Map of the messages to be sent on SilKit.
|
||||
std::mutex m_QueueMutex; ///< Protection for message map.
|
||||
|
||||
SilKit::Services::Orchestration::ILifecycleService* m_SilKitLifeCycleService = nullptr; ///< SilKit lifecycle service.
|
||||
SilKit::Services::Can::ICanController* m_SilKitCanController = nullptr; ///< SilKit CAN1 Controller interface.
|
||||
sdv::core::ITimerSimulationStep* m_TimerSimulationStep = nullptr; ///< Timer simulation step.
|
||||
std::unique_ptr<SilKit::IParticipant> m_SilKitParticipant = nullptr; ///< SilKit participant.
|
||||
std::string m_SilKitParticipantName; ///< Configured SilKit participants.
|
||||
bool m_SilKitIsSynchronousMode = false; ///< SilKit sync mode when true.
|
||||
SilKit::Services::Orchestration::ILifecycleService* m_SilKitLifeCycleService = nullptr; ///< SilKit lifecycle service.
|
||||
SilKit::Services::Can::ICanController* m_SilKitCanController = nullptr; ///< SilKit CAN1 Controller interface.
|
||||
sdv::core::ITimerSimulationStep* m_TimerSimulationStep = nullptr; ///< Timer simulation step.
|
||||
std::unique_ptr<SilKit::IParticipant> m_SilKitParticipant = nullptr; ///< SilKit participant.
|
||||
std::string m_SilKitParticipantName; ///< Configured SilKit participants.
|
||||
bool m_SilKitIsSynchronousMode = false; ///< SilKit sync mode when true.
|
||||
bool m_SilKitDebugInfo = false; ///< SilKit debug information when true.
|
||||
|
||||
uint32_t m_maxCanDataLength = 8; ///< maximum size of the CAN message.
|
||||
std::atomic<sdv::EObjectStatus> m_eStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
|
||||
uint32_t m_maxCanDataLength = 8; ///< maximum size of the CAN message.
|
||||
std::string m_SilKitNetwork; ///< Declaration of SilKit CAN network.
|
||||
std::string m_SilKitJSONConfigContent; ///< SilKit config JSON.
|
||||
std::string m_SilKitRegistryUri;
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT(CCANSilKit)
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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
|
||||
#*******************************************************************************
|
||||
|
||||
# Define project
|
||||
project(can_com_sim VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "can_com_sim.h"
|
||||
#include <support/toml.h>
|
||||
#include "../../global/ascformat/ascreader.cpp"
|
||||
@@ -9,51 +22,30 @@ CCANSimulation::CCANSimulation()
|
||||
CCANSimulation::~CCANSimulation()
|
||||
{}
|
||||
|
||||
void CCANSimulation::Initialize(const sdv::u8string& rssObjectConfig)
|
||||
bool CCANSimulation::OnInitialize()
|
||||
{
|
||||
try
|
||||
if (m_pathSource.empty() && m_pathTarget.empty())
|
||||
{
|
||||
sdv::toml::CTOMLParser config(rssObjectConfig.c_str());
|
||||
auto nodeSource = config.GetDirect("Source");
|
||||
if (nodeSource.GetType() == sdv::toml::ENodeType::node_string)
|
||||
m_pathSource = nodeSource.GetValue();
|
||||
auto nodeTarget = config.GetDirect("Target");
|
||||
if (nodeTarget.GetType() == sdv::toml::ENodeType::node_string)
|
||||
m_pathTarget = nodeTarget.GetValue();
|
||||
if (m_pathSource.empty() && m_pathTarget.empty())
|
||||
{
|
||||
SDV_LOG(sdv::core::ELogSeverity::error,
|
||||
"At least the source or the target ASC files must be specified.");
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
}
|
||||
else if (m_pathSource == m_pathTarget)
|
||||
{
|
||||
SDV_LOG(sdv::core::ELogSeverity::error,
|
||||
"Source and target ASC files '" + m_pathSource.generic_u8string() + "' cannot be the same.");
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
}
|
||||
else if (std::filesystem::exists(m_pathTarget))
|
||||
{
|
||||
SDV_LOG(sdv::core::ELogSeverity::warning,
|
||||
"Target ASC file '" + m_pathSource.generic_u8string() + "' will be overwritten.");
|
||||
}
|
||||
else if (m_pathSource.empty() && m_pathTarget.empty())
|
||||
{
|
||||
SDV_LOG(sdv::core::ELogSeverity::error, "No ASC file configured for reading or writing.");
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
}
|
||||
SDV_LOG(sdv::core::ELogSeverity::error,
|
||||
"At least the source or the target ASC files must be specified.");
|
||||
return false;
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& e)
|
||||
else if (m_pathSource == m_pathTarget)
|
||||
{
|
||||
SDV_LOG(sdv::core::ELogSeverity::error, "Configuration could not be read: ", e.what());
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
SDV_LOG(sdv::core::ELogSeverity::error,
|
||||
"Source and target ASC files '" + m_pathSource.generic_u8string() + "' cannot be the same.");
|
||||
return false;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
else if (std::filesystem::exists(m_pathTarget))
|
||||
{
|
||||
SDV_LOG(sdv::core::ELogSeverity::error, "Configuration could not be read: ", e.what());
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
SDV_LOG(sdv::core::ELogSeverity::warning,
|
||||
"Target ASC file '" + m_pathSource.generic_u8string() + "' will be overwritten.");
|
||||
}
|
||||
else if (m_pathSource.empty() && m_pathTarget.empty())
|
||||
{
|
||||
SDV_LOG(sdv::core::ELogSeverity::error, "No ASC file configured for reading or writing.");
|
||||
return false;
|
||||
}
|
||||
if (m_eStatus == sdv::EObjectStatus::initialization_failure) return;
|
||||
|
||||
// Initialize the ASC writer
|
||||
if (!m_pathTarget.empty())
|
||||
@@ -69,60 +61,36 @@ void CCANSimulation::Initialize(const sdv::u8string& rssObjectConfig)
|
||||
{
|
||||
SDV_LOG(sdv::core::ELogSeverity::error,
|
||||
"Failed to read ASC file '" + m_pathSource.generic_u8string() + "' for CAN playback.");
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!m_pathSource.empty() && !m_reader.GetMessageCount())
|
||||
{
|
||||
SDV_LOG(sdv::core::ELogSeverity::error,
|
||||
"No messages in ASC file '" + m_pathSource.generic_u8string() + "' found. File must contain 'Begin TriggerBlock' and 'End TriggerBlock' line.");
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!m_pathSource.empty())
|
||||
SDV_LOG(sdv::core::ELogSeverity::info,
|
||||
"CAN simulator ASC file '" + m_pathSource.generic_u8string() + "' contains ", m_reader.GetMessageCount(), " messages.");
|
||||
|
||||
// Update the status
|
||||
m_eStatus = sdv::EObjectStatus::initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CCANSimulation::GetStatus() const
|
||||
void CCANSimulation::OnChangeToConfigMode()
|
||||
{
|
||||
return m_eStatus;
|
||||
// Stop playback
|
||||
m_reader.StopPlayback();
|
||||
}
|
||||
|
||||
void CCANSimulation::SetOperationMode(sdv::EOperationMode eMode)
|
||||
bool CCANSimulation::OnChangeToRunningMode()
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eStatus == sdv::EObjectStatus::running || m_eStatus == sdv::EObjectStatus::initialized)
|
||||
{
|
||||
m_eStatus = sdv::EObjectStatus::configuring;
|
||||
|
||||
// Stop playback
|
||||
m_reader.StopPlayback();
|
||||
}
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eStatus == sdv::EObjectStatus::configuring || m_eStatus == sdv::EObjectStatus::initialized)
|
||||
{
|
||||
m_eStatus = sdv::EObjectStatus::running;
|
||||
|
||||
// Start playback
|
||||
m_reader.StartPlayback([&](const asc::SCanMessage& rsMsg) { PlaybackFunc(rsMsg); });
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Start playback
|
||||
m_reader.StartPlayback([&](const asc::SCanMessage& rsMsg) { PlaybackFunc(rsMsg); });
|
||||
return true;
|
||||
}
|
||||
|
||||
void CCANSimulation::Shutdown()
|
||||
void CCANSimulation::OnShutdown()
|
||||
{
|
||||
m_eStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
|
||||
// Stop playback
|
||||
m_reader.StopPlayback();
|
||||
|
||||
@@ -133,14 +101,11 @@ void CCANSimulation::Shutdown()
|
||||
SDV_LOG(sdv::core::ELogSeverity::error,
|
||||
"Failed to write ASC file '" + m_pathTarget.generic_u8string() + "' with CAN recording.");
|
||||
}
|
||||
|
||||
// Update the status
|
||||
m_eStatus = sdv::EObjectStatus::destruction_pending;
|
||||
}
|
||||
|
||||
void CCANSimulation::RegisterReceiver(/*in*/ sdv::can::IReceive* pReceiver)
|
||||
{
|
||||
if (m_eStatus != sdv::EObjectStatus::configuring) return;
|
||||
if (GetObjectState() != sdv::EObjectState::configuring) return;
|
||||
if (!pReceiver) return;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxReceivers);
|
||||
@@ -161,7 +126,7 @@ void CCANSimulation::UnregisterReceiver(/*in*/ sdv::can::IReceive* pReceiver)
|
||||
|
||||
void CCANSimulation::Send(/*in*/ const sdv::can::SMessage& sMsg, /*in*/ uint32_t uiIfcIndex)
|
||||
{
|
||||
if (m_eStatus != sdv::EObjectStatus::running) return;
|
||||
if (GetObjectState() != sdv::EObjectState::running) return;
|
||||
|
||||
asc::SCanMessage sAscCan{};
|
||||
sAscCan.uiChannel = uiIfcIndex + 1;
|
||||
@@ -177,7 +142,7 @@ void CCANSimulation::Send(/*in*/ const sdv::can::SMessage& sMsg, /*in*/ uint32_t
|
||||
sdv::sequence<sdv::u8string> CCANSimulation::GetInterfaces() const
|
||||
{
|
||||
sdv::sequence<sdv::u8string> seqIfcNames;
|
||||
if (m_eStatus != sdv::EObjectStatus::running) return seqIfcNames;
|
||||
if (GetObjectState() != sdv::EObjectState::running) return seqIfcNames;
|
||||
std::unique_lock<std::mutex> lock(m_mtxInterfaces);
|
||||
for (const auto& rprInterface : m_vecInterfaces)
|
||||
seqIfcNames.push_back(rprInterface.second);
|
||||
@@ -186,7 +151,7 @@ sdv::sequence<sdv::u8string> CCANSimulation::GetInterfaces() const
|
||||
|
||||
void CCANSimulation::PlaybackFunc(const asc::SCanMessage& rsMsg)
|
||||
{
|
||||
if (m_eStatus != sdv::EObjectStatus::running) return;
|
||||
if (GetObjectState() != sdv::EObjectState::running) return;
|
||||
|
||||
// Create sdv CAN message
|
||||
sdv::can::SMessage sSdvCan{};
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 CAN_COM_SIMULATION_H
|
||||
#define CAN_COM_SIMULATION_H
|
||||
|
||||
@@ -15,8 +28,7 @@
|
||||
/**
|
||||
* @brief Component to establish Socket CAN communication between VAPI and external application
|
||||
*/
|
||||
class CCANSimulation : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::can::IRegisterReceiver,
|
||||
public sdv::can::ISend, sdv::can::IInformation
|
||||
class CCANSimulation : public sdv::CSdvObject, public sdv::can::IRegisterReceiver, public sdv::can::ISend, sdv::can::IInformation
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@@ -31,39 +43,47 @@ public:
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
|
||||
SDV_INTERFACE_ENTRY(sdv::can::IRegisterReceiver)
|
||||
SDV_INTERFACE_ENTRY(sdv::can::ISend)
|
||||
SDV_INTERFACE_ENTRY(sdv::can::IInformation)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device)
|
||||
// Declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_bus)
|
||||
DECLARE_OBJECT_CLASS_NAME("CAN_Com_Sim")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("CAN_Communication_Object")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
*/
|
||||
virtual void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
// Parameter map
|
||||
BEGIN_SDV_PARAM_MAP()
|
||||
SDV_PARAM_PATH_ENTRY(m_pathSource, "Source", "", "Path to the source ASC file.")
|
||||
SDV_PARAM_PATH_ENTRY(m_pathTarget, "Target", "", "Path to the target ASC file.")
|
||||
END_SDV_PARAM_MAP()
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize.
|
||||
* @return Returns 'true' when the initialization was successful, 'false' when not.
|
||||
*/
|
||||
virtual sdv::EObjectStatus GetStatus() const override;
|
||||
virtual bool OnInitialize() override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
* @brief Change to configuration mode event. After this a call to this function locked parameters can be changed again.
|
||||
* Overload of sdv::CSdvObject::OnChangeToConfigMode.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
virtual void OnChangeToConfigMode() override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
* @brief Change to running mode event. Parameters were locked before the call to this event. Overload of
|
||||
* sdv::CSdvObject::OnChangeToRunningMode.
|
||||
* @return Returns 'true' when the configuration is valid and the running instances could be started. Otherwise returns
|
||||
* 'false'.
|
||||
*/
|
||||
virtual void Shutdown() override;
|
||||
virtual bool OnChangeToRunningMode() override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown.
|
||||
*/
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Register a CAN message receiver. Overload of sdv::can::IRegisterReceiver::RegisterReceiver.
|
||||
@@ -98,7 +118,6 @@ private:
|
||||
*/
|
||||
void PlaybackFunc(const asc::SCanMessage& rsMsg);
|
||||
|
||||
std::atomic<sdv::EObjectStatus> m_eStatus = sdv::EObjectStatus::initialization_pending; ///< Object status
|
||||
std::thread m_threadReceive; ///< Receive thread.
|
||||
mutable std::mutex m_mtxReceivers; ///< Protect the receiver set.
|
||||
std::set<sdv::can::IReceive*> m_setReceivers; ///< Set with receiver interfaces.
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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:
|
||||
# Sudipta Durjoy - initial API and implementation
|
||||
#*******************************************************************************
|
||||
|
||||
# Build for linux only
|
||||
if(UNIX)
|
||||
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Sudipta Durjoy - initial API and implementation
|
||||
* Thomas Pfleiderer - refactored and finalized
|
||||
********************************************************************************/
|
||||
|
||||
#include "can_com_sockets.h"
|
||||
|
||||
void CCANSockets::Initialize(const sdv::u8string& rssObjectConfig)
|
||||
bool CCANSockets::OnInitialize()
|
||||
{
|
||||
std::deque<std::string> vecConfigInterfaces;
|
||||
try
|
||||
{
|
||||
sdv::toml::CTOMLParser config(rssObjectConfig.c_str());
|
||||
sdv::toml::CTOMLParser config(GetObjectConfig());
|
||||
sdv::toml::CNodeCollection nodeSource = config.GetDirect("canSockets");
|
||||
if (nodeSource.GetType() == sdv::toml::ENodeType::node_array)
|
||||
{
|
||||
@@ -35,8 +49,7 @@ void CCANSockets::Initialize(const sdv::u8string& rssObjectConfig)
|
||||
catch (const sdv::toml::XTOMLParseException& e)
|
||||
{
|
||||
SDV_LOG_ERROR("Configuration could not be read: ", e.what());
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vecConfigInterfaces.size() == 0)
|
||||
@@ -46,13 +59,40 @@ void CCANSockets::Initialize(const sdv::u8string& rssObjectConfig)
|
||||
|
||||
if (!SetupCANSockets(vecConfigInterfaces))
|
||||
{
|
||||
m_eStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
LogAllCanInterfaceNames();
|
||||
return false;
|
||||
}
|
||||
|
||||
LogConfigurations();
|
||||
m_threadReceive = std::thread(&CCANSockets::ReceiveThreadFunc, this);
|
||||
|
||||
m_eStatus = sdv::EObjectStatus::initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CCANSockets::LogConfigurations()
|
||||
{
|
||||
for (const auto& socket : m_vecSockets)
|
||||
{
|
||||
if ((socket.localSocket > 0) && (socket.networkInterface > 0))
|
||||
{
|
||||
SDV_LOG_INFO("Configured socket: ", socket.name, ", index: ", socket.networkInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCANSockets::LogAllCanInterfaceNames()
|
||||
{
|
||||
// Retrieve the list of available can interfaces
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
if (getifaddrs(&ifaddr) != -1)
|
||||
{
|
||||
SDV_LOG_INFO("List of available can socket interfaces:");
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
|
||||
{
|
||||
SDV_LOG_INFO(ifa->ifa_name);
|
||||
}
|
||||
freeifaddrs(ifaddr);
|
||||
}
|
||||
}
|
||||
|
||||
bool CCANSockets::SetupCANSockets(const std::deque<std::string>& vecConfigInterfaces)
|
||||
@@ -60,13 +100,16 @@ bool CCANSockets::SetupCANSockets(const std::deque<std::string>& vecConfigInterf
|
||||
// Retrieve the list of available interfaces
|
||||
std::set<std::string> availableInterfaces;
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
if (getifaddrs(&ifaddr) != -1)
|
||||
if (getifaddrs(&ifaddr) == -1)
|
||||
{
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
|
||||
{
|
||||
availableInterfaces.insert(ifa->ifa_name);
|
||||
}
|
||||
SDV_LOG_ERROR(" Error: getifaddrs()");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
|
||||
{
|
||||
availableInterfaces.insert(ifa->ifa_name);
|
||||
}
|
||||
freeifaddrs(ifaddr);
|
||||
|
||||
CreateAndBindSockets(vecConfigInterfaces, availableInterfaces);
|
||||
@@ -152,32 +195,8 @@ void CCANSockets::CreateAndBindSockets(const std::deque<std::string>& vecConfigI
|
||||
}
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CCANSockets::GetStatus() const
|
||||
void CCANSockets::OnShutdown()
|
||||
{
|
||||
return m_eStatus;
|
||||
}
|
||||
|
||||
void CCANSockets::SetOperationMode(sdv::EOperationMode eMode)
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eStatus == sdv::EObjectStatus::running || m_eStatus == sdv::EObjectStatus::initialized)
|
||||
m_eStatus = sdv::EObjectStatus::configuring;
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eStatus == sdv::EObjectStatus::configuring || m_eStatus == sdv::EObjectStatus::initialized)
|
||||
m_eStatus = sdv::EObjectStatus::running;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CCANSockets::Shutdown()
|
||||
{
|
||||
m_eStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
|
||||
// Wait until the receiving thread is finished.
|
||||
if (m_threadReceive.joinable())
|
||||
m_threadReceive.join();
|
||||
@@ -190,13 +209,11 @@ void CCANSockets::Shutdown()
|
||||
close(socket.localSocket);
|
||||
}
|
||||
}
|
||||
|
||||
m_eStatus = sdv::EObjectStatus::destruction_pending;
|
||||
}
|
||||
|
||||
void CCANSockets::RegisterReceiver(/*in*/ sdv::can::IReceive* pReceiver)
|
||||
{
|
||||
if (m_eStatus != sdv::EObjectStatus::configuring) return;
|
||||
if (GetObjectState() != sdv::EObjectState::configuring) return;
|
||||
if (!pReceiver) return;
|
||||
|
||||
SDV_LOG_INFO("Registering VAPI CAN communication receiver...");
|
||||
@@ -222,7 +239,7 @@ void CCANSockets::UnregisterReceiver(/*in*/ sdv::can::IReceive* pReceiver)
|
||||
sdv::sequence<sdv::u8string> CCANSockets::GetInterfaces() const
|
||||
{
|
||||
sdv::sequence<sdv::u8string> seqIfcNames;
|
||||
if (m_eStatus != sdv::EObjectStatus::running)
|
||||
if (GetObjectState() != sdv::EObjectState::running)
|
||||
{
|
||||
return seqIfcNames;
|
||||
}
|
||||
@@ -241,7 +258,7 @@ sdv::sequence<sdv::u8string> CCANSockets::GetInterfaces() const
|
||||
|
||||
void CCANSockets::Send(const sdv::can::SMessage& sMsg, uint32_t uiConfigIndex)
|
||||
{
|
||||
if (m_eStatus != sdv::EObjectStatus::running) return;
|
||||
if (GetObjectState() != sdv::EObjectState::running) return;
|
||||
if (sMsg.bCanFd) return; // CAN-FD not supported
|
||||
if (sMsg.seqData.size() > 8) return; // Invalid message length.
|
||||
|
||||
@@ -267,15 +284,17 @@ void CCANSockets::Send(const sdv::can::SMessage& sMsg, uint32_t uiConfigIndex)
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSockets);
|
||||
|
||||
auto it = m_vecSockets.begin();
|
||||
std::advance(it, uiConfigIndex);
|
||||
if (it != m_vecSockets.end())
|
||||
for (const auto& socket : m_vecSockets)
|
||||
{
|
||||
if ((it->localSocket > 0) && (it->networkInterface > 0))
|
||||
if ((socket.localSocket > 0) && (socket.networkInterface > 0))
|
||||
{
|
||||
sAddr.can_ifindex = it->networkInterface;
|
||||
sAddr.can_family = AF_CAN;
|
||||
sendto(it->localSocket, &sFrame, sizeof(can_frame), 0, reinterpret_cast<sockaddr*>(&sAddr), sizeof(sAddr));
|
||||
if ((uint)socket.networkInterface == uiConfigIndex)
|
||||
{
|
||||
sAddr.can_ifindex = socket.networkInterface;
|
||||
sAddr.can_family = AF_CAN;
|
||||
sendto(socket.localSocket, &sFrame, sizeof(can_frame), 0, reinterpret_cast<sockaddr*>(&sAddr), sizeof(sAddr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,16 +304,16 @@ void CCANSockets::ReceiveThreadFunc()
|
||||
while (true)
|
||||
{
|
||||
enum {retry, cont, exit} eNextStep = exit;
|
||||
switch (m_eStatus)
|
||||
switch (GetObjectState())
|
||||
{
|
||||
case sdv::EObjectStatus::configuring:
|
||||
case sdv::EObjectStatus::initialization_pending:
|
||||
case sdv::EObjectStatus::initialized:
|
||||
case sdv::EObjectStatus::initializing:
|
||||
case sdv::EObjectState::configuring:
|
||||
case sdv::EObjectState::initialization_pending:
|
||||
case sdv::EObjectState::initialized:
|
||||
case sdv::EObjectState::initializing:
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
eNextStep = retry;
|
||||
break;
|
||||
case sdv::EObjectStatus::running:
|
||||
case sdv::EObjectState::running:
|
||||
eNextStep = cont;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Sudipta Durjoy - initial API and implementation
|
||||
* Thomas Pfleiderer - refactored and finalized
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef CAN_COM_SOCKET_H
|
||||
#define CAN_COM_SOCKET_H
|
||||
|
||||
@@ -29,7 +43,7 @@
|
||||
/**
|
||||
* @brief Component to establish Socket CAN communication between VAPI and external application
|
||||
*/
|
||||
class CCANSockets : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::can::IRegisterReceiver,
|
||||
class CCANSockets : public sdv::CSdvObject, public sdv::can::IRegisterReceiver,
|
||||
public sdv::can::ISend, sdv::can::IInformation
|
||||
{
|
||||
public:
|
||||
@@ -42,39 +56,27 @@ public:
|
||||
SDV_INTERFACE_ENTRY(sdv::can::IInformation)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_bus)
|
||||
DECLARE_OBJECT_CLASS_NAME("CAN_Com_Sockets")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("CAN_Communication_Object")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
* @brief Initialize the object. Overload of sdv::CSdvObject::OnInitialize.
|
||||
* The configuration contains either one interface name a list of interface names.
|
||||
* The Send() method must use the index of this list to determine the interface
|
||||
* In case of a single interface name the index is 0.
|
||||
* canSockets = "vcan0"
|
||||
* or
|
||||
* canSockets = ["vcan1", "vcan8", "vcan9", "vcan2"]
|
||||
* @return Returns 'true' when the initialization was successful, 'false' when not.
|
||||
*/
|
||||
virtual void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
virtual bool OnInitialize() override;
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
* @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown.
|
||||
*/
|
||||
virtual sdv::EObjectStatus GetStatus() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
*/
|
||||
virtual void Shutdown() override;
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Register a CAN message receiver. Overload of sdv::can::IRegisterReceiver::RegisterReceiver.
|
||||
@@ -124,6 +126,16 @@ private:
|
||||
void CreateAndBindSockets(const std::deque<std::string>& vecConfigInterfaces,
|
||||
const std::set<std::string>& availableInterfaces);
|
||||
|
||||
/**
|
||||
* @brief Write log information about the configured can sockets
|
||||
*/
|
||||
void LogConfigurations();
|
||||
|
||||
/**
|
||||
* @brief Write log information about the existing can hardware
|
||||
*/
|
||||
void LogAllCanInterfaceNames();
|
||||
|
||||
/**
|
||||
* @brief Socket definition structure
|
||||
*/
|
||||
@@ -134,7 +146,6 @@ private:
|
||||
std::string name; ///< interface name, can be empty in case of an invalid socket element
|
||||
};
|
||||
|
||||
std::atomic<sdv::EObjectStatus> m_eStatus = sdv::EObjectStatus::initialization_pending; ///< Object status
|
||||
std::thread m_threadReceive; ///< Receive thread.
|
||||
mutable std::mutex m_mtxReceivers; ///< Protect the receiver set.
|
||||
std::set<sdv::can::IReceive*> m_setReceivers; ///< Set with receiver interfaces.
|
||||
|
||||
@@ -12,6 +12,7 @@ Class = "DataDispatchService"
|
||||
[[Component]]
|
||||
Path = "can_com_sockets.sdv"
|
||||
Class = "CAN_Com_Sockets"
|
||||
[Component.Parameters]
|
||||
canSockets=["llcecan0"]
|
||||
|
||||
[[Component]]
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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
|
||||
#*******************************************************************************
|
||||
|
||||
# Define project
|
||||
project(sdv_services_core VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
@@ -42,7 +55,10 @@ add_library(core_services SHARED
|
||||
"installation_composer.h"
|
||||
"installation_composer.cpp"
|
||||
|
||||
"toml_parser/lexer_toml_token.h" "toml_parser/lexer_toml_token.cpp" "toml_parser/miscellaneous.h" "toml_parser/miscellaneous.cpp")
|
||||
"toml_parser/lexer_toml_token.h" "toml_parser/lexer_toml_token.cpp" "toml_parser/miscellaneous.h" "toml_parser/miscellaneous.cpp" "toml_parser/code_snippet.h" "toml_parser/code_snippet.cpp" "app_settings.h" "app_settings.cpp" "app_config_file.h" "app_config_file.cpp")
|
||||
|
||||
# Compiler settiings
|
||||
add_compile_definitions(SDV_NO_EXPORT_DEFINITION)
|
||||
|
||||
# Link target
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 APP_CONFIG_H
|
||||
#define APP_CONFIG_H
|
||||
|
||||
@@ -9,6 +22,7 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "app_config_file.h"
|
||||
|
||||
// @cond DOXYGEN_IGNORE
|
||||
// Components are installed using two procedures:
|
||||
@@ -25,6 +39,12 @@
|
||||
// The installation might need companion files to be installed in various relative sub-directories.
|
||||
// @endcond
|
||||
|
||||
/**
|
||||
* @brief Get the location of the core_services.sdv.
|
||||
* @return Path to the directory containing the loaded core directory.
|
||||
*/
|
||||
std::filesystem::path GetCoreDirectory();
|
||||
|
||||
/**
|
||||
* @brief Application configuration service
|
||||
* @details In the configuration system objects, devices, basic services, complex services and apps are defined and will be started
|
||||
@@ -33,33 +53,6 @@
|
||||
* Loading a configuration extends the current configuration. Saving a configuration includes all objects since the last
|
||||
* configuration not including the components present before the last load.
|
||||
*
|
||||
* The configuration file is a TOML file with the following format:
|
||||
* @code
|
||||
* [Configuration]
|
||||
* Version = 100 # Configuration file version.
|
||||
*
|
||||
* [[Component]]
|
||||
* Path = "my_module.sdv # Relative to the executable or absolute path to the module - not used for main and isolated
|
||||
* # applications since the components must be installed.
|
||||
* Class = "MyComponent" # Class name of the component.
|
||||
* Name = "MyPersonalComponent" # Optional instance name of the component. If not provided, the name will automatically be the
|
||||
* # default name of the component or if not available the class name of the component.
|
||||
* AttributeXYZ = 123 # Additional settings for the component provided during initialization.
|
||||
*
|
||||
* [[Component]]
|
||||
* Class = "MyComponent2" # Class name of the component - if also present in "my_module.sdv" doesn't need additional
|
||||
* # 'Path' value. The component name is taken from the default name of the component.
|
||||
*
|
||||
* [[Component]]
|
||||
* Class = "MyComponent" # Class name of the component; class is the same as before.
|
||||
* Name = "MyPersonalComponent2" # Specific name identifying another instance of the component.
|
||||
*
|
||||
* [[Module]]
|
||||
* Path = "my_other_module.sdv # Relative to the executable or absolute path to the module - not used for main and isolated
|
||||
* # applications since the components must be installed. This path might contain components not
|
||||
* # started, but known by the repository (utilities).
|
||||
* @endcode
|
||||
*
|
||||
* For the main application there are several configuration files:
|
||||
* - Platform configuration (OS support, middleware support, vehicle bus and Ethernet interface configuration - system objects)
|
||||
* - Vehicle interface configuration (DBC, port assignments - devices)
|
||||
@@ -85,7 +78,10 @@ public:
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Load the installation manifests for core, executable and user components. Main and isolated applications only.
|
||||
* @brief Load/reload the installation manifests for core, executable and user components.
|
||||
* @details The installation manifests contain the module and component class information that is used for finding component
|
||||
* classes. Standalone applications only load the core manifest. Server applications additionally load the manifest according to
|
||||
* their instance ID.
|
||||
* @return Returns 'true when successful; 'false' when not.
|
||||
*/
|
||||
bool LoadInstallationManifests();
|
||||
@@ -95,6 +91,25 @@ public:
|
||||
*/
|
||||
void UnloadInstallatonManifests();
|
||||
|
||||
/**
|
||||
* @brief Load the configuration files listed in the settings.
|
||||
* @details Load the configuration files stored in the settings file (for server applications) or provided through the startup
|
||||
* configuration (for local applications). For server applications, the system configurations are loaded and provided to the
|
||||
* repository for starting the components. After the system configuration, the user configuration is loaded. The module section
|
||||
* and path information in the configuration files is ignored (if available at all) for server applications. For local
|
||||
* applications there are no system configurations to load and only the user configuration is loaded which was provided during
|
||||
* startup. The loading of the modules precedes the loading and starting of the components.
|
||||
* @return Returns 'true' when the loading was successful. 'False' when not.
|
||||
*/
|
||||
bool LoadAppConfigs();
|
||||
|
||||
/**
|
||||
* @brief Save the configuration files listed in the settings.
|
||||
* @remarks When running in maintenance mode, all configurations are updated. In all other modes, only the
|
||||
* user application config is updated.
|
||||
*/
|
||||
void SaveAppConfigs();
|
||||
|
||||
/**
|
||||
* @brief Process the provided configuration by loading modules and creating objects/stubs/proxies defined in the
|
||||
* configuration string. Processing a configuration resets the baseline before processing. Overload of
|
||||
@@ -123,10 +138,24 @@ public:
|
||||
* @attention Configuration changes can only occur when the system is in configuration mode.
|
||||
* @param[in] ssConfigPath Path to the file containing the configuration (TOML). The path can be an absolute as well as a
|
||||
* relative path. In case a relative path is provided, the configuration is stored relative to the executable directory.
|
||||
* @return Returns 'true' on success; 'false' otherwise.
|
||||
* @return Returns 'true' on success (or no changes detected); 'false' otherwise.
|
||||
*/
|
||||
virtual bool SaveConfig(/*in*/ const sdv::u8string& ssConfigPath) const override;
|
||||
|
||||
/**
|
||||
* @brief Generate the configuration TOML string. Overload of sdv::core::IConfig::GenerateConfigString.
|
||||
* @return The generated configuration string.
|
||||
*/
|
||||
virtual sdv::u8string GenerateConfigString() const override;
|
||||
|
||||
/**
|
||||
* @brief Close the current configuration. Overload of sdv::core::IConfig::CloseConfig.
|
||||
* @details This will close und unload the components and modules from the current configuration as well as dependent
|
||||
* components that builds on top of the components being closed. Components that the current configuration depends on
|
||||
* are not closed.
|
||||
*/
|
||||
virtual void CloseConfig() override;
|
||||
|
||||
/**
|
||||
* @brief Add a search path to a folder where a config file can be found. Overload of
|
||||
* sdv::core::IConfig::AddDirectorySearchDir.
|
||||
@@ -136,7 +165,9 @@ public:
|
||||
virtual bool AddConfigSearchDir(/*in*/ const sdv::u8string& ssDir) override;
|
||||
|
||||
/**
|
||||
* @brief Reset the configuration baseline.
|
||||
* @brief Reset the configuration baseline. Overload of sdv::core::IConfig::ResetConfigBaseline.
|
||||
* @remarks When running as server, the config baseline is limited to the user configuration, which only consists of
|
||||
* complex services. For standalone setups, the configuration can also contain basic services and devices.
|
||||
* @details The configuration baseline determines what belongs to the current configuration. Any object being added
|
||||
* after this baseline will be stored in a configuration file.
|
||||
*/
|
||||
@@ -209,17 +240,37 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Search for the installed component with the specific class name.
|
||||
* @details Find the first component containing a class with the specified name. For main and isolated applications.
|
||||
* The order of checking the installation manifest is core-manifest, manifest in executable directory and manifest in supplied
|
||||
* installation directory.
|
||||
* @details Find the first component containing a class with the specified name. The order of checking the installation manifest
|
||||
* is core-manifest, manifest in executable directory and manifest in supplied installation directory. For standalone
|
||||
* applications only the core and executable manifests are checked.
|
||||
* @remarks Components of system objects specified in the user installation are not returned.
|
||||
* @param[in] rssClass Reference to the class that should be searched for. The class is compared to the class name and the
|
||||
* default name in the manifest.
|
||||
* @return Optionally returns the component manifest.
|
||||
* @return Optionally returns the component class information.
|
||||
*/
|
||||
std::optional<CInstallManifest::SComponent> FindInstalledComponent(const std::string& rssClass) const;
|
||||
std::optional<sdv::SClassInfo> FindInstalledComponent(const std::string& rssClass) const;
|
||||
|
||||
/**
|
||||
* @brief Remove any components that are mentioned in the manifest from the configurations available in the settings file.
|
||||
* @remarks Only available when running as maintenance or main application. If running as main application limited to removal
|
||||
* from the user configuration only.
|
||||
* @param[in] rManifest Reference to the manifest file containing the installation components.
|
||||
* @return Returns whether the removal was successful (also when there was nothing to remove) or not successful (when the
|
||||
* components were in one of the system configurations and the application is not running as maintenance application).
|
||||
*/
|
||||
bool RemoveFromConfig(const CInstallManifest& rManifest);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief For standalone applications, compose a proper absolute configuration file path based a relative path and the search
|
||||
* directories provided.
|
||||
* @remarks The path could point to a non-existing file (in case of the save configuration call). The parent directory needs
|
||||
* to exist.
|
||||
* @param[in] rpathConfigFile Reference to the path containing a relatice or absolute path name to the configuration file.
|
||||
* @return Returns a fully qualified path to the configuration file. Or an empty path in case of failure.
|
||||
*/
|
||||
std::filesystem::path ComposeConfigPathStandaloneApp(const std::filesystem::path& rpathConfigFile) const;
|
||||
|
||||
/**
|
||||
* @brief Add config folders of the core_services and the executable to the search paths if not done so already.
|
||||
*/
|
||||
@@ -233,17 +284,23 @@ private:
|
||||
sdv::sequence<sdv::installation::SFileDesc> seqFiles; ///< Companion files
|
||||
};
|
||||
|
||||
std::mutex m_mtxSearchPaths; ///< Access protection to directory list.
|
||||
std::list<std::filesystem::path> m_lstSearchPaths; ///< List of search directories.
|
||||
std::filesystem::path m_pathLastSearchDir; ///< The last search directory to also save the file to.
|
||||
std::mutex m_mtxInstallations; ///< Access protection to the installations.
|
||||
CInstallManifest m_manifestCore; ///< Install manifest for core components (main and isolated apps).
|
||||
CInstallManifest m_manifestExe; ///< Install manifest for exe components (main and isolated apps).
|
||||
std::vector<CInstallManifest> m_vecUserManifests; ///< Install manifests for user components (main and isolated apps).
|
||||
std::map<std::string, SInstallation> m_mapInstallations; ///< Installation map.
|
||||
std::mutex m_mtxSearchPaths; ///< Access protection to directory list.
|
||||
std::list<std::filesystem::path> m_lstSearchPaths; ///< List of search directories.
|
||||
std::filesystem::path m_pathLastSearchDir; ///< The last search directory to also save the file to.
|
||||
std::mutex m_mtxInstallations; ///< Access protection to the installations.
|
||||
CInstallManifest m_manifestCore; ///< Install manifest for core components (main and isolated apps).
|
||||
CInstallManifest m_manifestExe; ///< Install manifest for exe components (main and isolated apps).
|
||||
std::vector<CInstallManifest> m_vecUserManifests; ///< Install manifests for user components (main and isolated apps).
|
||||
std::map<std::string, SInstallation> m_mapInstallations; ///< Installation map.
|
||||
std::vector<CAppConfigFile> m_vecSysConfigs; ///< System configurations.
|
||||
CAppConfigFile m_configUserConfig; ///< User configuration file.
|
||||
};
|
||||
|
||||
#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST
|
||||
/**
|
||||
* @brief Return the application config class.
|
||||
* @return Reference to the application config class.
|
||||
*/
|
||||
CAppConfig& GetAppConfig();
|
||||
|
||||
/**
|
||||
* @brief App config service class.
|
||||
@@ -270,7 +327,7 @@ public:
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("ConfigService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
@@ -292,8 +349,6 @@ public:
|
||||
*/
|
||||
static bool EnableAppInstallAccess();
|
||||
};
|
||||
DEFINE_SDV_OBJECT_NO_EXPORT(CAppConfigService)
|
||||
|
||||
#endif
|
||||
DEFINE_SDV_OBJECT(CAppConfigService)
|
||||
|
||||
#endif // !defined APP_CONFIG_H
|
||||
816
sdv_services/core/app_config_file.cpp
Normal file
816
sdv_services/core/app_config_file.cpp
Normal file
@@ -0,0 +1,816 @@
|
||||
/********************************************************************************
|
||||
* 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 "app_config_file.h"
|
||||
#include <support/toml.h>
|
||||
#include "app_settings.h"
|
||||
#include "../../global/ipc_named_mutex.h"
|
||||
#include "toml_parser/parser_toml.h"
|
||||
#include <support/toml.h>
|
||||
#include "installation_manifest.h"
|
||||
#include <set>
|
||||
|
||||
CAppConfigFile::CAppConfigFile(const std::filesystem::path& rpathConfigFile)
|
||||
{
|
||||
switch (GetAppSettings().GetContextType())
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
m_pathConfigFile = GetAppSettings().GetInstallDir() / rpathConfigFile;
|
||||
break;
|
||||
default:
|
||||
m_pathConfigFile = rpathConfigFile;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!LoadConfigFile())
|
||||
{
|
||||
SDV_LOG_ERROR("ERROR: failed to load the configuration file: ", rpathConfigFile.generic_u8string());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void CAppConfigFile::Clear()
|
||||
{
|
||||
m_pathConfigFile.clear();
|
||||
m_bLoaded = false;
|
||||
m_vecComponentList.clear();
|
||||
m_vecClassList.clear();
|
||||
m_vecModuleList.clear();
|
||||
}
|
||||
|
||||
bool CAppConfigFile::IsLoaded() const
|
||||
{
|
||||
return m_bLoaded;
|
||||
}
|
||||
|
||||
const std::filesystem::path& CAppConfigFile::ConfigPath() const
|
||||
{
|
||||
return m_pathConfigFile;
|
||||
}
|
||||
|
||||
bool CAppConfigFile::LoadConfigFile()
|
||||
{
|
||||
if (!m_pathConfigFile.has_filename() || !std::filesystem::exists(m_pathConfigFile.parent_path()))
|
||||
return false;
|
||||
if (!std::filesystem::exists(m_pathConfigFile))
|
||||
{
|
||||
m_bLoaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ifstream fstream(m_pathConfigFile);
|
||||
std::string ssConfig((std::istreambuf_iterator<char>(fstream)), std::istreambuf_iterator<char>());
|
||||
fstream.close();
|
||||
|
||||
return LoadConfigFromString(ssConfig);
|
||||
}
|
||||
|
||||
bool CAppConfigFile::LoadConfigFromString(const std::string& rssConfig)
|
||||
{
|
||||
// Determine whether running in main, isolation or maintenance mode.
|
||||
bool bServerApp = false;
|
||||
switch (GetAppSettings().GetContextType())
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
bServerApp = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Read the configuration
|
||||
toml_parser::CParser parserConfig(rssConfig);
|
||||
|
||||
// Check for the version
|
||||
sdv::toml::CNodeCollection nodeConfig(&parserConfig.Root());
|
||||
uint32_t uiVersion = nodeConfig.GetDirect("Configuration.Version").GetValue();
|
||||
if (uiVersion != SDVFrameworkInterfaceVersion)
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid version of application settings file (expected version ",
|
||||
SDVFrameworkInterfaceVersion,
|
||||
", but available version ",
|
||||
uiVersion,
|
||||
")");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate through the module list (if not running as server application).
|
||||
if (!bServerApp)
|
||||
{
|
||||
sdv::toml::CNodeCollection nodeModules = nodeConfig.GetDirect("Module");
|
||||
for (size_t nIndex = 0; nIndex < nodeModules.GetCount(); nIndex++)
|
||||
{
|
||||
sdv::toml::CNodeCollection nodeModule = nodeModules.Get(nIndex);
|
||||
SModule sModule;
|
||||
sModule.pathModule = nodeModule.GetDirect("Path").GetValueAsPath();
|
||||
auto itModule = std::find_if(m_vecModuleList.begin(), m_vecModuleList.end(),
|
||||
[&](const SModule& rsModuleStored) { return rsModuleStored.pathModule == sModule.pathModule; });
|
||||
if (itModule == m_vecModuleList.end())
|
||||
m_vecModuleList.push_back(std::move(sModule));
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through the class list
|
||||
sdv::toml::CNodeCollection arrayClasses = nodeConfig.GetDirect("Class");
|
||||
for (size_t nIndex = 0; nIndex < arrayClasses.GetCount(); nIndex++)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableClass = arrayClasses.Get(nIndex);
|
||||
sdv::SClassInfo sClass = TOML2ClassInfo(parserConfig.Root().Cast<toml_parser::CNodeCollection>(), nIndex);
|
||||
if (sClass.ssName.empty() || sClass.eType == sdv::EObjectType::undefined) continue;
|
||||
auto itClass = std::find_if(m_vecClassList.begin(), m_vecClassList.end(),
|
||||
[&](const sdv::SClassInfo& rsClassStored) { return rsClassStored.ssName == sClass.ssName; });
|
||||
if (itClass == m_vecClassList.end())
|
||||
m_vecClassList.push_back(std::move(sClass));
|
||||
}
|
||||
|
||||
// Iterate through the component list
|
||||
sdv::toml::CNodeCollection nodeComponents = nodeConfig.GetDirect("Component");
|
||||
for (size_t nIndex = 0; nIndex < nodeComponents.GetCount(); nIndex++)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableComponent = nodeComponents.Get(nIndex);
|
||||
SComponent sComponent;
|
||||
if (!bServerApp)
|
||||
sComponent.pathModule = tableComponent.GetDirect("Path").GetValue().get<std::string>();
|
||||
sComponent.ssClassName = tableComponent.GetDirect("Class").GetValue().get<std::string>();
|
||||
if (sComponent.ssClassName.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("Missing class name for the class definition in the configuration file '", m_pathConfigFile, "'.");
|
||||
return false;
|
||||
}
|
||||
sComponent.ssInstanceName = tableComponent.GetDirect("Name").GetValue().get<std::string>();
|
||||
// NOTE: The name could be empty. The system will automatically select a name.
|
||||
sdv::toml::CNodeCollection tableParams = tableComponent.GetDirect("Parameters");
|
||||
if (tableParams.IsValid() && tableParams.GetCount())
|
||||
sComponent.ssParameterTOML = tableParams.GetTOML();
|
||||
auto itComponent = std::find_if(m_vecComponentList.begin(), m_vecComponentList.end(),
|
||||
[&](const SComponent& rsComponentStored) { return rsComponentStored.ssClassName == sComponent.ssClassName &&
|
||||
rsComponentStored.ssInstanceName == sComponent.ssInstanceName; });
|
||||
if (itComponent == m_vecComponentList.end())
|
||||
m_vecComponentList.push_back(std::move(sComponent));
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to parse application configuration '", m_pathConfigFile.generic_u8string(), "': ", rexcept.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_bLoaded = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAppConfigFile::SaveConfigFile(const std::filesystem::path& rpathTarget /*= std::filesystem::path()*/,
|
||||
bool bSafeParamsOnly /*= false*/) const
|
||||
{
|
||||
// Protect against multiple write actions at the same time.
|
||||
std::string ssMutexName = "LockSdvConfigFile_" + std::to_string(GetAppSettings().GetInstanceID()) + "_" +
|
||||
rpathTarget.filename().generic_u8string();
|
||||
ipc::named_mutex mtx(ssMutexName);
|
||||
// Warning of cppcheck for locking a local mutex, which doesn't have any effect. Since this is a named mutex between
|
||||
// applciations, the warning is not correct. Suppress warning.
|
||||
// cppcheck-suppress localMutex
|
||||
std::unique_lock<ipc::named_mutex> lock(mtx);
|
||||
|
||||
std::filesystem::path pathTargetCopy = rpathTarget;
|
||||
if (pathTargetCopy.empty()) pathTargetCopy = m_pathConfigFile;
|
||||
if (pathTargetCopy.empty()) return false;
|
||||
if (!std::filesystem::exists(pathTargetCopy.parent_path())) return false;
|
||||
|
||||
// If the configuration is not existing, create a default configuration file...
|
||||
std::string ssConfig;
|
||||
if (std::filesystem::exists(pathTargetCopy))
|
||||
{
|
||||
// Open the existing settings file
|
||||
std::ifstream fstream(m_pathConfigFile);
|
||||
if (!fstream.is_open())
|
||||
{
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Cannot open the application configuration file." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the config file
|
||||
ssConfig = std::string((std::istreambuf_iterator<char>(fstream)), std::istreambuf_iterator<char>());
|
||||
if (ssConfig.empty())
|
||||
{
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Cannot read the application settings file; will use default." << std::endl;
|
||||
}
|
||||
}
|
||||
bool bChanged = false;
|
||||
if (!UpdateConfigString(ssConfig, bChanged, bSafeParamsOnly)) return false;
|
||||
if (!bChanged) return true; // No change needed
|
||||
|
||||
std::ofstream fstream(pathTargetCopy, std::ios::trunc);
|
||||
if (!fstream.is_open())
|
||||
{
|
||||
SDV_LOG_ERROR("Cannot write the application settings file '", m_pathConfigFile.generic_u8string(), "'.");
|
||||
return false;
|
||||
}
|
||||
fstream << ssConfig;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAppConfigFile::UpdateConfigString(std::string& rssContent, bool& rbChanged, bool /*bSaveParamsOnly*/ /*= false*/) const
|
||||
{
|
||||
// Reset change flag
|
||||
rbChanged = false;
|
||||
|
||||
// Determine whether running in main, isolation or maintenance mode.
|
||||
bool bServerApp = false;
|
||||
switch (GetAppSettings().GetContextType())
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
bServerApp = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// If the configuration is not existing, create a default configuration file...
|
||||
std::string ssConfig = rssContent;
|
||||
if (ssConfig.empty())
|
||||
{
|
||||
ssConfig = R"toml(# Configuration file
|
||||
[Configuration]
|
||||
Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(
|
||||
|
||||
# The configuration consists of several sections:
|
||||
# - Modules Modules can contain component classes, which are only made accessible, but not automatically loaded. This
|
||||
# section applies to standalone applications only. Server applications use installations to provide accessible
|
||||
# component classes.
|
||||
# - Classes The classes that are present in a module and contain specific class information.
|
||||
# - Components The components that are started when loading the configuration.
|
||||
#
|
||||
#
|
||||
# The modules specify the location of the modules relative to the executable or with an absolute path. They are only available for
|
||||
# standalone applications and are ignored by server based application, who install the modules rather than provide them in a
|
||||
# configuration. A module has the following attribute:
|
||||
# - Path [Compulsory] Identifying the module path of components which should be made accessible. The path can be
|
||||
# relative to the application executable (or relocation path) or can be an absolute path to the module in the
|
||||
# system.
|
||||
#
|
||||
# Example:
|
||||
# [[Module]]
|
||||
# Path = "my_module.sdv"
|
||||
#
|
||||
#
|
||||
# Classes are used to specify default parameter for a component instance. These parameter can be overridden by the component instance
|
||||
# when specifying the parameter with identical name in the component section. The class has the following attributes:
|
||||
# - Path [Optional] Identifying the module path of the component. Only used for standalone applications. If not
|
||||
# specified the component class must be known in the system (e.g. through the module section or the component
|
||||
# section).
|
||||
# - Class [Compulsory] Name of the class that has the default pateters.
|
||||
# - Parameters [Optional] Table containing component instance specific parameters which are default parameters of all
|
||||
# instances of the components with of this class.
|
||||
# Example:
|
||||
# [[Class]]
|
||||
# Path = "my_module.sdv"
|
||||
# Class = "MyComponent"
|
||||
# [Class.Parameters]
|
||||
# AttributeA = "default_blep"
|
||||
# AttributeB = 1234
|
||||
# [Component.Parameters.GroupC]
|
||||
# AttributeC1 = 4567.7890
|
||||
# AttributeC2 = "This is a string"
|
||||
#
|
||||
#
|
||||
# Components are class instances that are instantiated during the loading of the configuration. They are stored as table array
|
||||
# and contain as a minimum a class name. They can have instance specific parameters, which are stored as a table within the
|
||||
# component. Components are instantiated in the order of appearance. A component can have the following attributes:
|
||||
# - Path [Optional] Identifying the module path of the component. Only used for standalone applications. If not
|
||||
# specified the component class must be known in the system (e.g. through the module section or the class
|
||||
# section).
|
||||
# - Class [Compulsory] Name of the class that has to be instantiated.
|
||||
# - Name [Optional] Name of the component instance. If not available, the class name determines the component name.
|
||||
# - Parameters [Optional] Table containing the component instance specific parameters. They override default component class
|
||||
# parameters.
|
||||
#
|
||||
# Example:
|
||||
# [[Component]]
|
||||
# Path = "my_module.sdv"
|
||||
# Class = "MyComponent"
|
||||
# Name = "MyPersonalComponent"
|
||||
# [Component.Parameters]
|
||||
# AttributeA = "blep"
|
||||
# AttributeB = 123
|
||||
# [Component.Parameters.GroupC]
|
||||
# AttributeC1 = 456.789
|
||||
# AttributeC2 = "This is a text"
|
||||
#
|
||||
|
||||
)toml";
|
||||
rbChanged = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Read the configuration
|
||||
toml_parser::CParser parserConfig(ssConfig);
|
||||
|
||||
// Check for the version
|
||||
sdv::toml::CNodeCollection nodeConfig(&parserConfig.Root());
|
||||
uint32_t uiVersion = nodeConfig.GetDirect("Configuration.Version").GetValue();
|
||||
if (uiVersion != SDVFrameworkInterfaceVersion)
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid version of application settings file (expected version ", SDVFrameworkInterfaceVersion,
|
||||
", but available version ", uiVersion, ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate and update through the module list (if not running as server application).
|
||||
if (!bServerApp)
|
||||
{
|
||||
// Remove modules not in the list any more.
|
||||
// Modules both in the vector and in the list are removed from the vector
|
||||
// Modules leftover in the vector are added to the list
|
||||
auto vecModuleListCopy = m_vecModuleList;
|
||||
sdv::toml::CNodeCollection nodeModules = nodeConfig.GetDirect("Module");
|
||||
for (size_t nIndex = nodeModules.GetCount() - 1; nIndex < nodeModules.GetCount(); --nIndex)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableModule = nodeModules.Get(nIndex);
|
||||
std::filesystem::path pathModule = tableModule.GetDirect("Path").GetValue().get<std::string>();
|
||||
auto itModule = std::find_if(vecModuleListCopy.begin(), vecModuleListCopy.end(),
|
||||
[&](const SModule& rsModule) { return pathModule == rsModule.pathModule; });
|
||||
if (itModule == vecModuleListCopy.end())
|
||||
{
|
||||
tableModule.Delete();
|
||||
rbChanged = true;
|
||||
}
|
||||
else
|
||||
vecModuleListCopy.erase(itModule);
|
||||
}
|
||||
for (const SModule& rsModule : vecModuleListCopy)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableModule = nodeConfig.AddTableArray("Module");
|
||||
tableModule.AddValue("Path", rsModule.pathModule);
|
||||
rbChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through and update the class list
|
||||
// Remove classes not in the list any more.
|
||||
// Classes both in the vector and in the list are removed from the vector
|
||||
// Classes leftover in the vector are added to the list
|
||||
auto vecClassListCopy = m_vecClassList;
|
||||
sdv::toml::CNodeCollection arrayClasses = nodeConfig.GetDirect("Class");
|
||||
for (size_t nIndex = arrayClasses.GetCount() -1; nIndex < arrayClasses.GetCount(); --nIndex)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableClass = arrayClasses.Get(nIndex);
|
||||
std::filesystem::path pathModule;
|
||||
if (!bServerApp)
|
||||
pathModule = tableClass.GetDirect("Path").GetValue().get<std::string>();
|
||||
std::string ssClassName = tableClass.GetDirect("Class").GetValue().get<std::string>();
|
||||
auto itClass = std::find_if(vecClassListCopy.begin(),
|
||||
vecClassListCopy.end(),
|
||||
[&](const sdv::SClassInfo& rsClass) { return ssClassName == rsClass.ssName; });
|
||||
if (itClass == vecClassListCopy.end())
|
||||
{
|
||||
tableClass.Delete();
|
||||
rbChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update of path
|
||||
if (!bServerApp)
|
||||
{
|
||||
if (pathModule.generic_u8string() != itClass->ssModulePath)
|
||||
{
|
||||
pathModule = std::filesystem::u8path(static_cast<std::string>(itClass->ssModulePath));
|
||||
sdv::toml::CNode nodePath = tableClass.GetDirect("Path");
|
||||
if (!pathModule.empty())
|
||||
{
|
||||
if (nodePath)
|
||||
nodePath.SetValue(pathModule);
|
||||
else
|
||||
tableClass.AddValue("Path", pathModule);
|
||||
}
|
||||
else if (nodePath)
|
||||
nodePath.Delete();
|
||||
rbChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the parameters - class parameters are default parameters provided by the object. Overwrite existing
|
||||
// default parameters.
|
||||
sdv::toml::CNodeCollection tableParams = tableClass.GetDirect("Parameters");
|
||||
sdv::u8string ssExistingTOML;
|
||||
if (tableParams) ssExistingTOML = tableParams.GetTOML();
|
||||
if (ssExistingTOML != itClass->ssDefaultConfig)
|
||||
{
|
||||
if (tableParams) tableParams.Delete();
|
||||
if (!itClass->ssDefaultConfig.empty())
|
||||
tableClass.InsertTOML(sdv::toml::npos, itClass->ssDefaultConfig);
|
||||
rbChanged = true;
|
||||
}
|
||||
|
||||
vecClassListCopy.erase(itClass);
|
||||
}
|
||||
}
|
||||
for (const sdv::SClassInfo& rsClass : vecClassListCopy)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableClass = nodeConfig.AddTableArray("Class");
|
||||
tableClass.AddValue("Class", rsClass.ssName);
|
||||
if (!bServerApp)
|
||||
tableClass.AddValue("Path", std::filesystem::u8path(static_cast<std::string>(rsClass.ssModulePath)));
|
||||
if (!rsClass.ssDefaultConfig.empty())
|
||||
tableClass.InsertTOML(sdv::toml::npos, rsClass.ssDefaultConfig);
|
||||
rbChanged = true;
|
||||
}
|
||||
|
||||
// Iterate through and update the component list
|
||||
// Remove components not in the list any more.
|
||||
// Components both in the vector and in the list are removed from the vector
|
||||
// Components leftover in the vector are added to the list
|
||||
auto vecComponentListCopy = m_vecComponentList;
|
||||
sdv::toml::CNodeCollection nodeComponents = nodeConfig.GetDirect("Component");
|
||||
for (size_t nIndex = nodeComponents.GetCount() - 1; nIndex < nodeComponents.GetCount(); --nIndex)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableComponent = nodeComponents.Get(nIndex);
|
||||
std::filesystem::path pathModule;
|
||||
if (!bServerApp)
|
||||
pathModule = tableComponent.GetDirect("Path").GetValue().get<std::string>();
|
||||
std::string ssClassName = tableComponent.GetDirect("Class").GetValue().get<std::string>();
|
||||
std::string ssInstanceName = tableComponent.GetDirect("Name").GetValue().get<std::string>();
|
||||
|
||||
auto itComponent = std::find_if(vecComponentListCopy.begin(), vecComponentListCopy.end(),
|
||||
[&](const SComponent& rsComponent)
|
||||
{
|
||||
return ssInstanceName.empty() ? (ssClassName == rsComponent.ssClassName && rsComponent.ssInstanceName.empty()) :
|
||||
ssInstanceName == rsComponent.ssInstanceName;
|
||||
});
|
||||
if (itComponent == vecComponentListCopy.end())
|
||||
{
|
||||
// The class and the instance names must be identical
|
||||
tableComponent.Delete();
|
||||
rbChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update of path
|
||||
if (!bServerApp)
|
||||
{
|
||||
if (pathModule != itComponent->pathModule)
|
||||
{
|
||||
pathModule = itComponent->pathModule;
|
||||
sdv::toml::CNode nodePath = tableComponent.GetDirect("Path");
|
||||
if (!pathModule.empty())
|
||||
{
|
||||
if (nodePath)
|
||||
nodePath.SetValue(pathModule);
|
||||
else
|
||||
tableComponent.AddValue("Path", pathModule);
|
||||
}
|
||||
else if (nodePath)
|
||||
nodePath.Delete();
|
||||
rbChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the parameters - component parameters should be updated, not simply overwritten. Use the combine function.
|
||||
sdv::toml::CNodeCollection tableParams = tableComponent.GetDirect("Parameters");
|
||||
sdv::u8string ssExistingTOML;
|
||||
if (tableParams) ssExistingTOML = tableParams.GetTOML();
|
||||
if (!itComponent->ssParameterTOML.empty())
|
||||
{
|
||||
// Update needed?
|
||||
if (tableParams && ssExistingTOML != itComponent->ssParameterTOML)
|
||||
{
|
||||
// Get the underlying node collection
|
||||
toml_parser::CTable* pComponent =
|
||||
dynamic_cast<toml_parser::CTable*>(static_cast<sdv::IInterfaceAccess*>(tableParams.GetInterface()));
|
||||
if (!pComponent)
|
||||
{
|
||||
SDV_LOG_ERROR("Failure to get access to an internal interface within the TOML parser.");
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
toml_parser::CParser parserParams(itComponent->ssParameterTOML);
|
||||
pComponent->Combine(parserParams.Root().Cast<toml_parser::CNodeCollection>());
|
||||
}
|
||||
catch (const toml_parser::XTOMLParseException& /*rexcept*/)
|
||||
{
|
||||
SDV_LOG_WARNING("The parameter TOML cannot be interpreted; no saving possible.");
|
||||
}
|
||||
rbChanged = true;
|
||||
}
|
||||
else if (!tableParams)
|
||||
{
|
||||
// Simply add the parameters
|
||||
tableParams = tableComponent.InsertTable(sdv::toml::npos, "Parameters");
|
||||
tableParams.InsertTOML(sdv::toml::npos, itComponent->ssParameterTOML);
|
||||
rbChanged = true;
|
||||
}
|
||||
}
|
||||
vecComponentListCopy.erase(itComponent);
|
||||
}
|
||||
}
|
||||
for (const SComponent& rsComponent : vecComponentListCopy)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableComponent = nodeConfig.AddTableArray("Component");
|
||||
if (!bServerApp && !rsComponent.pathModule.empty())
|
||||
tableComponent.AddValue("Path", rsComponent.pathModule);
|
||||
tableComponent.AddValue("Class", rsComponent.ssClassName);
|
||||
if (!rsComponent.ssInstanceName.empty())
|
||||
tableComponent.AddValue("Name", rsComponent.ssInstanceName);
|
||||
if (!rsComponent.ssParameterTOML.empty())
|
||||
{
|
||||
sdv::toml::CNodeCollection tableParams = tableComponent.InsertTable(sdv::toml::npos, "Parameters");
|
||||
tableParams.InsertTOML(sdv::toml::npos, rsComponent.ssParameterTOML);
|
||||
}
|
||||
rbChanged = true;
|
||||
}
|
||||
|
||||
// Save the configuration file if needed
|
||||
if (rbChanged)
|
||||
{
|
||||
rssContent = parserConfig.GenerateTOML();
|
||||
rbChanged = true;
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to parse configuration: ", rexcept.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<CAppConfigFile::SComponent>& CAppConfigFile::GetComponentList() const
|
||||
{
|
||||
return m_vecComponentList;
|
||||
}
|
||||
|
||||
const std::vector<sdv::SClassInfo>& CAppConfigFile::GetClassList() const
|
||||
{
|
||||
return m_vecClassList;
|
||||
}
|
||||
|
||||
const std::vector<CAppConfigFile::SModule>& CAppConfigFile::GetModuleList() const
|
||||
{
|
||||
return m_vecModuleList;
|
||||
}
|
||||
|
||||
bool CAppConfigFile::InsertComponent(size_t nIndex, const std::filesystem::path& rpathModule, const std::string& rssClassName,
|
||||
const std::string& rssInstanceName, const TParameterVector& rvecParameters)
|
||||
{
|
||||
// Valid parameter?
|
||||
if (rssClassName.empty()) return false;
|
||||
|
||||
// Check for a duplicate instance name. If there is no instance name, check for a component with the same class name without
|
||||
// the instance name. This is not an error!
|
||||
if (!rssInstanceName.empty())
|
||||
{
|
||||
if (std::find_if(m_vecComponentList.begin(), m_vecComponentList.end(), [&](const SComponent& rsComponent)
|
||||
{ return rsComponent.ssInstanceName == rssInstanceName; }) != m_vecComponentList.end())
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (std::find_if(m_vecComponentList.begin(), m_vecComponentList.end(), [&](const SComponent& rsComponent)
|
||||
{ return rsComponent.ssInstanceName.empty() && rsComponent.ssClassName == rssClassName; }) != m_vecComponentList.end())
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for the module to already be added to the module list. Remove it from the list if it is.
|
||||
if (!rpathModule.empty())
|
||||
{
|
||||
auto itModule = std::find_if(m_vecModuleList.begin(), m_vecModuleList.end(), [&](const SModule& rsModule)
|
||||
{ return rsModule.pathModule == rpathModule; });
|
||||
if (itModule != m_vecModuleList.end()) m_vecModuleList.erase(itModule);
|
||||
}
|
||||
|
||||
// Create a parameter TOML
|
||||
std::string ssParameterTOML;
|
||||
try
|
||||
{
|
||||
// Create a TOML string
|
||||
toml_parser::CParser parser("");
|
||||
sdv::toml::CNodeCollection root(&parser.Root());
|
||||
|
||||
// Split the key in a group and a parameter name
|
||||
auto fnSplit = [](const std::string& rssKey) -> std::pair<std::string, std::string>
|
||||
{
|
||||
size_t nSeparator = rssKey.find_last_of('.');
|
||||
if (nSeparator == std::string::npos) return std::make_pair("", rssKey);
|
||||
return std::make_pair(rssKey.substr(0, nSeparator), rssKey.substr(nSeparator + 1));
|
||||
};
|
||||
|
||||
// Iterate through the parameters
|
||||
std::string ssGroup;
|
||||
sdv::toml::CNodeCollection group(root);
|
||||
for (const auto& prParameter : rvecParameters)
|
||||
{
|
||||
// Split the key in a group and parameter name.
|
||||
auto prKey = fnSplit(prParameter.first);
|
||||
|
||||
// Need to add a group?
|
||||
if (prKey.first != ssGroup)
|
||||
{
|
||||
group = root.InsertTable(sdv::toml::npos, prKey.first);
|
||||
ssGroup = prKey.first;
|
||||
}
|
||||
|
||||
// Add the parameter
|
||||
group.InsertValue(sdv::toml::npos, prKey.second, prParameter.second);
|
||||
}
|
||||
ssParameterTOML = parser.GenerateTOML();
|
||||
}
|
||||
catch (const toml_parser::XTOMLParseException& /*rexcept*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the instance
|
||||
SComponent sComponent;
|
||||
sComponent.pathModule = rpathModule;
|
||||
sComponent.ssClassName = rssClassName;
|
||||
sComponent.ssInstanceName = rssInstanceName;
|
||||
sComponent.ssParameterTOML = std::move(ssParameterTOML);
|
||||
m_vecComponentList.insert((nIndex < m_vecComponentList.size() ? m_vecComponentList.begin() + nIndex : m_vecComponentList.end()),
|
||||
std::move(sComponent));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CAppConfigFile::RemoveComponent(const std::string& rssInstanceName)
|
||||
{
|
||||
// Find and destroy
|
||||
auto itComponent = std::find_if(m_vecComponentList.begin(),
|
||||
m_vecComponentList.end(),
|
||||
[&](const SComponent& rsComponent) { return rsComponent.ssInstanceName == rssInstanceName; });
|
||||
if (itComponent != m_vecComponentList.end())
|
||||
m_vecComponentList.erase(itComponent);
|
||||
}
|
||||
|
||||
bool CAppConfigFile::InsertModule(size_t nIndex, const std::filesystem::path& rpathModule)
|
||||
{
|
||||
// Check for the proper context; only allowed when running as standalone (not main, isolated or maintenace).
|
||||
switch (GetAppSettings().GetContextType())
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (rpathModule.empty())
|
||||
return false;
|
||||
|
||||
// Check for the module to exist already in either the component or in the module list
|
||||
if (std::find_if(m_vecComponentList.begin(),
|
||||
m_vecComponentList.end(),
|
||||
[&](const SComponent& rsComponent) { return rsComponent.pathModule == rpathModule; })
|
||||
!= m_vecComponentList.end())
|
||||
return true;
|
||||
if (std::find_if(m_vecModuleList.begin(),
|
||||
m_vecModuleList.end(),
|
||||
[&](const SModule& rsModule) { return rsModule.pathModule == rpathModule; })
|
||||
!= m_vecModuleList.end())
|
||||
return true;
|
||||
|
||||
// Add the module to the module list
|
||||
SModule sModule;
|
||||
sModule.pathModule = rpathModule;
|
||||
m_vecModuleList.insert(
|
||||
(nIndex < m_vecModuleList.size() ? m_vecModuleList.begin() + nIndex : m_vecModuleList.end()), std::move(sModule));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CAppConfigFile::RemoveModule(const std::filesystem::path& rpathModule)
|
||||
{
|
||||
// Check for the proper context; only allowed when running as standalone (not main, isolated or maintenace).
|
||||
switch (GetAppSettings().GetContextType())
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Find and destroy (any module and component with the provided path).
|
||||
auto itModule = std::find_if(m_vecModuleList.begin(),
|
||||
m_vecModuleList.end(),
|
||||
[&](const SModule& rsModule) { return rsModule.pathModule == rpathModule; });
|
||||
if (itModule != m_vecModuleList.end())
|
||||
m_vecModuleList.erase(itModule);
|
||||
while (true)
|
||||
{
|
||||
auto itComponent = std::find_if(m_vecComponentList.begin(),
|
||||
m_vecComponentList.end(),
|
||||
[&](const SComponent& rsComponent) { return rsComponent.pathModule == rpathModule; });
|
||||
if (itComponent == m_vecComponentList.end())
|
||||
break;
|
||||
m_vecComponentList.erase(itComponent);
|
||||
}
|
||||
}
|
||||
|
||||
CAppConfigFile::EMergeResult CAppConfigFile::MergeConfigFile(const std::filesystem::path& rpathConfigFile)
|
||||
{
|
||||
CAppConfigFile configNew(rpathConfigFile);
|
||||
if (!configNew.LoadConfigFile()) return EMergeResult::not_successful;
|
||||
|
||||
size_t nSucceeded = 0;
|
||||
size_t nFailed = 0;
|
||||
|
||||
// Add modules
|
||||
auto vecNewModules = configNew.GetModuleList();
|
||||
for (const auto& sNewModule : vecNewModules)
|
||||
{
|
||||
if (std::find_if(m_vecModuleList.begin(), m_vecModuleList.end(), [&sNewModule](const SModule& rsExistingModule)
|
||||
{ return sNewModule.pathModule == rsExistingModule.pathModule; }) == m_vecModuleList.end())
|
||||
m_vecModuleList.push_back(sNewModule);
|
||||
++nSucceeded;
|
||||
}
|
||||
|
||||
// Add/update classes
|
||||
auto vecNewClasses = configNew.GetClassList();
|
||||
for (const auto& sNewClass : vecNewClasses)
|
||||
{
|
||||
auto itClass = std::find_if(m_vecClassList.begin(), m_vecClassList.end(),
|
||||
[&sNewClass](const sdv::SClassInfo& rsExistingClass)
|
||||
{ return sNewClass.ssName == rsExistingClass.ssName; });
|
||||
if (itClass == m_vecClassList.end())
|
||||
m_vecClassList.push_back(sNewClass);
|
||||
else
|
||||
*itClass = sNewClass;
|
||||
++nSucceeded;
|
||||
}
|
||||
|
||||
// Merge components
|
||||
auto vecNewComponents = configNew.GetComponentList();
|
||||
for (const auto& sNewComponent : vecNewComponents)
|
||||
{
|
||||
auto itComponent = std::find_if(m_vecComponentList.begin(), m_vecComponentList.end(),
|
||||
[&sNewComponent](const SComponent& rsExistingComponent)
|
||||
{
|
||||
if (sNewComponent.ssInstanceName.empty())
|
||||
{
|
||||
if (!rsExistingComponent.ssInstanceName.empty()) return false;
|
||||
return sNewComponent.ssClassName == rsExistingComponent.ssClassName;
|
||||
}
|
||||
else
|
||||
return sNewComponent.ssInstanceName == rsExistingComponent.ssInstanceName;
|
||||
});
|
||||
if (itComponent == m_vecComponentList.end())
|
||||
m_vecComponentList.push_back(sNewComponent);
|
||||
else
|
||||
{
|
||||
// Only update component information if the components are of identical type
|
||||
if (itComponent->ssClassName != sNewComponent.ssClassName)
|
||||
{
|
||||
++nFailed;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Add additional possibilty to replace the parameters
|
||||
|
||||
// Merge the parameters of the existing component.
|
||||
if (!sNewComponent.ssParameterTOML.empty())
|
||||
{
|
||||
if (itComponent->ssParameterTOML.empty())
|
||||
itComponent->ssParameterTOML = sNewComponent.ssParameterTOML;
|
||||
else
|
||||
try
|
||||
{
|
||||
toml_parser::CParser parserNew(itComponent->ssParameterTOML), parserExisting(sNewComponent.ssParameterTOML);
|
||||
parserExisting.Root().Combine(parserNew.Root().Cast<toml_parser::CNodeCollection>());
|
||||
itComponent->ssParameterTOML = parserExisting.GenerateTOML();
|
||||
}
|
||||
catch (const toml_parser::XTOMLParseException&)
|
||||
{
|
||||
++nFailed;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++nSucceeded;
|
||||
}
|
||||
}
|
||||
return nFailed ? (nSucceeded ? EMergeResult::partly_successfull : EMergeResult::not_successful) : EMergeResult::successful;
|
||||
}
|
||||
297
sdv_services/core/app_config_file.h
Normal file
297
sdv_services/core/app_config_file.h
Normal file
@@ -0,0 +1,297 @@
|
||||
/********************************************************************************
|
||||
* 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 APP_CONFIG_FILE_H
|
||||
#define APP_CONFIG_FILE_H
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <interfaces/config.h>
|
||||
|
||||
/**
|
||||
* @brief Parameter vector type holding parameter name and parameter value paires.
|
||||
*/
|
||||
using TParameterVector = std::vector<std::pair<std::string, sdv::any_t>>;
|
||||
|
||||
/**
|
||||
* @brief Application configuration file loading and saving the configuration in a TOML file.
|
||||
* @details The configuration file is a TOML and follows the specification of TOML 1.0 (https://toml.io/en/v1.0.0). The
|
||||
* configuration file starts with the "Configuration" table, which identifies that this a configuration file. The table needs to
|
||||
* contain the version of the configuration. For example:
|
||||
* @code
|
||||
* [Configuration]
|
||||
* Version = 100
|
||||
* @endcode
|
||||
*
|
||||
* The configuration consists of several sections:
|
||||
* - Modules Modules can contain component classes, which are only made accessible, but not automatically loaded. This
|
||||
* section applies to standalone applications only. Server applications use installations to provide accessible
|
||||
* component classes.
|
||||
* - Classes The classes that are present in a module and contain specific class information.
|
||||
* - Components The components that are started when loading the configuration.
|
||||
*
|
||||
* The modules specify the location of the modules relative to the executable or with an absolute path. They are only available for
|
||||
* standalone applications and are ignored by server based application, who install the modules rather than provide them in a
|
||||
* configuration. A module has the following attribute:
|
||||
* - Path [Compulsory] Identifying the module path of components which should be made accessible. The path can be
|
||||
* relative to the application executable (or relocation path) or can be an absolute path to the module in the
|
||||
* system.
|
||||
* Example:
|
||||
* @code
|
||||
* [[Module]]
|
||||
* Path = "my_module.sdv"
|
||||
* @endcode
|
||||
*
|
||||
* Classes are used to specify default parameter for a component instance. These parameter can be overridden by the component instance
|
||||
* when specifying the parameter with identical name in the component section. The class has the following attributes:
|
||||
* - Path [Optional] Identifying the module path of the component. Only used for standalone applications. If not
|
||||
* specified the component class must be known in the system (e.g. through the module section or the component
|
||||
* section).
|
||||
* - Name [Compulsory] Name of the class.
|
||||
* - Aliases [Optional] Array of strings with alternative names for this class.
|
||||
* - DefaultName [Optional] Name to use when instantiating the component. If not specified, the class name will be used.
|
||||
* - Type [Compulsory] Object type this class represents.
|
||||
* - Singleton [Optional] Boolean specifying whether the component allows more than one instantiation. Default is false.
|
||||
* - Dependencies [Optional] Array of strings with component instances that this class depends on.
|
||||
* - Parameters [Optional] Table containing component instance specific parameters which are default parameters of all
|
||||
* instances of the components with of this class.
|
||||
* Example:
|
||||
* @code
|
||||
* [[Class]]
|
||||
* Path = "my_module.sdv"
|
||||
* Name = "MyComponent"
|
||||
* Aliases = ["Component1", "ComponentA"]
|
||||
* DefaultName = "MyComp"
|
||||
* Type = "Device"
|
||||
* Singleton = false
|
||||
* Dependencies = ["Component15", "Component16"]
|
||||
* [Class.Parameters]
|
||||
* AttributeA = "default_blep"
|
||||
* AttributeB = 1234
|
||||
* AttributeC = 4567.7890
|
||||
*@endcode
|
||||
*
|
||||
* Components are class instances that are instantiated during the loading of the configuration. They are stored as table array
|
||||
* and contain as a minimum a class name. They can have instance specific parameters, which are stored as a table within the
|
||||
* component. Components are instantiated in the order of appearance. A component can have the following attributes:
|
||||
* - Path [Optional] Identifying the module path of the component. Only used for standalone applications. If not
|
||||
* specified the component class must be known in the system (e.g. through the module section or the class
|
||||
* section).
|
||||
* - Class [Compulsory] Name of the class that has to be instantiated.
|
||||
* - Name [Optional] Name of the component instance. If not available, the class name determines the component name.
|
||||
* - Parameters [Optional] Table containing the component instance specific parameters. They override default component class
|
||||
* parameters.
|
||||
* Example:
|
||||
* @code
|
||||
* [[Component]]
|
||||
* Path = "my_module.sdv"
|
||||
* Class = "MyComponent"
|
||||
* Name = "MyPersonalComponent"
|
||||
* [Component.Parameters]
|
||||
* AttributeA = "blep"
|
||||
* AttributeB = 123
|
||||
* AttributeC = 456.789
|
||||
* AttributeD = [1, 2, 3, 4]
|
||||
* @endcode
|
||||
*/
|
||||
class CAppConfigFile
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief default constructor.
|
||||
*/
|
||||
CAppConfigFile() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructor with the config path. The config file is automatically loaded.
|
||||
* @param[in] rpathConfigFile Path to the configuration. The path must be a file name when running in main, isolation or
|
||||
* maintenance mode. Otherwise the path should contain the location of the config file.
|
||||
* The file path will be located in the root directory of the instance.
|
||||
*/
|
||||
CAppConfigFile(const std::filesystem::path& rpathConfigFile);
|
||||
|
||||
/**
|
||||
* @brief Clear the configuration.
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* @brief Is the configuration loaded or created?
|
||||
* @return Returns whether the file is loaded.
|
||||
*/
|
||||
bool IsLoaded() const;
|
||||
|
||||
/**
|
||||
* @brief Get the configuration file path.
|
||||
* @return The config file path.
|
||||
*/
|
||||
const std::filesystem::path& ConfigPath() const;
|
||||
|
||||
/**
|
||||
* @brief Load the application config file.
|
||||
* @remarks When there is no config file, this is not an error.
|
||||
* @return Returns whether the loading was successful.
|
||||
*/
|
||||
bool LoadConfigFile();
|
||||
|
||||
/**
|
||||
* @brief Load the application config from string.
|
||||
* @param[in] rssConfig Reference to the configuration string.
|
||||
* @return Returns whether the loading was successful.
|
||||
*/
|
||||
bool LoadConfigFromString(const std::string& rssConfig);
|
||||
|
||||
/**
|
||||
* @brief Save the application config file (or create when not existing yet).
|
||||
* @param[in] rpathTarget Reference to a target path. When set, the file is saved to the target file. If not set, the original
|
||||
* file is used.
|
||||
* @param[in] bSafeParamsOnly When set, only the parameters are stored in the configuration.
|
||||
* @return Returns whether the saving was successful (returns 'true' when there is no change is needed).
|
||||
*/
|
||||
bool SaveConfigFile(const std::filesystem::path& rpathTarget = std::filesystem::path(), bool bSafeParamsOnly = false) const;
|
||||
|
||||
/**
|
||||
* @brief Update the configuration string with the stored configuration.
|
||||
* @param[in, out] rssContent Reference to the string providing the current configuration being updated by this function.
|
||||
* @param[in, out] rbChanged Reference to the boolean being set when the string was changed.
|
||||
* @param[in] bSaveParamsOnly When set, only the parameters are stored in the configuration.
|
||||
* @return Returns whether the update was successful (even when no change occurred).
|
||||
*/
|
||||
bool UpdateConfigString(std::string& rssContent, bool& rbChanged, bool bSaveParamsOnly = false) const;
|
||||
|
||||
/**
|
||||
* @brief Module structure (only used in standalone applications).
|
||||
*/
|
||||
struct SModule
|
||||
{
|
||||
std::filesystem::path pathModule; ///< Component module path.
|
||||
};
|
||||
|
||||
///**
|
||||
// * @brief Component class structure (containing default parameters).
|
||||
// */
|
||||
//struct SClass
|
||||
//{
|
||||
// std::filesystem::path pathModule; ///< Optional class module path. Only used in standalone applications.
|
||||
// std::string ssClassName; ///< Class name of the component
|
||||
// std::string ssParameterTOML; ///< Parameter configuration (excluding [Parameters]-group).
|
||||
//};
|
||||
|
||||
/**
|
||||
* @brief Component structure
|
||||
*/
|
||||
struct SComponent
|
||||
{
|
||||
std::filesystem::path pathModule; ///< Optional component module path. Only used in standalone applications.
|
||||
std::string ssClassName; ///< Class name of the component.
|
||||
std::string ssInstanceName; ///< Optional instance name. If not provided, will be identical to the class
|
||||
///< name.
|
||||
std::string ssParameterTOML; ///< Parameter configuration (excluding [Parameters]-group).
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get the component list.
|
||||
* @return Reference to the vector containing the components in order of the configuration.
|
||||
*/
|
||||
const std::vector<SComponent>& GetComponentList() const;
|
||||
|
||||
/**
|
||||
* @brief Get the class list.
|
||||
* @remarks Only available when running as standalone application.
|
||||
* @return Reference to the vector containing the classes in order of the configuration.
|
||||
*/
|
||||
const std::vector<sdv::SClassInfo>& GetClassList() const;
|
||||
|
||||
/**
|
||||
* @brief Get the module list.
|
||||
* @remarks Only available when running as standalone application.
|
||||
* @return Reference to the vector containing the modules in order of the configuration.
|
||||
*/
|
||||
const std::vector<SModule>& GetModuleList() const;
|
||||
|
||||
/**
|
||||
* @brief Insert a component at the provided location.
|
||||
* @param[in] nIndex The index location to insert the component before or when larger than the component list, at the end of
|
||||
* the component list.
|
||||
* @param[in] rpathModule Reference to the path of the module. Only used when running as standalone application.
|
||||
* @param[in] rssClassName Reference to the string containing the module class name of the class to be instantiated when
|
||||
* loading the configuration.
|
||||
* @param[in] rssInstanceName Reference to the string containing an optional instantiating name of the component instance. Using
|
||||
* additional names allows multiple instantiation of the same component.
|
||||
* @param[in] rvecParameters Reference to the optional parameter vector belonging to the object instance.
|
||||
* @return Returns whether inserting the component was successful. If a component with the same class and instance names and
|
||||
* the same path is installed, the function succeeds. In all other cases, having a duplicate class or instance name will result
|
||||
* in a function failure.
|
||||
*/
|
||||
bool InsertComponent(size_t nIndex, const std::filesystem::path& rpathModule, const std::string& rssClassName,
|
||||
const std::string& rssInstanceName, const TParameterVector& rvecParameters);
|
||||
|
||||
/**
|
||||
* @brief Remove the component with the provided instance name.
|
||||
* @param[in] rssInstanceName Reference to the instance name of the component. If the component uses the class name for its
|
||||
* instance, the class name will be used for the search.
|
||||
*/
|
||||
void RemoveComponent(const std::string& rssInstanceName);
|
||||
|
||||
/**
|
||||
* @brief Insert a module into the configuration. Modules listed in the configuration are loaded, but components are not
|
||||
* started automatically. This might be useful if the module contains utilities that might be loaded on demand.
|
||||
* @remarks Modules can only be added when running as standalone application.
|
||||
* @remarks The modules are only added once. If a module is present in either the component or the module list, it is not added
|
||||
* again, and the function returns 'true'.
|
||||
* @param[in] nIndex The index location to insert the module before or when larger than the module list, at the end of the
|
||||
* module list.
|
||||
* @param[in] rpathModule Reference to the path of the module.
|
||||
* @return Returns whether inserting the module was successful. If a module with the same path is installed, the function
|
||||
* succeeds.
|
||||
*/
|
||||
bool InsertModule(size_t nIndex, const std::filesystem::path& rpathModule);
|
||||
|
||||
/**
|
||||
* @brief Remove the module with the provided module name.
|
||||
* @remarks Removing the module will also remove any components that are registered for the module.
|
||||
* @remarks Modules can only be removed when running as standalone application.
|
||||
* @param[in] rpathModule Reference to the path of the module.
|
||||
*/
|
||||
void RemoveModule(const std::filesystem::path& rpathModule);
|
||||
|
||||
enum class EMergeResult
|
||||
{
|
||||
successful,
|
||||
partly_successfull,
|
||||
not_successful
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Merge a configuration into this configuration.
|
||||
* @details All modules, classes and components will be added or updated. If a classes already exist, the class parameters will
|
||||
* be overwritten (or removed if present before). If a component already exist, the component parameters will be updated (new
|
||||
* parameters added, parameters existing in both configurations updated and other parameters left unchanged).
|
||||
* @param[in] rpathConfigFile Reference to the config file to merge.
|
||||
* @return Returns whether the configuration could be merged.
|
||||
*/
|
||||
EMergeResult MergeConfigFile(const std::filesystem::path& rpathConfigFile);
|
||||
|
||||
private:
|
||||
std::filesystem::path m_pathConfigFile; ///< Path to the configuration file. If running as main, isolated or
|
||||
///< maintenance application, the config file must be located at the
|
||||
///< installation directory.
|
||||
bool m_bLoaded = false; ///< Set when the configuration has loaded.
|
||||
std::vector<SComponent> m_vecComponentList; ///< Component list containing instantiation info for components.
|
||||
std::vector<sdv::SClassInfo> m_vecClassList; ///< Class list containing default parameters for component classes.
|
||||
std::vector<SModule> m_vecModuleList; ///< Module list containing the module paths.
|
||||
};
|
||||
|
||||
|
||||
#endif // !defined APP_CONFIG_FILE_H
|
||||
@@ -1,40 +1,26 @@
|
||||
/********************************************************************************
|
||||
* 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 "app_control.h"
|
||||
#include "module_control.h"
|
||||
#include "repository.h"
|
||||
#include "sdv_core.h"
|
||||
#include "../../global/exec_dir_helper.h"
|
||||
#include "../../global/base64.h"
|
||||
#include "../../global/flags.h"
|
||||
#include "../../global/tracefifo/trace_fifo.cpp"
|
||||
#include "toml_parser/parser_toml.h"
|
||||
#include "local_shutdown_request.h"
|
||||
|
||||
const char* szSettingsTemplate = R"code(# Settings file
|
||||
[Settings]
|
||||
Version = 100
|
||||
|
||||
# The system config array can contain zero or more configurations that are loaded at the time
|
||||
# the system ist started. It is advisable to split the configurations in:
|
||||
# platform config - containing all the components needed to interact with the OS,
|
||||
# middleware, vehicle bus, Ethernet.
|
||||
# vehicle interface - containing the vehicle bus interpretation components like data link
|
||||
# based on DBC and devices for their abstraction.
|
||||
# vehicle abstraction - containing the basic services
|
||||
# Load the system configurations by providing the "SystemConfig" keyword as an array of strings.
|
||||
# A relative path is relative to the installation directory (being "exe_location/instance_id").
|
||||
#
|
||||
# Example:
|
||||
# SystemConfig = [ "platform.toml", "vehicle_ifc.toml", "vehicle_abstract.toml" ]
|
||||
#
|
||||
|
||||
# The application config contains the configuration file that can be updated when services and
|
||||
# apps are being added to the system (or being removed from the system). Load the application
|
||||
# config by providing the "AppConfig" keyword as a string value. A relative path is relative to
|
||||
# the installation directory (being "exe_location/instance_id").
|
||||
#
|
||||
# Example
|
||||
# AppConfig = "app_config.toml"
|
||||
)code";
|
||||
#include "app_settings.h"
|
||||
#include "logger_control.h"
|
||||
#include "app_config.h"
|
||||
|
||||
/**
|
||||
* @brief Specific exit handler helping to shut down after startup. In case the shutdown wasn't explicitly executed.
|
||||
@@ -53,55 +39,10 @@ void ExitHandler()
|
||||
}
|
||||
}
|
||||
|
||||
CAppControl::CAppControl()
|
||||
{}
|
||||
|
||||
CAppControl::~CAppControl()
|
||||
{}
|
||||
|
||||
bool CAppControl::IsMainApplication() const
|
||||
CAppControl& GetAppControl()
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::main;
|
||||
}
|
||||
|
||||
bool CAppControl::IsIsolatedApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::isolated;
|
||||
}
|
||||
|
||||
bool CAppControl::IsStandaloneApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::standalone;
|
||||
}
|
||||
|
||||
bool CAppControl::IsEssentialApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::essential;
|
||||
}
|
||||
|
||||
bool CAppControl::IsMaintenanceApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::maintenance;
|
||||
}
|
||||
|
||||
bool CAppControl::IsExternalApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::external;
|
||||
}
|
||||
|
||||
sdv::app::EAppContext CAppControl::GetContextType() const
|
||||
{
|
||||
return m_eContextMode;
|
||||
}
|
||||
|
||||
uint32_t CAppControl::GetInstanceID() const
|
||||
{
|
||||
return m_uiInstanceID;
|
||||
}
|
||||
|
||||
uint32_t CAppControl::GetRetries() const
|
||||
{
|
||||
return m_uiRetries;
|
||||
static CAppControl app_control;
|
||||
return app_control;
|
||||
}
|
||||
|
||||
bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfaceAccess* pEventHandler)
|
||||
@@ -122,7 +63,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
BroadcastOperationState(sdv::app::EAppOperationState::initializing);
|
||||
|
||||
// Process the application config.
|
||||
bool bRet = ProcessAppConfig(ssConfig);
|
||||
bool bRet = GetAppSettings().ProcessAppStartupConfig(ssConfig);
|
||||
|
||||
// Undo logging interception
|
||||
sstreamCOUT.rdbuf()->pubsync();
|
||||
@@ -138,16 +79,16 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
std::cout << sstreamCOUT.str();
|
||||
std::clog << sstreamCLOG.str();
|
||||
std::cerr << sstreamCERR.str();
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the stream buffer and attach the streams if the application control is initialized as main app.
|
||||
if (m_eContextMode == sdv::app::EAppContext::main)
|
||||
if (GetAppSettings().IsMainApplication())
|
||||
{
|
||||
m_fifoTraceStreamBuffer.SetInstanceID(m_uiInstanceID);
|
||||
m_fifoTraceStreamBuffer.SetInstanceID(GetAppSettings().GetInstanceID());
|
||||
m_fifoTraceStreamBuffer.Open(1000);
|
||||
std::cout << "**********************************************" << std::endl;
|
||||
}
|
||||
@@ -158,18 +99,18 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
std::cerr << sstreamCERR.str();
|
||||
|
||||
// Check for a correctly opened stream buffer
|
||||
if (m_eContextMode == sdv::app::EAppContext::main && !m_fifoTraceStreamBuffer.IsOpened())
|
||||
if (GetAppSettings().IsMainApplication() && !m_fifoTraceStreamBuffer.IsOpened())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Log streaming could not be initialized; cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow only one instance if running as main application
|
||||
if (m_eContextMode == sdv::app::EAppContext::main)
|
||||
if (GetAppSettings().IsMainApplication())
|
||||
{
|
||||
m_pathLockFile = GetExecDirectory() / ("sdv_core_" + std::to_string(m_uiInstanceID) + ".lock");
|
||||
m_pathLockFile = GetExecDirectory() / ("sdv_core_" + std::to_string(GetAppSettings().GetInstanceID()) + ".lock");
|
||||
#ifdef _WIN32
|
||||
// Checkf or the existence of a lock file. If existing, do not continue.
|
||||
if (std::filesystem::exists(m_pathLockFile)) return false;
|
||||
@@ -214,19 +155,19 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
// Load the core services
|
||||
if (!fnLoadModule("core_services.sdv"))
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to load the Core Services. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the logger module if one is specified
|
||||
if (!m_pathLoggerModule.empty())
|
||||
if (!GetAppSettings().GetLoggerModulePath().empty())
|
||||
{
|
||||
m_tLoggerModuleID = fnLoadModule(m_pathLoggerModule.u8string());
|
||||
m_tLoggerModuleID = fnLoadModule(GetAppSettings().GetLoggerModulePath().u8string());
|
||||
if (!m_tLoggerModuleID)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to load the custom logger. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
@@ -234,12 +175,12 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
}
|
||||
|
||||
// Start the logger and assign it to the logger control.
|
||||
fnCreateObject(m_ssLoggerClass, m_ssLoggerClass, "");
|
||||
sdv::IInterfaceAccess* pLoggerObj = GetRepository().GetObject(m_ssLoggerClass);
|
||||
fnCreateObject(GetAppSettings().GetLoggerClass(), GetAppSettings().GetLoggerClass(), "");
|
||||
sdv::IInterfaceAccess* pLoggerObj = GetRepository().GetObject(GetAppSettings().GetLoggerClass());
|
||||
if (!pLoggerObj)
|
||||
{
|
||||
GetRepository().DestroyObject2(m_ssLoggerClass);
|
||||
if (!m_bSilent)
|
||||
GetRepository().DestroyObject2(GetAppSettings().GetLoggerClass());
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to start the logger. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
@@ -249,14 +190,14 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
sdv::core::ILogger* pLogger = pLoggerObj->GetInterface<sdv::core::ILogger>();
|
||||
if (!pLoggerConfig || !pLogger)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to start the logger. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
}
|
||||
if (!m_ssProgramTag.empty())
|
||||
pLoggerConfig->SetProgramTag(m_ssProgramTag);
|
||||
pLoggerConfig->SetLogFilter(m_eSeverityFilter, m_eSeverityViewFilter);
|
||||
if (!GetAppSettings().GetLoggerProgramTag().empty())
|
||||
pLoggerConfig->SetProgramTag(GetAppSettings().GetLoggerProgramTag());
|
||||
pLoggerConfig->SetLogFilter(GetAppSettings().GetLoggerSeverityFilter(), GetAppSettings().GetConsoleSeverityFilter());
|
||||
GetLoggerControl().SetLogger(pLogger);
|
||||
|
||||
// Create the core service objects
|
||||
@@ -266,7 +207,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
bRet = bRet && fnCreateObject("ConfigService", "ConfigService", "");
|
||||
if (!bRet)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
{
|
||||
std::cerr << "ERROR: Failed to start the Core Services. Cannot continue!" << std::endl;
|
||||
if (!ssErrorString.empty())
|
||||
@@ -278,8 +219,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
|
||||
// Load specific services
|
||||
bool bLoadRPCClient = false, bLoadRPCServer = false;
|
||||
bool bLoadInstallationManifests = false;
|
||||
switch (m_eContextMode)
|
||||
switch (GetAppSettings().GetContextType())
|
||||
{
|
||||
case sdv::app::EAppContext::standalone:
|
||||
break;
|
||||
@@ -287,11 +227,9 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
bLoadRPCClient = true;
|
||||
break;
|
||||
case sdv::app::EAppContext::isolated:
|
||||
bLoadInstallationManifests = true;
|
||||
bLoadRPCClient = true;
|
||||
break;
|
||||
case sdv::app::EAppContext::main:
|
||||
bLoadInstallationManifests = true;
|
||||
bLoadRPCClient = true;
|
||||
bLoadRPCServer = true;
|
||||
break;
|
||||
@@ -302,22 +240,28 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
bLoadRPCClient = true;
|
||||
break;
|
||||
default:
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Invalid Run-As mode. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load installation manifests for main and isolated applications.
|
||||
if (bLoadInstallationManifests)
|
||||
// Load the application settings.
|
||||
if (!GetAppSettings().LoadSettingsFile())
|
||||
{
|
||||
if (!GetAppConfig().LoadInstallationManifests())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to load installation manifests. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
}
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to load application settings file. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load installation manifests. For standalone applications, only the core manifest is loaded.
|
||||
if (!GetAppConfig().LoadInstallationManifests())
|
||||
{
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to load installation manifests. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load process control
|
||||
@@ -360,7 +304,7 @@ Type = "Local"
|
||||
|
||||
if (!bRet)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to load IPC server components. Cannot continue!" << std::endl;
|
||||
if (!ssErrorString.empty())
|
||||
std::cerr << "REASON: " << ssErrorString << std::endl;
|
||||
@@ -394,7 +338,7 @@ Type = "Local"
|
||||
|
||||
if (!bRet)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to load IPC client components. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
@@ -403,7 +347,7 @@ Type = "Local"
|
||||
|
||||
if (!bRet)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to start core components. Cannot continue!" << std::endl;
|
||||
if (!ssErrorString.empty())
|
||||
std::cerr << "REASON: " << ssErrorString << std::endl;
|
||||
@@ -418,15 +362,15 @@ Type = "Local"
|
||||
|
||||
SetConfigMode();
|
||||
|
||||
if (IsMainApplication())
|
||||
if (GetAppSettings().IsMainApplication())
|
||||
{
|
||||
// Load system configs - they need to be present completely
|
||||
for (const std::filesystem::path& rpathConfig : m_vecSysConfigs)
|
||||
for (const std::filesystem::path& rpathConfig : GetAppSettings().GetSystemConfigPaths())
|
||||
{
|
||||
sdv::core::EConfigProcessResult eResult = GetAppConfig().LoadConfig(rpathConfig.generic_u8string());
|
||||
if (eResult != sdv::core::EConfigProcessResult::successful)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Cannot load or partly load the system configuration: " <<
|
||||
rpathConfig.generic_u8string() << std::endl;
|
||||
Shutdown(true);
|
||||
@@ -438,22 +382,25 @@ Type = "Local"
|
||||
GetAppConfig().ResetConfigBaseline();
|
||||
|
||||
// Load the application config - they can also be partly there
|
||||
if (!m_pathAppConfig.empty())
|
||||
if (!GetAppSettings().GetUserConfigPath().empty())
|
||||
{
|
||||
sdv::core::EConfigProcessResult eResult = GetAppConfig().LoadConfig(m_pathAppConfig.generic_u8string());
|
||||
sdv::core::EConfigProcessResult eResult =
|
||||
GetAppConfig().LoadConfig(GetAppSettings().GetUserConfigPath().generic_u8string());
|
||||
if (eResult == sdv::core::EConfigProcessResult::failed)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Cannot load application configuration: " << m_pathAppConfig.generic_u8string() << std::endl;
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Cannot load application configuration: " <<
|
||||
GetAppSettings().GetUserConfigPath().generic_u8string() << std::endl;
|
||||
Shutdown(true);
|
||||
return false;
|
||||
}
|
||||
else if (eResult != sdv::core::EConfigProcessResult::partially_successful)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "WARNING: Partially could not load application configuration: " <<
|
||||
m_pathAppConfig.generic_u8string() << std::endl;
|
||||
GetAppSettings().GetUserConfigPath().generic_u8string() << std::endl;
|
||||
}
|
||||
m_bAutoSaveConfig = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +424,7 @@ void CAppControl::RunLoop()
|
||||
// Treat local running differently from main and isolated app running.
|
||||
// Note... Maintenance apps should not use the loop
|
||||
bool bLocal = true;
|
||||
switch (m_eContextMode)
|
||||
switch (GetAppSettings().GetContextType())
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
@@ -494,12 +441,12 @@ void CAppControl::RunLoop()
|
||||
CShutdownRequestListener listener;
|
||||
if (bLocal)
|
||||
{
|
||||
listener = std::move(CShutdownRequestListener(m_uiInstanceID));
|
||||
listener = std::move(CShutdownRequestListener(GetAppSettings().GetInstanceID()));
|
||||
if (!listener.IsValid())
|
||||
throw sdv::XAccessDenied(); // Another instance is already running.
|
||||
}
|
||||
|
||||
if (m_bVerbose)
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Entering loop" << std::endl;
|
||||
|
||||
m_bRunLoop = true;
|
||||
@@ -520,7 +467,7 @@ void CAppControl::RunLoop()
|
||||
m_pEvent->ProcessEvent(sEvent);
|
||||
}
|
||||
}
|
||||
if (m_bVerbose)
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Leaving loop" << std::endl;
|
||||
}
|
||||
|
||||
@@ -543,8 +490,11 @@ void CAppControl::Shutdown(/*in*/ bool bForce)
|
||||
// Disable automatic configuration saving.
|
||||
m_bAutoSaveConfig = false;
|
||||
|
||||
// Update the application settings file
|
||||
GetAppSettings().SaveSettingsFile();
|
||||
|
||||
// Destroy all objects... this should also remove any registered services, except the custom logger.
|
||||
GetRepository().DestroyAllObjects(std::vector<std::string>({ m_ssLoggerClass }), bForce);
|
||||
GetRepository().DestroyAllObjects(std::vector<std::string>({GetAppSettings().GetLoggerClass()}), bForce);
|
||||
|
||||
// Unload all modules... this should destroy all running services, except the custom logger
|
||||
GetModuleControl().UnloadAll(std::vector<sdv::core::TModuleID>({ m_tLoggerModuleID }));
|
||||
@@ -587,7 +537,7 @@ void CAppControl::Shutdown(/*in*/ bool bForce)
|
||||
}
|
||||
|
||||
// End trace streaming
|
||||
if (m_eContextMode == sdv::app::EAppContext::main)
|
||||
if (GetAppSettings().IsMainApplication())
|
||||
{
|
||||
std::cout << "**********************************************" << std::endl;
|
||||
|
||||
@@ -596,22 +546,11 @@ void CAppControl::Shutdown(/*in*/ bool bForce)
|
||||
}
|
||||
|
||||
BroadcastOperationState(sdv::app::EAppOperationState::not_started);
|
||||
m_eContextMode = sdv::app::EAppContext::no_context;
|
||||
m_uiInstanceID = 0u;
|
||||
m_pEvent = nullptr;
|
||||
m_ssLoggerClass.clear();
|
||||
m_tLoggerModuleID = 0;
|
||||
m_pathLoggerModule.clear();
|
||||
m_ssProgramTag.clear();
|
||||
m_eSeverityFilter = sdv::core::ELogSeverity::info;
|
||||
m_pathInstallDir.clear();
|
||||
m_pathRootDir.clear();
|
||||
m_vecSysConfigs.clear();
|
||||
m_pathAppConfig.clear();
|
||||
m_bAutoSaveConfig = false;
|
||||
m_bEnableAutoSave = false;
|
||||
m_bSilent = false;
|
||||
m_bVerbose = false;
|
||||
GetAppSettings().Reset();
|
||||
}
|
||||
|
||||
void CAppControl::RequestShutdown()
|
||||
@@ -625,11 +564,6 @@ sdv::app::EAppOperationState CAppControl::GetOperationState() const
|
||||
return m_eState;
|
||||
}
|
||||
|
||||
uint32_t CAppControl::GetInstance() const
|
||||
{
|
||||
return m_uiInstanceID;
|
||||
}
|
||||
|
||||
void CAppControl::SetConfigMode()
|
||||
{
|
||||
GetRepository().SetConfigMode();
|
||||
@@ -644,42 +578,6 @@ void CAppControl::SetRunningMode()
|
||||
BroadcastOperationState(sdv::app::EAppOperationState::running);
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::u8string> CAppControl::GetNames() const
|
||||
{
|
||||
sdv::sequence<sdv::u8string> seqNames = {"app.instance_id", "console.info_level"};
|
||||
return seqNames;
|
||||
}
|
||||
|
||||
sdv::any_t CAppControl::Get(/*in*/ const sdv::u8string& ssAttribute) const
|
||||
{
|
||||
if (ssAttribute == "app.instance_id") return sdv::any_t(m_uiInstanceID);
|
||||
if (ssAttribute == "console.info_level")
|
||||
{
|
||||
if (m_bSilent) return "silent";
|
||||
if (m_bVerbose) return "verbose";
|
||||
return "normal";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CAppControl::Set(/*in*/ const sdv::u8string& /*ssAttribute*/, /*in*/ sdv::any_t /*anyAttribute*/)
|
||||
{
|
||||
// Currently there are not setting attributes...
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t CAppControl::GetFlags(/*in*/ const sdv::u8string& ssAttribute) const
|
||||
{
|
||||
if (ssAttribute == "app.instance_id") return hlpr::flags<sdv::EAttributeFlags>(sdv::EAttributeFlags::read_only);
|
||||
if (ssAttribute == "console.info_level") return hlpr::flags<sdv::EAttributeFlags>(sdv::EAttributeFlags::read_only);
|
||||
return 0u;
|
||||
}
|
||||
|
||||
std::filesystem::path CAppControl::GetInstallDir() const
|
||||
{
|
||||
return m_pathInstallDir;
|
||||
}
|
||||
|
||||
void CAppControl::DisableAutoConfigUpdate()
|
||||
{
|
||||
m_bEnableAutoSave = false;
|
||||
@@ -693,20 +591,11 @@ void CAppControl::EnableAutoConfigUpdate()
|
||||
void CAppControl::TriggerConfigUpdate()
|
||||
{
|
||||
if (!m_bAutoSaveConfig || !m_bEnableAutoSave) return;
|
||||
if (m_pathAppConfig.empty()) return;
|
||||
if (GetAppSettings().GetUserConfigPath().empty())
|
||||
return;
|
||||
|
||||
if (!GetAppConfig().SaveConfig(m_pathAppConfig.generic_u8string()))
|
||||
SDV_LOG_ERROR("Failed to automatically save the configuration ", m_pathAppConfig.generic_u8string());
|
||||
}
|
||||
|
||||
bool CAppControl::IsConsoleSilent() const
|
||||
{
|
||||
return m_bSilent;
|
||||
}
|
||||
|
||||
bool CAppControl::IsConsoleVerbose() const
|
||||
{
|
||||
return m_bVerbose;
|
||||
if (!GetAppConfig().SaveConfig(GetAppSettings().GetUserConfigPath().generic_u8string()))
|
||||
SDV_LOG_ERROR("Failed to automatically save the configuration ", GetAppSettings().GetUserConfigPath().generic_u8string());
|
||||
}
|
||||
|
||||
void CAppControl::BroadcastOperationState(sdv::app::EAppOperationState eState)
|
||||
@@ -721,255 +610,7 @@ void CAppControl::BroadcastOperationState(sdv::app::EAppOperationState eState)
|
||||
}
|
||||
}
|
||||
|
||||
bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
|
||||
{
|
||||
toml_parser::CParser parserConfig;
|
||||
std::string ssError;
|
||||
try
|
||||
{
|
||||
// Read the configuration
|
||||
if (!parserConfig.Process(rssConfig)) return false;
|
||||
|
||||
} catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
{
|
||||
ssError = std::string("ERROR: Failed to parse application configuration: ") + rexcept.what();
|
||||
}
|
||||
|
||||
// Get the reporting settings (if this succeeded at all...)
|
||||
auto ptrReport = parserConfig.Root().Direct("Console.Report");
|
||||
if (ptrReport && ptrReport->GetValue() == "Silent") m_bSilent = true;
|
||||
if (ptrReport && ptrReport->GetValue() == "Verbose") m_bVerbose = true;
|
||||
|
||||
// Report the outstanding error (if there is one...)
|
||||
if (!ssError.empty())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << ssError << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow a custom logger to be defined
|
||||
std::filesystem::path pathLoggerModule;
|
||||
std::string ssLoggerClass;
|
||||
std::shared_ptr<toml_parser::CNode> ptrLogHandlerPath = parserConfig.Root().Direct("LogHandler.Path");
|
||||
std::shared_ptr<toml_parser::CNode> ptrLogHandlerClass = parserConfig.Root().Direct("LogHandler.Class");
|
||||
if (ptrLogHandlerPath && !ptrLogHandlerClass)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to process application log: custom logger handler module path supplied, but no class "
|
||||
"defined!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!ptrLogHandlerPath && ptrLogHandlerClass)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to process application log: custom logger handler class supplied, but no module "
|
||||
"defined!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (ptrLogHandlerPath)
|
||||
{
|
||||
m_pathLoggerModule = static_cast<std::string>(ptrLogHandlerPath->GetValue());
|
||||
m_ssLoggerClass = static_cast<std::string>(ptrLogHandlerClass->GetValue());
|
||||
} else
|
||||
{
|
||||
// Default logger
|
||||
m_ssLoggerClass = "DefaultLoggerService";
|
||||
}
|
||||
|
||||
// Get an optional program tag for the logger
|
||||
std::shared_ptr<toml_parser::CNode> ptrLogPogramTag = parserConfig.Root().Direct("LogHandler.Tag");
|
||||
if (ptrLogPogramTag) m_ssProgramTag = static_cast<std::string>(ptrLogPogramTag->GetValue());
|
||||
|
||||
// Get the application-mode
|
||||
std::string ssApplication = "Standalone";
|
||||
std::shared_ptr<toml_parser::CNode> ptrApplication = parserConfig.Root().Direct("Application.Mode");
|
||||
if (ptrApplication) ssApplication = static_cast<std::string>(ptrApplication->GetValue());
|
||||
if (ssApplication == "Standalone") m_eContextMode = sdv::app::EAppContext::standalone;
|
||||
else if (ssApplication == "External") m_eContextMode = sdv::app::EAppContext::external;
|
||||
else if (ssApplication == "Isolated") m_eContextMode = sdv::app::EAppContext::isolated;
|
||||
else if (ssApplication == "Main") m_eContextMode = sdv::app::EAppContext::main;
|
||||
else if (ssApplication == "Essential") m_eContextMode = sdv::app::EAppContext::essential;
|
||||
else if (ssApplication == "Maintenance") m_eContextMode = sdv::app::EAppContext::maintenance;
|
||||
else
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to process startup config: invalid application-mode specified for core library: " <<
|
||||
ssApplication << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the severity level filter for the logger
|
||||
auto fnTranslateSevFilter = [this](const std::string& rssLogFilter, const sdv::core::ELogSeverity eDefault)
|
||||
{
|
||||
sdv::core::ELogSeverity eSeverityFilter = eDefault;
|
||||
if (rssLogFilter == "Trace") eSeverityFilter = sdv::core::ELogSeverity::trace;
|
||||
else if (rssLogFilter == "Debug") eSeverityFilter = sdv::core::ELogSeverity::debug;
|
||||
else if (rssLogFilter == "Info") eSeverityFilter = sdv::core::ELogSeverity::info;
|
||||
else if (rssLogFilter == "Warning") eSeverityFilter = sdv::core::ELogSeverity::warning;
|
||||
else if (rssLogFilter == "Error") eSeverityFilter = sdv::core::ELogSeverity::error;
|
||||
else if (rssLogFilter == "Fatal") eSeverityFilter = sdv::core::ELogSeverity::fatal;
|
||||
else if (!rssLogFilter.empty())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to process application log: invalid severity level filter: '" << rssLogFilter <<
|
||||
"'" << std::endl;
|
||||
}
|
||||
return eSeverityFilter;
|
||||
};
|
||||
sdv::core::ELogSeverity eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::error;
|
||||
if (IsMainApplication() || IsIsolatedApplication())
|
||||
eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::info;
|
||||
std::shared_ptr<toml_parser::CNode> ptrLogSeverityFilter = parserConfig.Root().Direct("LogHandler.Filter");
|
||||
m_eSeverityFilter = fnTranslateSevFilter(ptrLogSeverityFilter ? ptrLogSeverityFilter->GetValue() : "",
|
||||
sdv::core::ELogSeverity::info);
|
||||
ptrLogSeverityFilter = parserConfig.Root().Direct("LogHandler.ViewFilter");
|
||||
m_eSeverityViewFilter = fnTranslateSevFilter(ptrLogSeverityFilter ? ptrLogSeverityFilter->GetValue() : "",
|
||||
eLogDefaultViewSeverityFilter);
|
||||
|
||||
// Get the optional instance ID.
|
||||
std::shared_ptr<toml_parser::CNode> ptrInstance = parserConfig.Root().Direct("Application.Instance");
|
||||
if (ptrInstance)
|
||||
m_uiInstanceID = ptrInstance->GetValue();
|
||||
else
|
||||
m_uiInstanceID = 1000u;
|
||||
// Number of attempts to establish a connection to a running instance.
|
||||
std::shared_ptr<toml_parser::CNode> ptrRetries = parserConfig.Root().Direct("Application.Retries");
|
||||
if (ptrRetries)
|
||||
{
|
||||
m_uiRetries = ptrRetries->GetValue();
|
||||
if (m_uiRetries > 30)
|
||||
m_uiRetries = 30;
|
||||
else if (m_uiRetries < 3)
|
||||
m_uiRetries = 3;
|
||||
}
|
||||
|
||||
// Main and isolated apps specific information.
|
||||
if (IsMainApplication() || IsIsolatedApplication())
|
||||
{
|
||||
// Get the optional installation directory.
|
||||
std::shared_ptr<toml_parser::CNode> ptrInstallDir = parserConfig.Root().Direct("Application.InstallDir");
|
||||
if (ptrInstallDir)
|
||||
{
|
||||
m_pathRootDir = ptrInstallDir->GetValue();
|
||||
if (m_pathRootDir.is_relative())
|
||||
m_pathRootDir = GetExecDirectory() / m_pathRootDir;
|
||||
}
|
||||
else
|
||||
m_pathRootDir = GetExecDirectory() / std::to_string(m_uiInstanceID);
|
||||
m_pathInstallDir = m_pathRootDir;
|
||||
try
|
||||
{
|
||||
std::filesystem::create_directories(m_pathRootDir);
|
||||
}
|
||||
catch (const std::filesystem::filesystem_error& rexcept)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
{
|
||||
std::cerr << "Cannot create root directory: " << m_pathRootDir << std::endl;
|
||||
std::cerr << " Reason: " << rexcept.what() << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Maintenance and isolated applications cannot load specific configs. The others can specify a configuration file, but
|
||||
// not auto-updateable.
|
||||
if (!IsMaintenanceApplication() && !IsIsolatedApplication())
|
||||
{
|
||||
auto ptrConfigFile = parserConfig.Root().Direct("Application.Config");
|
||||
if (ptrConfigFile)
|
||||
m_pathAppConfig = ptrConfigFile->GetValue();
|
||||
}
|
||||
|
||||
// Read the settings... if existing. And only for the main application
|
||||
if (IsMainApplication())
|
||||
{
|
||||
// If the template is not existing, create the template...
|
||||
if (!std::filesystem::exists(m_pathRootDir / "settings.toml"))
|
||||
{
|
||||
std::ofstream fstream(m_pathRootDir / "settings.toml");
|
||||
if (!fstream.is_open())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to store application settings." << std::endl;
|
||||
return false;
|
||||
}
|
||||
fstream << szSettingsTemplate;
|
||||
fstream.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ifstream fstream(m_pathRootDir / "settings.toml");
|
||||
std::string ssContent((std::istreambuf_iterator<char>(fstream)), std::istreambuf_iterator<char>());
|
||||
try
|
||||
{
|
||||
// Read the configuration
|
||||
toml_parser::CParser parserSettings(ssContent);
|
||||
|
||||
// Check for the version
|
||||
auto ptrVersion = parserSettings.Root().Direct("Settings.Version");
|
||||
if (!ptrVersion)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Missing version in application settings file." << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (ptrVersion->GetValue() != SDVFrameworkInterfaceVersion)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Invalid version of application settings file (expected version " <<
|
||||
SDVFrameworkInterfaceVersion << ", but available version " <<
|
||||
static_cast<uint32_t>(ptrVersion->GetValue()) << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the system configurations
|
||||
auto ptrSystemConfigs = parserSettings.Root().Direct("Settings.SystemConfig");
|
||||
if (ptrSystemConfigs && ptrSystemConfigs->Cast<toml_parser::CArray>())
|
||||
{
|
||||
for (uint32_t uiIndex = 0; uiIndex < ptrSystemConfigs->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
|
||||
{
|
||||
auto ptrSystemConfig = ptrSystemConfigs->Cast<toml_parser::CArray>()->Get(uiIndex);
|
||||
if (!ptrSystemConfig) continue;
|
||||
m_vecSysConfigs.push_back(static_cast<std::string>(ptrSystemConfig->GetValue()));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the application config - but only when not specified over the app-control-config.
|
||||
if (m_pathAppConfig.empty())
|
||||
{
|
||||
auto ptrAppConfig = parserSettings.Root().Direct("Settings.AppConfig");
|
||||
if (ptrAppConfig)
|
||||
{
|
||||
// Path available. Enable auto-update.
|
||||
m_pathAppConfig = static_cast<std::string>(ptrAppConfig->GetValue());
|
||||
m_bAutoSaveConfig = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to parse application settings: " << rexcept.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST
|
||||
|
||||
CAppControl& CAppControlService::GetAppControl()
|
||||
{
|
||||
return ::GetAppControl();
|
||||
}
|
||||
|
||||
bool CAppControlService::EnableAppShutdownRequestAccess() const
|
||||
{
|
||||
return ::GetAppControl().IsMainApplication() || ::GetAppControl().IsIsolatedApplication();
|
||||
return ::GetAppSettings().IsMainApplication() || ::GetAppSettings().IsIsolatedApplication();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 APP_CONTROL_H
|
||||
#define APP_CONTROL_H
|
||||
|
||||
@@ -6,136 +19,53 @@
|
||||
#include <support/interface_ptr.h>
|
||||
#include "../../global/tracefifo/trace_fifo.h"
|
||||
|
||||
///
|
||||
/// @brief Application control class.
|
||||
/// @details The application control class is responsible for the startup and shutdown of the system. Since the system is (re-)used
|
||||
/// in many applications, the startup behavior can be determined by the provided configuration as string as argument to the
|
||||
/// startup function.
|
||||
/// The configuration uses the TOML format and is defined as follows:
|
||||
/// @code
|
||||
/// # Optional use of customized log handler
|
||||
/// [LogHandler]
|
||||
/// Class = "" # Component class name of a custom logger (optional)
|
||||
/// Path = "" # Component module path of a custom logger (optional)
|
||||
/// Tag = "" # Program tag to use instead of the name SDV_LOG_<pid>
|
||||
/// Filter = "" # Lowest severity filter to use when logging (Trace, Debug, Info, Warning, Error, Fatal). Default severity
|
||||
/// # level filter is Info (meaning Debug and Trace messages are not being stored).
|
||||
/// ViewFilter = "" # Lowest severity filter to use when logging (Trace, Debug, Info, Warning, Error, Fatal). Default severity
|
||||
/// # level filter is Error (meaning Debug, Trace, Info and Warning messages are not being shown).
|
||||
///
|
||||
/// # Application behavior definition
|
||||
/// # Mode = "Standalone" (default) app->no RPC + core services + additional configurations allowed
|
||||
/// # Mode = "External" app->RPC client only + local services + target service(s) --> connection information through listener
|
||||
/// # Mode = "Isolated" app->RPC client only + local services + target service(s) --> connection information needed
|
||||
/// # Mode = "Main" app->RPC server + core services --> access key needed
|
||||
/// # Mode = "Essential" app->local services + additional configurations allowed
|
||||
/// # Mode = "Maintenance" app->RPC client only + local services + maintenance service --> connection information needed + access key
|
||||
/// # Instance = 1234
|
||||
/// [Application]
|
||||
/// Mode = "Main"
|
||||
/// Instance = 1234 # Optional instance ID to be used with main and isolated applications. Has no influence on other
|
||||
/// # applications. Default instance ID is 1000. The connection listener is using the instance ID to allow
|
||||
/// # connections from an external application to the main application. Furthermore, the instance ID is
|
||||
/// # used to locate the installation of SDV components. The location of the SDV components is relative to
|
||||
/// # the executable (unless a target directory is supplied) added with the instance and the installations:
|
||||
/// # <exe_path>/<instance>/<installation>
|
||||
/// InstallDir = "./test" # Optional custom installation directory to be used with main and isolated applications. Has no
|
||||
/// # influence on other applications. The default location for installations is the location of the
|
||||
/// # executable. Specifying a different directory will change the location of installations to
|
||||
/// # <install_directory>/<instance>/<installation>
|
||||
/// # NOTE The directory of the core library and the directory of the running executable are always added
|
||||
/// # to the system if they contain an installation manifest.
|
||||
///
|
||||
/// # Optional configuration that should be loaded (not for maintenance and isolated applications). This overrides the application
|
||||
/// # config from the settings (only main application). Automatic saving the configuration is not supported.
|
||||
/// Config = "abc.toml"
|
||||
///
|
||||
/// #Console output
|
||||
/// [Console]
|
||||
/// Report = "Silent" # Either "Silent", "Normal" or "Verbose" for no, normal or extensive messages.
|
||||
///
|
||||
/// # Search directories
|
||||
/// @endcode
|
||||
///
|
||||
/// TODO: Add config ignore list (e.g. platform.toml, vehicle_ifc.toml and vehicle_abstract.toml).
|
||||
/// Add dedicated config (rather than standard config) as startup param.
|
||||
///
|
||||
class CAppControl : public sdv::IInterfaceAccess, public sdv::app::IAppContext, public sdv::app::IAppControl,
|
||||
public sdv::app::IAppOperation, public sdv::app::IAppShutdownRequest, public sdv::IAttributes
|
||||
/**
|
||||
* @brief Application control class.
|
||||
* @details The application control class is responsible for the startup and shutdown of the system. The startup behavior can be
|
||||
* influenced through the provided startup configuration. Dependable on the configuration, the core context is either server
|
||||
* operated (for main, isolated or maintenance applications) or locally operated (for standalone, external or essential
|
||||
* application).
|
||||
*
|
||||
* In case of a server operated startup sequence, the following startup procedure takes place:
|
||||
* - Core services are started
|
||||
* - Installation manifests are loaded (not for maintenance applications)
|
||||
* - This registers the available component classes and corresponding modules
|
||||
* - IPC and RPC services are started
|
||||
* - Switch system to configuration mode
|
||||
* - Installed system configurations are loaded and components are started
|
||||
* - This starts all system, device and interface abstraction level components with corresponding configuration.
|
||||
* - Installed user configuration will be loaded and components are started
|
||||
* - This will start the vehicle functions (in isolated processed where applicable).
|
||||
* - Switch system to running mode
|
||||
*
|
||||
* In case of a locally operated startup sequence, the following startup procedure takes place:
|
||||
* - Core services are started
|
||||
* - Core installation manifest is loaded
|
||||
* - This registers the available component classes and corresponding modules for core components
|
||||
* - For external application: RPC client is started
|
||||
* - Switch system to configuration mode
|
||||
* - Modules from provided configuration are loaded
|
||||
* - This registers the available component classes and corresponding modules
|
||||
* - Installed user configuration will be loaded and components are started
|
||||
* - This will start the vehicle functions.
|
||||
* - Switch system to running mode
|
||||
*/
|
||||
class CAppControl : public sdv::IInterfaceAccess, public sdv::app::IAppControl, public sdv::app::IAppOperation,
|
||||
public sdv::app::IAppShutdownRequest
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
CAppControl();
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CAppControl();
|
||||
CAppControl() = default;
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::app::IAppOperation)
|
||||
SDV_INTERFACE_ENTRY(sdv::app::IAppContext)
|
||||
SDV_INTERFACE_ENTRY(sdv::app::IAppControl)
|
||||
SDV_INTERFACE_ENTRY(sdv::app::IAppShutdownRequest)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is the main application.
|
||||
* @return Returns 'true' when the current application is the main application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsMainApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is an isolated application.
|
||||
* @return Returns 'true' when the current application is an isolated application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsIsolatedApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is a standalone application.
|
||||
* @return Returns 'true' when the current application is a standalone application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsStandaloneApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is an essential application.
|
||||
* @return Returns 'true' when the current application is an essential application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsEssentialApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is a maintenance application.
|
||||
* @return Returns 'true' when the current application is a maintenance application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsMaintenanceApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is an external application.
|
||||
* @return Returns 'true' when the current application is an external application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsExternalApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return the application context mode. Overload of sdv::app::IAppContext::GetContextType.
|
||||
* @return The context mode.
|
||||
*/
|
||||
sdv::app::EAppContext GetContextType() const override;
|
||||
|
||||
/**
|
||||
* @brief Return the core instance ID. Overload of sdv::app::IAppContext::GetContextType.
|
||||
* @return The instance ID.
|
||||
*/
|
||||
uint32_t GetInstanceID() const override;
|
||||
|
||||
/**
|
||||
* @brief Return the number of retries to establish a connection.
|
||||
* @return Number of retries.
|
||||
*/
|
||||
uint32_t GetRetries() const override;
|
||||
|
||||
/**
|
||||
* @brief Start the application. Overload of sdv::app::IAppControl::Startup.
|
||||
* @details The core will prepare for an application start based on the provided configuration. Per default, the
|
||||
@@ -178,15 +108,6 @@ public:
|
||||
*/
|
||||
virtual sdv::app::EAppOperationState GetOperationState() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the current running instance.
|
||||
* @details Get the instance. If not otherwise specified, the current instance depends on whether the application is running
|
||||
* as main or isolated application, in which case the instance is 1000. In all other cases the instance is 0. A dedicated
|
||||
* instance can be supplied through the app control.
|
||||
* @return The instance number.
|
||||
*/
|
||||
uint32_t GetInstance() const;
|
||||
|
||||
/**
|
||||
* @brief Switch from running mode to the configuration mode. Overload of sdv::app::IAppOperation::SetConfigMode.
|
||||
*/
|
||||
@@ -197,41 +118,6 @@ public:
|
||||
*/
|
||||
virtual void SetRunningMode() override;
|
||||
|
||||
/**
|
||||
* @brief Get a sequence with the available attribute names. Overload of sdv::IAttributes::GetNames.
|
||||
* @return The sequence of attribute names.
|
||||
*/
|
||||
virtual sdv::sequence<sdv::u8string> GetNames() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the attribute value. Overload of sdv::IAttributes::Get.
|
||||
* @param[in] ssAttribute Name of the attribute.
|
||||
* @return The attribute value or an empty any-value if the attribute wasn't found or didn't have a value.
|
||||
*/
|
||||
virtual sdv::any_t Get(/*in*/ const sdv::u8string& ssAttribute) const override;
|
||||
|
||||
/**
|
||||
* @brief Set the attribute value. Overload of sdv::IAttributes::Set.
|
||||
* @param[in] ssAttribute Name of the attribute.
|
||||
* @param[in] anyAttribute Attribute value to set.
|
||||
* @return Returns 'true' when setting the attribute was successful or 'false' when the attribute was not found or the
|
||||
* attribute is read-only or another error occurred.
|
||||
*/
|
||||
virtual bool Set(/*in*/ const sdv::u8string& ssAttribute, /*in*/ sdv::any_t anyAttribute) override;
|
||||
|
||||
/**
|
||||
* @brief Get the attribute flags belonging to a certain attribute. Overload of sdv::IAttributes::GetFlags.
|
||||
* @param[in] ssAttribute Name of the attribute.
|
||||
* @return Returns the attribute flags (zero or more EAttributeFlags flags) or 0 when the attribute could not be found.
|
||||
*/
|
||||
virtual uint32_t GetFlags(/*in*/ const sdv::u8string& ssAttribute) const override;
|
||||
|
||||
/**
|
||||
* @brief Get the installation directory of user components.
|
||||
* @return The location of the user components. Only is valid when used in main and isolated applications.
|
||||
*/
|
||||
std::filesystem::path GetInstallDir() const;
|
||||
|
||||
/**
|
||||
* @brief Disable the current auto update feature if enabled in the system settings.
|
||||
*/
|
||||
@@ -247,18 +133,6 @@ public:
|
||||
*/
|
||||
void TriggerConfigUpdate();
|
||||
|
||||
/**
|
||||
* @brief Should the console output be silent?
|
||||
* @return Returns whether the console output is silent.
|
||||
*/
|
||||
bool IsConsoleSilent() const;
|
||||
|
||||
/**
|
||||
* @brief Should the console output be verbose?
|
||||
* @return Returns whether the verbose console output is activated.
|
||||
*/
|
||||
bool IsConsoleVerbose() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Set the operation state and broadcast the state through the event.
|
||||
@@ -266,41 +140,24 @@ private:
|
||||
*/
|
||||
void BroadcastOperationState(sdv::app::EAppOperationState eState);
|
||||
|
||||
/**
|
||||
* @brief Process the application configuration before starting the system
|
||||
* @param[in] rssConfig Reference to the configuration content.
|
||||
* @return Returns 'true' when processing was successful; false when not.
|
||||
*/
|
||||
bool ProcessAppConfig(const sdv::u8string& rssConfig);
|
||||
|
||||
sdv::app::EAppContext m_eContextMode = sdv::app::EAppContext::no_context; ///< The application is running as...
|
||||
sdv::app::EAppOperationState m_eState = sdv::app::EAppOperationState::not_started; ///< The current operation state.
|
||||
sdv::app::IAppEvent* m_pEvent = nullptr; ///< Pointer to the app event interface.
|
||||
std::string m_ssLoggerClass; ///< Class name of a logger service.
|
||||
sdv::core::TModuleID m_tLoggerModuleID = 0; ///< ID of the logger module.
|
||||
std::filesystem::path m_pathLoggerModule; ///< Module name of a custom logger.
|
||||
std::string m_ssProgramTag; ///< Program tag to use when logging.
|
||||
sdv::core::ELogSeverity m_eSeverityFilter = sdv::core::ELogSeverity::info; ///< Severity level filter while logging.
|
||||
sdv::core::ELogSeverity m_eSeverityViewFilter = sdv::core::ELogSeverity::error; ///< Severity level filter while logging.
|
||||
uint32_t m_uiRetries = 0u; ///< Number of retries to establish a connection.
|
||||
uint32_t m_uiInstanceID = 0u; ///< Instance number.
|
||||
std::filesystem::path m_pathInstallDir; ///< Location of user component installations.
|
||||
std::filesystem::path m_pathRootDir; ///< Location of user component root directory.
|
||||
std::vector<std::filesystem::path> m_vecSysConfigs; ///< The system configurations from the settings file.
|
||||
std::filesystem::path m_pathAppConfig; ///< The application configuration from the settings file.
|
||||
bool m_bAutoSaveConfig = false; ///< System setting for automatic saving of the configuration.
|
||||
bool m_bEnableAutoSave = false; ///< When set and when enabled in the system settings, allows
|
||||
///< the automatic saving of the configuration.
|
||||
bool m_bRunLoop = false; ///< Used to detect end of running loop function.
|
||||
bool m_bSilent = false; ///< When set, no console reporting takes place.
|
||||
bool m_bVerbose = false; ///< When set, extensive console reporting takes place.
|
||||
std::filesystem::path m_pathLockFile; ///< Lock file path name.
|
||||
FILE* m_pLockFile = nullptr; ///< Lock file to test for other instances.
|
||||
CTraceFifoStdBuffer m_fifoTraceStreamBuffer; ///< Trace stream buffer to redirect std::log, std::out and
|
||||
///< std::err when running as service.
|
||||
bool m_bAutoSaveConfig = false; ///< System setting for automatic saving of the user configuration.
|
||||
};
|
||||
|
||||
#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST
|
||||
/**
|
||||
* @brief Return the application control.
|
||||
* @return Reference to the application control.
|
||||
*/
|
||||
CAppControl& GetAppControl();
|
||||
|
||||
/**
|
||||
* @brief App config service class.
|
||||
@@ -313,7 +170,6 @@ public:
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY_MEMBER(sdv::app::IAppOperation, GetAppControl())
|
||||
SDV_INTERFACE_ENTRY_MEMBER(sdv::IAttributes, GetAppControl())
|
||||
SDV_INTERFACE_SET_SECTION_CONDITION(EnableAppShutdownRequestAccess(), 1)
|
||||
SDV_INTERFACE_SECTION(1)
|
||||
SDV_INTERFACE_ENTRY_MEMBER(sdv::app::IAppShutdownRequest, GetAppControl())
|
||||
@@ -321,24 +177,16 @@ public:
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("AppControlService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Get access to the application control.
|
||||
* @return Returns the one global instance of the application config.
|
||||
*/
|
||||
static CAppControl& GetAppControl();
|
||||
|
||||
/**
|
||||
* @brief When set, the application shutdown request interface access will be enabled.
|
||||
* @return Returns 'true' when the access to the application configuration is granted; otherwise returns 'false'.
|
||||
*/
|
||||
bool EnableAppShutdownRequestAccess() const;
|
||||
};
|
||||
DEFINE_SDV_OBJECT_NO_EXPORT(CAppControlService)
|
||||
|
||||
#endif
|
||||
DEFINE_SDV_OBJECT(CAppControlService)
|
||||
|
||||
#endif // !defined APP_CONTROL_H
|
||||
|
||||
714
sdv_services/core/app_settings.cpp
Normal file
714
sdv_services/core/app_settings.cpp
Normal file
@@ -0,0 +1,714 @@
|
||||
/********************************************************************************
|
||||
* 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 "app_settings.h"
|
||||
#include "toml_parser/parser_toml.h"
|
||||
#include "../../global/flags.h"
|
||||
#include "../../global/exec_dir_helper.h"
|
||||
#include "../../global/ipc_named_mutex.h"
|
||||
#include <support/toml.h>
|
||||
|
||||
CAppSettings& GetAppSettings()
|
||||
{
|
||||
static CAppSettings app_settings;
|
||||
return app_settings;
|
||||
}
|
||||
|
||||
CAppSettings::CAppSettings()
|
||||
{}
|
||||
|
||||
CAppSettings::~CAppSettings()
|
||||
{}
|
||||
|
||||
bool CAppSettings::ProcessAppStartupConfig(const sdv::u8string& rssConfig)
|
||||
{
|
||||
toml_parser::CParser parserStartupConfig;
|
||||
std::string ssError;
|
||||
try
|
||||
{
|
||||
// Read the configuration
|
||||
if (!parserStartupConfig.Process(rssConfig))
|
||||
return false;
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
{
|
||||
ssError = std::string("ERROR: Failed to parse application configuration: ") + rexcept.what();
|
||||
}
|
||||
|
||||
sdv::toml::CNodeCollection tableStartupConfig(&parserStartupConfig.Root());
|
||||
|
||||
// Get the reporting settings (if this succeeded at all...)
|
||||
m_bSilent = tableStartupConfig.GetDirect("Console.Report").GetValue().get<std::string>() == "Silent";
|
||||
m_bVerbose = tableStartupConfig.GetDirect("Console.Report").GetValue().get<std::string>() == "Verbose";
|
||||
|
||||
// Report the outstanding error (if there is one...)
|
||||
if (!ssError.empty())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << ssError << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow a custom logger to be defined
|
||||
m_pathLoggerModule = tableStartupConfig.GetDirect("LogHandler.Path").GetValue().get<std::string>();
|
||||
m_ssLoggerClass = tableStartupConfig.GetDirect("LogHandler.Class").GetValue().get<std::string>();
|
||||
if (m_ssLoggerClass.empty())
|
||||
m_ssLoggerClass = "DefaultLoggerService";
|
||||
|
||||
// Get an optional program tag for the logger
|
||||
m_ssProgramTag = tableStartupConfig.GetDirect("LogHandler.Tag").GetValue().get<std::string>();
|
||||
|
||||
// Get the application-mode
|
||||
std::string ssApplication = tableStartupConfig.GetDirect("Application.Mode").GetValue();
|
||||
if (ssApplication.empty()) ssApplication = "Standalone";
|
||||
if (ssApplication == "Standalone")
|
||||
m_eContextMode = sdv::app::EAppContext::standalone;
|
||||
else if (ssApplication == "External")
|
||||
m_eContextMode = sdv::app::EAppContext::external;
|
||||
else if (ssApplication == "Isolated")
|
||||
m_eContextMode = sdv::app::EAppContext::isolated;
|
||||
else if (ssApplication == "Main")
|
||||
m_eContextMode = sdv::app::EAppContext::main;
|
||||
else if (ssApplication == "Essential")
|
||||
m_eContextMode = sdv::app::EAppContext::essential;
|
||||
else if (ssApplication == "Maintenance")
|
||||
m_eContextMode = sdv::app::EAppContext::maintenance;
|
||||
else
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to process startup config: invalid application-mode specified for core library: "
|
||||
<< ssApplication << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the severity level filter for the logger
|
||||
auto fnTranslateSevFilter = [this](const std::string& rssLogFilter, const sdv::core::ELogSeverity eDefault)
|
||||
{
|
||||
sdv::core::ELogSeverity eSeverityFilter = eDefault;
|
||||
if (rssLogFilter == "Trace")
|
||||
eSeverityFilter = sdv::core::ELogSeverity::trace;
|
||||
else if (rssLogFilter == "Debug")
|
||||
eSeverityFilter = sdv::core::ELogSeverity::debug;
|
||||
else if (rssLogFilter == "Info")
|
||||
eSeverityFilter = sdv::core::ELogSeverity::info;
|
||||
else if (rssLogFilter == "Warning")
|
||||
eSeverityFilter = sdv::core::ELogSeverity::warning;
|
||||
else if (rssLogFilter == "Error")
|
||||
eSeverityFilter = sdv::core::ELogSeverity::error;
|
||||
else if (rssLogFilter == "Fatal")
|
||||
eSeverityFilter = sdv::core::ELogSeverity::fatal;
|
||||
else if (!rssLogFilter.empty())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to process application log: invalid severity level filter: '" << rssLogFilter << "'"
|
||||
<< std::endl;
|
||||
}
|
||||
return eSeverityFilter;
|
||||
};
|
||||
sdv::core::ELogSeverity eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::error;
|
||||
if (IsMainApplication() || IsIsolatedApplication()) eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::info;
|
||||
m_eSeverityFilter = fnTranslateSevFilter(tableStartupConfig.GetDirect("LogHandler.Filter").GetValue(),
|
||||
sdv::core::ELogSeverity::info);
|
||||
m_eSeverityViewFilter = fnTranslateSevFilter(
|
||||
tableStartupConfig.GetDirect("LogHandler.ViewFilter").GetValue(), eLogDefaultViewSeverityFilter);
|
||||
|
||||
// Get the optional instance ID.
|
||||
sdv::any_t anyInstanceID = tableStartupConfig.GetDirect("Application.Instance").GetValue();
|
||||
if (anyInstanceID) m_uiInstanceID = anyInstanceID;
|
||||
else
|
||||
m_uiInstanceID = 1000u;
|
||||
|
||||
// Number of attempts to establish a connection to a running instance.
|
||||
m_uiRetries = tableStartupConfig.GetDirect("Application.Retries").GetValue();
|
||||
if (m_uiRetries > 30)
|
||||
m_uiRetries = 30;
|
||||
else if (m_uiRetries < 3)
|
||||
m_uiRetries = 3;
|
||||
|
||||
// Main and isolated apps specific information.
|
||||
if (IsMainApplication() || IsIsolatedApplication() || IsMaintenanceApplication())
|
||||
{
|
||||
// Get the optional installation directory.
|
||||
m_pathRootDir = tableStartupConfig.GetDirect("Application.InstallDir").GetValueAsPath();
|
||||
if (!m_pathRootDir.empty())
|
||||
{
|
||||
if (m_pathRootDir.is_relative())
|
||||
m_pathRootDir = GetExecDirectory() / m_pathRootDir;
|
||||
}
|
||||
else
|
||||
m_pathRootDir = GetExecDirectory();
|
||||
m_pathInstallDir = m_pathRootDir / std::to_string(m_uiInstanceID);
|
||||
try
|
||||
{
|
||||
std::filesystem::create_directories(m_pathInstallDir);
|
||||
}
|
||||
catch (const std::filesystem::filesystem_error& rexcept)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
{
|
||||
std::cerr << "Cannot create installation directory: " << m_pathInstallDir << std::endl;
|
||||
std::cerr << " Reason: " << rexcept.what() << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Maintenance, main and isolated applications cannot load specific configs. The others can specify a configuration file, but
|
||||
// not auto-updateable.
|
||||
if (!IsMainApplication() && !IsMaintenanceApplication() && !IsIsolatedApplication())
|
||||
m_pathUserConfig = tableStartupConfig.GetDirect("Application.Config").GetValueAsPath();
|
||||
|
||||
// Read the settings... if existing. And only for the main application
|
||||
if (IsMainApplication() && !LoadSettingsFile())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAppSettings::LoadSettingsFile()
|
||||
{
|
||||
// Check for the proper context
|
||||
switch (m_eContextMode)
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
break;
|
||||
default:
|
||||
return true; // Not an error...
|
||||
}
|
||||
|
||||
// If the template is not existing, this is not an error...
|
||||
if (!std::filesystem::exists(m_pathInstallDir / "settings.toml"))
|
||||
return true;
|
||||
|
||||
std::ifstream fstream(m_pathInstallDir / "settings.toml");
|
||||
std::string ssSettings((std::istreambuf_iterator<char>(fstream)), std::istreambuf_iterator<char>());
|
||||
fstream.close();
|
||||
|
||||
try
|
||||
{
|
||||
// Read the configuration
|
||||
toml_parser::CParser parserSettings(ssSettings);
|
||||
|
||||
// If there is no "Settings" table, this is not an error...
|
||||
sdv::toml::CNodeCollection tableSettings(parserSettings.Root().GetNodeDirect("Settings"));
|
||||
if (!tableSettings) return true;
|
||||
|
||||
// Check for the version
|
||||
uint32_t uiVersion = tableSettings.GetDirect("Version").GetValue();
|
||||
if (uiVersion != SDVFrameworkInterfaceVersion)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Invalid version of application settings file (expected version "
|
||||
<< SDVFrameworkInterfaceVersion << ", but available version " << uiVersion << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the platform config - but only when not specified over the app-control-config.
|
||||
if (m_pathPlatformConfig.empty())
|
||||
m_pathPlatformConfig = tableSettings.GetDirect("PlatformConfig").GetValueAsPath();
|
||||
|
||||
// Get the vehicle interface config - but only when not specified over the app-control-config.
|
||||
if (m_pathVehIfcConfig.empty())
|
||||
m_pathVehIfcConfig = tableSettings.GetDirect("VehIfcConfig").GetValueAsPath();
|
||||
|
||||
// Get the vehicle abstraction config - but only when not specified over the app-control-config.
|
||||
if (m_pathVehAbstrConfig.empty())
|
||||
m_pathVehAbstrConfig = tableSettings.GetDirect("VehAbstrConfig").GetValueAsPath();
|
||||
|
||||
// Get the application config - but only when not specified over the app-control-config.
|
||||
if (m_pathUserConfig.empty())
|
||||
m_pathUserConfig = tableSettings.GetDirect("AppConfig").GetValueAsPath();
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to parse application settings: " << rexcept.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAppSettings::SaveSettingsFile()
|
||||
{
|
||||
// Check for the proper context
|
||||
switch (m_eContextMode)
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
break;
|
||||
default:
|
||||
return true; // Not an error...
|
||||
}
|
||||
|
||||
// Protect against multiple write actions at the same time.
|
||||
ipc::named_mutex mtx("LockSdvAppSettings_" + std::to_string(m_uiInstanceID));
|
||||
// Warning of cppcheck for locking a local mutex, which doesn't have any effect. Since this is a named mutex between
|
||||
// applciations, the warning is not correct. Suppress warning.
|
||||
// cppcheck-suppress localMutex
|
||||
std::unique_lock<ipc::named_mutex> lock(mtx);
|
||||
|
||||
const std::string ssSettingsTemplate = R"toml(# Settings file
|
||||
[Settings]
|
||||
Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(
|
||||
|
||||
# The system configuration is divided into:
|
||||
# platform config - containing all the components needed to interact with the OS,
|
||||
# middleware, vehicle bus, Ethernet.
|
||||
# vehicle interface - containing the vehicle bus interpretation components like data link
|
||||
# based on DBC and devices for their abstraction.
|
||||
# vehicle abstraction - containing the vehicle abstraction services
|
||||
# The configuration files are loaded exactly in that order, allowing the vehicle interface to
|
||||
# depend on the platform and the vehicle abstraction to depend on the vehicle interface.
|
||||
# The configurations are loaded if the PlatformConfig, VehIfcConfig and VehAbstrConfig keywords
|
||||
# are present and describe a valid configuration file.
|
||||
# A relative path is relative to the installation directory (being "exe_location/instance_id").
|
||||
#
|
||||
# Example:
|
||||
# PlatformConfig = "platform.toml"
|
||||
# VehIfcConfig = "vehicle_ifc.toml"
|
||||
# VehAbstrConfig = "vehicle_abstract.toml"
|
||||
#
|
||||
PlatformConfig = ""
|
||||
VehIfcConfig = ""
|
||||
VehAbstrConfig = ""
|
||||
|
||||
# The application config contains the configuration file that can be updated when services and
|
||||
# apps are being added to the system (or being removed from the system). Load the application
|
||||
# config by providing the "AppConfig" keyword as a string value. A relative path is relative to
|
||||
# the installation directory (being "exe_location/instance_id").
|
||||
#
|
||||
# Example
|
||||
# AppConfig = "app_config.toml"
|
||||
AppConfig = ""
|
||||
)toml";
|
||||
|
||||
// If the template is not existing, create a default settings file...
|
||||
std::string ssSettings;
|
||||
bool bChangeDetected = false;
|
||||
if (!std::filesystem::exists(m_pathInstallDir / "settings.toml"))
|
||||
{
|
||||
bChangeDetected = true;
|
||||
ssSettings = std::move(ssSettingsTemplate);
|
||||
m_bPlatformConfig = true;
|
||||
m_bVehIfcConfig = true;
|
||||
m_bVehAbstrConfig = true;
|
||||
m_bUserConfig = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Open the existing settings file
|
||||
std::ifstream fstream(m_pathInstallDir / "settings.toml");
|
||||
if (!fstream.is_open())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Cannot open the application settings file." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the settings file
|
||||
ssSettings = std::string((std::istreambuf_iterator<char>(fstream)), std::istreambuf_iterator<char>());
|
||||
if (ssSettings.empty())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Cannot read the application settings file; will use default." << std::endl;
|
||||
ssSettings = std::move(ssSettingsTemplate);
|
||||
bChangeDetected = true;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Read the settings
|
||||
toml_parser::CParser parserSettings(ssSettings);
|
||||
|
||||
// Check for the version
|
||||
sdv::toml::CNodeCollection tableRoot(&parserSettings.Root());
|
||||
if (!tableRoot)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Invalid TOML file '" << (m_pathInstallDir / "settings.toml").generic_u8string() << "'"
|
||||
<<
|
||||
std::endl;
|
||||
return false;
|
||||
}
|
||||
sdv::toml::CNodeCollection tableSettings = tableRoot.GetDirect("Settings");
|
||||
if (!tableSettings)
|
||||
tableSettings = tableRoot.AddTable("Settings");
|
||||
if (!tableSettings)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Invalid 'Settings' table." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t uiVersion = tableSettings.GetDirect("Version").GetValue();
|
||||
if (uiVersion != SDVFrameworkInterfaceVersion)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Invalid version of application settings file (expected version "
|
||||
<< SDVFrameworkInterfaceVersion << ", but available version " << uiVersion << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generic update config file function
|
||||
auto fnUpdateConfig = [&](const std::string &rssConfigKey, const std::filesystem::path& rpathConfigFile)
|
||||
{
|
||||
sdv::toml::CNode nodeUserConfig = tableSettings.GetDirect(rssConfigKey);
|
||||
if (nodeUserConfig.GetValue().empty())
|
||||
{
|
||||
bChangeDetected = true;
|
||||
if (nodeUserConfig)
|
||||
nodeUserConfig.Delete();
|
||||
nodeUserConfig = tableSettings.AddValue(rssConfigKey, rpathConfigFile);
|
||||
if (!nodeUserConfig)
|
||||
{
|
||||
if (!m_bSilent) std::cerr << "ERROR: Cannot insert the \"Settings." << rssConfigKey <<
|
||||
"\" value; cannot process further." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (nodeUserConfig.GetValue() != rpathConfigFile)
|
||||
{
|
||||
bChangeDetected = true;
|
||||
if (!nodeUserConfig.SetValue(rpathConfigFile))
|
||||
{
|
||||
if (!m_bSilent) std::cerr << "ERROR: Cannot update the \"Settings." << rssConfigKey <<
|
||||
"\" value; cannot process further." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Update the configuration path values.
|
||||
if (m_bPlatformConfig && !fnUpdateConfig("PlatformConfig", m_pathPlatformConfig)) return false;
|
||||
if (m_bVehIfcConfig && !fnUpdateConfig("VehIfcConfig", m_pathVehIfcConfig)) return false;
|
||||
if (m_bVehAbstrConfig && !fnUpdateConfig("VehAbstrConfig", m_pathVehAbstrConfig)) return false;
|
||||
if (m_bUserConfig && !fnUpdateConfig("AppConfig", m_pathUserConfig)) return false;
|
||||
|
||||
// Save the settings file if needed
|
||||
if (bChangeDetected)
|
||||
{
|
||||
std::ofstream fstream(m_pathInstallDir / "settings.toml", std::ios::trunc);
|
||||
if (!fstream.is_open())
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Cannot write the application settings file." << std::endl;
|
||||
return false;
|
||||
}
|
||||
fstream << parserSettings.GenerateTOML();
|
||||
|
||||
m_bPlatformConfig = false;
|
||||
m_bVehIfcConfig = false;
|
||||
m_bVehAbstrConfig = false;
|
||||
m_bUserConfig = false;
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
std::cerr << "ERROR: Failed to parse application settings: " << rexcept.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAppSettings::IsMainApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::main;
|
||||
}
|
||||
|
||||
bool CAppSettings::IsIsolatedApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::isolated;
|
||||
}
|
||||
|
||||
bool CAppSettings::IsStandaloneApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::standalone;
|
||||
}
|
||||
|
||||
bool CAppSettings::IsEssentialApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::essential;
|
||||
}
|
||||
|
||||
bool CAppSettings::IsMaintenanceApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::maintenance;
|
||||
}
|
||||
|
||||
bool CAppSettings::IsExternalApplication() const
|
||||
{
|
||||
return m_eContextMode == sdv::app::EAppContext::external;
|
||||
}
|
||||
|
||||
sdv::app::EAppContext CAppSettings::GetContextType() const
|
||||
{
|
||||
return m_eContextMode;
|
||||
}
|
||||
|
||||
uint32_t CAppSettings::GetInstanceID() const
|
||||
{
|
||||
return m_uiInstanceID;
|
||||
}
|
||||
|
||||
uint32_t CAppSettings::GetRetries() const
|
||||
{
|
||||
return m_uiRetries;
|
||||
}
|
||||
|
||||
std::string CAppSettings::GetLoggerClass() const
|
||||
{
|
||||
return m_ssLoggerClass;
|
||||
}
|
||||
|
||||
std::filesystem::path CAppSettings::GetLoggerModulePath() const
|
||||
{
|
||||
return m_pathLoggerModule;
|
||||
}
|
||||
|
||||
std::string CAppSettings::GetLoggerProgramTag() const
|
||||
{
|
||||
return m_ssProgramTag;
|
||||
}
|
||||
|
||||
sdv::core::ELogSeverity CAppSettings::GetLoggerSeverityFilter() const
|
||||
{
|
||||
return m_eSeverityFilter;
|
||||
}
|
||||
|
||||
sdv::core::ELogSeverity CAppSettings::GetConsoleSeverityFilter() const
|
||||
{
|
||||
return m_eSeverityViewFilter;
|
||||
}
|
||||
|
||||
bool CAppSettings::IsConsoleSilent() const
|
||||
{
|
||||
return m_bSilent;
|
||||
}
|
||||
|
||||
bool CAppSettings::IsConsoleVerbose() const
|
||||
{
|
||||
return m_bVerbose;
|
||||
}
|
||||
|
||||
std::filesystem::path CAppSettings::GetRootDir() const
|
||||
{
|
||||
return m_pathRootDir;
|
||||
}
|
||||
|
||||
std::filesystem::path CAppSettings::GetInstallDir() const
|
||||
{
|
||||
return m_pathInstallDir;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> CAppSettings::GetSystemConfigPaths() const
|
||||
{
|
||||
std::vector<std::filesystem::path> vecSysConfigs;
|
||||
if (!m_pathPlatformConfig.empty())
|
||||
vecSysConfigs.push_back(m_pathPlatformConfig);
|
||||
if (!m_pathVehIfcConfig.empty())
|
||||
vecSysConfigs.push_back(m_pathVehIfcConfig);
|
||||
if (!m_pathVehAbstrConfig.empty())
|
||||
vecSysConfigs.push_back(m_pathVehAbstrConfig);
|
||||
return vecSysConfigs;
|
||||
}
|
||||
|
||||
std::filesystem::path CAppSettings::GetConfigPath(EConfigType eType) const
|
||||
{
|
||||
// Is running as main application?
|
||||
if (!IsMainApplication() && !IsMaintenanceApplication()) return {};
|
||||
|
||||
switch (eType)
|
||||
{
|
||||
case EConfigType::platform_config:
|
||||
if (!m_pathPlatformConfig.empty()) return m_pathPlatformConfig;
|
||||
return "platform.toml";
|
||||
case EConfigType::vehicle_interface_config:
|
||||
if (!m_pathVehIfcConfig.empty()) return m_pathVehIfcConfig;
|
||||
return "vehicle_ifc.toml";
|
||||
case EConfigType::vehicle_abstraction_config:
|
||||
if (!m_pathVehAbstrConfig.empty()) return m_pathVehAbstrConfig;
|
||||
return "vehicle_abstract.toml";
|
||||
case EConfigType::user_config:
|
||||
if (!m_pathUserConfig.empty()) return m_pathUserConfig;
|
||||
return "app_config.toml";
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CAppSettings::EnableConfig(EConfigType eType)
|
||||
{
|
||||
// Is running as main application?
|
||||
if (!IsMainApplication() && !IsMaintenanceApplication()) return false;
|
||||
|
||||
switch (eType)
|
||||
{
|
||||
case EConfigType::platform_config:
|
||||
m_bPlatformConfig = true;
|
||||
if (!m_pathPlatformConfig.empty()) return true;
|
||||
m_pathPlatformConfig = "platform.toml";
|
||||
break;
|
||||
case EConfigType::vehicle_interface_config:
|
||||
m_bVehIfcConfig = true;
|
||||
if (!m_pathVehIfcConfig.empty()) return true;
|
||||
m_pathVehIfcConfig = "vehicle_ifc.toml";
|
||||
break;
|
||||
case EConfigType::vehicle_abstraction_config:
|
||||
m_bVehAbstrConfig = true;
|
||||
if (!m_pathVehAbstrConfig.empty()) return true;
|
||||
m_pathVehAbstrConfig = "vehicle_abstract.toml";
|
||||
break;
|
||||
case EConfigType::user_config:
|
||||
m_bUserConfig = true;
|
||||
if (!m_pathUserConfig.empty()) return true;
|
||||
m_pathUserConfig = "app_config.toml";
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAppSettings::DisableConfig(EConfigType eType)
|
||||
{
|
||||
// Is running as main application?
|
||||
if (!IsMainApplication() && !IsMaintenanceApplication()) return false;
|
||||
|
||||
switch (eType)
|
||||
{
|
||||
case EConfigType::platform_config:
|
||||
m_pathPlatformConfig.clear();
|
||||
m_bPlatformConfig = true;
|
||||
break;
|
||||
case EConfigType::vehicle_interface_config:
|
||||
m_pathVehIfcConfig.clear();
|
||||
m_bVehIfcConfig = true;
|
||||
break;
|
||||
case EConfigType::vehicle_abstraction_config:
|
||||
m_pathVehAbstrConfig.clear();
|
||||
m_bVehAbstrConfig = true;
|
||||
break;
|
||||
case EConfigType::user_config:
|
||||
m_pathUserConfig.clear();
|
||||
m_bUserConfig = true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::filesystem::path CAppSettings::GetUserConfigPath() const
|
||||
{
|
||||
return m_pathUserConfig;
|
||||
}
|
||||
|
||||
bool CAppSettings::SetUserConfigPath(const std::filesystem::path& rpathConfig)
|
||||
{
|
||||
// Is running as server application? Then the user configuration is limited to a filename only.
|
||||
if (IsMainApplication() || IsMaintenanceApplication() || IsIsolatedApplication())
|
||||
{
|
||||
// Must be a filename only.
|
||||
if (!rpathConfig.has_filename() || rpathConfig.has_parent_path())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assign the path
|
||||
m_pathUserConfig = rpathConfig;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAppSettings::RemoveUserConfigPath()
|
||||
{
|
||||
// Is running as main application?
|
||||
if (!IsMainApplication()) return false;
|
||||
|
||||
// Clear the path
|
||||
m_pathUserConfig.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::u8string> CAppSettings::GetNames() const
|
||||
{
|
||||
sdv::sequence<sdv::u8string> seqNames = {"app.instance_id", "console.info_level"};
|
||||
return seqNames;
|
||||
}
|
||||
|
||||
sdv::any_t CAppSettings::Get(/*in*/ const sdv::u8string& ssAttribute) const
|
||||
{
|
||||
if (ssAttribute == "app.instance_id")
|
||||
return sdv::any_t(m_uiInstanceID);
|
||||
if (ssAttribute == "console.info_level")
|
||||
{
|
||||
if (m_bSilent)
|
||||
return "silent";
|
||||
if (m_bVerbose)
|
||||
return "verbose";
|
||||
return "normal";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CAppSettings::Set(/*in*/ const sdv::u8string& /*ssAttribute*/, /*in*/ sdv::any_t /*anyAttribute*/)
|
||||
{
|
||||
// Currently there are not setting attributes...
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t CAppSettings::GetFlags(/*in*/ const sdv::u8string& ssAttribute) const
|
||||
{
|
||||
if (ssAttribute == "app.instance_id")
|
||||
return hlpr::flags<sdv::EAttributeFlags>(sdv::EAttributeFlags::read_only);
|
||||
if (ssAttribute == "console.info_level")
|
||||
return hlpr::flags<sdv::EAttributeFlags>(sdv::EAttributeFlags::read_only);
|
||||
return 0u;
|
||||
}
|
||||
|
||||
void CAppSettings::Reset()
|
||||
{
|
||||
m_eContextMode = sdv::app::EAppContext::no_context;
|
||||
m_pathLoggerModule.clear();
|
||||
m_ssLoggerClass.clear();
|
||||
m_ssProgramTag.clear();
|
||||
m_eSeverityFilter = sdv::core::ELogSeverity::info;
|
||||
m_eSeverityViewFilter = sdv::core::ELogSeverity::error;
|
||||
m_uiInstanceID = 0u;
|
||||
m_bSilent = false;
|
||||
m_bVerbose = false;
|
||||
m_pathRootDir.clear();
|
||||
m_pathInstallDir.clear();
|
||||
m_pathPlatformConfig.clear();
|
||||
m_pathVehIfcConfig.clear();
|
||||
m_pathVehAbstrConfig.clear();
|
||||
m_pathUserConfig.clear();
|
||||
m_bPlatformConfig = false;
|
||||
m_bVehIfcConfig = false;
|
||||
m_bVehAbstrConfig = false;
|
||||
m_bUserConfig = false;
|
||||
}
|
||||
|
||||
CAppSettings& CAppSettingsService::GetAppSettings()
|
||||
{
|
||||
return ::GetAppSettings();
|
||||
}
|
||||
417
sdv_services/core/app_settings.h
Normal file
417
sdv_services/core/app_settings.h
Normal file
@@ -0,0 +1,417 @@
|
||||
/********************************************************************************
|
||||
* 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 APP_SETTINGS_H
|
||||
#define APP_SETTINGS_H
|
||||
|
||||
#include <interfaces/app.h>
|
||||
#include <support/component_impl.h>
|
||||
|
||||
/**
|
||||
* @brief Application settings class.
|
||||
* @details The application settings class is responsible for interpretation of the initial application startup configuration and
|
||||
* reading and writing the application main settings.
|
||||
* The startup configuration is a string using the TOML format and is defined as follows:
|
||||
* @code
|
||||
* # Optional use of customized log handler
|
||||
* [LogHandler]
|
||||
* Class = "" # Component class name of a custom logger (optional)
|
||||
* Path = "" # Component module path of a custom logger (optional)
|
||||
* Tag = "" # Program tag to use instead of the name SDV_LOG_<pid>
|
||||
* Filter = "" # Lowest severity filter to use when logging (Trace, Debug, Info, Warning, Error, Fatal). Default severity
|
||||
* # level filter is Info (meaning Debug and Trace messages are not being stored).
|
||||
* ViewFilter = "" # Lowest severity filter to use when logging (Trace, Debug, Info, Warning, Error, Fatal). Default severity
|
||||
* # level filter is Error (meaning Debug, Trace, Info and Warning messages are not being shown).
|
||||
*
|
||||
* # Application behavior definition
|
||||
* # Mode = "Standalone" (default) app->no RPC + core services + additional configurations allowed
|
||||
* # Mode = "External" app->RPC client only + local services + target service(s) --> connection information through listener
|
||||
* # Mode = "Isolated" app->RPC client only + local services + target service(s) --> connection information needed
|
||||
* # Mode = "Main" app->RPC server + core services --> access key needed
|
||||
* # Mode = "Essential" app->local services + additional configurations allowed
|
||||
* # Mode = "Maintenance" app->RPC client only + local services + maintenance service --> connection information needed + access key
|
||||
* # Instance = 1234
|
||||
* [Application]
|
||||
* Mode = "Main"
|
||||
* Instance = 1234 # Optional instance ID to be used with main and isolated applications. Has no influence on other
|
||||
* # applications. Default instance ID is 1000. The connection listener is using the instance ID to allow
|
||||
* # connections from an external application to the main application. Furthermore, the instance ID is
|
||||
* # used to locate the installation of SDV components. The location of the SDV components is relative to
|
||||
* # the executable (unless a target directory is supplied) added with the instance and the installations:
|
||||
* # <exe_path>/<instance>/<installation>
|
||||
* InstallDir = "./test" # Optional custom installation directory to be used with main and isolated applications. Has no
|
||||
* # influence on other applications. The default location for installations is the location of the
|
||||
* # executable. Specifying a different directory will change the location of installations to
|
||||
* # <install_directory>/<instance>/<installation>
|
||||
* # NOTE The directory of the core library and the directory of the running executable are always added
|
||||
* # to the system if they contain an installation manifest.
|
||||
*
|
||||
* # Optional configuration that should be loaded (only for local applications).
|
||||
* Config = "abc.toml"
|
||||
*
|
||||
* #Console output
|
||||
* [Console]
|
||||
* Report = "Silent" # Either "Silent", "Normal" or "Verbose" for no, normal or extensive messages.
|
||||
*
|
||||
* # Search directories
|
||||
* @endcode
|
||||
*
|
||||
* @todo Add config ignore list (e.g. platform.toml, vehicle_ifc.toml and vehicle_abstract.toml).
|
||||
* @todo Add dedicated config (rather than standard config) as startup param.
|
||||
*
|
||||
* The settings file is a TOML file with the following structure:
|
||||
* @code
|
||||
* [Settings]
|
||||
* Version = 100
|
||||
*
|
||||
* # The system configuration is divided into:
|
||||
* # platform config - containing all the components needed to interact with the OS,
|
||||
* # middleware, vehicle bus, Ethernet.
|
||||
* # vehicle interface - containing the vehicle bus interpretation components like data link
|
||||
* # based on DBC and devices for their abstraction.
|
||||
* # vehicle abstraction - containing the vehicle abstraction services
|
||||
* # The configuration files are loaded exactly in that order, allowing the vehicle interface to
|
||||
* # depend on the platform and the vehicle abstraction to depend on the vehicle interface.
|
||||
* # The configurations are loaded if the PlatformConfig, VehIfcConfig and VehAbstrConfig keywords
|
||||
* # are present and describe a valid configuration file.
|
||||
* # A relative path is relative to the installation directory (being "exe_location/instance_id").
|
||||
* #
|
||||
* # Example:
|
||||
* # PlatformConfig = "platform.toml"
|
||||
* # VehIfcConfig = "vehicle_ifc.toml"
|
||||
* # VehAbstrConfig = "vehicle_abstract.toml"
|
||||
* #
|
||||
* PlatformConfig = ""
|
||||
* VehIfcConfig = ""
|
||||
* VehAbstrConfig = ""
|
||||
*
|
||||
* # The application config contains the configuration file that can be updated when services and
|
||||
* # apps are being added to the system (or being removed from the system). Load the application
|
||||
* # config by providing the "AppConfig" keyword as a string value. A relative path is relative to
|
||||
* # the installation directory (being "exe_location/instance_id").
|
||||
* #
|
||||
* # Example
|
||||
* # AppConfig = "app_config.toml"
|
||||
* AppConfig = ""
|
||||
* @endcode
|
||||
*/
|
||||
class CAppSettings : public sdv::IInterfaceAccess, public sdv::app::IAppContext, public sdv::IAttributes
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
CAppSettings();
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CAppSettings();
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::app::IAppContext)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Process the application starrtup configuration.
|
||||
* @param[in] rssConfig Reference to the configuration content (TOML format).
|
||||
* @return Returns 'true' when processing was successful; false when not.
|
||||
*/
|
||||
bool ProcessAppStartupConfig(const sdv::u8string& rssConfig);
|
||||
|
||||
/**
|
||||
* @brief Load the application settings file.
|
||||
* @attention Only works if the application is running in main, isolation or maintenance mode.
|
||||
* @remarks When there is no settings file, this is not an error. Default settings will be assumed.
|
||||
* @return Returns whether the loading was successful.
|
||||
*/
|
||||
bool LoadSettingsFile();
|
||||
|
||||
/**
|
||||
* @brief Save the application settings file (or create when not existing yet).
|
||||
* @attention Only works if the application is running in main, isolation or maintenance mode.
|
||||
* @return Returns whether the saving was successful.
|
||||
*/
|
||||
bool SaveSettingsFile();
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is the main application.
|
||||
* @return Returns 'true' when the current application is the main application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsMainApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is an isolated application.
|
||||
* @return Returns 'true' when the current application is an isolated application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsIsolatedApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is a standalone application.
|
||||
* @return Returns 'true' when the current application is a standalone application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsStandaloneApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is an essential application.
|
||||
* @return Returns 'true' when the current application is an essential application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsEssentialApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is a maintenance application.
|
||||
* @return Returns 'true' when the current application is a maintenance application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsMaintenanceApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is an external application.
|
||||
* @return Returns 'true' when the current application is an external application; otherwise returns 'false'.
|
||||
*/
|
||||
bool IsExternalApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Return the application context mode. Overload of sdv::app::IAppContext::GetContextType.
|
||||
* @return The context mode.
|
||||
*/
|
||||
sdv::app::EAppContext GetContextType() const override;
|
||||
|
||||
/**
|
||||
* @brief Return the core instance ID. Overload of sdv::app::IAppContext::GetContextType.
|
||||
* @details Get the instance. If not otherwise specified, the current instance depends on whether the application is running
|
||||
* as main or isolated application, in which case the instance is 1000. In all other cases the instance is 0. An instance
|
||||
* ID can be supplied through the app startup configuration.
|
||||
* @return The core instance ID.
|
||||
*/
|
||||
uint32_t GetInstanceID() const override;
|
||||
|
||||
/**
|
||||
* @brief Return the number of retries to establish a connection. Overload of sdv::app::IAppContext::GetRetries.
|
||||
* @return Number of retries.
|
||||
*/
|
||||
uint32_t GetRetries() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the class name of a logger service, if specified in the application startup configuration.
|
||||
* @return The logger class name.
|
||||
*/
|
||||
std::string GetLoggerClass() const;
|
||||
|
||||
/**
|
||||
* @brief Get the logger service module path, if specified in the application startup configuration.
|
||||
* @return The logger module path.
|
||||
*/
|
||||
std::filesystem::path GetLoggerModulePath() const;
|
||||
|
||||
/**
|
||||
* @brief Get the logger program tag, if specified in the application startup configuration.
|
||||
* @return The logger program tag.
|
||||
*/
|
||||
std::string GetLoggerProgramTag() const;
|
||||
|
||||
/**
|
||||
* @brief Get the logger severity filter, if specified in the application startup configuration.
|
||||
* @return The logger severity filter.
|
||||
*/
|
||||
sdv::core::ELogSeverity GetLoggerSeverityFilter() const;
|
||||
|
||||
/**
|
||||
* @brief Get the console reporting severity file, if specified in the application startup configuration.
|
||||
* @return The console reporting severity filter value.
|
||||
*/
|
||||
sdv::core::ELogSeverity GetConsoleSeverityFilter() const;
|
||||
|
||||
/**
|
||||
* @brief Should the console output be silent?
|
||||
* @return Returns whether the console output is silent.
|
||||
*/
|
||||
bool IsConsoleSilent() const;
|
||||
|
||||
/**
|
||||
* @brief Should the console output be verbose?
|
||||
* @return Returns whether the verbose console output is activated.
|
||||
*/
|
||||
bool IsConsoleVerbose() const;
|
||||
|
||||
/**
|
||||
* @brief Get the root directory for the application.
|
||||
* @remarks Is only valid when used in main, isolated and maintenance applications.
|
||||
* @return The location of root directory.
|
||||
*/
|
||||
std::filesystem::path GetRootDir() const;
|
||||
|
||||
/**
|
||||
* @brief Get the installation directory of user components (root directory / instance ID).
|
||||
* @remarks Is only valid when used in main, isolated and maintenance applications.
|
||||
* @return The location of the installation director.
|
||||
*/
|
||||
std::filesystem::path GetInstallDir() const;
|
||||
|
||||
/**
|
||||
* @brief Get a vector with the system configuration paths (relative to the installation directory) as specified in the
|
||||
* settings file.
|
||||
* @return The vector of system configuration paths.
|
||||
*/
|
||||
std::vector<std::filesystem::path> GetSystemConfigPaths() const;
|
||||
|
||||
/**
|
||||
* @brief Configuration type
|
||||
*/
|
||||
enum class EConfigType
|
||||
{
|
||||
platform_config, ///< Contains the platform configuration
|
||||
vehicle_interface_config, ///< Contains the vehicle interface configuration
|
||||
vehicle_abstraction_config, ///< Contains the vehicle abstraction configuration
|
||||
user_config ///< Contains the user configuration
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get the stored or default configuration path name.
|
||||
* @attention Setting a path is only valid when running as main application.
|
||||
* @return The path name dependent on the configuration type. If no path name was configured, the default path name is returned.
|
||||
*/
|
||||
std::filesystem::path GetConfigPath(EConfigType eType) const;
|
||||
|
||||
/**
|
||||
* @brief Enable a configuration file in the application settings.
|
||||
* @attention The configuration file needs to be located at the root directory of the instance installation.
|
||||
* @attention Setting a path is only valid when running as main application.
|
||||
* @param[in] eType The configuration type to set the path for.
|
||||
* @return Returns 'true' when adding the config file path was successful (or when the path already exists in the settings);
|
||||
* otherwise returns 'false'.
|
||||
*/
|
||||
bool EnableConfig(EConfigType eType);
|
||||
|
||||
/**
|
||||
* @brief Disable and remove a configuration file from the application settings.
|
||||
* @attention The configuration file needs to be located at the root directory of the instance installation.
|
||||
* @attention Removing a psth is only valid when running as main application.
|
||||
* @param[in] eType The configuration type to remove the path from.
|
||||
* @return Returns 'true' when the removal was successful.
|
||||
*/
|
||||
bool DisableConfig(EConfigType eType);
|
||||
|
||||
/**
|
||||
* @brief Get the path to the user copnfiguration (relative to the installation directory) as specified in the settings file.
|
||||
* @return Path to the user configuration.
|
||||
*/
|
||||
std::filesystem::path GetUserConfigPath() const;
|
||||
|
||||
/**
|
||||
* @brief Set the user configuration file into the application settings. If a user configuration file is stored already in the
|
||||
* settings file, the configuration file name is replaced by the new configuration file name.
|
||||
* @remarks Setting the user config path is only valid for local applications. For server application, the path is managed by
|
||||
* enabling the configuration.
|
||||
* @param[in] rpathConfig Reference to the path containing the configuration file name.
|
||||
* @return Returns 'true' when setting the config file path was successful; otherwise returns 'false'.
|
||||
*/
|
||||
bool SetUserConfigPath(const std::filesystem::path& rpathConfig);
|
||||
|
||||
/**
|
||||
* @brief Remove the user configuration file from the application settings.
|
||||
* @attention Removing the path is only valid when running as main application.
|
||||
* @return Returns 'true' when the removal was successful.
|
||||
*/
|
||||
bool RemoveUserConfigPath();
|
||||
|
||||
/**
|
||||
* @brief Get a sequence with the available attribute names. Overload of sdv::IAttributes::GetNames.
|
||||
* @return The sequence of attribute names.
|
||||
*/
|
||||
virtual sdv::sequence<sdv::u8string> GetNames() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the attribute value. Overload of sdv::IAttributes::Get.
|
||||
* @param[in] ssAttribute Name of the attribute.
|
||||
* @return The attribute value or an empty any-value if the attribute wasn't found or didn't have a value.
|
||||
*/
|
||||
virtual sdv::any_t Get(/*in*/ const sdv::u8string& ssAttribute) const override;
|
||||
|
||||
/**
|
||||
* @brief Set the attribute value. Overload of sdv::IAttributes::Set.
|
||||
* @param[in] ssAttribute Name of the attribute.
|
||||
* @param[in] anyAttribute Attribute value to set.
|
||||
* @return Returns 'true' when setting the attribute was successful or 'false' when the attribute was not found or the
|
||||
* attribute is read-only or another error occurred.
|
||||
*/
|
||||
virtual bool Set(/*in*/ const sdv::u8string& ssAttribute, /*in*/ sdv::any_t anyAttribute) override;
|
||||
|
||||
/**
|
||||
* @brief Get the attribute flags belonging to a certain attribute. Overload of sdv::IAttributes::GetFlags.
|
||||
* @param[in] ssAttribute Name of the attribute.
|
||||
* @return Returns the attribute flags (zero or more EAttributeFlags flags) or 0 when the attribute could not be found.
|
||||
*/
|
||||
virtual uint32_t GetFlags(/*in*/ const sdv::u8string& ssAttribute) const override;
|
||||
|
||||
/**
|
||||
* @brief Reset the settings after a shutdown.
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
sdv::app::EAppContext m_eContextMode = sdv::app::EAppContext::no_context; ///< The application is running as...
|
||||
uint32_t m_uiInstanceID = 0u; ///< Instance number.
|
||||
uint32_t m_uiRetries = 0u; ///< Number of retries to establish a connection.
|
||||
std::string m_ssLoggerClass; ///< Class name of a logger service.
|
||||
std::filesystem::path m_pathLoggerModule; ///< Module name of a custom logger.
|
||||
std::string m_ssProgramTag; ///< Program tag to use when logging.
|
||||
sdv::core::ELogSeverity m_eSeverityFilter = sdv::core::ELogSeverity::info; ///< Severity level filter while logging.
|
||||
sdv::core::ELogSeverity m_eSeverityViewFilter = sdv::core::ELogSeverity::error; ///< Severity level filter while logging.
|
||||
bool m_bSilent = false; ///< When set, no console reporting takes place.
|
||||
bool m_bVerbose = false; ///< When set, extensive console reporting takes place.
|
||||
std::filesystem::path m_pathRootDir; ///< Location of user component root directory.
|
||||
std::filesystem::path m_pathInstallDir; ///< Location of user component installations (root with instance).
|
||||
std::filesystem::path m_pathPlatformConfig; ///< The platform configuration from the settings file.
|
||||
std::filesystem::path m_pathVehIfcConfig; ///< The vehicle interface configuration from the settings file.
|
||||
std::filesystem::path m_pathVehAbstrConfig; ///< The vehicle abstraction configuration from the settings file.
|
||||
std::filesystem::path m_pathUserConfig; ///< The user configuration from the settings file.
|
||||
bool m_bPlatformConfig = false; ///< Platform config was explicitly enabled/disabled.
|
||||
bool m_bVehIfcConfig = false; ///< Vehicle interface config was explicitly enabled/disabled.
|
||||
bool m_bVehAbstrConfig = false; ///< Vehicle abstraction config was explicitly enabled/disabled.
|
||||
bool m_bUserConfig = false; ///< User config was explicitly enabled/disabled.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return the application settings class.
|
||||
* @return Reference to the application settings.
|
||||
*/
|
||||
CAppSettings& GetAppSettings();
|
||||
|
||||
/**
|
||||
* @brief App settings service class.
|
||||
*/
|
||||
class CAppSettingsService : public sdv::CSdvObject
|
||||
{
|
||||
public:
|
||||
CAppSettingsService() = default;
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY_MEMBER(sdv::IAttributes, GetAppSettings())
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("AppSettingsService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Get access to the application settings.
|
||||
* @return Returns the one global instance of the application config.
|
||||
*/
|
||||
static CAppSettings& GetAppSettings();
|
||||
};
|
||||
DEFINE_SDV_OBJECT(CAppSettingsService)
|
||||
|
||||
#endif // !defined APP_SETTINGS_H
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "installation_composer.h"
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
@@ -59,18 +72,18 @@ std::vector<std::filesystem::path> CInstallComposer::AddModule(const std::filesy
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a base path is supplied, the path needs to be absolute.
|
||||
if (!rpathBasePath.is_absolute() || !std::filesystem::exists(rpathBasePath) || !std::filesystem::is_directory(rpathBasePath))
|
||||
// The base path needs to be absolute.
|
||||
if (!rpathBasePath.is_absolute() || !std::filesystem::exists(rpathBasePath) ||
|
||||
!std::filesystem::is_directory(rpathBasePath))
|
||||
{
|
||||
sdv::XInvalidPath exception;
|
||||
exception.ssPath = rpathRelTargetDir.generic_u8string();
|
||||
exception.ssPath = rpathBasePath.generic_u8string();
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for the module path
|
||||
std::string ssModulePathCopy = rssModulePath;
|
||||
if (ssModulePathCopy.empty())
|
||||
if (rssModulePath.empty())
|
||||
{
|
||||
// Base path must be present
|
||||
if (rpathBasePath.empty())
|
||||
@@ -114,7 +127,7 @@ std::vector<std::filesystem::path> CInstallComposer::AddModule(const std::filesy
|
||||
}
|
||||
|
||||
// Get the list of files
|
||||
auto vecFiles = CollectWildcardPath(rpathBasePath, ssModulePathCopy, eAlgorithm);
|
||||
auto vecFiles = CollectWildcardPath(rpathBasePath, rssModulePath, eAlgorithm);
|
||||
|
||||
// For each file, check whether the file is somewhere within the base path (if provided) and add the file to the module list.
|
||||
for (const std::filesystem::path& rpathFile : vecFiles)
|
||||
@@ -166,6 +179,8 @@ sdv::pointer<uint8_t> CInstallComposer::Compose(const std::string& rssInstallNam
|
||||
|
||||
// Installation manifest
|
||||
CInstallManifest manifest;
|
||||
// cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!manifest.Create(rssInstallName))
|
||||
{
|
||||
sdv::installation::XFailedManifestCreation exception;
|
||||
@@ -228,6 +243,8 @@ bool CInstallComposer::Compose(const std::filesystem::path& rpathPackage, const
|
||||
|
||||
// Installation manifest
|
||||
CInstallManifest manifest;
|
||||
// cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!manifest.Create(rssInstallName))
|
||||
{
|
||||
sdv::installation::XFailedManifestCreation exception;
|
||||
@@ -365,6 +382,8 @@ CInstallManifest CInstallComposer::ComposeDirect(const std::string& rssInstallNa
|
||||
|
||||
// CReate the installation manifest
|
||||
CInstallManifest manifest;
|
||||
// cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!manifest.Create(rssInstallName))
|
||||
{
|
||||
sdv::installation::XFailedManifestCreation exception;
|
||||
@@ -459,6 +478,8 @@ CInstallManifest CInstallComposer::ComposeInstallManifest(const std::string& rss
|
||||
|
||||
// Installation manifest
|
||||
CInstallManifest manifest;
|
||||
// cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!manifest.Create(rssInstallName))
|
||||
{
|
||||
sdv::installation::XFailedManifestCreation exception;
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 INSTALL_PACKAGING_H
|
||||
#define INSTALL_PACKAGING_H
|
||||
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
/********************************************************************************
|
||||
* 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 "installation_manifest.h"
|
||||
#include "toml_parser/parser_toml.h"
|
||||
#include <support/component_impl.h>
|
||||
#include <support/serdes.h>
|
||||
#include <support/toml.h>
|
||||
|
||||
#if defined _WIN32 && defined __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
@@ -11,6 +25,74 @@
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
std::string ClassInfo2TOML(const sdv::SClassInfo& rsClass)
|
||||
{
|
||||
toml_parser::CParser parser("");
|
||||
sdv::toml::CNodeCollection nodeRoot(&parser.Root());
|
||||
auto nodeClass = nodeRoot.AddTableArray("Class");
|
||||
if (!rsClass.ssModulePath.empty())
|
||||
nodeClass.AddValue("Path", rsClass.ssModulePath);
|
||||
if (rsClass.ssName.empty()) return {};
|
||||
nodeClass.AddValue("Name", rsClass.ssName);
|
||||
if (!rsClass.seqClassAliases.empty())
|
||||
{
|
||||
auto nodeAliases = nodeClass.AddArray("Aliases");
|
||||
for (const auto& rssAlias : rsClass.seqClassAliases)
|
||||
nodeAliases.AddValue("", rssAlias);
|
||||
}
|
||||
if (!rsClass.ssDefaultObjectName.empty() && rsClass.ssDefaultObjectName != rsClass.ssName)
|
||||
nodeClass.AddValue("DefaultName", rsClass.ssDefaultObjectName);
|
||||
if (!rsClass.ssDefaultConfig.empty())
|
||||
nodeClass.AddTOML(rsClass.ssDefaultConfig);
|
||||
if (rsClass.eType == sdv::EObjectType::undefined) return {};
|
||||
nodeClass.AddValue("Type", ::sdv::ObjectType2String(rsClass.eType));
|
||||
if (rsClass.uiFlags & static_cast<uint32_t>(sdv::EObjectFlags::singleton))
|
||||
nodeClass.AddValue("Singleton", true);
|
||||
if (!rsClass.seqDependencies.empty())
|
||||
{
|
||||
auto nodeDependencies = nodeClass.AddArray("Dependencies");
|
||||
for (const auto& rssDependency : rsClass.seqDependencies)
|
||||
nodeDependencies.AddValue("" , rssDependency);
|
||||
}
|
||||
|
||||
return parser.GenerateTOML();
|
||||
}
|
||||
|
||||
sdv::SClassInfo TOML2ClassInfo(const std::string& rssTOML, size_t nIndex /*= 0*/)
|
||||
{
|
||||
toml_parser::CParser parser(rssTOML);
|
||||
return TOML2ClassInfo(parser.Root().Cast<toml_parser::CNodeCollection>(), nIndex);
|
||||
}
|
||||
|
||||
sdv::SClassInfo TOML2ClassInfo(const std::shared_ptr<toml_parser::CNodeCollection>& rptrTOML, size_t nIndex /*= 0*/)
|
||||
{
|
||||
if (!rptrTOML) return {};
|
||||
sdv::toml::CNodeCollection nodeRoot(rptrTOML.get());
|
||||
sdv::toml::CNodeCollection nodeClass = nodeRoot.GetDirect("Class[" + std::to_string(nIndex) + "]");
|
||||
if (!nodeClass)
|
||||
nodeClass = nodeRoot.GetDirect("Class");
|
||||
if (!nodeClass) return {};
|
||||
|
||||
sdv::SClassInfo sClass{};
|
||||
sClass.ssModulePath = nodeClass.GetDirect("Path").GetValueAsString();
|
||||
sClass.ssName = nodeClass.GetDirect("Name").GetValueAsString();
|
||||
sdv::toml::CNodeCollection nodeAliases = nodeClass.GetDirect("Aliases");
|
||||
for (size_t nAliasIndex = 0; nAliasIndex < nodeAliases.GetCount(); nAliasIndex++)
|
||||
sClass.seqClassAliases.push_back(nodeAliases.Get(nAliasIndex).GetValueAsString());
|
||||
sClass.ssDefaultObjectName = nodeClass.GetDirect("DefaultName").GetValueAsString();
|
||||
sClass.ssDefaultConfig = nodeClass.GetDirect("Parameters").GetTOML();
|
||||
sClass.eType = sdv::String2ObjectType(nodeClass.GetDirect("Type").GetValue());
|
||||
if (static_cast<bool>(nodeClass.GetDirect("Singleton").GetValue()))
|
||||
sClass.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
|
||||
sdv::toml::CNodeCollection nodeDependencies = nodeClass.GetDirect("Dependencies");
|
||||
for (size_t nDependencyIndex = 0; nDependencyIndex < nodeDependencies.GetCount(); nDependencyIndex++)
|
||||
sClass.seqDependencies.push_back(nodeDependencies.Get(nDependencyIndex).GetValueAsString());
|
||||
if (sClass.ssName.empty() || sClass.eType == sdv::EObjectType::undefined)
|
||||
return {};
|
||||
|
||||
return sClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the module manifest from the binary.
|
||||
* @param[in] rpathModule Reference to the module to read the manifest from.
|
||||
@@ -165,61 +247,43 @@ bool CInstallManifest::Read(const std::string& rssManifest, bool bBlockSystemObj
|
||||
|
||||
// Parse the manifest
|
||||
toml_parser::CParser parser(rssManifest);
|
||||
sdv::toml::CNodeCollection nodeRoot(&parser.Root());
|
||||
|
||||
// Get the installation version - must be identical to the interface version
|
||||
auto ptrInstallVersionNode = parser.Root().Direct("Installation.Version");
|
||||
if (!ptrInstallVersionNode || ptrInstallVersionNode->GetValue() != SDVFrameworkInterfaceVersion) return false;
|
||||
if (nodeRoot.GetDirect("Installation.Version").GetValue() != SDVFrameworkInterfaceVersion) return false;
|
||||
|
||||
// Get the installation name
|
||||
auto ptrInstallNameNode = parser.Root().Direct("Installation.Name");
|
||||
if (!ptrInstallNameNode) return false;
|
||||
m_ssInstallName = static_cast<std::string>(ptrInstallNameNode->GetValue());
|
||||
m_ssInstallName = nodeRoot.GetDirect("Installation.Name").GetValueAsString();
|
||||
if (m_ssInstallName.empty()) return false;
|
||||
|
||||
// Get installation properties. The properties are optional
|
||||
auto ptrProperties = parser.Root().Direct("Properties");
|
||||
std::shared_ptr<toml_parser::CTable> ptrPropertyTable;
|
||||
if (ptrProperties) ptrPropertyTable = ptrProperties->Cast<toml_parser::CTable>();
|
||||
if (ptrPropertyTable)
|
||||
sdv::toml::CNodeCollection nodeProperties = nodeRoot.GetDirect("Properties");
|
||||
for (size_t nIndex = 0; nIndex < nodeProperties.GetCount(); nIndex++)
|
||||
{
|
||||
for (uint32_t uiIndex = 0; uiIndex < ptrPropertyTable->GetCount(); uiIndex++)
|
||||
{
|
||||
auto ptrProperty = ptrPropertyTable->Get(uiIndex);
|
||||
if (ptrProperty)
|
||||
m_mapProperties[ptrProperty->GetName()] = static_cast<std::string>(ptrProperty->GetValue());
|
||||
}
|
||||
auto nodeProperty = nodeProperties.Get(nIndex);
|
||||
if (nodeProperty)
|
||||
m_mapProperties[nodeProperty.GetName()] = nodeProperty.GetValueAsString();
|
||||
}
|
||||
|
||||
// Build the module list
|
||||
auto ptrModulesNode = parser.Root().Direct("Module");
|
||||
if (!ptrModulesNode) return true; // No modules in the manifest
|
||||
auto ptrModuleArrayNode = ptrModulesNode->Cast<toml_parser::CArray>();
|
||||
if (!ptrModuleArrayNode) return false; // Must be array
|
||||
for (uint32_t uiModuleIndex = 0; uiModuleIndex < ptrModuleArrayNode->GetCount(); uiModuleIndex++)
|
||||
sdv::toml::CNodeCollection nodeModules = nodeRoot.GetDirect("Module");
|
||||
if (!nodeModules) return true; // No modules in the manifest
|
||||
for (size_t nIndex = 0; nIndex < nodeModules.GetCount(); nIndex++)
|
||||
{
|
||||
// Get the module
|
||||
auto ptrModule = ptrModuleArrayNode->Get(uiModuleIndex);
|
||||
if (!ptrModule) continue;
|
||||
sdv::toml::CNodeCollection nodeModule = nodeModules.Get(nIndex);
|
||||
if (!nodeModule) continue;
|
||||
|
||||
// Get the module path
|
||||
auto ptrModulePath = ptrModule->Direct("Path");
|
||||
if (!ptrModulePath) continue;
|
||||
std::filesystem::path pathModule = static_cast<std::string>(ptrModulePath->GetValue());
|
||||
std::filesystem::path pathModule = nodeModule.GetDirect("Path").GetValueAsPath();
|
||||
std::string ssModuleManifest;
|
||||
|
||||
// Get the component list (if available)
|
||||
auto ptrModuleComponents = ptrModule->Direct("Component");
|
||||
if (ptrModuleComponents)
|
||||
{
|
||||
// The module manifest contains the TOML text of the component array
|
||||
auto ptrModuleComponentArray = ptrModuleComponents->Cast<toml_parser::CArray>();
|
||||
if (ptrModuleComponentArray)
|
||||
ssModuleManifest = ptrModuleComponents->GenerateTOML();
|
||||
}
|
||||
// Get the class list (if available) and get the fitting TOML for the classes.
|
||||
sdv::toml::CNodeCollection nodeClasses = nodeModule.GetDirect("Class");
|
||||
if (nodeClasses) ssModuleManifest = nodeClasses.GetTOML();
|
||||
|
||||
// Add the module
|
||||
m_vecModules.push_back(SModule(pathModule, ssModuleManifest,
|
||||
m_bBlockSystemObjects));
|
||||
m_vecModules.push_back(SModule(pathModule, ssModuleManifest, m_bBlockSystemObjects));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -290,7 +354,7 @@ bool CInstallManifest::AddModule(const std::filesystem::path& rpathModulePath,
|
||||
auto ptrInterfaceNode = parser.Root().Direct("Interface.Version");
|
||||
if (!ptrInterfaceNode) return false;
|
||||
if (ptrInterfaceNode->GetValue() != SDVFrameworkInterfaceVersion) return false;
|
||||
auto ptrComponentsNode = parser.Root().Direct("Component");
|
||||
auto ptrComponentsNode = parser.Root().Direct("Class");
|
||||
if (!ptrComponentsNode) return true; // No component available in the manifest
|
||||
if (!ptrComponentsNode->Cast<toml_parser::CArray>()) return false;
|
||||
ssComponentsManifest = ptrComponentsNode->GenerateTOML();
|
||||
@@ -330,39 +394,39 @@ std::string CInstallManifest::FindModuleManifest(const std::filesystem::path& rp
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<CInstallManifest::SComponent> CInstallManifest::FindComponentByClass(const std::string& rssClass) const
|
||||
std::optional<sdv::SClassInfo> CInstallManifest::FindComponentByClass(const std::string& rssClass) const
|
||||
{
|
||||
// Search for the correct module
|
||||
SComponent sRet{};
|
||||
sdv::SClassInfo sRet{};
|
||||
auto itModule = std::find_if(m_vecModules.begin(), m_vecModules.end(), [&](const SModule& rsEntry)
|
||||
{
|
||||
return std::find_if(rsEntry.vecComponents.begin(), rsEntry.vecComponents.end(),
|
||||
[&](const SComponent& sComponent)
|
||||
return std::find_if(rsEntry.vecClasses.begin(), rsEntry.vecClasses.end(),
|
||||
[&](const sdv::SClassInfo& sClass)
|
||||
{
|
||||
// Note, use the class, alias and the default object name for searching...
|
||||
if (sComponent.ssClassName == rssClass ||
|
||||
std::find(sComponent.seqAliases.begin(), sComponent.seqAliases.end(), rssClass) !=
|
||||
sComponent.seqAliases.end())
|
||||
if (sClass.ssName == rssClass ||
|
||||
std::find(sClass.seqClassAliases.begin(), sClass.seqClassAliases.end(), rssClass) !=
|
||||
sClass.seqClassAliases.end())
|
||||
{
|
||||
sRet = sComponent;
|
||||
sRet = sClass;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}) != rsEntry.vecComponents.end();
|
||||
}) != rsEntry.vecClasses.end();
|
||||
});
|
||||
if (itModule != m_vecModules.end()) return sRet;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<CInstallManifest::SComponent> CInstallManifest::ComponentList() const
|
||||
std::vector<sdv::SClassInfo> CInstallManifest::ClassList() const
|
||||
{
|
||||
std::vector<SComponent> vecComponents;
|
||||
std::vector<sdv::SClassInfo> vecClasses;
|
||||
for (const auto& rsModule : m_vecModules)
|
||||
{
|
||||
for (const auto& rsComponent : rsModule.vecComponents)
|
||||
vecComponents.push_back(rsComponent);
|
||||
for (const auto& rsComponent : rsModule.vecClasses)
|
||||
vecClasses.push_back(rsComponent);
|
||||
}
|
||||
return vecComponents;
|
||||
return vecClasses;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> CInstallManifest::ModuleList() const
|
||||
@@ -409,67 +473,65 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
|
||||
{
|
||||
// Parse the manifest and extract information from them...
|
||||
toml_parser::CParser parser(rssManifest);
|
||||
auto ptrComponents = parser.Root().Direct("Component");
|
||||
if (!ptrComponents) return; // No objects...
|
||||
auto ptrComponentArray = ptrComponents->Cast<toml_parser::CArray>();
|
||||
if (!ptrComponentArray) return; // No objects...
|
||||
for (uint32_t uiIndex = 0; uiIndex < ptrComponentArray->GetCount(); uiIndex++)
|
||||
auto ptrClasses = parser.Root().Direct("Class");
|
||||
if (!ptrClasses) return; // No objects...
|
||||
auto ptrClassArray = ptrClasses->Cast<toml_parser::CArray>();
|
||||
if (!ptrClassArray) return; // No objects...
|
||||
for (uint32_t uiIndex = 0; uiIndex < ptrClassArray->GetCount(); uiIndex++)
|
||||
{
|
||||
auto ptrComponent = ptrComponentArray->Get(uiIndex);
|
||||
if (!ptrComponent) continue;
|
||||
//auto ptrClass = ptrClassArray->Get(uiIndex);
|
||||
//if (!ptrClass) continue;
|
||||
|
||||
// Fill in the component structure
|
||||
SComponent sComponent{};
|
||||
//sComponent.pathModule = rpathModule;
|
||||
sComponent.pathRelModule = rpathRelModule;
|
||||
sComponent.ssManifest = ptrComponent->GenerateTOML(toml_parser::CGenContext("Component"));
|
||||
auto ptrClassName = ptrComponent->Direct("Class");
|
||||
if (!ptrClassName) continue;
|
||||
sComponent.ssClassName = static_cast<std::string>(ptrClassName->GetValue());
|
||||
auto ptrAliases = ptrComponent->Direct("Aliases");
|
||||
if (ptrAliases)
|
||||
{
|
||||
auto ptrAliasesArray = ptrAliases->Cast<toml_parser::CArray>();
|
||||
for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++)
|
||||
{
|
||||
auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex);
|
||||
if (ptrClassAlias)
|
||||
sComponent.seqAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue()));
|
||||
}
|
||||
}
|
||||
auto ptrDefaultName = ptrComponent->Direct("DefaultName");
|
||||
if (ptrDefaultName) sComponent.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue());
|
||||
else sComponent.ssDefaultObjectName = sComponent.ssClassName;
|
||||
auto ptrType = ptrComponent->Direct("Type");
|
||||
if (!ptrType) continue;
|
||||
std::string ssType = static_cast<std::string>(ptrType->GetValue());
|
||||
if (ssType == "System") sComponent.eType = sdv::EObjectType::SystemObject;
|
||||
else if (ssType == "Device") sComponent.eType = sdv::EObjectType::Device;
|
||||
else if (ssType == "BasicService") sComponent.eType = sdv::EObjectType::BasicService;
|
||||
else if (ssType == "ComplexService") sComponent.eType = sdv::EObjectType::ComplexService;
|
||||
else if (ssType == "App") sComponent.eType = sdv::EObjectType::Application;
|
||||
else if (ssType == "Proxy") sComponent.eType = sdv::EObjectType::Proxy;
|
||||
else if (ssType == "Stub") sComponent.eType = sdv::EObjectType::Stub;
|
||||
else if (ssType == "Utility") sComponent.eType = sdv::EObjectType::Utility;
|
||||
else continue;
|
||||
if (bBlockSystemObjects && sComponent.eType == sdv::EObjectType::SystemObject) continue;
|
||||
auto ptrSingleton = ptrComponent->Direct("Singleton");
|
||||
if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue()))
|
||||
sComponent.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
|
||||
auto ptrDependencies = ptrComponent->Direct("Dependencies");
|
||||
if (ptrDependencies)
|
||||
{
|
||||
auto ptrDependencyArray = ptrDependencies->Cast<toml_parser::CArray>();
|
||||
for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount();
|
||||
uiDependencyIndex++)
|
||||
{
|
||||
auto ptrDependsOn = ptrDependencyArray->Get(uiDependencyIndex);
|
||||
if (ptrDependsOn)
|
||||
sComponent.seqDependencies.push_back(static_cast<sdv::u8string>(ptrDependsOn->GetValue()));
|
||||
}
|
||||
}
|
||||
//// Fill in the component structure
|
||||
//sdv::SClassInfo sClass{};
|
||||
////sClass.pathModule = rpathModule;
|
||||
//sClass.ssModulePath = rpathRelModule.generic_u8string();
|
||||
////sClass.ssManifest = ptrClass->GenerateTOML(toml_parser::CGenContext("Class"));
|
||||
//auto ptrClassName = ptrClass->Direct("Name");
|
||||
//if (!ptrClassName) continue;
|
||||
//sClass.ssName = ptrClassName->GetValue();
|
||||
//auto ptrAliases = ptrClass->Direct("Aliases");
|
||||
//if (ptrAliases)
|
||||
//{
|
||||
// auto ptrAliasesArray = ptrAliases->Cast<toml_parser::CArray>();
|
||||
// for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++)
|
||||
// {
|
||||
// auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex);
|
||||
// if (ptrClassAlias)
|
||||
// sClass.seqClassAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue()));
|
||||
// }
|
||||
//}
|
||||
//auto ptrDefaultName = ptrClass->Direct("DefaultName");
|
||||
//if (ptrDefaultName) sClass.ssDefaultObjectName = ptrDefaultName->GetValue();
|
||||
//else sClass.ssDefaultObjectName = sClass.ssName;
|
||||
//auto ptrType = ptrClass->Direct("Type");
|
||||
//if (!ptrType) continue;
|
||||
//sClass.eType = sdv::String2ObjectType(ptrType->GetValue());
|
||||
//if (sClass.eType == sdv::EObjectType::Undefined) continue;
|
||||
//if (bBlockSystemObjects && sClass.eType == sdv::EObjectType::system_object) continue;
|
||||
//auto ptrSingleton = ptrClass->Direct("Singleton");
|
||||
//if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue()))
|
||||
// sClass.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
|
||||
//auto ptrDependencies = ptrClass->Direct("Dependencies");
|
||||
//if (ptrDependencies)
|
||||
//{
|
||||
// auto ptrDependencyArray = ptrDependencies->Cast<toml_parser::CArray>();
|
||||
// for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount();
|
||||
// uiDependencyIndex++)
|
||||
// {
|
||||
// auto ptrDependsOn = ptrDependencyArray->Get(uiDependencyIndex);
|
||||
// if (ptrDependsOn)
|
||||
// sClass.seqDependencies.push_back(static_cast<sdv::u8string>(ptrDependsOn->GetValue()));
|
||||
// }
|
||||
//}
|
||||
|
||||
vecComponents.push_back(sComponent);
|
||||
//vecClasses.push_back(sClass);
|
||||
|
||||
auto sClass = TOML2ClassInfo(parser.Root().Cast<toml_parser::CNodeCollection>(), uiIndex);
|
||||
if (bBlockSystemObjects && sClass.eType == sdv::EObjectType::system_object) continue;
|
||||
if (sClass.eType == sdv::EObjectType::undefined) continue;
|
||||
sClass.ssModulePath = rpathRelModule.generic_u8string();
|
||||
vecClasses.push_back(sClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 INSTALL_MANIFEST_H
|
||||
#define INSTALL_MANIFEST_H
|
||||
|
||||
@@ -7,9 +20,37 @@
|
||||
#include <optional>
|
||||
#include <interfaces/core.h>
|
||||
#include <interfaces/config.h>
|
||||
#include "toml_parser/parser_toml.h"
|
||||
#include <map>
|
||||
#include <cstdlib>
|
||||
|
||||
/**
|
||||
* @brief Create a TOML string using the class information.
|
||||
* @param[in] rsClass Reference to the class structure.
|
||||
* @return Returns a string to the class table rray entry or an empty string when the type of class name were invalid or empty.
|
||||
*/
|
||||
std::string ClassInfo2TOML(const sdv::SClassInfo& rsClass);
|
||||
|
||||
/**
|
||||
* @brief Extract the class information from a TOML class configuration.
|
||||
* @remarks The class information could be stored as a table array entry (then the index is used to extract the information). It
|
||||
* also could be a table. Then the index parameter is ignored.
|
||||
* @param[in] rssTOML Reference to the string containing the TOML class configuration.
|
||||
* @param[in] nIndex The index in the table array to provide the class information for.
|
||||
* @return Returns the class information or an empty class structure when no information is available.
|
||||
*/
|
||||
sdv::SClassInfo TOML2ClassInfo(const std::string& rssTOML, size_t nIndex = 0);
|
||||
|
||||
/**
|
||||
* @brief Extract the class information from a TOML class configuration.
|
||||
* @remarks The class information could be stored as a table array entry (then the index is used to extract the information). It
|
||||
* also could be a table. Then the index parameter is ignored.
|
||||
* @param[in] rptrTOML Reference to the smart pointer holding the TOML class configuration.
|
||||
* @param[in] nIndex The index in the table array to provide the class information for.
|
||||
* @return Returns the class information or an empty class structure when no information is available.
|
||||
*/
|
||||
sdv::SClassInfo TOML2ClassInfo(const std::shared_ptr<toml_parser::CNodeCollection>& rptrTOML, size_t nIndex = 0);
|
||||
|
||||
/**
|
||||
* @brief Check whether a relative path is directing to a parent of the base path.
|
||||
* @details Detect whether a relative path joined to the base path is referring to a parent of the base path. For example, the
|
||||
@@ -87,7 +128,7 @@ inline bool RefersToRelativeParent(const std::filesystem::path& rpathRelative)
|
||||
* [[Module]]
|
||||
* Path = "mallard.sdv # Relative path to the module
|
||||
*
|
||||
* [[Module.Component]] # Component manifest
|
||||
* [[Module.Class]] # Component manifest
|
||||
* Class = "Mallard class" # The name of the class
|
||||
* Aliases = ["Duck", "Pont duck"] # Optional list of aliases
|
||||
* DefaultName = "Duck" # Optional default name for the class instance
|
||||
@@ -108,21 +149,6 @@ inline bool RefersToRelativeParent(const std::filesystem::path& rpathRelative)
|
||||
class CInstallManifest
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Manifest information belonging to the component.
|
||||
*/
|
||||
struct SComponent
|
||||
{
|
||||
std::filesystem::path pathRelModule; ///< Relative module path (relative to the installation directory).
|
||||
std::string ssManifest; ///< Component manifest.
|
||||
std::string ssClassName; ///< String representing the class name.
|
||||
sdv::sequence<sdv::u8string> seqAliases; ///< Sequence containing zero or more class name aliases.
|
||||
std::string ssDefaultObjectName; ///< The default object name.
|
||||
sdv::EObjectType eType; ///< Type of object.
|
||||
uint32_t uiFlags; ///< Zero or more object flags from EObjectFlags.
|
||||
sdv::sequence<sdv::u8string> seqDependencies; ///< This component depends on...
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*/
|
||||
@@ -224,13 +250,13 @@ public:
|
||||
* @param[in] rssClass Reference to the class name of the component.
|
||||
* @return The component manifest information.
|
||||
*/
|
||||
std::optional<SComponent> FindComponentByClass(const std::string& rssClass) const;
|
||||
std::optional<sdv::SClassInfo> FindComponentByClass(const std::string& rssClass) const;
|
||||
|
||||
/**
|
||||
* @brief Get a vector of all components stored in this installation manifest.
|
||||
* @return The component manifest vector.
|
||||
* @brief Get a vector of all component classes stored in this installation manifest.
|
||||
* @return The component class list vector.
|
||||
*/
|
||||
std::vector<SComponent> ComponentList() const;
|
||||
std::vector<sdv::SClassInfo> ClassList() const;
|
||||
|
||||
/**
|
||||
* @brief Get the module list.
|
||||
@@ -283,9 +309,9 @@ private:
|
||||
*/
|
||||
SModule(const std::filesystem::path& rpathRelModule, const std::string& rssManifest, bool bBlockSystemObjects);
|
||||
|
||||
std::filesystem::path pathRelModule; ///< Relative module path (relative to the installation directory).
|
||||
std::string ssManifest; ///< Manifest containing the components.
|
||||
std::vector<SComponent> vecComponents; ///< Vector with contained components
|
||||
std::filesystem::path pathRelModule; ///< Relative module path (relative to the installation directory).
|
||||
std::string ssManifest; ///< Manifest containing the component classes.
|
||||
std::vector<sdv::SClassInfo> vecClasses; ///< Vector with contained component classes.
|
||||
};
|
||||
|
||||
std::string m_ssInstallName; ///< Installation name.
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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 "iso_monitor.h"
|
||||
#include "sdv_core.h"
|
||||
#include "app_control.h"
|
||||
|
||||
CIsoMonitor::CIsoMonitor(sdv::IInterfaceAccess* pObject) :
|
||||
@@ -16,16 +28,16 @@ void CIsoMonitor::Initialize(/*in*/ const sdv::u8string& ssObjectConfig)
|
||||
if (m_pObjectControl)
|
||||
{
|
||||
m_pObjectControl->Initialize(ssObjectConfig);
|
||||
m_eObjectStatus = m_pObjectControl->GetStatus();
|
||||
m_eObjectState = m_pObjectControl->GetObjectState();
|
||||
}
|
||||
else
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialized;
|
||||
m_eObjectState = sdv::EObjectState::initialized;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CIsoMonitor::GetStatus() const
|
||||
sdv::EObjectState CIsoMonitor::GetObjectState() const
|
||||
{
|
||||
if (m_pObjectControl) return m_pObjectControl->GetStatus();
|
||||
return m_eObjectStatus;
|
||||
if (m_pObjectControl) return m_pObjectControl->GetObjectState();
|
||||
return m_eObjectState;
|
||||
}
|
||||
|
||||
void CIsoMonitor::SetOperationMode(/*in*/ sdv::EOperationMode eMode)
|
||||
@@ -33,15 +45,22 @@ void CIsoMonitor::SetOperationMode(/*in*/ sdv::EOperationMode eMode)
|
||||
if (m_pObjectControl) m_pObjectControl->SetOperationMode(eMode);
|
||||
}
|
||||
|
||||
sdv::u8string CIsoMonitor::GetObjectConfig() const
|
||||
{
|
||||
if (m_pObjectControl)
|
||||
return m_pObjectControl->GetObjectConfig();
|
||||
return {};
|
||||
}
|
||||
|
||||
void CIsoMonitor::Shutdown()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
m_eObjectState = sdv::EObjectState::shutdown_in_progress;
|
||||
if (m_pObjectControl)
|
||||
{
|
||||
m_pObjectControl->Shutdown();
|
||||
m_eObjectStatus = m_pObjectControl->GetStatus();
|
||||
m_eObjectState = m_pObjectControl->GetObjectState();
|
||||
}
|
||||
m_eObjectStatus = sdv::EObjectStatus::destruction_pending;
|
||||
m_eObjectState = sdv::EObjectState::destruction_pending;
|
||||
GetAppControl().RequestShutdown();
|
||||
m_pObjectControl = nullptr;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 ISOLATION_OBJECT_MONITOR_H
|
||||
#define ISOLATION_OBJECT_MONITOR_H
|
||||
|
||||
@@ -34,10 +47,10 @@ public:
|
||||
virtual void Initialize(/*in*/ const sdv::u8string& ssObjectConfig) override;
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
* @brief Get the current state of the object. Overload of sdv::IObjectControl::GetObjectState.
|
||||
* @return Return the current state of the object.
|
||||
*/
|
||||
virtual sdv::EObjectStatus GetStatus() const override;
|
||||
virtual sdv::EObjectState GetObjectState() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
@@ -45,12 +58,18 @@ public:
|
||||
*/
|
||||
virtual void SetOperationMode(/*in*/ sdv::EOperationMode eMode) override;
|
||||
|
||||
/**
|
||||
* @brief Get the object configuration for persistence.
|
||||
* @return The object configuration as TOML string.
|
||||
*/
|
||||
virtual sdv::u8string GetObjectConfig() const override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
* @attention Implement calls to other SDV objects here as this is no longer considered safe during the destructor of the object!
|
||||
* After a call to shutdown any threads/callbacks/etc that could call other SDV objects need to have been stopped.
|
||||
* The SDV object itself is to remain in a state where it can respond to calls to its interfaces as other objects may still call it during the shutdown sequence!
|
||||
* Any subsequent call to GetStatus should return EObjectStatus::destruction_pending
|
||||
* Any subsequent call to GetObjectState should return EObjectState::destruction_pending
|
||||
*/
|
||||
virtual void Shutdown() override;
|
||||
|
||||
@@ -63,7 +82,7 @@ public:
|
||||
private:
|
||||
sdv::TInterfaceAccessPtr m_ptrObject; ///< Smart pointer to the object.
|
||||
sdv::IObjectControl* m_pObjectControl = nullptr; ///< Pointer to the object control of the application
|
||||
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status (in case there is no object control).
|
||||
sdv::EObjectState m_eObjectState = sdv::EObjectState::initialization_pending; ///< Object status (in case there is no object control).
|
||||
};
|
||||
|
||||
#endif // !defined ISOLATION_OBJECT_MONITOR_H
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 LOCAL_SHUTDOWN_REQUEST_H
|
||||
#define LOCAL_SHUTDOWN_REQUEST_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "log_csv_writer.h"
|
||||
#include <sstream>
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 LOG_CSV_WRITER_H
|
||||
#define LOG_CSV_WRITER_H
|
||||
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
/********************************************************************************
|
||||
* 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 "logger.h"
|
||||
#include <sstream>
|
||||
#include "sdv_core.h"
|
||||
#include "../../global/exec_dir_helper.h"
|
||||
|
||||
#ifdef __unix__
|
||||
@@ -10,6 +22,12 @@
|
||||
#include <unistd.h> // for getpid
|
||||
#endif
|
||||
|
||||
CLogger& GetDefaultLogger()
|
||||
{
|
||||
static CLogger default_logger;
|
||||
return default_logger;
|
||||
}
|
||||
|
||||
CLogger::~CLogger()
|
||||
{
|
||||
#ifdef __unix__
|
||||
@@ -140,12 +158,3 @@ std::string CLogger::GetDateTime(std::chrono::time_point<std::chrono::system_clo
|
||||
output << std::put_time(std::localtime(&time), timeFormat.c_str());
|
||||
return output.str();
|
||||
}
|
||||
|
||||
#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST
|
||||
|
||||
CLogger& CLoggerService::GetDefaultLogger()
|
||||
{
|
||||
return ::GetDefaultLogger();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
@@ -95,7 +108,12 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST
|
||||
/**
|
||||
* @brief Return the default logger.
|
||||
* @attention Use the logger control to access the logger.
|
||||
* @return Reference to the default logger.
|
||||
*/
|
||||
CLogger& GetDefaultLogger();
|
||||
|
||||
/**
|
||||
* @brief Logger service
|
||||
@@ -111,18 +129,11 @@ public:
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("DefaultLoggerService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Get access to the repository.
|
||||
* @return Returns a reference to the one repository of this module.
|
||||
*/
|
||||
static CLogger& GetDefaultLogger();
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT_NO_EXPORT(CLoggerService)
|
||||
#endif
|
||||
DEFINE_SDV_OBJECT(CLoggerService)
|
||||
|
||||
#endif // !defined LOGGER_H
|
||||
@@ -1,9 +1,28 @@
|
||||
/********************************************************************************
|
||||
* 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 "logger_control.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <process.h> // Needed for _getpid
|
||||
#endif
|
||||
|
||||
CLoggerControl& GetLoggerControl()
|
||||
{
|
||||
static CLoggerControl logger_control;
|
||||
return logger_control;
|
||||
}
|
||||
|
||||
CLoggerControl::~CLoggerControl()
|
||||
{
|
||||
// Reset the logger. This will finialize an open log.
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 LOGGER_CONTROL_H
|
||||
#define LOGGER_CONTROL_H
|
||||
|
||||
@@ -66,4 +79,10 @@ private:
|
||||
sdv::core::ILogger* m_pLogger = nullptr; ///< Interface for the actual logger.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return the logger control.
|
||||
* @return Reference to the logger control.
|
||||
*/
|
||||
CLoggerControl& GetLoggerControl();
|
||||
|
||||
#endif // !defined LOGGER_CONTROL_H
|
||||
@@ -1,5 +1,24 @@
|
||||
/********************************************************************************
|
||||
* 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 "memory.h"
|
||||
|
||||
CMemoryManager& GetMemoryManager()
|
||||
{
|
||||
static CMemoryManager memory_mgr;
|
||||
return memory_mgr;
|
||||
}
|
||||
|
||||
sdv::pointer<uint8_t> CMemoryManager::Allocate(uint32_t uiLength)
|
||||
{
|
||||
return sdv::internal::make_ptr<uint8_t>(this, uiLength);
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 MEMORY_H
|
||||
#define MEMORY_H
|
||||
|
||||
@@ -54,4 +67,10 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return the memory manager.
|
||||
* @return Reference to the memory manager.
|
||||
*/
|
||||
CMemoryManager& GetMemoryManager();
|
||||
|
||||
#endif // !define MEMORY_H
|
||||
@@ -1,8 +1,23 @@
|
||||
/********************************************************************************
|
||||
* 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 "module.h"
|
||||
#include "sdv_core.h"
|
||||
#include <support/component_impl.h>
|
||||
#include <support/local_service_access.h>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include "toml_parser/parser_toml.h"
|
||||
#include "repository.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// Resolve conflict
|
||||
@@ -56,7 +71,7 @@ std::string CModuleInst::GetDefaultObjectName(const std::string& ssClassName) co
|
||||
{
|
||||
auto itClass = m_mapClassInfo.find(ssClassName);
|
||||
if (itClass == m_mapClassInfo.end()) return std::string();
|
||||
return itClass->second.ssDefaultObjectName.empty() ? itClass->second.ssClassName : itClass->second.ssDefaultObjectName;
|
||||
return itClass->second.ssDefaultObjectName.empty() ? itClass->second.ssName : itClass->second.ssDefaultObjectName;
|
||||
}
|
||||
|
||||
bool CModuleInst::IsSingleton(const std::string& ssClassName) const
|
||||
@@ -109,13 +124,13 @@ sdv::core::TModuleID CModuleInst::GetModuleID() const
|
||||
sdv::core::SModuleInfo CModuleInst::GetModuleInfo() const
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mtxModule);
|
||||
sdv::core::SModuleInfo sInfo{};
|
||||
sInfo.tModuleID = m_tModuleID;
|
||||
sInfo.ssPath = m_pathModule.filename().generic_u8string();
|
||||
sInfo.ssFilename = m_pathModule.filename().generic_u8string();
|
||||
sInfo.uiVersion = m_uiIfcVersion;
|
||||
sInfo.bActive = m_fnActiveObjects ? m_fnActiveObjects() : false;
|
||||
return sInfo;
|
||||
sdv::core::SModuleInfo sClass{};
|
||||
sClass.tModuleID = m_tModuleID;
|
||||
sClass.ssPath = m_pathModule.filename().generic_u8string();
|
||||
sClass.ssFilename = m_pathModule.filename().generic_u8string();
|
||||
sClass.uiVersion = m_uiIfcVersion;
|
||||
sClass.bActive = m_fnActiveObjects ? m_fnActiveObjects() : false;
|
||||
return sClass;
|
||||
}
|
||||
|
||||
std::optional<sdv::SClassInfo> CModuleInst::GetClassInfo(const std::string& rssClassName) const
|
||||
@@ -230,23 +245,23 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
|
||||
}
|
||||
|
||||
// Get available classes
|
||||
auto ptrComponents = parser.Root().Direct("Component");
|
||||
if (!ptrComponents || !ptrComponents->Cast<toml_parser::CArray>())
|
||||
auto ptrClasses = parser.Root().Direct("Class");
|
||||
if (!ptrClasses || !ptrClasses->Cast<toml_parser::CArray>())
|
||||
{
|
||||
SDV_LOG_ERROR("Error opening SDV module: ", rpathModule.generic_u8string(), " error: no components available");
|
||||
Unload(true);
|
||||
return false;
|
||||
}
|
||||
for (std::uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
|
||||
for (std::uint32_t uiIndex = 0; uiIndex < ptrClasses->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
|
||||
{
|
||||
// Fill in the class info.
|
||||
sdv::SClassInfo sInfo{};
|
||||
auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
|
||||
if (!ptrComponent) continue;
|
||||
auto ptrClassName = ptrComponent->Direct("Class");
|
||||
sdv::SClassInfo sClass{};
|
||||
auto ptrClass = ptrClasses->Cast<toml_parser::CArray>()->Get(uiIndex);
|
||||
if (!ptrClass) continue;
|
||||
auto ptrClassName = ptrClass->Direct("Name");
|
||||
if (!ptrClassName) continue;
|
||||
sInfo.ssClassName = static_cast<std::string>(ptrClassName->GetValue());
|
||||
auto ptrAliases = ptrComponent->Direct("Aliases");
|
||||
sClass.ssName = static_cast<std::string>(ptrClassName->GetValue());
|
||||
auto ptrAliases = ptrClass->Direct("Aliases");
|
||||
if (ptrAliases)
|
||||
{
|
||||
auto ptrAliasesArray = ptrAliases->Cast<toml_parser::CArray>();
|
||||
@@ -254,28 +269,21 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
|
||||
{
|
||||
auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex);
|
||||
if (ptrClassAlias)
|
||||
sInfo.seqClassAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue()));
|
||||
sClass.seqClassAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue()));
|
||||
}
|
||||
}
|
||||
auto ptrDefaultName = ptrComponent->Direct("DefaultName");
|
||||
if (ptrDefaultName) sInfo.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue());
|
||||
else sInfo.ssDefaultObjectName = sInfo.ssClassName;
|
||||
auto ptrType = ptrComponent->Direct("Type");
|
||||
auto ptrDefaultName = ptrClass->Direct("DefaultName");
|
||||
if (ptrDefaultName) sClass.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue());
|
||||
else sClass.ssDefaultObjectName = sClass.ssName;
|
||||
auto ptrType = ptrClass->Direct("Type");
|
||||
if (!ptrType) continue;
|
||||
std::string ssType = static_cast<std::string>(ptrType->GetValue());
|
||||
if (ssType == "System") sInfo.eType = sdv::EObjectType::SystemObject;
|
||||
else if (ssType == "Device") sInfo.eType = sdv::EObjectType::Device;
|
||||
else if (ssType == "BasicService") sInfo.eType = sdv::EObjectType::BasicService;
|
||||
else if (ssType == "ComplexService") sInfo.eType = sdv::EObjectType::ComplexService;
|
||||
else if (ssType == "App") sInfo.eType = sdv::EObjectType::Application;
|
||||
else if (ssType == "Proxy") sInfo.eType = sdv::EObjectType::Proxy;
|
||||
else if (ssType == "Stub") sInfo.eType = sdv::EObjectType::Stub;
|
||||
else if (ssType == "Utility") sInfo.eType = sdv::EObjectType::Utility;
|
||||
else continue;
|
||||
auto ptrSingleton = ptrComponent->Direct("Singleton");
|
||||
sClass.eType = sdv::String2ObjectType(ptrType->GetValue());
|
||||
if (sClass.eType == sdv::EObjectType::undefined)
|
||||
continue;
|
||||
auto ptrSingleton = ptrClass->Direct("Singleton");
|
||||
if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue()))
|
||||
sInfo.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
|
||||
auto ptrDependencies = ptrComponent->Direct("Dependencies");
|
||||
sClass.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
|
||||
auto ptrDependencies = ptrClass->Direct("Dependencies");
|
||||
if (ptrDependencies)
|
||||
{
|
||||
auto ptrDependencyArray = ptrDependencies->Cast<toml_parser::CArray>();
|
||||
@@ -284,11 +292,11 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
|
||||
{
|
||||
auto ptrDependsOn = ptrDependencyArray->Get(uiDependencyIndex);
|
||||
if (ptrDependsOn)
|
||||
sInfo.seqDependencies.push_back(static_cast<sdv::u8string>(ptrDependsOn->GetValue()));
|
||||
sClass.seqDependencies.push_back(static_cast<sdv::u8string>(ptrDependsOn->GetValue()));
|
||||
}
|
||||
}
|
||||
|
||||
m_mapClassInfo[sInfo.ssClassName] = sInfo;
|
||||
m_mapClassInfo[sClass.ssName] = sClass;
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException&)
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 MODULE_H
|
||||
#define MODULE_H
|
||||
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
/********************************************************************************
|
||||
* 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 "module_control.h"
|
||||
#include "sdv_core.h"
|
||||
#include <algorithm>
|
||||
#include "../../global/exec_dir_helper.h"
|
||||
#include "toml_parser/parser_toml.h"
|
||||
#include "toml_parser//parser_node_toml.h"
|
||||
#include "app_settings.h"
|
||||
#include "app_config.h"
|
||||
|
||||
/// @cond DOXYGEN_IGNORE
|
||||
#ifdef _WIN32
|
||||
@@ -16,8 +30,14 @@
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
CModuleControl::CModuleControl()
|
||||
{}
|
||||
// GetModuleControl might be redirected for unit tests.
|
||||
#ifndef GetModuleControl
|
||||
CModuleControl& GetModuleControl()
|
||||
{
|
||||
static CModuleControl module_control;
|
||||
return module_control;
|
||||
}
|
||||
#endif // !defined GetModuleControl
|
||||
|
||||
CModuleControl::~CModuleControl()
|
||||
{
|
||||
@@ -123,7 +143,7 @@ sdv::core::TModuleID CModuleControl::Load(const sdv::u8string& ssModulePath)
|
||||
pathModule = static_cast<std::string>(ssModulePath);
|
||||
else
|
||||
{
|
||||
if (GetAppControl().IsMainApplication() || GetAppControl().IsIsolatedApplication())
|
||||
if (GetAppSettings().IsMainApplication() || GetAppSettings().IsIsolatedApplication())
|
||||
{
|
||||
// Check the installation for the module.
|
||||
pathModule = GetAppConfig().FindInstalledModule(static_cast<std::string>(ssModulePath));
|
||||
@@ -180,7 +200,6 @@ sdv::core::TModuleID CModuleControl::Load(const sdv::u8string& ssModulePath)
|
||||
// Create a new instance (even if the module could not be found).
|
||||
std::shared_ptr<CModuleInst> ptrModule = std::make_shared<CModuleInst>(static_cast<std::string>(ssModulePath), pathModule);
|
||||
m_lstModules.push_back(ptrModule);
|
||||
m_setConfigModules.insert(ptrModule->GetModuleID());
|
||||
if (!ptrModule->IsValid())
|
||||
{
|
||||
SDV_LOG_ERROR("The module was not found \"", ssModulePath, "\"");
|
||||
@@ -234,12 +253,13 @@ std::shared_ptr<CModuleInst> CModuleControl::FindModuleByClass(const std::string
|
||||
// For main and isolated applications, check whether the module is in one of the installation manifests.
|
||||
auto optManifest = GetAppConfig().FindInstalledComponent(rssClass);
|
||||
if (!optManifest) return nullptr;
|
||||
auto ssManifest = GetAppConfig().FindInstalledModuleManifest(optManifest->pathRelModule);
|
||||
std::filesystem::path pathModule = std::filesystem::u8path(static_cast<std::string>(optManifest->ssModulePath));
|
||||
auto ssManifest = GetAppConfig().FindInstalledModuleManifest(pathModule);
|
||||
if (ssManifest.empty()) return nullptr;
|
||||
lock.unlock();
|
||||
|
||||
// Load the module
|
||||
return GetModule(ContextLoad(optManifest->pathRelModule, ssManifest));
|
||||
return GetModule(ContextLoad(pathModule, ssManifest));
|
||||
}
|
||||
|
||||
std::shared_ptr<CModuleInst> CModuleControl::GetModule(sdv::core::TModuleID tModuleID) const
|
||||
@@ -281,28 +301,84 @@ void CModuleControl::ResetConfigBaseline()
|
||||
m_setConfigModules.clear();
|
||||
}
|
||||
|
||||
std::string CModuleControl::SaveConfig(const std::set<std::filesystem::path>& rsetIgnoreModule)
|
||||
sdv::core::EConfigProcessResult CModuleControl::LoadModulesFromConfig(const CAppConfigFile& rconfig, bool bAllowPartialLoad)
|
||||
{
|
||||
// TODO EVE: Extract the parameters from the configuration and store them at the class info.
|
||||
|
||||
switch (GetAppSettings().GetContextType())
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
return sdv::core::EConfigProcessResult::successful; // Do not load anything
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// First load the modules in the module list
|
||||
auto vecModules = rconfig.GetModuleList();
|
||||
size_t nSuccess = 0, nFail = 0;
|
||||
for (const auto& rsModule : vecModules)
|
||||
{
|
||||
sdv::core::TModuleID tModuleID = Load(rsModule.pathModule.generic_u8string());
|
||||
if (tModuleID)
|
||||
++nSuccess;
|
||||
else
|
||||
++nFail;
|
||||
}
|
||||
|
||||
// Load all the modules from the class list
|
||||
auto vecClasses = rconfig.GetClassList();
|
||||
for (const auto& rsClass : vecClasses)
|
||||
{
|
||||
if (rsClass.ssModulePath.empty()) continue;
|
||||
sdv::core::TModuleID tModuleID = Load(rsClass.ssModulePath);
|
||||
if (tModuleID)
|
||||
++nSuccess;
|
||||
else
|
||||
++nFail;
|
||||
}
|
||||
|
||||
// Load all the modules from the component list
|
||||
auto vecComponents = rconfig.GetComponentList();
|
||||
for (const auto& rsComponent : vecComponents)
|
||||
{
|
||||
if (rsComponent.pathModule.empty()) continue;
|
||||
sdv::core::TModuleID tModuleID = Load(rsComponent.pathModule.generic_u8string());
|
||||
if (tModuleID)
|
||||
++nSuccess;
|
||||
else
|
||||
++nFail;
|
||||
}
|
||||
|
||||
if (!bAllowPartialLoad && nFail) return sdv::core::EConfigProcessResult::failed;
|
||||
if (!nFail) return sdv::core::EConfigProcessResult::successful;
|
||||
if (nSuccess) return sdv::core::EConfigProcessResult::partially_successful;
|
||||
return sdv::core::EConfigProcessResult::failed;
|
||||
}
|
||||
|
||||
std::string CModuleControl::SaveConfig(const std::set<std::filesystem::path>& /*rsetIgnoreModule*/)
|
||||
{
|
||||
std::stringstream sstream;
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mtxModules);
|
||||
//std::unique_lock<std::recursive_mutex> lock(m_mtxModules);
|
||||
|
||||
// Add all the loaded modules
|
||||
for (const std::shared_ptr<CModuleInst>& rptrModule : m_lstModules)
|
||||
{
|
||||
if (m_setConfigModules.find(rptrModule->GetModuleID()) != m_setConfigModules.end() &&
|
||||
rsetIgnoreModule.find(rptrModule->GetModuleConfigPath()) != rsetIgnoreModule.end())
|
||||
{
|
||||
sstream << std::endl;
|
||||
sstream << "[[Module]]" << std::endl;
|
||||
sstream << "Path = \"" << rptrModule->GetModuleConfigPath().generic_u8string() << "\"" << std::endl;
|
||||
}
|
||||
}
|
||||
//// Add all the loaded modules
|
||||
//for (const std::shared_ptr<CModuleInst>& rptrModule : m_lstModules)
|
||||
//{
|
||||
// if (m_setConfigModules.find(rptrModule->GetModuleID()) != m_setConfigModules.end() &&
|
||||
// rsetIgnoreModule.find(rptrModule->GetModuleConfigPath()) != rsetIgnoreModule.end())
|
||||
// {
|
||||
// sstream << std::endl;
|
||||
// sstream << "[[Module]]" << std::endl;
|
||||
// sstream << "Path = \"" << rptrModule->GetModuleConfigPath().generic_u8string() << "\"" << std::endl;
|
||||
// }
|
||||
//}
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
sdv::core::TModuleID CModuleControl::ContextLoad(const std::filesystem::path& rpathModule, const std::string& rssManifest)
|
||||
{
|
||||
if (GetAppControl().IsMaintenanceApplication()) return 0; // Not allowed
|
||||
if (GetAppSettings().IsMaintenanceApplication()) return 0; // Not allowed
|
||||
|
||||
// Run through the manifest and check for complex services, applications and utilities.
|
||||
// TODO EVE: Temporary suppression of cppcheck warning.
|
||||
@@ -357,7 +433,6 @@ bool CModuleControl::ContextUnload(sdv::core::TModuleID tModuleID, bool bForce)
|
||||
return ptrModule && ptrModule->GetModuleID() == tModuleID;
|
||||
});
|
||||
if (itModule == m_lstModules.end()) return true; // Cannot find the module to unload. This is not an error!
|
||||
m_setConfigModules.erase(tModuleID); // Remove from config if part of it.
|
||||
|
||||
// Check whether it is possible to unload.
|
||||
bool bSuccess = (*itModule)->Unload(bForce);
|
||||
@@ -395,17 +470,8 @@ void CModuleControl::AddCurrentPath()
|
||||
if (pathExeDir != pathCoreDir) m_lstSearchPaths.push_back(pathExeDir);
|
||||
}
|
||||
|
||||
#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST
|
||||
|
||||
CModuleControl& CModuleControlService::GetModuleControl()
|
||||
{
|
||||
return ::GetModuleControl();
|
||||
}
|
||||
|
||||
bool CModuleControlService::EnableModuleControlAccess()
|
||||
{
|
||||
return GetAppControl().IsStandaloneApplication() ||
|
||||
GetAppControl().IsEssentialApplication();
|
||||
return GetAppSettings().IsStandaloneApplication() ||
|
||||
GetAppSettings().IsEssentialApplication();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/**
|
||||
* @file module_control.h
|
||||
* @author Sudipta Babu Durjoy FRD DISDS1 (mailto:sudipta.durjoy@zf.com) & Erik Verhoeven FRD DISDS1 (mailto:erik.verhoeven@zf.com)
|
||||
* @brief This file contains the implementation for loading VAPI/SDV modules and accessing the exported functions related to
|
||||
* VAPI/SDV modules on Windows and Posix
|
||||
* @version 2.0
|
||||
* @date 2024-04-03
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* @copyright Copyright ZF Friedrichshafen AG (c) 2021-2025
|
||||
* 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 MODULE_CONTROL_H
|
||||
#define MODULE_CONTROL_H
|
||||
@@ -21,6 +22,7 @@
|
||||
#include <set>
|
||||
#include "toml_parser/parser_toml.h"
|
||||
#include "module.h"
|
||||
#include "app_config_file.h"
|
||||
|
||||
/**
|
||||
* @brief Module control class
|
||||
@@ -34,7 +36,7 @@ public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
CModuleControl();
|
||||
CModuleControl() = default;
|
||||
|
||||
/**
|
||||
* @brief No copy constructor
|
||||
@@ -153,6 +155,17 @@ public:
|
||||
*/
|
||||
void ResetConfigBaseline();
|
||||
|
||||
/**
|
||||
* @brief Load all modules from the configuration and build a component class list.
|
||||
* @attention This function doesn't do anything when the application is not running as a local application. In that case, the
|
||||
* function returns no failure.
|
||||
* @remarks Already loaded modules are not loaded again. This is not seen as a failure.
|
||||
* @param[in] rconfig Reference to the configuration file.
|
||||
* @param[in] bAllowPartialLoad When set, allow partial loading the configuration (one or more components).
|
||||
* @return Returns the load result.
|
||||
*/
|
||||
sdv::core::EConfigProcessResult LoadModulesFromConfig(const CAppConfigFile& rconfig, bool bAllowPartialLoad);
|
||||
|
||||
/**
|
||||
* @brief Save the configuration of all modules.
|
||||
* @param[in] rsetIgnoreModule Set of modules not needing to add.
|
||||
@@ -213,7 +226,11 @@ private:
|
||||
TConfigSet m_setConfigModules; ///< Set with the modules for storing in the configuration.
|
||||
};
|
||||
|
||||
#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST
|
||||
/**
|
||||
* @brief Return the module control.
|
||||
* @return Reference to the module control.
|
||||
*/
|
||||
CModuleControl& GetModuleControl();
|
||||
|
||||
/**
|
||||
* @brief Module control service
|
||||
@@ -234,16 +251,10 @@ public:
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("ModuleControlService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Get access to the module control
|
||||
* @return Returns the one global instance of the module control.
|
||||
*/
|
||||
static CModuleControl& GetModuleControl();
|
||||
|
||||
/**
|
||||
* @brief When set, the module control will be enabled.
|
||||
* @return Returns whether access to the module control is granted.
|
||||
@@ -251,9 +262,7 @@ public:
|
||||
static bool EnableModuleControlAccess();
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT_NO_EXPORT(CModuleControlService)
|
||||
#endif
|
||||
|
||||
DEFINE_SDV_OBJECT(CModuleControlService)
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __GNUC__
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "object_lifetime_control.h"
|
||||
#include "sdv_core.h"
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 OBJECT_LIFETIME_CONTROL_H
|
||||
#define OBJECT_LIFETIME_CONTROL_H
|
||||
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
/********************************************************************************
|
||||
* 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 "repository.h"
|
||||
#include "support/interface_ptr.h"
|
||||
#include "sdv_core.h"
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include "object_lifetime_control.h"
|
||||
#include "../../global/base64.h"
|
||||
#include "module_control.h"
|
||||
#include "app_config.h"
|
||||
#include "app_control.h"
|
||||
#include "app_settings.h"
|
||||
|
||||
/**
|
||||
* @brief Get the object type string
|
||||
* @param[in] eType The object type to get the string from.
|
||||
* @return The object type string.
|
||||
*/
|
||||
std::string GetObjectTypeString(sdv::EObjectType eType)
|
||||
// GetRepository might be redirected for unit tests.
|
||||
#ifndef GetRepository
|
||||
CRepository& GetRepository()
|
||||
{
|
||||
std::string ssType;
|
||||
switch (eType)
|
||||
{
|
||||
case sdv::EObjectType::SystemObject: ssType = "system object"; break;
|
||||
case sdv::EObjectType::Device: ssType = "device"; break;
|
||||
case sdv::EObjectType::BasicService: ssType = "basic_service"; break;
|
||||
case sdv::EObjectType::ComplexService: ssType = "complex_service"; break;
|
||||
case sdv::EObjectType::Application: ssType = "application"; break;
|
||||
case sdv::EObjectType::Proxy: ssType = "proxy"; break;
|
||||
case sdv::EObjectType::Stub: ssType = "stub"; break;
|
||||
case sdv::EObjectType::Utility: ssType = "utility"; break;
|
||||
default: ssType = "unknown"; break;
|
||||
}
|
||||
return ssType;
|
||||
static CRepository repository;
|
||||
return repository;
|
||||
}
|
||||
#endif // !defined GetRepository
|
||||
|
||||
void CRepository::SetConfigMode()
|
||||
{
|
||||
@@ -66,8 +68,8 @@ sdv::IInterfaceAccess* CRepository::GetObject(const sdv::u8string& ssObjectName)
|
||||
// In case the object is not in the service map and this is a main or isolated application, create the object if the object is
|
||||
// known in the installation manifest and is a system object.
|
||||
auto optManifest = GetAppConfig().FindInstalledComponent(ssObjectName);
|
||||
if (optManifest && optManifest->eType == sdv::EObjectType::SystemObject)
|
||||
return GetObjectByID(CreateObject(optManifest->ssClassName, optManifest->ssDefaultObjectName, ""));
|
||||
if (optManifest && optManifest->eType == sdv::EObjectType::system_object)
|
||||
return GetObjectByID(CreateObject(optManifest->ssName, optManifest->ssDefaultObjectName, ""));
|
||||
|
||||
// Forward the request to core repository if one is linked here (this can only occur with isolated and external applications).
|
||||
if (!m_ptrCoreRepoAccess) return nullptr;
|
||||
@@ -114,10 +116,10 @@ sdv::IInterfaceAccess* CRepository::CreateUtility(/*in*/ const sdv::u8string& ss
|
||||
SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!");
|
||||
return nullptr;
|
||||
}
|
||||
if (optClassInfo->eType != sdv::EObjectType::Utility)
|
||||
if (optClassInfo->eType != sdv::EObjectType::utility)
|
||||
{
|
||||
// Utilities and marshall objects cannot be created using the CreateObject function.
|
||||
SDV_LOG_ERROR("Creation of an utility of invalid type \"", GetObjectTypeString(optClassInfo->eType),
|
||||
SDV_LOG_ERROR("Creation of an utility of invalid type \"", sdv::ObjectType2String(optClassInfo->eType),
|
||||
"\" requested for class \"", ssClassName, "\"!");
|
||||
return nullptr;
|
||||
}
|
||||
@@ -147,7 +149,7 @@ sdv::IInterfaceAccess* CRepository::CreateUtility(/*in*/ const sdv::u8string& ss
|
||||
lock.unlock();
|
||||
|
||||
// Print info
|
||||
if (GetAppControl().IsConsoleVerbose())
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Creating a utility #" << ptrObjectEntry->tObjectID << " of type " << ptrObjectEntry->ssName << std::endl;
|
||||
|
||||
// Create the object
|
||||
@@ -160,7 +162,7 @@ sdv::IInterfaceAccess* CRepository::CreateUtility(/*in*/ const sdv::u8string& ss
|
||||
if (pObjectControl)
|
||||
{
|
||||
pObjectControl->Initialize(ssObjectConfig);
|
||||
if (pObjectControl->GetStatus() != sdv::EObjectStatus::running)
|
||||
if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized)
|
||||
{
|
||||
// Destroy the object
|
||||
ptrModule->DestroyObject(ptrObject);
|
||||
@@ -222,10 +224,10 @@ sdv::IInterfaceAccess* CRepository::CreateProxyObject(/*in*/ sdv::interface_id i
|
||||
SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!");
|
||||
return nullptr;
|
||||
}
|
||||
if (optClassInfo->eType != sdv::EObjectType::Proxy)
|
||||
if (optClassInfo->eType != sdv::EObjectType::proxy)
|
||||
{
|
||||
// Utilities and marshall objects cannot be created using the CreateObject function.
|
||||
SDV_LOG_ERROR("Creation of an object of invalid type \"", GetObjectTypeString(optClassInfo->eType),
|
||||
SDV_LOG_ERROR("Creation of an object of invalid type \"", sdv::ObjectType2String(optClassInfo->eType),
|
||||
"\" requested for class \"", ssClassName, "\"!");
|
||||
return nullptr;
|
||||
}
|
||||
@@ -255,7 +257,7 @@ sdv::IInterfaceAccess* CRepository::CreateProxyObject(/*in*/ sdv::interface_id i
|
||||
lock.unlock();
|
||||
|
||||
// Print info
|
||||
if (GetAppControl().IsConsoleVerbose())
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Creating a proxy object #" << ptrObjectEntry->tObjectID << " of type " <<
|
||||
ptrObjectEntry->ssName << std::endl;
|
||||
|
||||
@@ -269,7 +271,7 @@ sdv::IInterfaceAccess* CRepository::CreateProxyObject(/*in*/ sdv::interface_id i
|
||||
if (pObjectControl)
|
||||
{
|
||||
pObjectControl->Initialize("");
|
||||
if (pObjectControl->GetStatus() != sdv::EObjectStatus::running)
|
||||
if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized)
|
||||
{
|
||||
// Destroy the object
|
||||
ptrModule->DestroyObject(ptrObject);
|
||||
@@ -331,10 +333,10 @@ sdv::IInterfaceAccess* CRepository::CreateStubObject(/*in*/ sdv::interface_id id
|
||||
SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!");
|
||||
return nullptr;
|
||||
}
|
||||
if (optClassInfo->eType != sdv::EObjectType::Stub)
|
||||
if (optClassInfo->eType != sdv::EObjectType::stub)
|
||||
{
|
||||
// Utilities and marshall objects cannot be created using the CreateObject function.
|
||||
SDV_LOG_ERROR("Creation of an object of invalid type \"", GetObjectTypeString(optClassInfo->eType),
|
||||
SDV_LOG_ERROR("Creation of an object of invalid type \"", sdv::ObjectType2String(optClassInfo->eType),
|
||||
"\" requested for class \"", ssClassName, "\"!");
|
||||
return nullptr;
|
||||
}
|
||||
@@ -364,7 +366,7 @@ sdv::IInterfaceAccess* CRepository::CreateStubObject(/*in*/ sdv::interface_id id
|
||||
lock.unlock();
|
||||
|
||||
// Print info
|
||||
if (GetAppControl().IsConsoleVerbose())
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Creating a stub #" << ptrObjectEntry->tObjectID << " of type " << ptrObjectEntry->ssName << std::endl;
|
||||
|
||||
// Create the object
|
||||
@@ -377,7 +379,7 @@ sdv::IInterfaceAccess* CRepository::CreateStubObject(/*in*/ sdv::interface_id id
|
||||
if (pObjectControl)
|
||||
{
|
||||
pObjectControl->Initialize("");
|
||||
if (pObjectControl->GetStatus() != sdv::EObjectStatus::running)
|
||||
if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized)
|
||||
{
|
||||
// Destroy the object
|
||||
ptrModule->DestroyObject(ptrObject);
|
||||
@@ -436,6 +438,9 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Request the parameters from the class info and create a config with them. Then add the parameters from the supplied
|
||||
// config and overwrite existing and extend non-existing parameters. Then create a new config string.
|
||||
|
||||
// Check the class type
|
||||
auto optClassInfo = ptrModule->GetClassInfo(ssClassName);
|
||||
if (!optClassInfo)
|
||||
@@ -445,22 +450,27 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName
|
||||
}
|
||||
bool bError = false; // Creation not allowed
|
||||
bool bIsolate = false; // Creation should be isolated
|
||||
bool bDeviceAndBasicServiceAllowed = GetAppControl().IsMainApplication() || GetAppControl().IsStandaloneApplication() ||
|
||||
GetAppControl().IsEssentialApplication();
|
||||
bool bComplexServiceAllowed = !GetAppControl().IsMaintenanceApplication() &&
|
||||
((!GetAppControl().IsIsolatedApplication() && !GetAppControl().IsExternalApplication()) || !m_bIsoObjectLoaded);
|
||||
bool bDeviceAndBasicServiceAllowed = GetAppSettings().IsMainApplication() || GetAppSettings().IsStandaloneApplication() ||
|
||||
GetAppSettings().IsEssentialApplication();
|
||||
bool bComplexServiceAllowed = !GetAppSettings().IsMaintenanceApplication() &&
|
||||
((!GetAppSettings().IsIsolatedApplication() && !GetAppSettings().IsExternalApplication()) || !m_bIsoObjectLoaded);
|
||||
switch (optClassInfo->eType)
|
||||
{
|
||||
case sdv::EObjectType::SystemObject:
|
||||
case sdv::EObjectType::system_object:
|
||||
break;
|
||||
case sdv::EObjectType::Device:
|
||||
case sdv::EObjectType::device:
|
||||
case sdv::EObjectType::platform_abstraction:
|
||||
case sdv::EObjectType::vehicle_bus:
|
||||
bError = !bDeviceAndBasicServiceAllowed;
|
||||
break;
|
||||
case sdv::EObjectType::BasicService:
|
||||
case sdv::EObjectType::basic_service:
|
||||
case sdv::EObjectType::sensor:
|
||||
case sdv::EObjectType::actuator:
|
||||
bError = !bDeviceAndBasicServiceAllowed;
|
||||
break;
|
||||
case sdv::EObjectType::ComplexService:
|
||||
bIsolate = GetAppControl().IsMainApplication();
|
||||
case sdv::EObjectType::complex_service:
|
||||
case sdv::EObjectType::vehicle_function:
|
||||
bIsolate = GetAppSettings().IsMainApplication();
|
||||
bError = !bComplexServiceAllowed;
|
||||
m_bIsoObjectLoaded = true;
|
||||
break;
|
||||
@@ -471,17 +481,69 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName
|
||||
if (bError)
|
||||
{
|
||||
// Utilities and marshall objects cannot be created using the CreateObject function.
|
||||
SDV_LOG_ERROR("Creation of an object of invalid/unallowed type \"", GetObjectTypeString(optClassInfo->eType),
|
||||
SDV_LOG_ERROR("Creation of an object of invalid/unallowed type \"", sdv::ObjectType2String(optClassInfo->eType),
|
||||
"\" requested for class \"", ssClassName, "\"!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check for an object name. If not existing get the default name (being either one specified by the object or the class name).
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
std::string ssObjectName2 = ssObjectName;
|
||||
if (ssObjectName2.empty())
|
||||
ssObjectName2 = optClassInfo->ssDefaultObjectName;
|
||||
if (ssObjectName2.empty())
|
||||
ssObjectName2 = ssClassName;
|
||||
|
||||
// Check for an object with the same name.
|
||||
auto itPreviousService = m_mapServiceObjects.find(ssObjectName2);
|
||||
if (itPreviousService != m_mapServiceObjects.end())
|
||||
{
|
||||
// Object entry is valid?
|
||||
if (!*itPreviousService->second)
|
||||
{
|
||||
// This should not occur... there is a previous object in the service map, but the object is empty.
|
||||
SDV_LOG_ERROR("Object creation requested for class \"", ssClassName, "\", but object with the same name \"",
|
||||
ssObjectName2, "\" was already instantiated, but cannot be found!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Trying to create an object with the same name is not an error if the classes are identical.
|
||||
if ((*itPreviousService->second)->sClassInfo.ssName == ssClassName)
|
||||
return (*itPreviousService->second)->tObjectID;
|
||||
|
||||
// Object name was already used by another class. This is an error.
|
||||
SDV_LOG_ERROR("Object creation requested for class \"", ssClassName, "\", but object with the same name \"",
|
||||
ssObjectName2, "\" was already instantiated for class \"", (*itPreviousService->second)->sClassInfo.ssName,
|
||||
"\"!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check with singleton objects if the object was already instantiated.
|
||||
if (optClassInfo->uiFlags & static_cast<uint32_t>(sdv::EObjectFlags::singleton))
|
||||
{
|
||||
for (const auto& rprService : m_mapServiceObjects)
|
||||
{
|
||||
if (!*rprService.second)
|
||||
continue;
|
||||
if ((*rprService.second)->ptrModule && (*rprService.second)->ptrModule->GetModuleID() == ptrModule->GetModuleID()
|
||||
&& (*rprService.second)->sClassInfo.ssName == ssClassName)
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation requested but object from the same class \"", ssClassName,
|
||||
"\" was already instantiated and only one instance is allowed!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock; the next function will lock again.
|
||||
lock.unlock();
|
||||
|
||||
// Create an isolated object.
|
||||
if (bIsolate)
|
||||
return CreateIsolatedObject(*optClassInfo, ssObjectName, ssObjectConfig);
|
||||
return CreateIsolatedObject(*optClassInfo, ssObjectName2, ssObjectConfig);
|
||||
|
||||
// Create an internal object.
|
||||
return InternalCreateObject(ptrModule, *optClassInfo, ssObjectName, ssObjectConfig);
|
||||
return InternalCreateObject(ptrModule, *optClassInfo, ssObjectName2, ssObjectConfig);
|
||||
}
|
||||
|
||||
sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModuleID tModuleID,
|
||||
@@ -505,25 +567,30 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu
|
||||
return false;
|
||||
}
|
||||
bool bError = false;
|
||||
bool bDeviceAndBasicServiceAllowed = GetAppControl().IsMainApplication() || GetAppControl().IsStandaloneApplication() ||
|
||||
GetAppControl().IsEssentialApplication();
|
||||
bool bComplexServiceAllowed = !GetAppControl().IsMaintenanceApplication() &&
|
||||
((!GetAppControl().IsIsolatedApplication() && !GetAppControl().IsExternalApplication()) || !m_bIsoObjectLoaded);
|
||||
bool bDeviceAndBasicServiceAllowed = GetAppSettings().IsMainApplication() || GetAppSettings().IsStandaloneApplication() ||
|
||||
GetAppSettings().IsEssentialApplication();
|
||||
bool bComplexServiceAllowed = !GetAppSettings().IsMaintenanceApplication() &&
|
||||
((!GetAppSettings().IsIsolatedApplication() && !GetAppSettings().IsExternalApplication()) || !m_bIsoObjectLoaded);
|
||||
switch (optClassInfo->eType)
|
||||
{
|
||||
case sdv::EObjectType::SystemObject:
|
||||
case sdv::EObjectType::system_object:
|
||||
break;
|
||||
case sdv::EObjectType::Device:
|
||||
case sdv::EObjectType::device:
|
||||
case sdv::EObjectType::platform_abstraction:
|
||||
case sdv::EObjectType::vehicle_bus:
|
||||
// Allowed?
|
||||
bError = !bDeviceAndBasicServiceAllowed;
|
||||
break;
|
||||
case sdv::EObjectType::BasicService:
|
||||
case sdv::EObjectType::basic_service:
|
||||
case sdv::EObjectType::sensor:
|
||||
case sdv::EObjectType::actuator:
|
||||
// Allowed?
|
||||
bError = !bDeviceAndBasicServiceAllowed;
|
||||
break;
|
||||
case sdv::EObjectType::ComplexService:
|
||||
case sdv::EObjectType::complex_service:
|
||||
case sdv::EObjectType::vehicle_function:
|
||||
// Isolation (which is needed for the main application) is not supported for direct creation.
|
||||
bError = !bComplexServiceAllowed || GetAppControl().IsMainApplication();
|
||||
bError = !bComplexServiceAllowed || GetAppSettings().IsMainApplication();
|
||||
m_bIsoObjectLoaded = true;
|
||||
break;
|
||||
default:
|
||||
@@ -533,13 +600,73 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu
|
||||
if (bError)
|
||||
{
|
||||
// Utilities and marshall objects cannot be created using the CreateObject function.
|
||||
SDV_LOG_ERROR("Creation of an object of invalid type \"", GetObjectTypeString(optClassInfo->eType),
|
||||
SDV_LOG_ERROR("Creation of an object of invalid type \"", sdv::ObjectType2String(optClassInfo->eType),
|
||||
"\" requested for class \"", ssClassName, "\"!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check for an object name. If not existing get the default name (being either one specified by the object or the class name).
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
std::string ssObjectName2 = ssObjectName;
|
||||
if (ssObjectName2.empty())
|
||||
ssObjectName2 = optClassInfo->ssDefaultObjectName;
|
||||
if (ssObjectName2.empty())
|
||||
ssObjectName2 = ssClassName;
|
||||
|
||||
// Check for an object with the same name.
|
||||
auto itPreviousService = m_mapServiceObjects.find(ssObjectName2);
|
||||
if (itPreviousService != m_mapServiceObjects.end())
|
||||
{
|
||||
// Object entry is valid?
|
||||
if (!*itPreviousService->second)
|
||||
{
|
||||
// This should not occur... there is a previous object in the service map, but the object is empty.
|
||||
SDV_LOG_ERROR("Object creation requested for class \"",
|
||||
ssClassName,
|
||||
"\", but object with the same name \"",
|
||||
ssObjectName2,
|
||||
"\" was already instantiated, but cannot be found!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Trying to create an object with the same name is not an error if the classes are identical.
|
||||
if ((*itPreviousService->second)->sClassInfo.ssName == ssClassName)
|
||||
return (*itPreviousService->second)->tObjectID;
|
||||
|
||||
// Object name was already used by another class. This is an error.
|
||||
SDV_LOG_ERROR("Object creation requested for class \"",
|
||||
ssClassName,
|
||||
"\", but object with the same name \"",
|
||||
ssObjectName2,
|
||||
"\" was already instantiated for class \"",
|
||||
(*itPreviousService->second)->sClassInfo.ssName,
|
||||
"\"!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check with singleton objects if the object was already instantiated.
|
||||
if (optClassInfo->uiFlags & static_cast<uint32_t>(sdv::EObjectFlags::singleton))
|
||||
{
|
||||
for (const auto& rprService : m_mapServiceObjects)
|
||||
{
|
||||
if (!*rprService.second)
|
||||
continue;
|
||||
if ((*rprService.second)->ptrModule && (*rprService.second)->ptrModule->GetModuleID() == ptrModule->GetModuleID()
|
||||
&& (*rprService.second)->sClassInfo.ssName == ssClassName)
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation requested but object from the same class \"",
|
||||
ssClassName,
|
||||
"\" was already instantiated and only one instance is allowed!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock; the next function will lock again.
|
||||
lock.unlock();
|
||||
|
||||
// Create an internal object.
|
||||
return InternalCreateObject(ptrModule, *optClassInfo, ssObjectName, ssObjectConfig);
|
||||
return InternalCreateObject(ptrModule, *optClassInfo, ssObjectName2, ssObjectConfig);
|
||||
}
|
||||
|
||||
bool CRepository::DestroyObject(/*in*/ const sdv::u8string& ssObjectName)
|
||||
@@ -555,9 +682,9 @@ bool CRepository::DestroyObject2(/*in*/ const sdv::u8string& ssObjectName)
|
||||
|
||||
// Print info
|
||||
auto ptrObjectEntry = *itService->second;
|
||||
if (GetAppControl().IsConsoleVerbose())
|
||||
std::cout << "Destroy a " << GetObjectTypeString(ptrObjectEntry->sClassInfo.eType) << " #" << ptrObjectEntry->tObjectID <<
|
||||
" of type " << ptrObjectEntry->sClassInfo.ssClassName << " with the name " << ssObjectName << std::endl;
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Destroy a " << sdv::ObjectType2String(ptrObjectEntry->sClassInfo.eType) << " #" << ptrObjectEntry->tObjectID <<
|
||||
" of type " << ptrObjectEntry->sClassInfo.ssName << " with the name " << ssObjectName << std::endl;
|
||||
|
||||
// Remove the service
|
||||
m_lstOrderedServiceObjects.erase(itService->second);
|
||||
@@ -569,7 +696,6 @@ bool CRepository::DestroyObject2(/*in*/ const sdv::u8string& ssObjectName)
|
||||
// Remove the object from the list and map.
|
||||
if (!ptrObjectEntry) return false; // Failure: should not happen!
|
||||
m_mapObjects.erase(ptrObjectEntry->tObjectID);
|
||||
m_setConfigObjects.erase(ptrObjectEntry->tObjectID); // Erase from config if part of config.
|
||||
lock.unlock();
|
||||
|
||||
// Try to destroy objects that depend on this object first
|
||||
@@ -594,13 +720,13 @@ bool CRepository::DestroyObject2(/*in*/ const sdv::u8string& ssObjectName)
|
||||
if (ptrDependingObject->bControlled) DestroyObject(ptrDependingObject->ssName);
|
||||
}
|
||||
};
|
||||
fnDestroyDependingObjects(GetDependingObjectInstancesByClass(ptrObjectEntry->sClassInfo.ssClassName));
|
||||
if (ptrObjectEntry->sClassInfo.ssClassName != ptrObjectEntry->sClassInfo.ssDefaultObjectName)
|
||||
fnDestroyDependingObjects(GetDependingObjectInstancesByClass(ptrObjectEntry->sClassInfo.ssName));
|
||||
if (ptrObjectEntry->sClassInfo.ssName != ptrObjectEntry->sClassInfo.ssDefaultObjectName)
|
||||
fnDestroyDependingObjects(GetDependingObjectInstancesByClass(ptrObjectEntry->sClassInfo.ssDefaultObjectName));
|
||||
|
||||
// Shutdown the object (not all objects expose IObjectControl).
|
||||
auto* pObjectControl = ptrObjectEntry->ptrObject.GetInterface<sdv::IObjectControl>();
|
||||
if (pObjectControl && pObjectControl->GetStatus() != sdv::EObjectStatus::destruction_pending)
|
||||
if (pObjectControl && pObjectControl->GetObjectState() != sdv::EObjectState::destruction_pending)
|
||||
pObjectControl->Shutdown();
|
||||
|
||||
// Destroy the object (not all objects have a module and some are wrapped by an isolation monitor).
|
||||
@@ -646,7 +772,7 @@ sdv::core::TObjectID CRepository::RegisterObject(IInterfaceAccess* pObjectIfc, c
|
||||
lock.unlock();
|
||||
if (!ptrIsolatedObject) return 0; // Already deleted
|
||||
if (ptrIsolatedObject->ptrObject) return 0; // only one time registration allowed.
|
||||
if (GetAppControl().IsConsoleVerbose())
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Registering the isolated object #" << ptrIsolatedObject->tObjectID << " with the name " << ssObjectName << std::endl;
|
||||
ptrIsolatedObject->ptrObject = pObjectIfc;
|
||||
std::unique_lock<std::mutex> lockObject(ptrIsolatedObject->mtxConnect);
|
||||
@@ -673,7 +799,7 @@ sdv::core::TObjectID CRepository::RegisterObject(IInterfaceAccess* pObjectIfc, c
|
||||
ptrObjectEntry->bControlled = true;
|
||||
|
||||
// Print info
|
||||
if (GetAppControl().IsConsoleVerbose())
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Registering a application object #" << tObjectID << " with the name " << ssObjectName << std::endl;
|
||||
|
||||
return tObjectID;
|
||||
@@ -695,19 +821,11 @@ sdv::SClassInfo CRepository::FindClass(/*in*/ const sdv::u8string& ssClassName)
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
|
||||
// For main and isolated applications, search in the installation.
|
||||
if (GetAppControl().IsMainApplication() || GetAppControl().IsIsolatedApplication())
|
||||
if (GetAppSettings().IsMainApplication() || GetAppSettings().IsIsolatedApplication())
|
||||
{
|
||||
auto optManifest = GetAppConfig().FindInstalledComponent(ssClassName);
|
||||
if (!optManifest) return {};
|
||||
sdv::SClassInfo sClassInfo{};
|
||||
sClassInfo.ssModulePath = optManifest->pathRelModule.generic_u8string();
|
||||
sClassInfo.ssClassName = optManifest->ssClassName;
|
||||
sClassInfo.seqClassAliases = optManifest->seqAliases;
|
||||
sClassInfo.ssDefaultObjectName = optManifest->ssDefaultObjectName;
|
||||
sClassInfo.eType = optManifest->eType;
|
||||
sClassInfo.uiFlags = optManifest->uiFlags;
|
||||
sClassInfo.seqDependencies = optManifest->seqDependencies;
|
||||
return sClassInfo;
|
||||
return *optManifest;
|
||||
}
|
||||
|
||||
// Get the information through the module.
|
||||
@@ -833,7 +951,7 @@ void CRepository::OnDestroyObject(sdv::IInterfaceAccess* pObject)
|
||||
|
||||
// Shutdown the object
|
||||
auto* pObjectControl = ptrObjectEntry->ptrObject.GetInterface<sdv::IObjectControl>();
|
||||
if (pObjectControl && pObjectControl->GetStatus() != sdv::EObjectStatus::destruction_pending)
|
||||
if (pObjectControl && pObjectControl->GetObjectState() != sdv::EObjectState::destruction_pending)
|
||||
pObjectControl->Shutdown();
|
||||
|
||||
// Destroy the object
|
||||
@@ -864,7 +982,7 @@ void CRepository::DestroyModuleObjects(sdv::core::TModuleID tModuleID)
|
||||
|
||||
// Shutdown the object
|
||||
auto* pObjectControl = ptrObjectEntry->ptrObject.GetInterface<sdv::IObjectControl>();
|
||||
if (pObjectControl && pObjectControl->GetStatus() != sdv::EObjectStatus::destruction_pending)
|
||||
if (pObjectControl && pObjectControl->GetObjectState() != sdv::EObjectState::destruction_pending)
|
||||
pObjectControl->Shutdown();
|
||||
|
||||
// Destroy the object
|
||||
@@ -924,34 +1042,114 @@ void CRepository::DestroyAllObjects(const std::vector<std::string>& rvecIgnoreOb
|
||||
|
||||
void CRepository::ResetConfigBaseline()
|
||||
{
|
||||
// Reset the configuration set.
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
m_setConfigObjects.clear();
|
||||
for (const auto& rvtObject : m_mapObjects)
|
||||
m_setConfigObjects.insert(rvtObject.first);
|
||||
lock.unlock();
|
||||
|
||||
GetModuleControl().ResetConfigBaseline();
|
||||
}
|
||||
|
||||
sdv::core::EConfigProcessResult CRepository::StartFromConfig(const CAppConfigFile& rconfig, bool bAllowPartialLoad)
|
||||
{
|
||||
// First load the modules
|
||||
sdv::core::EConfigProcessResult eResult = GetModuleControl().LoadModulesFromConfig(rconfig, bAllowPartialLoad);
|
||||
if (eResult == sdv::core::EConfigProcessResult::failed) return eResult;
|
||||
|
||||
bool bRunsAsServer = false;
|
||||
switch (GetAppSettings().GetContextType())
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
bRunsAsServer = true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Load all the components from the component list
|
||||
auto vecComponents = rconfig.GetComponentList();
|
||||
size_t nSuccess = 0, nFail = 0;
|
||||
std::vector<std::string> vecLoadedObjects;
|
||||
for (const auto& rsComponent : vecComponents)
|
||||
{
|
||||
// If there a path is stored with the component and not running a server, load and get the module ID and then start a
|
||||
// specific component. If there is no path stored, use the default object creation function.
|
||||
sdv::core::TObjectID tObjectID = 0;
|
||||
if (!bRunsAsServer && !rsComponent.pathModule.empty())
|
||||
{
|
||||
sdv::core::TModuleID tModuleID = GetModuleControl().Load(rsComponent.pathModule.generic_u8string());
|
||||
if (tModuleID)
|
||||
tObjectID = CreateObjectFromModule(tModuleID, rsComponent.ssClassName, rsComponent.ssInstanceName, rsComponent.ssParameterTOML);
|
||||
} else
|
||||
tObjectID = CreateObject(rsComponent.ssClassName, rsComponent.ssInstanceName, rsComponent.ssParameterTOML);
|
||||
|
||||
if (tObjectID)
|
||||
{
|
||||
++nSuccess;
|
||||
// Request the object name. This name is unique, but might be assigned automatically.
|
||||
vecLoadedObjects.push_back(GetObjectInfo(tObjectID).ssObjectName);
|
||||
} else
|
||||
++nFail;
|
||||
if (!bAllowPartialLoad && nFail)
|
||||
break;
|
||||
}
|
||||
|
||||
// Destroy the objects when an error occurred during loading.
|
||||
if (!bAllowPartialLoad && nFail)
|
||||
{
|
||||
for (const std::string& rssName : vecLoadedObjects)
|
||||
DestroyObject(rssName);
|
||||
return sdv::core::EConfigProcessResult::failed;
|
||||
}
|
||||
|
||||
// TODO: The class list contains the parameters to set for each component.
|
||||
//// Load the class information from the class list
|
||||
// auto vecClasses = rconfig.GetClassList();
|
||||
// for (const auto& rsClass : vecClasses)
|
||||
//{
|
||||
|
||||
// if (rsClass.ssModulePath.empty())
|
||||
// continue;
|
||||
// sdv::core::TModuleID tModuleID = Load(std::filesystem::u8path(rsClass.ssModulePath));
|
||||
// if (tModuleID)
|
||||
// ++nSuccess;
|
||||
// else
|
||||
// ++nFail;
|
||||
//}
|
||||
|
||||
//if (!bAllowPartialLoad && nFail)
|
||||
// return sdv::core::EConfigProcessResult::failed;
|
||||
if (!nFail)
|
||||
return sdv::core::EConfigProcessResult::successful;
|
||||
if (nSuccess)
|
||||
return sdv::core::EConfigProcessResult::partially_successful;
|
||||
return sdv::core::EConfigProcessResult::failed;
|
||||
}
|
||||
|
||||
std::string CRepository::SaveConfig()
|
||||
{
|
||||
std::stringstream sstream;
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
std::set<std::filesystem::path> setModules;
|
||||
for (const std::shared_ptr<SObjectEntry>& rptrObject : m_lstOrderedServiceObjects)
|
||||
{
|
||||
if (m_setConfigObjects.find(rptrObject->tObjectID) != m_setConfigObjects.end())
|
||||
{
|
||||
sstream << std::endl;
|
||||
sstream << "[[Component]]" << std::endl;
|
||||
sstream << "Path = \"" << rptrObject->ptrModule->GetModulePath().generic_u8string() << "\"" << std::endl;
|
||||
setModules.insert(rptrObject->ptrModule->GetModulePath());
|
||||
sstream << "Class = \"" << rptrObject->sClassInfo.ssClassName << "\"" << std::endl;
|
||||
sstream << "Name = \"" << rptrObject->ssName << "\"" << std::endl;
|
||||
// TODO: attributes...
|
||||
}
|
||||
}
|
||||
//std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
//std::set<std::filesystem::path> setModules;
|
||||
//for (const std::shared_ptr<SObjectEntry>& rptrObject : m_lstOrderedServiceObjects)
|
||||
//{
|
||||
// if (m_setConfigObjects.find(rptrObject->tObjectID) != m_setConfigObjects.end())
|
||||
// {
|
||||
// sstream << std::endl;
|
||||
// sstream << "[[Component]]" << std::endl;
|
||||
// sstream << "Path = \"" << rptrObject->ptrModule->GetModulePath().generic_u8string() << "\"" << std::endl;
|
||||
// setModules.insert(rptrObject->ptrModule->GetModulePath());
|
||||
// sstream << "Class = \"" << rptrObject->sClassInfo.ssName << "\"" << std::endl;
|
||||
// sstream << "Name = \"" << rptrObject->ssName << "\"" << std::endl;
|
||||
// // TODO: attributes...
|
||||
// }
|
||||
//}
|
||||
|
||||
// Add the modules
|
||||
sstream << GetModuleControl().SaveConfig(setModules);
|
||||
//// Add the modules
|
||||
//sstream << GetModuleControl().SaveConfig(setModules);
|
||||
|
||||
return sstream.str();
|
||||
}
|
||||
@@ -960,10 +1158,27 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
|
||||
const sdv::u8string& rssObjectConfig)
|
||||
{
|
||||
// Check whether running as main application.
|
||||
if (!GetAppControl().IsMainApplication()) return 0;
|
||||
if (!GetAppSettings().IsMainApplication()) return 0;
|
||||
|
||||
// Check the object type
|
||||
if (rsClassInfo.eType != sdv::EObjectType::ComplexService && rsClassInfo.eType != sdv::EObjectType::Utility) return 0;
|
||||
if (rssObjectName.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation requested for class \"", rsClassInfo.ssName, "\", but no object name was provided!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check the object type and define whether this should be a controlled object
|
||||
bool bControlled = false;
|
||||
switch (rsClassInfo.eType)
|
||||
{
|
||||
case sdv::EObjectType::complex_service:
|
||||
case sdv::EObjectType::vehicle_function:
|
||||
bControlled = true;
|
||||
break;
|
||||
case sdv::EObjectType::utility:
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create server connection (to the repository service object... not to this class!).
|
||||
sdv::com::IConnectionControl* pConnectionControl =
|
||||
@@ -979,7 +1194,7 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
|
||||
// Create the isolation process configuration
|
||||
std::stringstream sstreamConfig;
|
||||
sstreamConfig << "[Isolation]" << std::endl;
|
||||
sstreamConfig << "Class = \"" << rsClassInfo.ssClassName << "\"" << std::endl;
|
||||
sstreamConfig << "Class = \"" << rsClassInfo.ssName << "\"" << std::endl;
|
||||
sstreamConfig << "Object = \"" << rssObjectName << "\"" << std::endl << std::endl;
|
||||
if (!rssObjectConfig.empty())
|
||||
{
|
||||
@@ -992,12 +1207,12 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
|
||||
// Create command line arguments
|
||||
sdv::sequence<sdv::u8string> seqArguments;
|
||||
seqArguments.push_back(Base64EncodePlainText(sstreamConfig.str()));
|
||||
if (GetAppControl().IsConsoleVerbose())
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
seqArguments.push_back("--verbose");
|
||||
else
|
||||
seqArguments.push_back("--silent");
|
||||
seqArguments.push_back("--instance" + std::to_string(GetAppControl().GetInstanceID()));
|
||||
seqArguments.push_back("--install_dir" + GetAppControl().GetInstallDir().generic_u8string());
|
||||
seqArguments.push_back("--instance" + std::to_string(GetAppSettings().GetInstanceID()));
|
||||
seqArguments.push_back("--install_dir" + GetAppSettings().GetRootDir().generic_u8string());
|
||||
|
||||
sdv::core::TObjectID tObjectID = CreateObjectID();
|
||||
auto ptrObjectEntry = std::make_shared<SObjectEntry>();
|
||||
@@ -1006,11 +1221,10 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!ptrObjectEntry)
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation failed for class \"", rsClassInfo.ssClassName, "\"!");
|
||||
SDV_LOG_ERROR("Object creation failed for class \"", rsClassInfo.ssName, "\"!");
|
||||
return 0;
|
||||
}
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
bool bControlled = rsClassInfo.eType == sdv::EObjectType::ComplexService;
|
||||
ptrObjectEntry->tObjectID = tObjectID;
|
||||
ptrObjectEntry->sClassInfo = rsClassInfo;
|
||||
ptrObjectEntry->ssName = rssObjectName;
|
||||
@@ -1019,15 +1233,14 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
|
||||
ptrObjectEntry->bIsolated = true;
|
||||
if (bControlled)
|
||||
m_mapServiceObjects[rssObjectName] = m_lstOrderedServiceObjects.insert(m_lstOrderedServiceObjects.end(), ptrObjectEntry);
|
||||
m_setConfigObjects.insert(tObjectID);
|
||||
m_mapObjects[tObjectID] = ptrObjectEntry;
|
||||
m_mapIsolatedObjects[rssObjectName] = ptrObjectEntry;
|
||||
lock.unlock();
|
||||
|
||||
// Print info
|
||||
if (GetAppControl().IsConsoleVerbose())
|
||||
std::cout << "Creating an isolated " << GetObjectTypeString(rsClassInfo.eType) << " #" << tObjectID << " of type " <<
|
||||
rsClassInfo.ssClassName << std::endl;
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Creating an isolated " << sdv::ObjectType2String(rsClassInfo.eType) << " #" << tObjectID << " of type " <<
|
||||
rsClassInfo.ssName << std::endl;
|
||||
|
||||
// Start the isolation process
|
||||
sdv::process::IProcessControl* pProcessControl = sdv::core::GetObject<sdv::process::IProcessControl>("ProcessControlService");
|
||||
@@ -1046,51 +1259,14 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
|
||||
sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr<CModuleInst>& rptrModule,
|
||||
const sdv::SClassInfo& rsClassInfo, const sdv::u8string& rssObjectName, const sdv::u8string& rssObjectConfig)
|
||||
{
|
||||
// Check for an object name. If not existing get the default name (being either one specified by the object or the class name).
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
std::string ssObjectName2 = rssObjectName;
|
||||
if (rssObjectName.empty())
|
||||
ssObjectName2 = rptrModule->GetDefaultObjectName(rsClassInfo.ssClassName);
|
||||
|
||||
// Check for an object with the same name.
|
||||
auto itPreviousService = m_mapServiceObjects.find(ssObjectName2);
|
||||
if (itPreviousService != m_mapServiceObjects.end())
|
||||
{
|
||||
// Object entry is valid?
|
||||
if (!*itPreviousService->second)
|
||||
{
|
||||
// This should not occur... there is a previous object in the service map, but the object is empty.
|
||||
SDV_LOG_ERROR("Object creation requested for class \"", rsClassInfo.ssClassName, "\", but object with the same name \"",
|
||||
ssObjectName2, "\" was already instantiated, but cannot be found!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Trying to create an object with the same name is not an error if the classes are identical.
|
||||
if ((*itPreviousService->second)->sClassInfo.ssClassName == rsClassInfo.ssClassName)
|
||||
return (*itPreviousService->second)->tObjectID;
|
||||
|
||||
// Object name was already used by another class. This is an error.
|
||||
SDV_LOG_ERROR("Object creation requested for class \"", rsClassInfo.ssClassName, "\", but object with the same name \"",
|
||||
ssObjectName2, "\" was already instantiated for class \"", (*itPreviousService->second)->sClassInfo.ssClassName, "\"!");
|
||||
SDV_LOG_ERROR("Object creation requested for class \"", rsClassInfo.ssName, "\", but no object name was provided!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check with singleton objects if the object was already instantiated.
|
||||
if (rsClassInfo.uiFlags & static_cast<uint32_t>(sdv::EObjectFlags::singleton))
|
||||
{
|
||||
for (const auto& rprService : m_mapServiceObjects)
|
||||
{
|
||||
if (!*rprService.second) continue;
|
||||
if ((*rprService.second)->ptrModule && (*rprService.second)->ptrModule->GetModuleID() == rptrModule->GetModuleID() &&
|
||||
(*rprService.second)->sClassInfo.ssClassName == rsClassInfo.ssClassName)
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation requested but object from the same class \"",
|
||||
rsClassInfo.ssClassName, "\" was already instantiated and only one instance is allowed!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for an object name. If not existing get the default name (being either one specified by the object or the class name).
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
// Reserve the object and return the lock
|
||||
sdv::core::TObjectID tObjectID = CreateObjectID();
|
||||
auto ptrObjectEntry = std::make_shared<SObjectEntry>();
|
||||
@@ -1099,31 +1275,30 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr<CMo
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!ptrObjectEntry)
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation failed for class \"", rsClassInfo.ssClassName, "\"!");
|
||||
SDV_LOG_ERROR("Object creation failed for class \"", rsClassInfo.ssName, "\"!");
|
||||
return 0;
|
||||
}
|
||||
m_mapServiceObjects[ssObjectName2] = m_lstOrderedServiceObjects.insert(m_lstOrderedServiceObjects.end(), ptrObjectEntry);
|
||||
m_setConfigObjects.insert(tObjectID);
|
||||
m_mapServiceObjects[rssObjectName] = m_lstOrderedServiceObjects.insert(m_lstOrderedServiceObjects.end(), ptrObjectEntry);
|
||||
m_mapObjects[tObjectID] = ptrObjectEntry;
|
||||
ptrObjectEntry->tObjectID = tObjectID;
|
||||
ptrObjectEntry->sClassInfo = rsClassInfo;
|
||||
ptrObjectEntry->ssName = ssObjectName2;
|
||||
ptrObjectEntry->ssName = rssObjectName;
|
||||
ptrObjectEntry->ssConfig = rssObjectConfig;
|
||||
ptrObjectEntry->ptrModule = rptrModule;
|
||||
ptrObjectEntry->bControlled = true;
|
||||
lock.unlock();
|
||||
|
||||
// Print info
|
||||
if (GetAppControl().IsConsoleVerbose())
|
||||
std::cout << "Creating a " << GetObjectTypeString(rsClassInfo.eType) << " #" << tObjectID << " of type " <<
|
||||
rsClassInfo.ssClassName << " with the name " << ssObjectName2 << std::endl;
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Creating a " << sdv::ObjectType2String(rsClassInfo.eType) << " #" << tObjectID << " of type " <<
|
||||
rsClassInfo.ssName << " with the name " << rssObjectName << std::endl;
|
||||
|
||||
// Create the object
|
||||
sdv::TInterfaceAccessPtr ptrObject = rptrModule->CreateObject(rsClassInfo.ssClassName);
|
||||
sdv::TInterfaceAccessPtr ptrObject = rptrModule->CreateObject(rsClassInfo.ssName);
|
||||
if (!ptrObject)
|
||||
{
|
||||
// Destroy the object again
|
||||
DestroyObject(ssObjectName2);
|
||||
DestroyObject(rssObjectName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1132,7 +1307,7 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr<CMo
|
||||
if (pObjectControl)
|
||||
{
|
||||
pObjectControl->Initialize(rssObjectConfig);
|
||||
if (pObjectControl->GetStatus() != sdv::EObjectStatus::initialized)
|
||||
if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized)
|
||||
{
|
||||
// Shutdown the object (even if the initialization didn't work properly).
|
||||
pObjectControl->Shutdown();
|
||||
@@ -1141,7 +1316,7 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr<CMo
|
||||
rptrModule->DestroyObject(ptrObject);
|
||||
|
||||
// Destroy the object again
|
||||
DestroyObject(ssObjectName2);
|
||||
DestroyObject(rssObjectName);
|
||||
return 0;
|
||||
}
|
||||
switch (GetAppControl().GetOperationState())
|
||||
@@ -1158,7 +1333,8 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr<CMo
|
||||
}
|
||||
|
||||
// For an one isolated object: add iso control and store this as pointer in object entry...
|
||||
if (rsClassInfo.eType == sdv::EObjectType::ComplexService && GetAppControl().IsIsolatedApplication())
|
||||
if ((rsClassInfo.eType == sdv::EObjectType::complex_service || rsClassInfo.eType == sdv::EObjectType::vehicle_function) &&
|
||||
GetAppSettings().IsIsolatedApplication())
|
||||
{
|
||||
ptrObjectEntry->ptrIsoMon = std::make_shared<CIsoMonitor>(ptrObject);
|
||||
ptrObjectEntry->ptrObject = ptrObjectEntry->ptrIsoMon.get();
|
||||
@@ -1199,32 +1375,23 @@ std::vector<sdv::core::TObjectID> CRepository::GetDependingObjectInstancesByClas
|
||||
return vecDependingObjects;
|
||||
}
|
||||
|
||||
#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST
|
||||
|
||||
CRepository& CRepositoryService::GetRepository()
|
||||
{
|
||||
return ::GetRepository();
|
||||
}
|
||||
|
||||
bool CRepositoryService::EnableRepositoryObjectControl()
|
||||
{
|
||||
return GetAppControl().IsMainApplication() ||
|
||||
GetAppControl().IsStandaloneApplication() ||
|
||||
GetAppControl().IsEssentialApplication() ||
|
||||
GetAppControl().IsIsolatedApplication();
|
||||
return GetAppSettings().IsMainApplication() ||
|
||||
GetAppSettings().IsStandaloneApplication() ||
|
||||
GetAppSettings().IsEssentialApplication() ||
|
||||
GetAppSettings().IsIsolatedApplication();
|
||||
}
|
||||
|
||||
bool CRepositoryService::EnableRepositoryRegisterForeignApp()
|
||||
{
|
||||
return GetAppControl().IsMainApplication() ||
|
||||
GetAppControl().IsStandaloneApplication() ||
|
||||
GetAppControl().IsEssentialApplication() ||
|
||||
GetAppControl().IsIsolatedApplication();
|
||||
return GetAppSettings().IsMainApplication() ||
|
||||
GetAppSettings().IsStandaloneApplication() ||
|
||||
GetAppSettings().IsEssentialApplication() ||
|
||||
GetAppSettings().IsIsolatedApplication();
|
||||
}
|
||||
|
||||
bool CRepositoryService::EnableRepositoryLink()
|
||||
{
|
||||
return GetAppControl().IsIsolatedApplication() || GetAppControl().IsExternalApplication();
|
||||
return GetAppSettings().IsIsolatedApplication() || GetAppSettings().IsExternalApplication();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,18 @@
|
||||
/********************************************************************************
|
||||
* 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 VAPI_REPOSITORY_H
|
||||
#define VAPI_REPOSITORY_H
|
||||
#ifndef REPOSITORY_H
|
||||
#define REPOSITORY_H
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
@@ -14,6 +26,7 @@
|
||||
#include "module.h"
|
||||
#include "object_lifetime_control.h"
|
||||
#include "iso_monitor.h"
|
||||
#include "app_config_file.h"
|
||||
|
||||
/**
|
||||
* @brief repository service providing functionality to load modules, create objects and access exiting objects
|
||||
@@ -248,6 +261,15 @@ public:
|
||||
*/
|
||||
void ResetConfigBaseline();
|
||||
|
||||
/**
|
||||
* @brief Start components from a configuration.
|
||||
* @remarks If the application runs as local application, the modules are loaded before starting the components.
|
||||
* @param[in] rconfig Reference to the configuration file.
|
||||
* @param[in] bAllowPartialLoad When set, allow partial loading the configuration (one or more components).
|
||||
* @return Returns the load result.
|
||||
*/
|
||||
sdv::core::EConfigProcessResult StartFromConfig(const CAppConfigFile& rconfig, bool bAllowPartialLoad);
|
||||
|
||||
/**
|
||||
* @brief Save the configuration of all components.
|
||||
* @return The string containing all the components.
|
||||
@@ -332,9 +354,14 @@ private:
|
||||
bool m_bIsoObjectLoaded = false; ///< When set, the isolated object has loaded. Do not allow
|
||||
///< another object of type complex service or utility to be
|
||||
///< created.
|
||||
///
|
||||
};
|
||||
|
||||
#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST
|
||||
/**
|
||||
* @brief Return the repository.
|
||||
* @return Reference to the repository.
|
||||
*/
|
||||
CRepository& GetRepository();
|
||||
|
||||
/**
|
||||
* @brief Repository service
|
||||
@@ -362,16 +389,10 @@ public:
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("RepositoryService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Get access to the repository.
|
||||
* @return Returns a reference to the one repository of this module.
|
||||
*/
|
||||
static CRepository& GetRepository();
|
||||
|
||||
/**
|
||||
* @brief When set, the repository object control access will be enabled.
|
||||
* @return Returns whether object control interface access is granted.
|
||||
@@ -391,7 +412,6 @@ public:
|
||||
static bool EnableRepositoryLink();
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT_NO_EXPORT(CRepositoryService)
|
||||
#endif
|
||||
DEFINE_SDV_OBJECT(CRepositoryService)
|
||||
|
||||
#endif // !define VAPI_REPOSITORY_H
|
||||
#endif // !define REPOSITORY_H
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "sdv_core.h"
|
||||
#include "toml_parser_util.h" // Insert to create the factory of this utility
|
||||
#include <fstream>
|
||||
@@ -21,120 +34,3 @@ CSDVCore& CSDVCore::GetInstance()
|
||||
return core;
|
||||
}
|
||||
|
||||
CAppControl& CSDVCore::GetAppControl()
|
||||
{
|
||||
return m_appctrl;
|
||||
}
|
||||
|
||||
CModuleControl& CSDVCore::GetModuleControl()
|
||||
{
|
||||
return m_modulectrl;
|
||||
}
|
||||
|
||||
CMemoryManager& CSDVCore::GetMemoryManager()
|
||||
{
|
||||
return m_memmgr;
|
||||
}
|
||||
|
||||
CRepository& CSDVCore::GetRepository()
|
||||
{
|
||||
return m_repository;
|
||||
}
|
||||
|
||||
CLoggerControl& CSDVCore::GetLoggerControl()
|
||||
{
|
||||
return m_loggerctrl;
|
||||
}
|
||||
|
||||
CLogger& CSDVCore::GetDefaultLogger()
|
||||
{
|
||||
return m_defaultlogger;
|
||||
}
|
||||
|
||||
CAppConfig& CSDVCore::GetAppConfig()
|
||||
{
|
||||
return m_appconfig;
|
||||
}
|
||||
|
||||
CAppControl& GetAppControl()
|
||||
{
|
||||
return CSDVCore::GetInstance().GetAppControl();
|
||||
}
|
||||
|
||||
CModuleControl& GetModuleControl()
|
||||
{
|
||||
return CSDVCore::GetInstance().GetModuleControl();
|
||||
}
|
||||
|
||||
CMemoryManager& GetMemoryManager()
|
||||
{
|
||||
return CSDVCore::GetInstance().GetMemoryManager();
|
||||
}
|
||||
|
||||
CRepository& GetRepository()
|
||||
{
|
||||
return CSDVCore::GetInstance().GetRepository();
|
||||
}
|
||||
|
||||
CLoggerControl& GetLoggerControl()
|
||||
{
|
||||
return CSDVCore::GetInstance().GetLoggerControl();
|
||||
}
|
||||
|
||||
CLogger& GetDefaultLogger()
|
||||
{
|
||||
return CSDVCore::GetInstance().GetDefaultLogger();
|
||||
}
|
||||
|
||||
CAppConfig& GetAppConfig()
|
||||
{
|
||||
return CSDVCore::GetInstance().GetAppConfig();
|
||||
}
|
||||
|
||||
std::filesystem::path GetCoreDirectory()
|
||||
{
|
||||
static std::filesystem::path pathCoreDir;
|
||||
if (!pathCoreDir.empty()) return pathCoreDir;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows specific
|
||||
std::wstring ssPath(32768, '\0');
|
||||
|
||||
MEMORY_BASIC_INFORMATION sMemInfo{};
|
||||
if (!VirtualQuery(&pathCoreDir, &sMemInfo, sizeof(sMemInfo))) return pathCoreDir;
|
||||
DWORD dwLength = GetModuleFileNameW(reinterpret_cast<HINSTANCE>(sMemInfo.AllocationBase), ssPath.data(), 32767);
|
||||
ssPath.resize(dwLength);
|
||||
pathCoreDir = std::filesystem::path(ssPath);
|
||||
return pathCoreDir.remove_filename();
|
||||
#elif __linux__
|
||||
// Read the maps file. It contains all loaded SOs.
|
||||
std::ifstream fstream("/proc/self/maps");
|
||||
std::stringstream sstreamMap;
|
||||
sstreamMap << fstream.rdbuf();
|
||||
std::string ssMap = sstreamMap.str();
|
||||
if (ssMap.empty())
|
||||
return pathCoreDir; // Some error
|
||||
|
||||
// Find the "core_services.sdv"
|
||||
size_t nPos = ssMap.find("core_services.sdv");
|
||||
if (nPos == std::string::npos) return pathCoreDir;
|
||||
size_t nEnd = nPos;
|
||||
|
||||
// Find the start... runbackwards until the beginning of the line and remember the earliest occurance of a slash
|
||||
size_t nBegin = 0;
|
||||
while (nPos && ssMap[nPos] != '\n')
|
||||
{
|
||||
if (ssMap[nPos] == '/')
|
||||
nBegin = nPos;
|
||||
nPos--;
|
||||
}
|
||||
if (!nBegin) nBegin = nPos;
|
||||
|
||||
// Return the path
|
||||
pathCoreDir = ssMap.substr(nBegin, nEnd - nBegin);
|
||||
|
||||
return pathCoreDir;
|
||||
#else
|
||||
#error The OS is not supported!
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
/********************************************************************************
|
||||
* 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 CORE_H
|
||||
#define CORE_H
|
||||
|
||||
#include <support/component_impl.h>
|
||||
#include "app_control.h"
|
||||
#include "app_settings.h"
|
||||
#include "module_control.h"
|
||||
#include "memory.h"
|
||||
#include "repository.h"
|
||||
@@ -27,11 +41,12 @@ public:
|
||||
~CSDVCore();
|
||||
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_CHAIN_MEMBER(m_appctrl)
|
||||
SDV_INTERFACE_CHAIN_MEMBER(m_modulectrl)
|
||||
SDV_INTERFACE_CHAIN_MEMBER(m_memmgr)
|
||||
SDV_INTERFACE_CHAIN_MEMBER(m_repository)
|
||||
SDV_INTERFACE_CHAIN_MEMBER(m_loggerctrl)
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetAppControl())
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetAppSettings())
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetModuleControl())
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetMemoryManager())
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetRepository())
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetLoggerControl())
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
@@ -39,57 +54,6 @@ public:
|
||||
* @return Reference to this class.
|
||||
*/
|
||||
static CSDVCore& GetInstance();
|
||||
|
||||
/**
|
||||
* @brief Return the application control.
|
||||
* @return Reference to the application control.
|
||||
*/
|
||||
CAppControl& GetAppControl();
|
||||
|
||||
/**
|
||||
* @brief Return the module control.
|
||||
* @return Reference to the module control.
|
||||
*/
|
||||
CModuleControl& GetModuleControl();
|
||||
|
||||
/**
|
||||
* @brief Return the memory manager.
|
||||
* @return Reference to the memory manager.
|
||||
*/
|
||||
CMemoryManager& GetMemoryManager();
|
||||
|
||||
/**
|
||||
* @brief Return the repository.
|
||||
* @return Reference to the repository.
|
||||
*/
|
||||
CRepository& GetRepository();
|
||||
|
||||
/**
|
||||
* @brief Return the logger control.
|
||||
* @return Reference to the logger control.
|
||||
*/
|
||||
CLoggerControl& GetLoggerControl();
|
||||
|
||||
/**
|
||||
* @brief Return the default logger.
|
||||
* @return Reference to the default logger.
|
||||
*/
|
||||
CLogger& GetDefaultLogger();
|
||||
|
||||
/**
|
||||
* @brief Return the application config class.
|
||||
* @return Reference to the application config class.
|
||||
*/
|
||||
CAppConfig& GetAppConfig();
|
||||
|
||||
private:
|
||||
CMemoryManager m_memmgr; ///< Memory manager - note: needs to be first in the list of members!
|
||||
CAppControl m_appctrl; ///< Application control
|
||||
CRepository m_repository; ///< Repository - note: repository should be present before module control!
|
||||
CModuleControl m_modulectrl; ///< Module control
|
||||
CLogger m_defaultlogger; ///< Default logger - note: the logger must be present before the logger control!
|
||||
CLoggerControl m_loggerctrl; ///< Logger control
|
||||
CAppConfig m_appconfig; ///< Application configuration class
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -98,53 +62,4 @@ private:
|
||||
*/
|
||||
extern "C" SDV_SYMBOL_PUBLIC sdv::IInterfaceAccess* SDVCore();
|
||||
|
||||
/**
|
||||
* @brief Return the application control.
|
||||
* @return Reference to the application control.
|
||||
*/
|
||||
CAppControl& GetAppControl();
|
||||
|
||||
/**
|
||||
* @brief Return the module control.
|
||||
* @return Reference to the module control.
|
||||
*/
|
||||
CModuleControl& GetModuleControl();
|
||||
|
||||
/**
|
||||
* @brief Return the memory manager.
|
||||
* @return Reference to the memory manager.
|
||||
*/
|
||||
CMemoryManager& GetMemoryManager();
|
||||
|
||||
/**
|
||||
* @brief Return the repository.
|
||||
* @return Reference to the repository.
|
||||
*/
|
||||
CRepository& GetRepository();
|
||||
|
||||
/**
|
||||
* @brief Return the logger control.
|
||||
* @return Reference to the logger control.
|
||||
*/
|
||||
CLoggerControl& GetLoggerControl();
|
||||
|
||||
/**
|
||||
* @brief Return the default logger.
|
||||
* @attention Use the logger control to access the logger.
|
||||
* @return Reference to the default logger.
|
||||
*/
|
||||
CLogger& GetDefaultLogger();
|
||||
|
||||
/**
|
||||
* @brief Return the application config class.
|
||||
* @return Reference to the application config class.
|
||||
*/
|
||||
CAppConfig& GetAppConfig();
|
||||
|
||||
/**
|
||||
* @brief Get the location of the core_services.sdv.
|
||||
* @return Path to the directory containing the loaded core directory.
|
||||
*/
|
||||
std::filesystem::path GetCoreDirectory();
|
||||
|
||||
#endif // !defined CORE_H
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#include "character_reader_utf_8.h"
|
||||
#include "exception.h"
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef CHARACTER_READER_UTF_8_H
|
||||
#define CHARACTER_READER_UTF_8_H
|
||||
|
||||
|
||||
425
sdv_services/core/toml_parser/code_snippet.cpp
Normal file
425
sdv_services/core/toml_parser/code_snippet.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/********************************************************************************
|
||||
* 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 "code_snippet.h"
|
||||
#include "parser_node_toml.h"
|
||||
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
void CCodeSnippet::SetTokenList(const std::list<CToken>& rlstTokens)
|
||||
{
|
||||
m_lstTokens = rlstTokens;
|
||||
m_bUseDefault = false;
|
||||
}
|
||||
|
||||
void CCodeSnippet::SetTokenList(std::list<CToken>&& rlstTokens)
|
||||
{
|
||||
m_lstTokens = std::move(rlstTokens);
|
||||
m_bUseDefault = false;
|
||||
}
|
||||
|
||||
std::string CCodeSnippet::Compose(EComposeMode eMode, const CGenContext& rContext, size_t nAssignmentOffset /*= 0*/,
|
||||
size_t nCommentOffset /*= 0*/) const
|
||||
{
|
||||
// Build the stream until the first comment.
|
||||
std::stringstream sstream;
|
||||
TTokenListIterator it = m_lstTokens.begin();
|
||||
bool bCommaAvailable = false;
|
||||
bool bPrintableCharsAvailable = false;
|
||||
bool bLastTokenIsComment = false;
|
||||
while (it != m_lstTokens.end() && it->Category() != ETokenCategory::token_comment)
|
||||
{
|
||||
// Add comma only when needed.
|
||||
bool bSkip = false;
|
||||
switch (it->Category())
|
||||
{
|
||||
case ETokenCategory::token_syntax_comma:
|
||||
if (eMode != EComposeMode::compose_behind || !rContext.CommaNeeded() || bCommaAvailable)
|
||||
{
|
||||
bSkip = true;
|
||||
break; // Only when placed behind a node
|
||||
}
|
||||
bCommaAvailable = true;
|
||||
break;
|
||||
case ETokenCategory::token_comment:
|
||||
bSkip = !rContext.CommentAndNewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = true;
|
||||
break;
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
bSkip = !rContext.NewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!bSkip)
|
||||
{
|
||||
sstream << it->RawString();
|
||||
bPrintableCharsAvailable = true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
// Determine the last comment.
|
||||
TTokenListIterator itFirstComment = it;
|
||||
TTokenListIterator itLastComment = it;
|
||||
while (it != m_lstTokens.end())
|
||||
{
|
||||
if (it->Category() == ETokenCategory::token_comment)
|
||||
itLastComment = it;
|
||||
++it;
|
||||
}
|
||||
|
||||
// Determine the code behind the last comment (and the obligatory newline).
|
||||
TTokenListIterator itPostComment = itLastComment;
|
||||
if (itPostComment != m_lstTokens.end())
|
||||
++itPostComment;
|
||||
if (itPostComment != m_lstTokens.end() && itPostComment->Category() == ETokenCategory::token_syntax_new_line)
|
||||
++itPostComment;
|
||||
|
||||
// Stream the comment
|
||||
if (!m_bCommentReplaced)
|
||||
{
|
||||
for (it = itFirstComment; it != itPostComment; ++it)
|
||||
{
|
||||
// Add comma only when needed.
|
||||
bool bSkip = false;
|
||||
switch (it->Category())
|
||||
{
|
||||
case ETokenCategory::token_syntax_comma:
|
||||
if (eMode != EComposeMode::compose_behind || !rContext.CommaNeeded() || bCommaAvailable)
|
||||
{
|
||||
bSkip = true;
|
||||
break; // Only when placed behind a node
|
||||
}
|
||||
bCommaAvailable = true;
|
||||
break;
|
||||
case ETokenCategory::token_comment:
|
||||
bSkip = !rContext.CommentAndNewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = true;
|
||||
break;
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
bSkip = !rContext.NewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!bSkip)
|
||||
{
|
||||
sstream << it->RawString();
|
||||
bPrintableCharsAvailable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case EComposeMode::compose_inline:
|
||||
case EComposeMode::compose_behind:
|
||||
if (itFirstComment == m_lstTokens.end() && !m_ssComment.empty())
|
||||
sstream << " ";
|
||||
break;
|
||||
case EComposeMode::compose_standalone_behind:
|
||||
if (itFirstComment == m_lstTokens.end() && !m_ssComment.empty())
|
||||
sstream << std::endl;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// TODO: Align the comment....
|
||||
size_t nPos = 0;
|
||||
bool bEmptyLine = false;
|
||||
while (nPos != std::string::npos)
|
||||
{
|
||||
// Find the chunk until the next newline or the end of the string
|
||||
size_t nNextPos = m_ssComment.find('\n', nPos);
|
||||
if (nNextPos != std::string::npos) nNextPos++;
|
||||
if (nNextPos >= m_ssComment.size()) nNextPos = std::string::npos;
|
||||
std::string ssChunk = m_ssComment.substr(nPos, nNextPos);
|
||||
nPos = nNextPos;
|
||||
|
||||
// Insert an extra line break, but only when there isn't a line-break already...
|
||||
if (!bEmptyLine && !sstream.str().empty())
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case EComposeMode::compose_before:
|
||||
case EComposeMode::compose_behind:
|
||||
case EComposeMode::compose_inline:
|
||||
sstream << std::string(nCommentOffset, ' ') << "#";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sstream << std::endl;
|
||||
}
|
||||
|
||||
if (ssChunk.find_first_not_of("\r\n") == std::string::npos)
|
||||
bEmptyLine = true;
|
||||
else
|
||||
{
|
||||
sstream << std::string(nCommentOffset, ' ') << "# " << ssChunk;
|
||||
if (ssChunk.find('\n') == std::string::npos)
|
||||
sstream << std::endl; // Comment is always followed by a newline.
|
||||
bEmptyLine = false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add spaces for next assignment
|
||||
switch (eMode)
|
||||
{
|
||||
case EComposeMode::compose_inline:
|
||||
if (itPostComment == m_lstTokens.end() && nAssignmentOffset)
|
||||
sstream << std::string(nAssignmentOffset, ' ');
|
||||
break;
|
||||
case EComposeMode::compose_standalone_before:
|
||||
if (itFirstComment == m_lstTokens.end() && !m_ssComment.empty())
|
||||
sstream << std::endl;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Stream the rest of the tokens
|
||||
for (it = itPostComment; it != m_lstTokens.end(); ++it)
|
||||
{
|
||||
bool bSkip = false;
|
||||
switch (it->Category())
|
||||
{
|
||||
case ETokenCategory::token_syntax_comma:
|
||||
if (eMode != EComposeMode::compose_behind || !rContext.CommaNeeded() || bCommaAvailable)
|
||||
{
|
||||
bSkip = true;
|
||||
break; // Only when placed behind a node
|
||||
}
|
||||
bCommaAvailable = true;
|
||||
break;
|
||||
case ETokenCategory::token_comment:
|
||||
bSkip = !rContext.CommentAndNewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = true;
|
||||
break;
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
bSkip = !rContext.NewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!bSkip)
|
||||
{
|
||||
sstream << it->RawString();
|
||||
bPrintableCharsAvailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Post processing
|
||||
switch (eMode)
|
||||
{
|
||||
case EComposeMode::compose_behind:
|
||||
// Was a comma provided?
|
||||
if (!bCommaAvailable && rContext.CommaNeeded())
|
||||
{
|
||||
std::string ssTemp = sstream.str();
|
||||
ssTemp.insert(0, bPrintableCharsAvailable ? "," : ", ");
|
||||
sstream.str(ssTemp);
|
||||
}
|
||||
|
||||
// Default newline needed
|
||||
if (rContext.FinalNewline() && ((m_lstTokens.empty() && m_ssComment.empty()) || bLastTokenIsComment))
|
||||
sstream << std::endl;
|
||||
break;
|
||||
case EComposeMode::compose_before:
|
||||
//if (!bPrintableCharsAvailable && rContext.Embedded())
|
||||
// sstream << " ";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
std::string CCodeSnippet::GetComment() const
|
||||
{
|
||||
if (m_bCommentReplaced)
|
||||
return m_ssComment;
|
||||
|
||||
std::stringstream sstream;
|
||||
size_t nNewLineCount = 0;
|
||||
for (const CToken& rToken : m_lstTokens)
|
||||
{
|
||||
// Deal with new line.
|
||||
if (rToken.Category() == ETokenCategory::token_syntax_new_line)
|
||||
{
|
||||
if (sstream.str().empty())
|
||||
continue;
|
||||
nNewLineCount++;
|
||||
if (nNewLineCount < 2)
|
||||
continue;
|
||||
sstream << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (rToken.Category() == ETokenCategory::token_whitespace)
|
||||
continue;
|
||||
nNewLineCount = 0;
|
||||
if (rToken.Category() == toml_parser::ETokenCategory::token_comment && !rToken.RawString().empty()
|
||||
&& rToken.RawString()[0] == '#')
|
||||
{
|
||||
// The comment doesn't have a newline inside the comment text. This allows us to glue multiple comments to one
|
||||
// large string.
|
||||
// A new line will be inserted on the following conditions:
|
||||
// - the line starts as comment, but only has whitespace.
|
||||
// - there are at least some characters in the stream already.
|
||||
// - the comment starts with a tab or multiple spaces
|
||||
if (rToken.RawString().size() <= 1 || rToken.RawString().find_first_not_of(" \t", 1) == std::string::npos)
|
||||
{
|
||||
// Line has only whitespace
|
||||
sstream << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (rToken.RawString()[1] == '\t' || rToken.RawString().substr(1, 2) == " "
|
||||
|| rToken.RawString().substr(1, 2) == " \t")
|
||||
{
|
||||
// Line starts with a tab, are there any characters in the stream already.
|
||||
if (!sstream.str().empty())
|
||||
sstream << std::endl;
|
||||
sstream << rToken.RawString().substr(1);
|
||||
continue;
|
||||
}
|
||||
size_t nStartComment = rToken.RawString()[1] == ' ' ? 2 : 1;
|
||||
if (!sstream.str().empty())
|
||||
{
|
||||
// The stream is not empty. Are there extra spaces (indicating a list or so)
|
||||
if (rToken.RawString().substr(1, 2) == " ")
|
||||
sstream << std::endl;
|
||||
else // No newline, but space?
|
||||
if (!std::isspace(sstream.str().back()))
|
||||
sstream << " ";
|
||||
}
|
||||
|
||||
// Add the content to the stream
|
||||
sstream << rToken.RawString().substr(nStartComment);
|
||||
}
|
||||
}
|
||||
|
||||
// Trim space at the end of the string
|
||||
std::string ssResult = sstream.str();
|
||||
while (!ssResult.empty() && std::isspace(ssResult.back()))
|
||||
ssResult.pop_back();
|
||||
return ssResult;
|
||||
}
|
||||
|
||||
void CCodeSnippet::SetComment(const std::string& rssComment)
|
||||
{
|
||||
// Just store the string. Further processing is done in the compose function.
|
||||
m_ssComment = rssComment;
|
||||
m_bCommentReplaced = true;
|
||||
m_bUseDefault = false;
|
||||
}
|
||||
|
||||
void CCodeSnippet::Clear()
|
||||
{
|
||||
m_ssComment.clear();
|
||||
m_lstTokens.clear();
|
||||
m_bCommentReplaced = false;
|
||||
m_bUseDefault = true;
|
||||
}
|
||||
|
||||
bool CCodeSnippet::HasCode() const
|
||||
{
|
||||
return !m_bUseDefault || (m_bCommentReplaced || !m_lstTokens.empty());
|
||||
}
|
||||
|
||||
void CCodeSnippet::RemoveFormat()
|
||||
{
|
||||
// Read the comment from the token list.
|
||||
if (!m_bCommentReplaced && !m_lstTokens.empty())
|
||||
{
|
||||
m_ssComment = GetComment();
|
||||
m_bCommentReplaced = !m_ssComment.empty();
|
||||
}
|
||||
|
||||
// Clear the token list
|
||||
m_lstTokens.clear();
|
||||
|
||||
m_bUseDefault = !m_bCommentReplaced;
|
||||
}
|
||||
|
||||
bool CCodeSnippet::HasComma() const
|
||||
{
|
||||
return !m_bCommentReplaced && (std::find_if(m_lstTokens.begin(), m_lstTokens.end(),
|
||||
[](const CToken& rToken) { return rToken.Category() == ETokenCategory::token_syntax_comma; }) != m_lstTokens.end());
|
||||
}
|
||||
|
||||
void CCodeSnippet::Insert(const CCodeSnippet& rCode)
|
||||
{
|
||||
// Something to do?
|
||||
if (!rCode.HasCode()) return;
|
||||
|
||||
// Simple assignment?
|
||||
if (!HasCode())
|
||||
{
|
||||
m_bCommentReplaced = rCode.m_bCommentReplaced;
|
||||
m_ssComment = rCode.m_ssComment;
|
||||
m_lstTokens = rCode.m_lstTokens;
|
||||
return;
|
||||
}
|
||||
|
||||
// Differentiate between comment text or token lists.
|
||||
if (m_bCommentReplaced)
|
||||
m_ssComment.insert(0, rCode.GetComment());
|
||||
else
|
||||
{
|
||||
if (rCode.m_bCommentReplaced)
|
||||
{
|
||||
m_ssComment = rCode.GetComment() + GetComment();
|
||||
m_bCommentReplaced = true;
|
||||
}
|
||||
else
|
||||
m_lstTokens.insert(m_lstTokens.begin(), rCode.m_lstTokens.begin(), rCode.m_lstTokens.end());
|
||||
}
|
||||
}
|
||||
|
||||
void CCodeSnippet::Append(const CCodeSnippet& rCode)
|
||||
{
|
||||
// Something to do?
|
||||
if (!rCode.HasCode()) return;
|
||||
|
||||
// Simple assignment?
|
||||
if (!HasCode())
|
||||
{
|
||||
m_bCommentReplaced = rCode.m_bCommentReplaced;
|
||||
m_ssComment = rCode.m_ssComment;
|
||||
m_lstTokens = rCode.m_lstTokens;
|
||||
return;
|
||||
}
|
||||
|
||||
// Differentiate between comment text or token lists.
|
||||
if (m_bCommentReplaced)
|
||||
m_ssComment.append(rCode.GetComment());
|
||||
else
|
||||
{
|
||||
if (rCode.m_bCommentReplaced)
|
||||
{
|
||||
m_ssComment = rCode.GetComment() + GetComment();
|
||||
m_bCommentReplaced = true;
|
||||
}
|
||||
else
|
||||
m_lstTokens.insert(m_lstTokens.end(), rCode.m_lstTokens.begin(), rCode.m_lstTokens.end());
|
||||
}
|
||||
}
|
||||
} // namespace toml_parser
|
||||
220
sdv_services/core/toml_parser/code_snippet.h
Normal file
220
sdv_services/core/toml_parser/code_snippet.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/********************************************************************************
|
||||
* 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 CODE_SNIPPET_H
|
||||
#define CODE_SNIPPET_H
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <interfaces/toml.h>
|
||||
#include "lexer_toml.h"
|
||||
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
// Forward declaration
|
||||
class CGenContext;
|
||||
|
||||
/**
|
||||
* @brief Comment or code snippet structure.
|
||||
* @details Each node has multiple code snippets used to reproduce the exact code. For example with an assignment:
|
||||
* @code
|
||||
*
|
||||
* # This is out of scope comment before
|
||||
*
|
||||
* # This is comment before
|
||||
* var_a = "abc" # comment behind
|
||||
* # more comment behind
|
||||
*
|
||||
* # This is out of scope comment behind
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* The code snippets are identified as follows:
|
||||
* @code
|
||||
* <out of scope comment before>
|
||||
* <comment before>
|
||||
* <space before><key><space>=<space><value><comment behind>
|
||||
* <out of scope comment behind>
|
||||
* @endcode
|
||||
*
|
||||
* Each code snippet has token a list and comment string.
|
||||
* - If the tokenlist is not empty and comment string is empty, the tokenlist determines the comment.
|
||||
* - If the tokenlist is empty, the comment string determines the comment. The following steps are taken during the
|
||||
* GetComment with the raw flag enabled:
|
||||
* - For the comment behind, the current position for the comment is determined by examining the length of the sibling
|
||||
* nodes. If the position is larger than 80, the start position is either 80 or one space character more than the
|
||||
* node length.
|
||||
* - For all other comment types, the start position is zero.
|
||||
* - Stream spaces until the start position is reached; then stream the comment character '#' followed by a space.
|
||||
* - The comment text is streamed word by word. If between the words a space exists, this can be replaced by a
|
||||
* newline when the current position with the new word would exceed column 132. After the new line spaces fill up
|
||||
* until the start position. Then the comment character and a space is streamed followed by more words from the
|
||||
* comment.
|
||||
* - If within the comment a newline is available, this is followed by an additional newline within the raw comment
|
||||
* stream. Also if multiple empty lines occur (lines with no character sor only whitespace), this is also
|
||||
* streamed with one additional newline at the beginning.
|
||||
* - The last word is followed by a newline.
|
||||
* - If tokenlist is not empty and comment string is not empty, the comments plus the newline following within the
|
||||
* tokenlist are replaced by the new comment text like described above, with the exception that the start position is
|
||||
* determined by the current position within the tokenlist.
|
||||
*/
|
||||
class CCodeSnippet
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Set the token list containing the code for this code snippet.
|
||||
* @param[in] rlstTokens Reference to the token list to be set.
|
||||
*/
|
||||
void SetTokenList(const std::list<CToken>& rlstTokens);
|
||||
|
||||
/**
|
||||
* @brief Set the token list containing the code for this code snippet.
|
||||
* @param[in] rlstTokens Reference to the token list to be set.
|
||||
*/
|
||||
void SetTokenList(std::list<CToken>&& rlstTokens);
|
||||
|
||||
/**
|
||||
* @brief Mode the code snippet composer should run in.
|
||||
*/
|
||||
enum class EComposeMode
|
||||
{
|
||||
compose_inline, ///< Compose as inline whitespace and comment. If there is no token list and no comment
|
||||
///< string, compose as one space. If there is only a comment string, insert a space, add
|
||||
///< the comment followed by an obligatory newline, and insert spaces until the next
|
||||
///< provided position. If there are tokens with a comment token, replace the comment. If
|
||||
///< there are tokens without comment, add the comment, newline and spaces.
|
||||
compose_before, ///< Compose as comment assigned to and located before the node. If there is no token list
|
||||
///< and no comment string, doesn't add anything. If there is only a comment string, adds
|
||||
///< the comment followed by the obligatory newline. If there are tokens with a comment
|
||||
///< token, replace the comment. If there are tokens without the comment, place the comment
|
||||
///< before the last newline or when not available, at the end of the tokens followed by a
|
||||
///< new newline.
|
||||
compose_behind, ///< Compose as comment assigned to and located behind the node. If there is no token list
|
||||
///< and no comment string, add a newline. If there is a comment string and no tokens,
|
||||
///< add a space, the comment string followed by the obligatory newline. If there is a token
|
||||
///< list without comment, add a comment before the newline or at the end with an additional
|
||||
///< newline.
|
||||
compose_standalone_before, ///< Compose as stand-alone comment before the node. Replace any token list if a comment
|
||||
///< string is available.
|
||||
compose_standalone_behind, ///< Compose as stand-alone comment behind the node. Replace any token list if a comment
|
||||
///< string is available.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Compose a conde string from the stored tokens and/or string.
|
||||
* @details The comment text to code translation is as follows:
|
||||
* @code
|
||||
* The following text might be split into multiple TOML comment lines.
|
||||
* @endcode
|
||||
* @code
|
||||
* # The following text might be split into
|
||||
* # multiple TOML comment lines.
|
||||
* @endcode
|
||||
* and
|
||||
* @code
|
||||
* This is part 1 of a text which will be as two separated blocks.
|
||||
* This is part 2 of a text which will be as two separated blocks.
|
||||
* @endcode
|
||||
* @code
|
||||
* # This is part 1 of a text which will be as
|
||||
* # two separated blocks.
|
||||
* #
|
||||
* # This is part 2 of a text which will be as
|
||||
* # two separated blocks.
|
||||
* @endcode
|
||||
* and
|
||||
* @code
|
||||
* This is a list or an indented text, which is streated as a list.
|
||||
* First indented text which belongs as a separated line.
|
||||
* Second indented text which belongs as a separate line.
|
||||
* @endcode
|
||||
* @code
|
||||
* # This is a list or an indented text, which is
|
||||
* # streated as a list.
|
||||
* # First indented text which belongs as a
|
||||
* # separated line.
|
||||
* # Second indented text which belongs as a
|
||||
* # separate line.
|
||||
* @endcode
|
||||
* @param[in] eMode The mode the composer should run in.
|
||||
* @param[in] rContext Reference to the node context used for the code generation.
|
||||
* @param[in] nAssignmentOffset The offset for a next assignent; only used for inline composition.
|
||||
* @param[in] nCommentOffset The offset to insert a multi-line comment; only used for inline and behind composition.
|
||||
* @return The composed code string.
|
||||
*/
|
||||
std::string Compose(EComposeMode eMode, const CGenContext& rContext, size_t nAssignmentOffset = 0, size_t nCommentOffset = 0) const;
|
||||
|
||||
/**
|
||||
* @brief Get the stored comment from the code snippet. A stored comment string supersedes a comment string from the
|
||||
* token list. Comment lines that belong together are combined together as a text without line-breaks.
|
||||
* @return Returns the stored comment string.
|
||||
*/
|
||||
std::string GetComment() const;
|
||||
|
||||
/**
|
||||
* @brief Set a comment string. The comment will be formatted in the compose function. Lines that should stay together
|
||||
* should not be separated by a line-break. Comments should be provided without the TOML comment character '#'.
|
||||
* @param[in] rssComment Reference to the string containing the comment text.
|
||||
*/
|
||||
void SetComment(const std::string& rssComment);
|
||||
|
||||
/**
|
||||
* @brief Remove the content of the code snippet.
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* @brief Does the snippet contain code?
|
||||
* @return Returns whether the snippet has code or not.
|
||||
*/
|
||||
bool HasCode() const;
|
||||
|
||||
/**
|
||||
* @brief This function will remove the formatting from the code.
|
||||
*/
|
||||
void RemoveFormat();
|
||||
|
||||
/**
|
||||
* @brief Does the code snippet have a comma in the token list?
|
||||
* @return Returns whether the token list is active and contains a comma token.
|
||||
*/
|
||||
bool HasComma() const;
|
||||
|
||||
/**
|
||||
* @brief Insert a code snippet before the existing comment.
|
||||
* @remarks In case the code has been replaced by a comment text of the current or the to be inserted code snippet, the
|
||||
* result will be a combined comment text, disregarding the format.
|
||||
* @param[in] rCode Reference to the code snippet to insert before the current code.
|
||||
*/
|
||||
void Insert(const CCodeSnippet& rCode);
|
||||
|
||||
/**
|
||||
* @brief Append a code snippet behind the existing comment.
|
||||
* @remarks In case the code has been replaced by a comment text of the current or the to be inserted code snippet, the
|
||||
* result will be a combined comment text, disregarding the format.
|
||||
* @param[in] rCode Reference to the code snippet to appended behind the current code.
|
||||
*/
|
||||
void Append(const CCodeSnippet& rCode);
|
||||
|
||||
private:
|
||||
std::list<CToken> m_lstTokens; ///< Token list for the code snippet in raw format.
|
||||
std::string m_ssComment; ///< The comment text for the code snippet in text format.
|
||||
bool m_bCommentReplaced = false; ///< Comment replaced with SetComment function.
|
||||
bool m_bUseDefault = false; ///< When set, the format was removed from the code.
|
||||
};
|
||||
} // namespace toml_parser
|
||||
|
||||
#endif // !defined CODE_SNIPPET_H
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef CONFIG_EXCEPTION_H
|
||||
#define CONFIG_EXCEPTION_H
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#include "lexer_toml.h"
|
||||
#include <interfaces/toml.h>
|
||||
#include "exception.h"
|
||||
@@ -176,7 +190,15 @@ namespace toml_parser
|
||||
}
|
||||
}
|
||||
|
||||
void CLexer::Reset()
|
||||
void CLexer::Clear()
|
||||
{
|
||||
m_lstTokens.clear();
|
||||
m_itCursor = m_lstTokens.end();
|
||||
while (!m_stackExpectations.empty())
|
||||
m_stackExpectations.pop();
|
||||
}
|
||||
|
||||
void CLexer::ResetCursor()
|
||||
{
|
||||
m_itCursor = m_lstTokens.begin();
|
||||
}
|
||||
@@ -275,6 +297,54 @@ namespace toml_parser
|
||||
|
||||
void CLexer::SmartExtendNodeRange(CNodeTokenRange& rTokenRange) const
|
||||
{
|
||||
/*
|
||||
Consider the following TOML code:
|
||||
|
||||
# Out of scope comment
|
||||
|
||||
|
||||
# Pre comment with spaces before
|
||||
# Pre comment
|
||||
[table] # Post comment
|
||||
# Post comment without indentation
|
||||
|
||||
|
||||
# Out of scope comment
|
||||
|
||||
# Pre comment
|
||||
var = "1234" # Post comment
|
||||
# More post comment with indentation
|
||||
# Pre comment of var2 due to indentation identical or smaller then var2 def indentation
|
||||
var2 = "5678"
|
||||
# Pre comment of var3
|
||||
var3 = 9012 # Post comment of var3
|
||||
# More post comment of var3 (due ot space at the beginning)
|
||||
# Pre comment of var4 due to lesser indentation
|
||||
# Pre comment of var4 due to previous pre comment
|
||||
# Pre comment of var4
|
||||
var4 = 3456
|
||||
# Out of scope comment due to missing post comment at same line as defintion
|
||||
|
||||
var5 = [ 8765, # Post comment for var5[0] (event behind the comma)
|
||||
# Pre comment for var[1]
|
||||
4321 ] # Post comment for var5
|
||||
|
||||
var6 = 7890 # Post comment of var6
|
||||
# Out of scope comment due to missing indentation
|
||||
|
||||
--------------------------------------------
|
||||
The function uses as input the token range of the definition and extends the definition with comments and whitespace that
|
||||
belong to the definition. Therefore the following rules count:
|
||||
- Pre comments are defined on the lines before if they are covering the complete line and do not have an indentation larger
|
||||
than the indentation of the definition.
|
||||
- Post comments are defined at the same line following the definition.
|
||||
- If a comment with indentation is supplied before the definition, it is a pre-comment if there is no post-comment defined
|
||||
that connects to this comment. Otherwise the comment belongs to the definition before.
|
||||
- If comments follow on the lines directly behind the definition, they are post-comments when the comment has started at
|
||||
the same line as the definition and each comment has an indentation larger than the indentation of the next definition.
|
||||
- The extended range always includes the complete line including the indentation before and the line-break behind.
|
||||
*/
|
||||
|
||||
// Define an iterator range type.
|
||||
using TRange = std::pair<TTokenListIterator, TTokenListIterator>;
|
||||
|
||||
@@ -285,7 +355,7 @@ namespace toml_parser
|
||||
// return A pair containing the begin and one past the end of the line.
|
||||
auto fnGetLine = [this](const TTokenListIterator& rit) -> TRange
|
||||
{
|
||||
// When the end of the list is reached, there is no more line.
|
||||
// When the end of the list is reached, there are no more tokens in the line.
|
||||
if (rit == m_lstTokens.end())
|
||||
return std::make_pair(m_lstTokens.end(), m_lstTokens.end());
|
||||
|
||||
@@ -392,6 +462,26 @@ namespace toml_parser
|
||||
return bComments;
|
||||
};
|
||||
|
||||
// Check a range for a comment at the end (as last token or just before the line break).
|
||||
// remarks No validity check is done for the supplied token iterator.
|
||||
// param[in] rprRange Pair with the start position and one past the end position of a range.
|
||||
// return Returns whether the line contains a post comment.
|
||||
auto fnPostComment = [this](const TRange& rprRange) -> bool
|
||||
{
|
||||
auto itToken = rprRange.second;
|
||||
if (itToken == rprRange.first)
|
||||
return false;
|
||||
--itToken;
|
||||
if (itToken->Category() == ETokenCategory::token_comment)
|
||||
return true;
|
||||
if (itToken->Category() != ETokenCategory::token_syntax_new_line)
|
||||
return false;
|
||||
if (itToken == rprRange.first)
|
||||
return false;
|
||||
--itToken;
|
||||
return itToken->Category() == ETokenCategory::token_comment;
|
||||
};
|
||||
|
||||
// Check a range for empty line(s) only (ignore whitespace and newlines).
|
||||
// remarks No validity check is done for the supplied token iterator.
|
||||
// param[in] rprRange Pair with the start position and one past the end position of a range.
|
||||
@@ -530,75 +620,127 @@ namespace toml_parser
|
||||
// The beginning and the end cannot be the same, except for an empty range.
|
||||
if (itTokenBegin == itTokenEnd) return;
|
||||
|
||||
// Determine line boundaries of the current range
|
||||
// Determine whether there is a post comment at all
|
||||
auto itTokenBeginLine = itTokenBegin == m_lstTokens.begin() ? itTokenBegin : fnGetLine(itTokenBegin).first;
|
||||
auto itTokenEndLine = itTokenEnd;
|
||||
--itTokenEndLine;
|
||||
itTokenEndLine = fnGetLine(itTokenEndLine).second;
|
||||
|
||||
// Are the comments following the statement?
|
||||
bool bCommentsSameLine = fnCommentsOnly(std::make_pair(itTokenEnd, itTokenEndLine));
|
||||
bool bPostCommentPresent = fnCommentsOnly(std::make_pair(itTokenEnd, itTokenEndLine));
|
||||
|
||||
// Is there only whitespace before or after the statement
|
||||
bool bWhitespaceBefore = fnEmptyLine(std::make_pair(itTokenBeginLine, itTokenBegin)) && !bIsParent;
|
||||
bool bWhitespaceAfter = fnEmptyLine(std::make_pair(itTokenEnd, itTokenEndLine));
|
||||
bool bWhitespaceBefore = fnEmptyLine(TRange{itTokenBeginLine, itTokenBegin}) && !bIsParent;
|
||||
bool bWhitespaceAfter = fnEmptyLine(TRange{itTokenEnd, itTokenEndLine});
|
||||
|
||||
// Get the indentation length of the line (whether there is other comments before or not).
|
||||
size_t nIndentLen = fnGetIndentation(std::make_pair(itTokenBeginLine, itTokenEnd));
|
||||
// Post comments or whitespace until the end of the line definitely belongs to the extended range.
|
||||
if (bPostCommentPresent || bWhitespaceAfter) itTokenEnd = itTokenEndLine;
|
||||
|
||||
// Extend the range to include the beginning of the line and the end of the line.
|
||||
TRange prExtended = std::make_pair(bWhitespaceBefore ? itTokenBeginLine : itTokenBegin,
|
||||
(bCommentsSameLine || bWhitespaceAfter) ? itTokenEndLine : itTokenEnd);
|
||||
// Get the indentation length of the definition.
|
||||
size_t nDefIndent = fnGetIndentation(std::make_pair(itTokenBeginLine, itTokenEnd));
|
||||
|
||||
// Deal with whitespace and optionally comments before
|
||||
TRange prLine = prExtended;
|
||||
bool bEmptyLineBefore = false;
|
||||
if (bWhitespaceBefore)
|
||||
// Classify the lines
|
||||
struct SCommentLine
|
||||
{
|
||||
// Check for comments preceeding the range. If previous lines are having comments only (and optionally whitespace), and
|
||||
// the indentation is identical or less than the indentation of the range, the comment belongs to the range.
|
||||
while (fnGetPrevLine(prLine) && fnCommentsOnly(prLine) && fnGetIndentation(prLine) <= nIndentLen)
|
||||
prExtended.first = prLine.first;
|
||||
TRange tLine;
|
||||
size_t nIndent;
|
||||
};
|
||||
std::list<SCommentLine> lstLinesBefore, lstLinesBehind;
|
||||
|
||||
// Check whether there is an empty line before the range or the range starts at the first token in the list.
|
||||
bEmptyLineBefore = prLine.first != prExtended.first ? fnEmptyLine(prLine) : prLine.first == m_lstTokens.begin();
|
||||
}
|
||||
|
||||
// Deal with whitespace and optionally comments following
|
||||
if (bWhitespaceAfter)
|
||||
// Classify the lines before if the definition doesn't have other definition parts before at the same line.
|
||||
TRange tLine = {itTokenBeginLine, itTokenEndLine};
|
||||
bool bIncludeAllBefore = false;
|
||||
while (bWhitespaceBefore && tLine.first != m_lstTokens.begin())
|
||||
{
|
||||
// Check for comments following the range. But only when there are comments at the same line and the indentation of the
|
||||
// next line is larger than the range indentation or there is an empty line following.
|
||||
TRange prPotential = prExtended;
|
||||
bool bUsePotential = false;
|
||||
prLine = prExtended;
|
||||
while (bCommentsSameLine && fnGetNextLine(prLine) && fnCommentsOnly(prLine))
|
||||
if (!fnGetPrevLine(tLine))
|
||||
{
|
||||
if (bUsePotential || fnGetIndentation(prLine) > nIndentLen)
|
||||
{
|
||||
bUsePotential = true;
|
||||
prPotential.second = prLine.second;
|
||||
}
|
||||
else
|
||||
prExtended.second = prLine.second;
|
||||
bIncludeAllBefore = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check whether there is an empty line following the range or the range ends at the end of the list.
|
||||
bool bEmptyLineBeyond = prLine.second == m_lstTokens.end() ? true : fnEmptyLine(prLine);
|
||||
|
||||
// If an empty line is following and a potential extension was detected, extend the range
|
||||
if (bEmptyLineBeyond && bUsePotential)
|
||||
prExtended.second = prPotential.second;
|
||||
|
||||
// If there is an empty line before, include any empty lines until the next token or the end of the list.
|
||||
while (bEmptyLineBefore && bEmptyLineBeyond && fnGetNextLine(prLine) && fnEmptyLine(prLine))
|
||||
prExtended.second = prLine.second;
|
||||
if (fnEmptyLine(tLine))
|
||||
{
|
||||
bIncludeAllBefore = true;
|
||||
break;
|
||||
}
|
||||
if (!fnCommentsOnly(tLine))
|
||||
{
|
||||
// Check for a post comment
|
||||
bIncludeAllBefore = !fnPostComment(tLine);
|
||||
break;
|
||||
}
|
||||
lstLinesBefore.emplace_front(SCommentLine{tLine, fnGetIndentation(tLine)});
|
||||
}
|
||||
|
||||
rTokenRange.ExtendedNode(CTokenRange(fnToken(prExtended.first), fnToken(prExtended.second)));
|
||||
// Classify the lines behind (when there is a post comment or only whitespace following).
|
||||
size_t nNextStatementIndent = 0;
|
||||
tLine = {itTokenBeginLine, itTokenEndLine};
|
||||
bool bIncludeAllBehind = false;
|
||||
while ((bPostCommentPresent || bWhitespaceAfter) && tLine.second != m_lstTokens.end())
|
||||
{
|
||||
if (!fnGetNextLine(tLine))
|
||||
{
|
||||
bIncludeAllBehind = true;
|
||||
break;
|
||||
}
|
||||
if (fnEmptyLine(tLine))
|
||||
{
|
||||
// Note: one empty line still belongs to the extended range.
|
||||
lstLinesBehind.emplace_back(SCommentLine{tLine, 0});
|
||||
bIncludeAllBehind = true;
|
||||
break;
|
||||
}
|
||||
size_t nIndent = fnGetIndentation(tLine);
|
||||
if (!fnCommentsOnly(tLine))
|
||||
{
|
||||
nNextStatementIndent = nIndent;
|
||||
break;
|
||||
}
|
||||
lstLinesBehind.emplace_back(SCommentLine{tLine, fnGetIndentation(tLine)});
|
||||
}
|
||||
|
||||
TRange tExtended{itTokenBegin, itTokenEnd};
|
||||
if (!lstLinesBefore.empty())
|
||||
{
|
||||
// Include all comments before, or only the comments with an indentation identical or smaller than the def indentation.
|
||||
if (bIncludeAllBefore)
|
||||
tExtended.first = lstLinesBefore.front().tLine.first;
|
||||
else
|
||||
{
|
||||
auto it = lstLinesBefore.crbegin();
|
||||
while (it != lstLinesBefore.crend())
|
||||
{
|
||||
// NOTE: Use of SCommentLine structure here, prevents unusedStruct warning of cppcheck
|
||||
const SCommentLine& rsLine = *it;
|
||||
if (rsLine.nIndent > nDefIndent)
|
||||
break;
|
||||
tExtended.first = rsLine.tLine.first;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!lstLinesBehind.empty())
|
||||
{
|
||||
// Include all comments behind, or only the comments with an indentation larger than the indentation of the next def.
|
||||
// The latter only when there is a post-comment.
|
||||
if (bIncludeAllBehind)
|
||||
tExtended.second = lstLinesBehind.back().tLine.second;
|
||||
else if (bPostCommentPresent)
|
||||
{
|
||||
auto it = lstLinesBehind.cbegin();
|
||||
while (it != lstLinesBehind.cend())
|
||||
{
|
||||
// NOTE: Use of SCommentLine structure here, prevents unusedStruct warning of cppcheck
|
||||
const SCommentLine& rsLine = *it;
|
||||
if (rsLine.nIndent <= nNextStatementIndent)
|
||||
break;
|
||||
tExtended.second = rsLine.tLine.second;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rTokenRange.ExtendedNode(CTokenRange(fnToken(tExtended.first), fnToken(tExtended.second)));
|
||||
|
||||
// Determine if there are any more nodes before the end of the list.
|
||||
auto itFinal = prExtended.second;
|
||||
auto itFinal = tExtended.second;
|
||||
bool bNextNodeFound = false;
|
||||
while (itFinal != m_lstTokens.end() && !bNextNodeFound)
|
||||
{
|
||||
@@ -1161,7 +1303,7 @@ namespace toml_parser
|
||||
{
|
||||
token = CToken(ETokenCategory::token_syntax_array_close);
|
||||
m_stackExpectations.pop();
|
||||
if (m_stackExpectations.top() == EExpectation::expect_value_once)
|
||||
if (!m_stackExpectations.empty() && m_stackExpectations.top() == EExpectation::expect_value_once)
|
||||
m_stackExpectations.pop();
|
||||
}
|
||||
else
|
||||
@@ -1184,7 +1326,7 @@ namespace toml_parser
|
||||
rReader.Consume();
|
||||
token = CToken(ETokenCategory::token_syntax_inline_table_close);
|
||||
m_stackExpectations.pop();
|
||||
if (m_stackExpectations.top() == EExpectation::expect_value_once)
|
||||
if (!m_stackExpectations.empty() && m_stackExpectations.top() == EExpectation::expect_value_once)
|
||||
m_stackExpectations.pop();
|
||||
break;
|
||||
case ',':
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef LEXER_TOML_H
|
||||
#define LEXER_TOML_H
|
||||
|
||||
@@ -11,6 +25,32 @@
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
/**
|
||||
* @brief Safe stack implementation for an enum returning a default value when the stack is empty.
|
||||
*/
|
||||
template <typename TEnum, TEnum TDefault>
|
||||
class enum_stack : public std::stack<TEnum>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Pop the top-most entry from the stack if the stack is not empty.
|
||||
*/
|
||||
void pop()
|
||||
{
|
||||
if (!std::stack<TEnum>::empty())
|
||||
std::stack<TEnum>::pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the value of the top-most entry of the stack or if empty a default value.
|
||||
* @return The top-most or default value.
|
||||
*/
|
||||
TEnum top() const
|
||||
{
|
||||
return std::stack<TEnum>::empty() ? TDefault : std::stack<TEnum>::top();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Node token range used to regenerate the source from the node entries.
|
||||
* @details A node can have several token ranges identifying code that belongs to the node or precedes or succeeds the node. The
|
||||
@@ -166,10 +206,15 @@ namespace toml_parser
|
||||
*/
|
||||
void Feed(const std::string& rssString, bool bValueOnly = false);
|
||||
|
||||
/**
|
||||
* @brief Clear the lexer content.
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* @brief Reset the lexer cursor position.
|
||||
*/
|
||||
void Reset();
|
||||
void ResetCursor();
|
||||
|
||||
/**
|
||||
* @brief Navigation modes supported by the lexer.
|
||||
@@ -362,11 +407,13 @@ namespace toml_parser
|
||||
*/
|
||||
enum class EExpectation
|
||||
{
|
||||
undefined, ///< Error case when code is supplied that was not expected.
|
||||
expect_key, ///< A key is expected over a value
|
||||
expect_value, ///< A value is expected over a key
|
||||
expect_value_once, ///< A value is expected over a key once
|
||||
};
|
||||
std::stack<EExpectation> m_stackExpectations; ///< Tracking of key or value expectations in nested structures
|
||||
enum_stack<EExpectation, EExpectation::undefined>
|
||||
m_stackExpectations; ///< Tracking of key or value expectations in nested structures
|
||||
|
||||
const std::vector<std::string>
|
||||
m_vecKeyDelimiters{"\n", "\t", "\r", " ", "", ".", "=", "]"}; ///< Characters that delimit a key
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#include "lexer_toml_token.h"
|
||||
#include <sstream>
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef LEXER_TOML_TOKEN_H
|
||||
#define LEXER_TOML_TOKEN_H
|
||||
|
||||
@@ -310,7 +324,7 @@ namespace toml_parser
|
||||
|
||||
union
|
||||
{
|
||||
std::string m_ssContentString; ///< Token string content (used with keys, strings and errors).
|
||||
std::string m_ssContentString; ///< Token string contentv (used with keys, strings and errors).
|
||||
int64_t m_iContentInteger; ///< Token integer content
|
||||
double m_dContentFloatingpoint; ///< Token floatingpoint content
|
||||
bool m_bContentBoolean; ///< Token boolean content
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#include "miscellaneous.h"
|
||||
#include "exception.h"
|
||||
#include <sstream>
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef MISCELLANEOUS_H
|
||||
#define MISCELLANEOUS_H
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef PARSER_NODE_TOML_H
|
||||
#define PARSER_NODE_TOML_H
|
||||
|
||||
@@ -9,10 +23,9 @@
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
#include <interfaces/toml.h>
|
||||
#include <support/interface_ptr.h>
|
||||
#include "lexer_toml.h"
|
||||
#include "miscellaneous.h"
|
||||
#include "code_snippet.h"
|
||||
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
@@ -30,9 +43,11 @@ namespace toml_parser
|
||||
*/
|
||||
enum class EGenerateOptions : uint32_t
|
||||
{
|
||||
inline_when_possible = 0x01, ///< Try to generate as much as possible as inline nodes.
|
||||
explicit_when_possible = 0x02, ///< Try to generate as much as possible as explicit nodes.
|
||||
no_comments = 0x10, ///< Do not include comments
|
||||
inline_when_possible = 0x01, ///< Try to generate as much as possible as inline nodes.
|
||||
explicit_when_possible = 0x02, ///< Try to generate as much as possible as explicit nodes.
|
||||
no_comments = 0x10, ///< Do not include comments.
|
||||
reduce_whitespace = 0x20, ///< Add comments, but reduce extra newlines before and after the node.
|
||||
full_header = 0x40, ///< When generating tables or table arrays, include the header in generated code.
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -50,7 +65,8 @@ namespace toml_parser
|
||||
CGenContext(const std::string& rssPrefixKey = std::string(), uint32_t uiOptions = 0);
|
||||
|
||||
/**
|
||||
* @brief Called by the node that is generating the TOML. If not initialized before, initializes with the provided node.
|
||||
* @brief Called by the node that is generating the TOML. If not initialized before, extract the context from the node and
|
||||
* assign this node as top node for the code generation.
|
||||
* @param[in] rptrNode Reference to the node that could be used for initialization as top most node.
|
||||
*/
|
||||
void InitTopMostNode(const std::shared_ptr<const CNode>& rptrNode);
|
||||
@@ -65,9 +81,12 @@ namespace toml_parser
|
||||
/**
|
||||
* @brief Create a copy of the context class with a new key context.
|
||||
* @param[in] rssNewKeyContext Reference to the string containing the new key context.
|
||||
* @param[in] rptrNode Reference to the node pointer to extract the context from.
|
||||
* @param[in] bLastNode When set, this is the last node in the current view.
|
||||
* @return The copy of the contetx class.
|
||||
*/
|
||||
CGenContext CopyWithContext(const std::string& rssNewKeyContext) const;
|
||||
CGenContext CopyWithContext(const std::string& rssNewKeyContext, const std::shared_ptr<CNode>& rptrNode,
|
||||
bool bLastNode) const;
|
||||
|
||||
/**
|
||||
* @brief Get the stored prefix key that should be used for the TOML code generation.
|
||||
@@ -81,6 +100,24 @@ namespace toml_parser
|
||||
*/
|
||||
const std::string& KeyContext() const;
|
||||
|
||||
/**
|
||||
* @brief The key path composed of the prefix and the relative key path.
|
||||
* @return Reference to the key path string.
|
||||
*/
|
||||
const std::string& KeyPath() const;
|
||||
|
||||
/**
|
||||
* @brief The key path composed of the key kontext and the relative key path.
|
||||
* @return Reference to the key path string.
|
||||
*/
|
||||
const std::string& FullKeyPath() const;
|
||||
|
||||
/**
|
||||
* @brief The relative key path, relative to the current context.
|
||||
* @return Reference to the key path string.
|
||||
*/
|
||||
const std::string& RelKeyPath() const;
|
||||
|
||||
/**
|
||||
* @brief Is this the top most node?
|
||||
* @return Returns when the node is the top most node.
|
||||
@@ -100,17 +137,115 @@ namespace toml_parser
|
||||
*/
|
||||
bool CheckOption(EGenerateOptions eOption) const;
|
||||
|
||||
/**
|
||||
* @brief Is the last-node-flag set?
|
||||
* @return Returns whether the last-node-flag has been set indicating the node using this context to be the last node within
|
||||
* the current view.
|
||||
*/
|
||||
bool LastNode() const;
|
||||
|
||||
/**
|
||||
* @brief Node presentation form.
|
||||
*/
|
||||
enum class EPresentation
|
||||
{
|
||||
standard, ///< Standard presentation (root or within table/table-array)
|
||||
standard_inline, ///< Inline presentation (root or within table/table-array)
|
||||
embedded, ///< Embedded presentation (within array or inline-table)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get the presentation form of the node.
|
||||
* @return The node presentation extracted from the node and the generation context.
|
||||
*/
|
||||
EPresentation Presentation() const;
|
||||
|
||||
/**
|
||||
* @brief Is the node a standard node?
|
||||
* @return Returns 'true' when the node is a standard node.
|
||||
*/
|
||||
bool Standard() const;
|
||||
|
||||
/**
|
||||
* @brief Is the node an inline node?
|
||||
* @remarks Embedded nodes are also inline.
|
||||
* @return Returns 'true' when the node is an inline node.
|
||||
*/
|
||||
bool Inline() const;
|
||||
|
||||
/**
|
||||
* @brief Is the node an embedded node (within an inline table or array)?
|
||||
* @return Returns 'true' when the node is an embedded node.
|
||||
*/
|
||||
bool Embedded() const;
|
||||
|
||||
/**
|
||||
* @brief Does the node need an assignment (key and when inline, equal sign)?
|
||||
* @remarks Embedded nodes within an array do not need an assignment.
|
||||
* @return Returns 'true' when the node needs an assignment.
|
||||
*/
|
||||
bool Assignment() const;
|
||||
|
||||
/**
|
||||
* @brief For an embedded node, is a comma indicating the next node needed?
|
||||
* @remarks Some inline arrays can have a final comma behind the last embedded node.
|
||||
* @return Returns 'true' when a comma is needed.
|
||||
*/
|
||||
bool CommaNeeded() const;
|
||||
|
||||
/**
|
||||
* @brief Are comments and newlines allowed? For an embedded node, this might be prohibited. But can also explicitly be
|
||||
* defined in the context.
|
||||
* @remarks Inline tables require a one line definition for the embedded nodes.
|
||||
* @return Returns 'true' if comments and newlines are allowed.
|
||||
*/
|
||||
bool CommentAndNewlineAllowed() const;
|
||||
|
||||
/**
|
||||
* @brief Are newlines allowed? For an embedded node, this might be prohibited. But can also explicitly be defined in the
|
||||
* context.
|
||||
* @remarks Inline tables require a one line definition for the embedded nodes.
|
||||
* @return Returns 'true' if comments and newlines are allowed.
|
||||
*/
|
||||
bool NewlineAllowed() const;
|
||||
|
||||
/**
|
||||
* @brief For a standard (inline) node, is a newline required at the end of the node definition?
|
||||
* @returns Returns'true' if a newline is required behind the node definition.
|
||||
*/
|
||||
bool FinalNewline() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const CNode> m_ptrTopMostNode; ///< Top most node that is used for the generation. The parent nodes of
|
||||
///< the top most node will not be part of the node generation and if
|
||||
///< they contain child nodes in their view, the nodes are printed by
|
||||
///< their parent and not by their view.
|
||||
std::string m_ssPrefixKey; ///< Prefix key to be used during the generation of the TOML code.
|
||||
std::string m_ssKeyContext; ///< string containing the current context. The string must follow the
|
||||
///< key rules for separation with bare, literal and quoted keys.
|
||||
uint32_t m_uiOptions = 0; ///< Zero or more options to take into account when creating the text to
|
||||
///< the TOML nodes.
|
||||
bool m_bTopMost = true; ///< Set when this context is the top most context.
|
||||
void ExtractContext(const std::shared_ptr<const CNode>& rptrNode);
|
||||
|
||||
std::shared_ptr<const CNode> m_ptrTopMostNode; ///< Top most node that is used for the generation. The
|
||||
///< parent nodes of the top most node will not be part of
|
||||
///< the node generation and if they contain child nodes in
|
||||
///< their view, the nodes are printed by their parent and
|
||||
///< not by their view.
|
||||
std::string m_ssPrefixKey; ///< Prefix key to be used during the generation of the TOML
|
||||
///< not by their view.
|
||||
std::string m_ssKeyContext2;
|
||||
std::string m_ssKeyContext; ///< String containing the current context. The string must
|
||||
///< follow the key rules for separation with bare, literal
|
||||
///< and quoted keys.
|
||||
std::string m_ssKeyPath; ///< The key path composed of the prefix and the relative
|
||||
///< key path.
|
||||
std::string m_ssFullKeyPath; ///< The full key path composed of the key context and the
|
||||
///< relative key path.
|
||||
std::string m_ssRelKeyPath; ///< The relative key path, relative to the current context.
|
||||
uint32_t m_uiOptions = 0; ///< Zero or more options to take into account when creating
|
||||
///< the text to the TOML nodes.
|
||||
bool m_bTopMost = true; ///< Set when this context is the top most context.
|
||||
bool m_bLastNode = false; ///< Is this the last node in the current view?
|
||||
bool m_bFinalLastNode = false; ///< When set, this is the last (top level) node of the node
|
||||
///< hierarchy. Only child nodes can still follow.
|
||||
EPresentation m_ePresentation = EPresentation::standard; ///< Presentation of the node.
|
||||
bool m_bOneLine = false; ///< Set when the node is not allowed to cover more than
|
||||
///< one line (except when using multi-line strings).
|
||||
bool m_bAssignment = false; ///< Does the node need an assignment.
|
||||
bool m_bCommaNeeded = false; ///< Is a comma needed following the node definition?
|
||||
bool m_bFinalNewline = false; ///< Is a final newline behind the definition required?
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -118,7 +253,7 @@ namespace toml_parser
|
||||
*/
|
||||
class CNode :
|
||||
public std::enable_shared_from_this<CNode>, public sdv::IInterfaceAccess, public sdv::toml::INodeInfo,
|
||||
public sdv::toml::INodeDelete, public sdv::toml::INodeUpdate
|
||||
public sdv::toml::INodeUpdate
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
@@ -150,7 +285,6 @@ namespace toml_parser
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::toml::INodeInfo)
|
||||
SDV_INTERFACE_ENTRY(sdv::toml::INodeDelete)
|
||||
SDV_INTERFACE_ENTRY(sdv::toml::INodeUpdate)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
@@ -194,8 +328,9 @@ namespace toml_parser
|
||||
virtual sdv::any_t GetValue() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the index of this node within the parent collection. Overload of sdv::toml::INodeInfo::GetIndex.
|
||||
* @return The index of the node within the parent collection node or npos when no parent is available.
|
||||
* @brief Get the index of this node within the view collection (either the assigned view or the parent). Overload of
|
||||
* sdv::toml::INodeInfo::GetIndex.
|
||||
* @return The index of the node within the view collection node or npos when no parent is available.
|
||||
*/
|
||||
virtual uint32_t GetIndex() const override;
|
||||
|
||||
@@ -214,27 +349,25 @@ namespace toml_parser
|
||||
|
||||
/**
|
||||
* @brief Set or replace a comment for the node. Overload of sdv::toml::INodeInfo::SetComment.
|
||||
* @remarks This function can also be used to insert whitespace (with or without comments) when used in raw mode.
|
||||
* Set the comment text for the node. If a comment is proided as text (normal behavior), the comment text will be
|
||||
* formatted automatically when generating the TOML text. If the comment is provided as raw comment, the text should
|
||||
* contain all whitespace and the comment '#' character before the comment text.
|
||||
* Comments inserted before the enode will be inserted on the line before the node uness the comment is provided in raw
|
||||
* format and is ended with a newline and optionally whitespace. Comment inserted behind the node will be inserted on
|
||||
* @details Set the comment text for the node. If a comment is provided as text (normal behavior), the comment text will
|
||||
* be formatted automatically when generating the TOML text. If the comment text should not contain the comment
|
||||
* character '#' before the comment text.
|
||||
* Comments inserted before the node will be inserted on the line before the node unless the comment is provided in raw
|
||||
* format and is ended with a line-break and optionally whitespace. Comment inserted behind the node will be inserted on
|
||||
* the same line as the node.
|
||||
* Comments provided as text is automatically wrapped to 80 characters if possible. Newlines in the text will cause a
|
||||
* new comment line to start.
|
||||
* @param[in] ssComment String containing the comment text or the raw comment string to set.
|
||||
* @param[in] uiFlags One or more ECommentFlags flags influencing the behavior of the comment.
|
||||
* Comments provided as text is automatically wrapped to 132 characters if possible. Line-breaks in the text will cause
|
||||
* a new comment line to start.
|
||||
* @param[in] eType The comment type to set the comment text for.
|
||||
* @param[in] ssComment String containing the comment text to set.
|
||||
*/
|
||||
virtual void SetComment(const sdv::u8string& ssComment, uint32_t uiFlags) override;
|
||||
virtual void SetComment(sdv::toml::INodeInfo::ECommentType eType, const sdv::u8string& ssComment) override;
|
||||
|
||||
/**
|
||||
* Get the current comment for the node. Overload of sdv::toml::INodeInfo::GetComment.
|
||||
* @remarks To receive the whitespace formatting the node, use this function in raw mode.
|
||||
* @param[in] uiFlags One or more ECommentFlags flags identifying the string format of the comment to return.
|
||||
* @param[in] eType The comment type to get the comment text of.
|
||||
* @return String with the comment text or an empty string if no comment is available.
|
||||
*/
|
||||
virtual sdv::u8string GetComment(uint32_t uiFlags) override;
|
||||
virtual sdv::u8string GetComment(sdv::toml::INodeInfo::ECommentType eType) override;
|
||||
|
||||
/**
|
||||
* @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments
|
||||
@@ -242,6 +375,18 @@ namespace toml_parser
|
||||
*/
|
||||
virtual void AutomaticFormat() override;
|
||||
|
||||
/**
|
||||
* @brief Is the node inline? Overload of sdv::toml::INodeInfo::IsInline.
|
||||
* @return Returns whether the node is defined as inline node.
|
||||
*/
|
||||
virtual bool IsInline() const override;
|
||||
|
||||
/**
|
||||
* @brief Is the node defined as standard node? Overload of sdv::toml::INodeInfo::IsStandard.
|
||||
* @return Returns whether the node is defined as standard node.
|
||||
*/
|
||||
virtual bool IsStandard() const override;
|
||||
|
||||
/**
|
||||
* @brief Update the node with TOML code information. The default implementation takes the comment and whitespace around the
|
||||
* node and stores this for node reconstruction.
|
||||
@@ -249,19 +394,6 @@ namespace toml_parser
|
||||
*/
|
||||
virtual void UpdateNodeCode(const CNodeTokenRange& rNodeRange);
|
||||
|
||||
/**
|
||||
* @brief Delete the current node. Overload of sdv::toml::INodeDelete::DeleteNode.
|
||||
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
|
||||
* @return Returns whether the deletion was successful.
|
||||
*/
|
||||
virtual bool DeleteNode() override;
|
||||
|
||||
/**
|
||||
* @brief Is this node marked as deleted?
|
||||
* @return Returns whether this node has been deleted.
|
||||
*/
|
||||
bool IsDeleted() const;
|
||||
|
||||
/**
|
||||
* @brief Change the key name of the node (if the node is not a value node of an array). Overload of
|
||||
* sdv::toml::INodeUpdate::ChangeName.
|
||||
@@ -275,7 +407,7 @@ namespace toml_parser
|
||||
/**
|
||||
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
|
||||
* @remarks Only valid for value nodes. Changing the value type is not supported.
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
|
||||
* string. Conversion is automatically done to int64, double float, bool or u8string.
|
||||
* @return Returns whether the value change was successful.
|
||||
*/
|
||||
@@ -297,6 +429,19 @@ namespace toml_parser
|
||||
*/
|
||||
virtual bool MoveDown() override;
|
||||
|
||||
/**
|
||||
* @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode.
|
||||
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
|
||||
* @return Returns whether the deletion was successful.
|
||||
*/
|
||||
virtual bool DeleteNode() override;
|
||||
|
||||
/**
|
||||
* @brief Is this node marked as deleted?
|
||||
* @return Returns whether this node has been deleted.
|
||||
*/
|
||||
bool IsDeleted() const;
|
||||
|
||||
/**
|
||||
* @brief Do a dynamic cast to one of the base types of the node.
|
||||
* @return Casted shared pointer to the base type if the type is valid, or an empty pointer if not.
|
||||
@@ -311,19 +456,19 @@ namespace toml_parser
|
||||
template <typename TNodeType>
|
||||
std::shared_ptr<const TNodeType> Cast() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the parent node pointer
|
||||
* @return Returns the parent node pointer or an empty pointer when no parent was assigned or the stored weak pointer could
|
||||
* not be locked.
|
||||
*/
|
||||
std::shared_ptr<CNodeCollection> GetParentPtr() const;
|
||||
|
||||
/**
|
||||
* @brief Set the parent node.
|
||||
* @param[in] rptrParent Reference to the node to assign to this node as a parent.
|
||||
*/
|
||||
void SetParentPtr(const std::shared_ptr<CNodeCollection>& rptrParent);
|
||||
|
||||
/**
|
||||
* @brief Gets the parent node pointer.
|
||||
* @return Returns the parent node pointer or an empty pointer when no parent was assigned or the stored weak pointer could
|
||||
* not be locked.
|
||||
*/
|
||||
std::shared_ptr<CNodeCollection> GetParentPtr() const;
|
||||
|
||||
/**
|
||||
* @brief Get the parent path of the node.
|
||||
* @return Return the parent path if existining and not a root.
|
||||
@@ -337,6 +482,12 @@ namespace toml_parser
|
||||
*/
|
||||
void SetViewPtr(const std::shared_ptr<CNodeCollection>& rptrView);
|
||||
|
||||
/**
|
||||
* @brief Gets the view definition node pointer.
|
||||
* @return Returns the store view definition node pointer or an empty pointer when no view was assigned.
|
||||
*/
|
||||
std::shared_ptr<CNodeCollection> GetViewPtr() const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the node is part of the view.
|
||||
* @details The node is part of the view if the supplied pointer is identical to the view definition pointer, when the view
|
||||
@@ -367,95 +518,8 @@ namespace toml_parser
|
||||
*/
|
||||
virtual std::string GenerateTOML(const CGenContext& rContext = CGenContext()) const = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Compose a custom path from the node key path using a key prefix and a context.
|
||||
* @param[in] rssPrefixKey The prefix to insert at as a base to the key tree.
|
||||
* @param[in] rssContext The context that is used to define the relative portion of the key. To determine the relative
|
||||
* portion, the context string contains the same prefix as is supplied in rssPrefixKey.
|
||||
* @return Returns the custom path composed of the prefix and the relative portion of the original path.
|
||||
*/
|
||||
std::string GetCustomPath(const std::string& rssPrefixKey, const std::string& rssContext) const;
|
||||
|
||||
/**
|
||||
* @brief Comment or code snippet structure.
|
||||
* @details Each node has multiple code snippets used to reproduce the exact code. For example with an assignment:
|
||||
* @code
|
||||
*
|
||||
* # This is out of scope comment before
|
||||
*
|
||||
* # This is comment before
|
||||
* var_a = "abc" # comment behind
|
||||
* # more comment behind
|
||||
*
|
||||
* # This is out of scope comment behind
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* The code snippets are identified as follows:
|
||||
* @code
|
||||
* <out of scope comment before>
|
||||
* <comment before>
|
||||
* <space before><key><space>=<space><value><comment behind>
|
||||
* <out of scope comment behind>
|
||||
* @endcode
|
||||
*/
|
||||
class CCodeSnippet
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Access the token list.
|
||||
* @return Reference to the list with tokens.
|
||||
*/
|
||||
std::list<CToken>& List();
|
||||
|
||||
/**
|
||||
* @brief Access the comment text string.
|
||||
* @return Reference to the comment string.
|
||||
*/
|
||||
std::string& Str();
|
||||
|
||||
/**
|
||||
* @brief Mode the code snippet composer should run in.
|
||||
*/
|
||||
enum class EComposeMode
|
||||
{
|
||||
compose_inline, ///< Compose as inline whitespace and comment. If there is no token list and no comment
|
||||
///< string, compose as one space. If there is only a comment string, insert a space, add
|
||||
///< the comment followed by an obligatory newline, and insert spaces until the next
|
||||
///< provided position. If there are tokens with a comment token, replace the comment. If
|
||||
///< there are tokens without comment, add the comment, newline and spaces.
|
||||
compose_before, ///< Compose as comment assigned to and located before the node. If there is no token list
|
||||
///< and no comment string, doesn't add anything. If there is only a comment string, adds
|
||||
///< the comment followed by the obligatory newline. If there are tokens with a comment
|
||||
///< token, replace the comment. If there are tokens without the comment, place the comment
|
||||
///< before the last newline or when not available, at the end of the tokens followed by a
|
||||
///< new newline.
|
||||
compose_behind, ///< Compose as comment assigned to and located behind the node. If there is no token list
|
||||
///< and no comment string, add a newline. If there is a comment string and no tokens,
|
||||
///< add a space, the comment string followed by the obligatory newline. If there is a token
|
||||
///< list without comment, add a comment before the newline or at the end with an additional
|
||||
///< newline.
|
||||
compose_standalone, ///< Compose as stand-alone comment. Replace any token list if a comment string is
|
||||
///< available.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Compose a conde string from the stored tokens and/or string.
|
||||
* @param[in] eMode The mode the composer should run in.
|
||||
* @param[in] nAssignmentOffset The offset for a next assignent; only used for inline composition.
|
||||
* @param[in] nCommentOffset The offset to insert a multi-line comment; only used for inline and behind composition.
|
||||
* @return The composed code string.
|
||||
*/
|
||||
std::string Compose(EComposeMode eMode, size_t nAssignmentOffset = 0, size_t nCommentOffset = 0) const;
|
||||
|
||||
private:
|
||||
std::list<CToken> m_lstTokens; ///< Token list for the code snippet in raw format.
|
||||
std::string m_ssComment; ///< The comment text for the code snippet in text format.
|
||||
};
|
||||
|
||||
// White space and comment preservation indices for code generation.
|
||||
const size_t m_nPreNodeCode = 0; ///< Code snippet before the node. Corresponds to
|
||||
const size_t m_nPreNodeCode = 0; ///< Code snippet before the node. Corresponds to
|
||||
///< sdv::toml::INodeInfo::ECommentFlags::comment_before.
|
||||
const size_t m_nPostNodeCode = 1; ///< Comment behind the node. Corresponds to
|
||||
///< sdv::toml::INodeInfo::ECommentFlags::comment_behind.
|
||||
@@ -473,7 +537,7 @@ namespace toml_parser
|
||||
|
||||
/**
|
||||
* @brief Get the code snippet.
|
||||
* @param[in] nIndex The comment type index to get the comment for.
|
||||
* @param[in] nIndex The comment type index to get the code for.
|
||||
* @param[in] rssKey Reference to the key to be used for code snippet identification.
|
||||
* @return Reference to the comment structure of the comment. If the provided index is not available in the vector,
|
||||
* returns an empty code snippet.
|
||||
@@ -481,15 +545,38 @@ namespace toml_parser
|
||||
const CCodeSnippet& CodeSnippet(size_t nIndex, const std::string& rssKey = std::string()) const;
|
||||
|
||||
/**
|
||||
* @brief Get the code snippet (write access). This allows moving the snippet from one node to the another node.
|
||||
* @remarks Since the request to the code snippet could change the location of the vector allocation, access to the code
|
||||
* snippet is valid until the next code snippet is requested.
|
||||
* @param[in] nIndex The comment type index to get the comment for.
|
||||
* @brief Get the code snippet (write access).
|
||||
* @param[in] nIndex The comment type index to get the code for.
|
||||
* @param[in] rssKey Reference to the key to be used for code snippet identification.
|
||||
* @return Reference to the comment structure of the comment.
|
||||
*/
|
||||
CCodeSnippet& CodeSnippet(size_t nIndex, const std::string& rssKey = std::string());
|
||||
|
||||
/**
|
||||
* @brief Get the code snippet using the comment type.
|
||||
* @param[in] eType The comment type to get the code for.
|
||||
* @return Reference to the comment structure of the comment. If the provided index is not available in the vector,
|
||||
* returns an empty code snippet.
|
||||
*/
|
||||
const CCodeSnippet& CodeSnippet(sdv::toml::INodeInfo::ECommentType eType) const;
|
||||
|
||||
/**
|
||||
* @brief Get the code snippet (write access) using the comment type.
|
||||
* @param[in] eType The comment type to get the code for.
|
||||
* @return Reference to the comment structure of the comment. If the provided index is not available in the vector,
|
||||
* returns an empty code snippet.
|
||||
*/
|
||||
CCodeSnippet& CodeSnippet(sdv::toml::INodeInfo::ECommentType eType);
|
||||
|
||||
/**
|
||||
* @brief Compose a custom path from the node key path using a key prefix and a context.
|
||||
* @param[in] rssPrefixKey The prefix to insert at as a base to the key tree.
|
||||
* @param[in] rssContext The context that is used to define the relative portion of the key. To determine the relative
|
||||
* portion, the context string contains the same prefix as is supplied in rssPrefixKey.
|
||||
* @return Returns the custom path composed of the prefix and the relative portion of the original path.
|
||||
*/
|
||||
std::string GetCustomPath(const std::string& rssPrefixKey, const std::string& rssContext) const;
|
||||
|
||||
private:
|
||||
std::weak_ptr<CNodeCollection> m_ptrParent; ///< Weak pointer to the parent node (if existing).
|
||||
std::weak_ptr<CNodeCollection> m_ptrView; ///< Weak pointer to the view node (if existing and explicitly set).
|
||||
@@ -610,6 +697,12 @@ namespace toml_parser
|
||||
*/
|
||||
std::string RawValueText() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief When updating the node, reset the raw value text.
|
||||
*/
|
||||
void ResetRawValueText();
|
||||
|
||||
private:
|
||||
std::string m_ssRawValue; ///< Raw value string.
|
||||
};
|
||||
@@ -647,7 +740,7 @@ namespace toml_parser
|
||||
/**
|
||||
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
|
||||
* @remarks Only valid for value nodes. Changing the value type is not supported.
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
|
||||
* string. Conversion is automatically done to int64, double float, bool or u8string.
|
||||
* @return Returns whether the value change was successful.
|
||||
*/
|
||||
@@ -660,7 +753,7 @@ namespace toml_parser
|
||||
virtual std::string ValueText() const override;
|
||||
|
||||
private:
|
||||
bool m_bVal = false; ///< Value in case of boolean node.
|
||||
bool m_bVal = false; ///< Value in case of virtual bool node.
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -696,7 +789,7 @@ namespace toml_parser
|
||||
/**
|
||||
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
|
||||
* @remarks Only valid for value nodes. Changing the value type is not supported.
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
|
||||
* string. Conversion is automatically done to int64, double float, bool or u8string.
|
||||
* @return Returns whether the value change was successful.
|
||||
*/
|
||||
@@ -745,7 +838,7 @@ namespace toml_parser
|
||||
/**
|
||||
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
|
||||
* @remarks Only valid for value nodes. Changing the value type is not supported.
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
|
||||
* string. Conversion is automatically done to int64, double float, bool or u8string.
|
||||
* @return Returns whether the value change was successful.
|
||||
*/
|
||||
@@ -806,7 +899,7 @@ namespace toml_parser
|
||||
/**
|
||||
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
|
||||
* @remarks Only valid for value nodes. Changing the value type is not supported.
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
|
||||
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
|
||||
* string. Conversion is automatically done to int64, double float, bool or u8string.
|
||||
* @return Returns whether the value change was successful.
|
||||
*/
|
||||
@@ -826,8 +919,11 @@ namespace toml_parser
|
||||
/**
|
||||
* @brief Base structure for arrays and tables.
|
||||
*/
|
||||
class CNodeCollection : public CNode, public sdv::toml::INodeCollection, public sdv::toml::INodeCollectionInsert
|
||||
class CNodeCollection : public CNode, public sdv::toml::INodeCollection, public sdv::toml::INodeCollectionInsert,
|
||||
public sdv::toml::INodeCollectionConvert
|
||||
{
|
||||
// Friend class CNode.
|
||||
friend CNode;
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
@@ -842,9 +938,16 @@ namespace toml_parser
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::toml::INodeCollection)
|
||||
SDV_INTERFACE_ENTRY(sdv::toml::INodeCollectionInsert)
|
||||
SDV_INTERFACE_ENTRY(sdv::toml::INodeCollectionConvert)
|
||||
SDV_INTERFACE_CHAIN_BASE(CNode)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments
|
||||
* will not be changed. Overload of sdv::toml::INodeInfo::AutomaticFormat.
|
||||
*/
|
||||
virtual void AutomaticFormat() override;
|
||||
|
||||
/**
|
||||
* @brief Returns the amount of nodes. Overload of sdv::toml::INodeCollection::GetCount.
|
||||
* @return The amount of nodes.
|
||||
@@ -900,7 +1003,7 @@ namespace toml_parser
|
||||
* @param[in] ssName Name of the node to insert. Will be ignored for an array collection. The name must adhere to the
|
||||
* key names defined by the TOML specification. Defining the key multiple times is not allowed. Quotation of key names
|
||||
* is done automatically; the parser decides itself whether the key is bare-key, a literal key or a quoted key.
|
||||
* @param[in] anyValue The value of the node, being either an integer, floating point number, boolean value or a string.
|
||||
* @param[in] anyValue The value of the node, being either an integer, floating point number, virtual bool value or a string.
|
||||
* Conversion is automatically done to int64, double float, bool or u8string.
|
||||
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
|
||||
*/
|
||||
@@ -928,13 +1031,15 @@ namespace toml_parser
|
||||
* collection count to insert the node at the end of the collection. Table nodes cannot be inserted before value nodes
|
||||
* or arrays. If the index is referencing a position before a value node or an array, the index is automatically
|
||||
* corrected.
|
||||
* @param[in] ssKeyName Name of the table node to insert. Will be ignored if the current node is an array collection.
|
||||
* @param[in] ssName Name of the table node to insert. Will be ignored if the current node is an array collection.
|
||||
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
|
||||
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
|
||||
* literal key or a quoted key.
|
||||
* @param[in] ePreference The preferred form of the node to be inserted.
|
||||
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* InsertTable(uint32_t uiIndex, const sdv::u8string& ssKeyName) override;
|
||||
virtual sdv::IInterfaceAccess* InsertTable(uint32_t uiIndex, const sdv::u8string& ssName,
|
||||
sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) override;
|
||||
|
||||
/**
|
||||
* @brief Insert a table array into the collection at the location before the supplied index. Overload of
|
||||
@@ -947,36 +1052,82 @@ namespace toml_parser
|
||||
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
|
||||
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
|
||||
* literal key or a quoted key.
|
||||
* @param[in] ePreference The preferred form of the node to be inserted.
|
||||
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* InsertTableArray(uint32_t uiIndex, const sdv::u8string& ssName) override;
|
||||
virtual sdv::IInterfaceAccess* InsertTableArray(uint32_t uiIndex, const sdv::u8string& ssName,
|
||||
sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) override;
|
||||
|
||||
/**
|
||||
* @brief Insert a TOML string as a child of the current collection node. If the collection is a table, the TOML string
|
||||
* should contain values and inline/external/array-table nodes with names. If the collection is an array, the TOML
|
||||
* string should contain and inline table nodes without names. Overload of sdv::toml::INodeCollectionInsert::InsertTOML.
|
||||
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
|
||||
* collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value
|
||||
* nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically
|
||||
* corrected.
|
||||
* @param[in] ssTOML The TOML string to insert.
|
||||
* @param[in] bRollbackOnPartly If only part of the nodes could be inserted, no node will be inserted.
|
||||
* @return The result of the insertion.
|
||||
*/
|
||||
virtual sdv::toml::INodeCollectionInsert::EInsertResult InsertTOML(const sdv::u8string& ssTOML,
|
||||
virtual sdv::toml::INodeCollectionInsert::EInsertResult InsertTOML(uint32_t uiIndex, const sdv::u8string& ssTOML,
|
||||
bool bRollbackOnPartly) override;
|
||||
|
||||
/**
|
||||
* @brief Delete the current node. Overload of sdv::toml::INodeDelete::DeleteNode.
|
||||
* @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode.
|
||||
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
|
||||
* @return Returns whether the deletion was successful.
|
||||
*/
|
||||
virtual bool DeleteNode() override;
|
||||
|
||||
//protected:
|
||||
/**
|
||||
* @brief Remove a node from the collection.
|
||||
* @brief Can the node convert to an inline definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeInline.
|
||||
* @return Returns whether the conversion to inline is possible. Returns 'true' when the node is already inline.
|
||||
*/
|
||||
virtual bool CanMakeInline() const override;
|
||||
|
||||
/**
|
||||
* @brief Convert the node to an inline node. Overload of sdv::toml::INodeCollectionConvert::MakeInline.
|
||||
* @return Returns whether the conversion was successful. Returns 'true' when the node was already inline.
|
||||
*/
|
||||
virtual bool MakeInline() override;
|
||||
|
||||
/**
|
||||
* @brief Can the node convert to a standard definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeStandard.
|
||||
* @return Returns whether the conversion to standard is possible. Returns 'true' when the node is already defined as
|
||||
* standard node.
|
||||
*/
|
||||
virtual bool CanMakeStandard() const override;
|
||||
|
||||
/**
|
||||
* @brief Convert the node to a standard node. Overload of sdv::toml::INodeCollectionConvert::MakeStandard.
|
||||
* @return Returns whether the conversion was successful. Returns 'true' when the node was already defined as standard
|
||||
* node.
|
||||
*/
|
||||
virtual bool MakeStandard() override;
|
||||
|
||||
/**
|
||||
* @brief Delete a node from the collection.
|
||||
* @remarks The node will not be deleted, but placed in the recycle bin. Deletion will take place at collection destruction.
|
||||
* @param[in] rptrNode Reference to the smart pointer pointing to the node to remove.
|
||||
* @return Returns whether the removal was successful.
|
||||
*/
|
||||
bool RemoveNode(const std::shared_ptr<CNode>& rptrNode);
|
||||
bool DeleteNode(const std::shared_ptr<CNode>& rptrNode);
|
||||
|
||||
/**
|
||||
* @brief Insert the node into the view.
|
||||
* @remarks The node must be a descendant (direct child or an indirect child) of the node.
|
||||
* @details Insert the node into the vector (and remove it from any previous vector if still assigned). Inline nodes can be
|
||||
* assigned to stadard and inline nodes. Standard nodes can only be assigned to standard nodes. In the vector, first the
|
||||
* inline nodes and then the standard nodes are located. This means that an inline node will be placed before standard nodes
|
||||
* and standard nodes behind inline nodes, regardless of the index provided.
|
||||
* @param[in] uiIndex Location within the vector to insert the node. Could be sdv::toml::npos to insert as last node.
|
||||
* @param[in] rptrNode Reference to the smart pointer to the node to set the view for. If the node is not a direct child
|
||||
* node, the view pointer of the node will be set.
|
||||
* @return Returns true when the insertion was successful, false when node (likely because the node to insert is a standard
|
||||
* node, whereas this node is an inline node.
|
||||
*/
|
||||
bool InsertIntoView(uint32_t uiIndex, const std::shared_ptr<CNode>& rptrNode);
|
||||
|
||||
/**
|
||||
* @brief Remove a node from a view.
|
||||
@@ -985,20 +1136,19 @@ namespace toml_parser
|
||||
*/
|
||||
bool RemoveFromView(const std::shared_ptr<CNode>& rptrNode);
|
||||
|
||||
/**
|
||||
* @brief Check whether this node is the last node in the collection.
|
||||
* @param[in] rptrNode Reference to the smart pointer pointing to the node to check for.
|
||||
* @return Returns whether the provided node is the last node in the collection.
|
||||
*/
|
||||
bool CheckLast(const std::shared_ptr<CNode>& rptrNode);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Find the index belonging to the provided node.
|
||||
* @param[in] rptrNode Reference to the smart pointer holding the node to return the index for.
|
||||
* @return Return the node index. Returns npos if the node could not be found.
|
||||
*/
|
||||
uint32_t FindIndex(const std::shared_ptr<CNode>& rptrNode);
|
||||
uint32_t FindIndex(const std::shared_ptr<CNode>& rptrNode) const;
|
||||
|
||||
/**
|
||||
* @brief Is the provided child node a direct or indirect child node?
|
||||
* @param[in] rptrNode Reference to the smart pointer of the potential descendant node.
|
||||
* @return Returns whether the provided node is a descendant of the this node.
|
||||
*/
|
||||
bool IsDescendant(const std::shared_ptr<CNode>& rptrNode) const;
|
||||
|
||||
/**
|
||||
* @brief Generic inserting function for nodes.
|
||||
@@ -1023,6 +1173,24 @@ namespace toml_parser
|
||||
template <typename TNodeType, typename... TArgs>
|
||||
std::shared_ptr<CNode> Insert(uint32_t uiIndex, const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs);
|
||||
|
||||
/**
|
||||
* @brief Combine the collection with the provided content (mathematical union).
|
||||
* @details Update the child nodes with the child nodes of the provided collection. Extend the collection with nodes that do
|
||||
* not exist and update the existing nodes with new values.
|
||||
* @param[in] rptrCollection Reference to the collection being used for this operation.
|
||||
* @return Returns whether the combination was successful.
|
||||
*/
|
||||
virtual bool Combine(const std::shared_ptr<CNodeCollection>& rptrCollection) = 0;
|
||||
|
||||
/**
|
||||
* @brief Reduce the collection with by the provided content (mathematical difference).
|
||||
* @details Reduce the child node with the child nodes already defined and identical in the provided collection. Different
|
||||
* nodes or nodes that are not present in the collection remain.
|
||||
* @param[in] rptrCollection Reference to the collection being used for this operation.
|
||||
* @return Returns whether the reduction was successful.
|
||||
*/
|
||||
virtual bool Reduce(const std::shared_ptr<CNodeCollection>& rptrCollection) = 0;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief When set, the child nodes need grouping (values following each other, tables and table arrays at the end).
|
||||
@@ -1156,8 +1324,23 @@ namespace toml_parser
|
||||
*/
|
||||
virtual void MakeExplicit() override;
|
||||
|
||||
/**
|
||||
* @brief Combine the collection with the provided content (mathematical union). Overload of CNodeCollection::Combine.
|
||||
* @details Update the child nodes with the child nodes of the provided collection. Extend the collection with nodes that do
|
||||
* not exist and update the existing nodes with new values.
|
||||
* @param[in] rptrCollection Reference to the collection being used for this operation.
|
||||
* @return Returns whether the combination was successful.
|
||||
*/
|
||||
virtual bool Combine(const std::shared_ptr<CNodeCollection>& rptrCollection) override;
|
||||
|
||||
//bool m_bOpenToAddChildren = true; ///< If internal table, the table can be extended until the table is closed.
|
||||
/**
|
||||
* @brief Reduce the collection with by the provided content (mathematical difference). Overload of CNodeCollection::Reduce.
|
||||
* @details Reduce the child node with the child nodes already defined and identical in the provided collection. Different
|
||||
* nodes or nodes that are not present in the collection remain.
|
||||
* @param[in] rptrCollection Reference to the collection being used for this operation.
|
||||
* @return Returns whether the reduction was successful.
|
||||
*/
|
||||
virtual bool Reduce(const std::shared_ptr<CNodeCollection>& rptrCollection) override;
|
||||
|
||||
private:
|
||||
bool m_bDefinedExplicitly = true; ///< When set, the table is defined explicitly.
|
||||
@@ -1267,6 +1450,13 @@ namespace toml_parser
|
||||
*/
|
||||
bool TableArray() const;
|
||||
|
||||
/**
|
||||
* @brief Can the node convert to a standard definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeStandard.
|
||||
* @return Returns whether the conversion to standard is possible. Returns 'true' when the node is already defined as
|
||||
* standard node.
|
||||
*/
|
||||
virtual bool CanMakeStandard() const override;
|
||||
|
||||
/**
|
||||
* @brief The derived class from the node collection can be inline or not. Overload of CNode::Inline.
|
||||
* @return Returns whether the node is an inline node.
|
||||
@@ -1287,9 +1477,34 @@ namespace toml_parser
|
||||
*/
|
||||
virtual bool Inline(bool bInline) override;
|
||||
|
||||
/**
|
||||
* @brief Does the last child node need a comma following the node?
|
||||
* @return Returns whether the array was defined with the last node requiring a comma following the node.
|
||||
*/
|
||||
bool LastNodeWithSucceedingComma() const;
|
||||
|
||||
/**
|
||||
* @brief Combine the collection with the provided content (mathematical union). Overload of CNodeCollection::Combine.
|
||||
* @details Update the child nodes with the child nodes of the provided collection. Extend the collection with nodes that do
|
||||
* not exist and update the existing nodes with new values.
|
||||
* @param[in] rptrCollection Reference to the collection being used for this operation.
|
||||
* @return Returns whether the combination was successful.
|
||||
*/
|
||||
virtual bool Combine(const std::shared_ptr<CNodeCollection>& rptrCollection) override;
|
||||
|
||||
/**
|
||||
* @brief Reduce the collection with by the provided content (mathematical difference). Overload of CNodeCollection::Reduce.
|
||||
* @details Reduce the child node with the child nodes already defined and identical in the provided collection. Different
|
||||
* nodes or nodes that are not present in the collection remain.
|
||||
* @param[in] rptrCollection Reference to the collection being used for this operation.
|
||||
* @return Returns whether the reduction was successful.
|
||||
*/
|
||||
virtual bool Reduce(const std::shared_ptr<CNodeCollection>& rptrCollection) override;
|
||||
|
||||
private:
|
||||
bool m_bDefinedExplicitly = true; ///< When set, the array is defined explicitly.
|
||||
bool m_bInline = false; ///< Flag determining whether the table is inline or not.
|
||||
bool m_bDefinedExplicitly = true; ///< When set, the array is defined explicitly.
|
||||
bool m_bInline = false; ///< Flag determining whether the table is inline or not.
|
||||
bool m_bLastChildNodeWithComma = false; ///< Set when the last child node of the array initially has a comma behind the node.
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1323,7 +1538,7 @@ namespace toml_parser
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Delete the current node. Overload of sdv::toml::INodeDelete::DeleteNode.
|
||||
* @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode.
|
||||
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
|
||||
* @return Returns whether the deletion was successful.
|
||||
*/
|
||||
@@ -1387,10 +1602,9 @@ namespace toml_parser
|
||||
{
|
||||
ptrTableArray = std::make_shared<CTableArray>(Parser(), prKey.first.get().StringValue(),
|
||||
prKey.first.get().RawString());
|
||||
ptrTableArray->SetParentPtr(Cast<CNodeCollection>());
|
||||
|
||||
// Add to the list
|
||||
m_lstNodes.push_back(ptrTableArray);
|
||||
// Set the parent pointer; this will add the node to the list.
|
||||
ptrTableArray->SetParentPtr(Cast<CNodeCollection>());
|
||||
}
|
||||
else
|
||||
ptrTableArray = (*itNode)->template Cast<CTableArray>();
|
||||
@@ -1401,6 +1615,7 @@ namespace toml_parser
|
||||
// Create the table.
|
||||
ptrNode = ptrTableArray->Insert<CTable>(sdv::toml::npos, CTokenRange(prKey.first, prKey.first.get().Next()),
|
||||
false, true);
|
||||
ptrNode->SetViewPtr(Cast<CNodeCollection>());
|
||||
} else if (!Cast<CArray>() && itNode != m_lstNodes.end())
|
||||
{
|
||||
// If existing... this might be a duplicate if not explicitly defined before.
|
||||
@@ -1422,10 +1637,9 @@ namespace toml_parser
|
||||
// Create the target node.
|
||||
ptrNode = std::make_shared<TNodeType>(Parser(), prKey.first.get().StringValue(), prKey.first.get().RawString(),
|
||||
rtArgs...);
|
||||
ptrNode->SetParentPtr(Cast<CNodeCollection>());
|
||||
|
||||
// Add to the list
|
||||
m_lstNodes.push_back(ptrNode);
|
||||
// Set the parent pointer; this will add the node to the list.
|
||||
ptrNode->SetParentPtr(Cast<CNodeCollection>());
|
||||
|
||||
// If the current node is implicit, take over the inline flag (this determines whether a sub-table definition is
|
||||
// allowed or not).
|
||||
@@ -1480,7 +1694,6 @@ namespace toml_parser
|
||||
// Create an implicit table node.
|
||||
ptrNode = std::make_shared<CTable>(Parser(), prKey.first.get().StringValue(), prKey.first.get().RawString(), false, false);
|
||||
ptrNode->SetParentPtr(Cast<CNodeCollection>());
|
||||
m_lstNodes.push_back(ptrNode);
|
||||
}
|
||||
|
||||
// Insert the node in the child node
|
||||
@@ -1490,15 +1703,14 @@ namespace toml_parser
|
||||
ptrNode = ptrNodeCollection->Insert<TNodeType>(sdv::toml::npos, prKey.second, rtArgs...);
|
||||
if (!ptrNode)
|
||||
throw XTOMLParseException("Could not create the node '" + prKey.first.get().StringValue() + "'.");
|
||||
|
||||
// This node is not the parent, but still presents of the created node. Add the view pointer that they are linked.
|
||||
ptrNode->SetViewPtr(Cast<CNodeCollection>());
|
||||
}
|
||||
|
||||
// Insert the node at the requested location if this node is inline or this is the view of the node.
|
||||
auto itPos = (static_cast<size_t>(uiIndex) >= m_vecNodeOrder.size()) ? m_vecNodeOrder.end() :
|
||||
m_vecNodeOrder.begin() + static_cast<size_t>(uiIndex);
|
||||
m_vecNodeOrder.insert(itPos, ptrNode);
|
||||
if (ptrNode->GetParentPtr() != Cast<CNodeCollection>())
|
||||
ptrNode->SetViewPtr(Cast<CNodeCollection>());
|
||||
|
||||
// Return the result
|
||||
return ptrNode;
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#include "parser_toml.h"
|
||||
#include <iostream>
|
||||
#include "miscellaneous.h"
|
||||
@@ -15,7 +29,7 @@ namespace toml_parser
|
||||
{
|
||||
m_ptrRoot.reset();
|
||||
m_ptrCurrentCollection.reset();
|
||||
m_lexer.Reset();
|
||||
m_lexer.Clear();
|
||||
while (!m_stackEnvironment.empty())
|
||||
m_stackEnvironment.pop();
|
||||
}
|
||||
@@ -109,6 +123,13 @@ namespace toml_parser
|
||||
|
||||
CNodeCollection& CParser::Root()
|
||||
{
|
||||
// Create the root node if not existing.
|
||||
if (!m_ptrRoot)
|
||||
{
|
||||
m_ptrRoot = std::make_shared<CRootTable>(*this);
|
||||
m_ptrCurrentCollection = m_ptrRoot;
|
||||
}
|
||||
|
||||
auto ptrCollection = m_ptrRoot->Cast<CTable>();
|
||||
return *ptrCollection.get();
|
||||
}
|
||||
@@ -246,7 +267,7 @@ namespace toml_parser
|
||||
auto ptrCurrentCollectionStored = m_ptrCurrentCollection;
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CArray>(sdv::toml::npos, rrangeKeyPath);
|
||||
m_ptrCurrentCollection = ptrNode->Cast<CNodeCollection>();
|
||||
m_stackEnvironment.push(EEnvironment::env_array);
|
||||
m_stackEnvironment.push(EEnvironment::array);
|
||||
ProcessArray(rNodeRange);
|
||||
m_stackEnvironment.pop();
|
||||
m_ptrCurrentCollection = ptrCurrentCollectionStored;
|
||||
@@ -257,7 +278,7 @@ namespace toml_parser
|
||||
auto ptrCurrentCollectionStored = m_ptrCurrentCollection;
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CTable>(sdv::toml::npos, rrangeKeyPath, true);
|
||||
m_ptrCurrentCollection = ptrNode->Cast<CNodeCollection>();
|
||||
m_stackEnvironment.push(EEnvironment::env_inline_table);
|
||||
m_stackEnvironment.push(EEnvironment::inline_table);
|
||||
ProcessInlineTable(rNodeRange);
|
||||
m_stackEnvironment.pop();
|
||||
m_ptrCurrentCollection = ptrCurrentCollectionStored;
|
||||
@@ -275,8 +296,9 @@ namespace toml_parser
|
||||
std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
|
||||
if (!m_stackEnvironment.empty())
|
||||
{
|
||||
// Skip newlines
|
||||
while (refToken.get().Category() == ETokenCategory::token_syntax_new_line)
|
||||
// Skip newlines if not an inline table
|
||||
while (m_stackEnvironment.top() != EEnvironment::inline_table &&
|
||||
refToken.get().Category() == ETokenCategory::token_syntax_new_line)
|
||||
{
|
||||
m_lexer.Consume();
|
||||
refToken = m_lexer.Peek();
|
||||
@@ -284,7 +306,7 @@ namespace toml_parser
|
||||
|
||||
switch (m_stackEnvironment.top())
|
||||
{
|
||||
case EEnvironment::env_array:
|
||||
case EEnvironment::array:
|
||||
{
|
||||
int32_t index = 1;
|
||||
while (refToken.get() && refToken.get().Category() == ETokenCategory::token_syntax_new_line)
|
||||
@@ -299,7 +321,7 @@ namespace toml_parser
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EEnvironment::env_inline_table:
|
||||
case EEnvironment::inline_table:
|
||||
if (!refToken.get()
|
||||
|| (refToken.get().Category() != ETokenCategory::token_syntax_comma
|
||||
&& refToken.get().Category() != ETokenCategory::token_syntax_inline_table_close))
|
||||
@@ -433,7 +455,7 @@ namespace toml_parser
|
||||
switch (refToken.get().Category())
|
||||
{
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
m_lexer.Consume();
|
||||
throw XTOMLParseException("Line break within an inline table is not allowed.");
|
||||
break;
|
||||
case ETokenCategory::token_syntax_comma:
|
||||
if (eExpect != EExpect::comma_or_end) throw XTOMLParseException("Expecting value or table end.");
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/********************************************************************************
|
||||
* 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:
|
||||
* Martin Stimpfl - initial API and implementation
|
||||
* Erik Verhoeven - writing TOML and whitespace preservation
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef PARSER_TOML_H
|
||||
#define PARSER_TOML_H
|
||||
|
||||
@@ -19,16 +33,11 @@ namespace toml_parser
|
||||
class CParser : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
CParser() = default;
|
||||
|
||||
/**
|
||||
* @brief Construct a new Parser object
|
||||
* @param[in] rssString UTF-8 encoded data of a TOML source
|
||||
*/
|
||||
CParser(const std::string& rssString);
|
||||
CParser(const std::string& rssString = std::string());
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
@@ -125,14 +134,15 @@ namespace toml_parser
|
||||
*/
|
||||
enum class EEnvironment
|
||||
{
|
||||
env_array, ///< Environment for an array
|
||||
env_inline_table ///< Environment for a table
|
||||
none, ///< No nested environment (used as default environment).
|
||||
array, ///< Environment for an array
|
||||
inline_table ///< Environment for a table
|
||||
};
|
||||
|
||||
std::stack<EEnvironment> m_stackEnvironment; ///< Tracking of environments in nested structures.
|
||||
std::shared_ptr<CRootTable> m_ptrRoot; ///< The one root node.
|
||||
std::shared_ptr<CNodeCollection> m_ptrCurrentCollection; ///< The current collection node.
|
||||
CLexer m_lexer; ///< Lexer.
|
||||
enum_stack<EEnvironment, EEnvironment::none> m_stackEnvironment; ///< Tracking of environments in nested structures.
|
||||
std::shared_ptr<CRootTable> m_ptrRoot; ///< The one root node.
|
||||
std::shared_ptr<CNodeCollection> m_ptrCurrentCollection; ///< The current collection node.
|
||||
CLexer m_lexer; ///< Lexer.
|
||||
};
|
||||
} // namespace toml_parser
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 CONFIG_UTILITY_H
|
||||
#define CONFIG_UTILITY_H
|
||||
|
||||
@@ -21,13 +34,13 @@ public:
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Utility)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::utility)
|
||||
DECLARE_OBJECT_CLASS_NAME("TOMLParserUtility")
|
||||
|
||||
private:
|
||||
toml_parser::CParser m_parser; ///< Configuration parser
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT_NO_EXPORT(CTOMLParserUtility)
|
||||
DEFINE_SDV_OBJECT(CTOMLParserUtility)
|
||||
|
||||
#endif // !defined CONFIG_UTILITY_H
|
||||
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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
|
||||
#*******************************************************************************
|
||||
|
||||
# Define project
|
||||
project(sdv_service_datadispatchservice VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "dispatchservice.h"
|
||||
|
||||
|
||||
@@ -9,7 +22,7 @@ CDispatchService::CDispatchService()
|
||||
sdv::IInterfaceAccess* CDispatchService::CreateTxTrigger(uint32_t uiCycleTime, uint32_t uiDelayTime, uint32_t uiBehaviorFlags,
|
||||
sdv::IInterfaceAccess* pTriggerCallback)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
if (GetObjectState() != sdv::EObjectState::configuring) return nullptr;
|
||||
|
||||
// Check for parameter validity
|
||||
if (!uiCycleTime && !(uiBehaviorFlags & static_cast<uint32_t>(sdv::core::ISignalTransmission::ETxTriggerBehavior::spontaneous)))
|
||||
@@ -39,7 +52,7 @@ sdv::IInterfaceAccess* CDispatchService::CreateTxTrigger(uint32_t uiCycleTime, u
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::RegisterTxSignal(/*in*/ const sdv::u8string& ssSignalName, /*in*/ sdv::any_t anyDefVal)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
if (GetObjectState() != sdv::EObjectState::configuring) return nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
|
||||
@@ -49,7 +62,7 @@ sdv::IInterfaceAccess* CDispatchService::RegisterTxSignal(/*in*/ const sdv::u8st
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::RegisterRxSignal(/*in*/ const sdv::u8string& ssSignalName)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
if (GetObjectState() != sdv::EObjectState::configuring) return nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
auto prSignal = m_mapRxSignals.try_emplace(ssSignalName, *this, ssSignalName, sdv::core::ESignalDirection::sigdir_rx);
|
||||
@@ -58,7 +71,7 @@ sdv::IInterfaceAccess* CDispatchService::RegisterRxSignal(/*in*/ const sdv::u8st
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::RequestSignalPublisher(/*in*/ const sdv::u8string& ssSignalName)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
if (GetObjectState() != sdv::EObjectState::configuring) return nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
auto itSignal = m_mapTxSignals.find(ssSignalName);
|
||||
@@ -70,7 +83,7 @@ sdv::IInterfaceAccess* CDispatchService::RequestSignalPublisher(/*in*/ const sdv
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::AddSignalSubscription(/*in*/ const sdv::u8string& ssSignalName, /*in*/ IInterfaceAccess* pSubscriber)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
if (GetObjectState() != sdv::EObjectState::configuring) return nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
auto itSignal = m_mapRxSignals.find(ssSignalName);
|
||||
@@ -118,42 +131,15 @@ uint64_t CDispatchService::GetDirectTransactionID() const
|
||||
return m_uiDirectTransactionID;
|
||||
}
|
||||
|
||||
void CDispatchService::Initialize(const sdv::u8string& /*ssObjectConfig*/)
|
||||
bool CDispatchService::OnInitialize()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::initializing;
|
||||
m_scheduler.Start();
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CDispatchService::GetStatus() const
|
||||
void CDispatchService::OnShutdown()
|
||||
{
|
||||
return m_eObjectStatus;
|
||||
}
|
||||
|
||||
void CDispatchService::SetOperationMode(sdv::EOperationMode eMode)
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::configuring;
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::configuring || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::running;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CDispatchService::Shutdown()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
|
||||
m_scheduler.Stop();
|
||||
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_pending;
|
||||
}
|
||||
|
||||
void CDispatchService::UnregisterSignal(/*in*/ const sdv::u8string& ssSignalName, sdv::core::ESignalDirection eDirection)
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 DISPATCH_SERVICE_H
|
||||
#define DISPATCH_SERVICE_H
|
||||
|
||||
@@ -28,7 +41,7 @@
|
||||
* @brief data dispatch service to read/write and react on signal changes
|
||||
*/
|
||||
class CDispatchService : public sdv::CSdvObject, public sdv::core::ISignalTransmission, public sdv::core::ISignalAccess,
|
||||
public sdv::core::IDispatchTransaction, public sdv::IObjectControl
|
||||
public sdv::core::IDispatchTransaction
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@@ -41,11 +54,10 @@ public:
|
||||
SDV_INTERFACE_ENTRY(sdv::core::ISignalTransmission)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::ISignalAccess)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::IDispatchTransaction)
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("DataDispatchService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
DECLARE_OBJECT_DEPENDENCIES("TaskTimerService")
|
||||
@@ -145,27 +157,15 @@ public:
|
||||
uint64_t GetDirectTransactionID() const;
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize.
|
||||
* @return Returns 'true' when the initialization was successful, 'false' when not.
|
||||
*/
|
||||
void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
virtual bool OnInitialize() override;
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
* @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown.
|
||||
*/
|
||||
sdv::EObjectStatus GetStatus() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
*/
|
||||
void Shutdown() override;
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Unregister a previously registered signal. This will render all subscriptions and provider connections invalid.
|
||||
@@ -205,7 +205,6 @@ private:
|
||||
mutable std::mutex m_mtxSignals; ///< Signal object map protection.
|
||||
std::map<sdv::u8string, CSignal> m_mapRxSignals; ///< Signal object map.
|
||||
std::map<sdv::u8string, CSignal> m_mapTxSignals; ///< Signal object map.
|
||||
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
|
||||
std::atomic_uint64_t m_uiTransactionCnt = 1ull; ///< Transaction counter.
|
||||
std::mutex m_mtxTransactions; ///< List with transactions access.
|
||||
std::list<CTransaction> m_lstTransactions; ///< List with transactions.
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "dispatchservice.h"
|
||||
#include "signal.h"
|
||||
#include "trigger.h"
|
||||
@@ -159,7 +172,7 @@ void CSignal::RemoveConsumer(CConsumer* pConsumer)
|
||||
|
||||
void CSignal::WriteFromProvider(const sdv::any_t& ranyVal, uint64_t uiTransactionID, std::set<CTrigger*>& rsetTriggers)
|
||||
{
|
||||
if (m_rDispatchSvc.GetStatus() != sdv::EObjectStatus::running) return;
|
||||
if (m_rDispatchSvc.GetObjectState() != sdv::EObjectState::running) return;
|
||||
|
||||
uint64_t uiTransactionIDTemp = uiTransactionID;
|
||||
if (!uiTransactionIDTemp) uiTransactionIDTemp = m_rDispatchSvc.GetDirectTransactionID();
|
||||
@@ -221,7 +234,7 @@ sdv::any_t CSignal::ReadFromConsumer(uint64_t uiTransactionID) const
|
||||
|
||||
void CSignal::DistributeToConsumers(const sdv::any_t& ranyVal)
|
||||
{
|
||||
if (m_rDispatchSvc.GetStatus() != sdv::EObjectStatus::running) return;
|
||||
if (m_rDispatchSvc.GetObjectState() != sdv::EObjectState::running) return;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignalObjects);
|
||||
for (auto& rvtConsumer : m_mapConsumers)
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 SIGNAL_H
|
||||
#define SIGNAL_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "dispatchservice.h"
|
||||
#include "transaction.h"
|
||||
#include "trigger.h"
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 TRANSACTION_H
|
||||
#define TRANSACTION_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "dispatchservice.h"
|
||||
#include "trigger.h"
|
||||
#include "signal.h"
|
||||
@@ -157,7 +170,7 @@ void CTrigger::RemoveSignal(/*in*/ const sdv::u8string& ssSignalName)
|
||||
|
||||
void CTrigger::Execute(EExecutionFlag eExecFlag /*= EExecutionFlag::spontaneous*/)
|
||||
{
|
||||
if (m_rDispatchSvc.GetStatus() != sdv::EObjectStatus::running) return;
|
||||
if (m_rDispatchSvc.GetObjectState() != sdv::EObjectState::running) return;
|
||||
|
||||
// Check for allowed execution
|
||||
if (eExecFlag == EExecutionFlag::spontaneous &&
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 TRIGGER_H
|
||||
#define TRIGGER_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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
|
||||
#*******************************************************************************
|
||||
|
||||
# Define project
|
||||
project(hardware_ident VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "hardware_ident.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
/**
|
||||
* @file process_control.h
|
||||
* @author Sudipta Babu Durjoy FRD DISS21 (mailto:sudipta.durjoy@zf.com)
|
||||
* @brief
|
||||
* @version 1.0
|
||||
* @date 2023-10-23
|
||||
*
|
||||
* @copyright Copyright ZF Friedrichshafen AG (c) 2023
|
||||
*
|
||||
*/
|
||||
/********************************************************************************
|
||||
* 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 HARDWARE_IDENT_H
|
||||
#define HARDWARE_IDENT_H
|
||||
@@ -27,7 +29,7 @@ public:
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("HardwareIdentificationService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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 cross-compilation toolchain file
|
||||
include(../../cross-compile-tools.cmake)
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "com_channel.h"
|
||||
#include "com_ctrl.h"
|
||||
#include "marshall_object.h"
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 COM_CHANNEL_H
|
||||
#define COM_CHANNEL_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "com_ctrl.h"
|
||||
#include <interfaces/ipc.h>
|
||||
#include <support/toml.h>
|
||||
@@ -14,36 +27,13 @@ CCommunicationControl::CCommunicationControl()
|
||||
CCommunicationControl::~CCommunicationControl()
|
||||
{}
|
||||
|
||||
void CCommunicationControl::Initialize(const sdv::u8string& /*ssObjectConfig*/)
|
||||
bool CCommunicationControl::OnInitialize()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CCommunicationControl::GetStatus() const
|
||||
void CCommunicationControl::OnShutdown()
|
||||
{
|
||||
return m_eObjectStatus;
|
||||
}
|
||||
|
||||
void CCommunicationControl::SetOperationMode(sdv::EOperationMode eMode)
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::configuring;
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::configuring || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::running;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CCommunicationControl::Shutdown()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
|
||||
// Wait for threads to terminate... in case one is still running
|
||||
// (use a copy to prevent circular access)
|
||||
@@ -64,8 +54,6 @@ void CCommunicationControl::Shutdown()
|
||||
lock.unlock();
|
||||
vecChannels.clear();
|
||||
mapStubObjects.clear();
|
||||
|
||||
m_eObjectStatus = sdv::EObjectStatus::destruction_pending;
|
||||
}
|
||||
|
||||
sdv::com::TConnectionID CCommunicationControl::CreateServerConnection(/*in*/ sdv::com::EChannelType eChannelType,
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 COM_CTRL_H
|
||||
#define COM_CTRL_H
|
||||
|
||||
@@ -12,7 +25,7 @@ class CMarshallObject;
|
||||
/**
|
||||
* @brief Test object simulating an component isolation service implementation for channel implementation testing
|
||||
*/
|
||||
class CCommunicationControl : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::com::IConnectionControl,
|
||||
class CCommunicationControl : public sdv::CSdvObject, public sdv::com::IConnectionControl,
|
||||
public sdv::ps::IMarshallAccess
|
||||
{
|
||||
public:
|
||||
@@ -30,36 +43,23 @@ public:
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ps::IMarshallAccess)
|
||||
SDV_INTERFACE_ENTRY(sdv::com::IConnectionControl)
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Component declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
DECLARE_OBJECT_CLASS_NAME("CommunicationControl")
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize.
|
||||
* @return Returns 'true' when the initialization was successful, 'false' when not.
|
||||
*/
|
||||
virtual void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
virtual bool OnInitialize() override;
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
* @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown.
|
||||
*/
|
||||
virtual sdv::EObjectStatus GetStatus() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
*/
|
||||
virtual void Shutdown() override;
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Create an IPC channel endpoint and use it for SDV communication. Overload of
|
||||
@@ -194,7 +194,6 @@ public:
|
||||
sdv::sequence<sdv::pointer<uint8_t>> CallStub(sdv::ps::TMarshallID tStubID, sdv::sequence<sdv::pointer<uint8_t>>& seqInputData);
|
||||
|
||||
private:
|
||||
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
|
||||
std::mutex m_mtxChannels; ///< Protect the channel map.
|
||||
std::vector<std::shared_ptr<CChannelConnector>> m_vecChannels; ///< Channel vector.
|
||||
std::vector<std::thread> m_vecInitialConnectMon; ///< Initial connection monitor.
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "marshall_object.h"
|
||||
#include "com_ctrl.h"
|
||||
#include <support/serdes.h>
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 MARSHALL_OBJECT_H
|
||||
#define MARSHALL_OBJECT_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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 cross-compilation toolchain file
|
||||
include(../../cross-compile-tools.cmake)
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "client.h"
|
||||
#include <support/toml.h>
|
||||
#include <interfaces/com.h>
|
||||
@@ -16,37 +29,13 @@ void CRepositoryProxy::DestroyObject()
|
||||
m_rClient.Disconnect(m_tConnection);
|
||||
}
|
||||
|
||||
void CClient::Initialize(const sdv::u8string& /*ssObjectConfig*/)
|
||||
bool CClient::OnInitialize()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CClient::GetStatus() const
|
||||
void CClient::OnShutdown()
|
||||
{
|
||||
return m_eObjectStatus;
|
||||
}
|
||||
|
||||
void CClient::SetOperationMode(sdv::EOperationMode eMode)
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::configuring;
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::configuring || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::running;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::Shutdown()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
|
||||
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
|
||||
if (!pConnectionControl)
|
||||
SDV_LOG_ERROR("Failed to get communication control!");
|
||||
@@ -60,8 +49,6 @@ void CClient::Shutdown()
|
||||
for (const auto& rvtRepository : mapRepositoryProxiesCopy)
|
||||
pConnectionControl->RemoveConnection(rvtRepository.first);
|
||||
}
|
||||
|
||||
m_eObjectStatus = sdv::EObjectStatus::destruction_pending;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CClient::Connect(const sdv::u8string& ssConnectString)
|
||||
@@ -126,14 +113,14 @@ Port = ")code" + std::to_string(uiPort) + R"code(
|
||||
else
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid or missing listener configuration for listener service!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
SetObjectIntoConfigErrorState();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid service configuration for listener service: ", rexcept.what(), "!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
SetObjectIntoConfigErrorState();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 CLIENT_H
|
||||
#define CLIENT_H
|
||||
|
||||
@@ -55,42 +68,29 @@ private:
|
||||
/**
|
||||
* @brief Client object
|
||||
*/
|
||||
class CClient : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::com::IClientConnect
|
||||
class CClient : public sdv::CSdvObject, public sdv::com::IClientConnect
|
||||
{
|
||||
public:
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
|
||||
SDV_INTERFACE_ENTRY(sdv::com::IClientConnect)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declaration
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("ConnectionService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
*/
|
||||
void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
*/
|
||||
sdv::EObjectStatus GetStatus() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize.
|
||||
* @return Returns 'true' when the initialization was successful, 'false' when not.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
virtual bool OnInitialize() override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
*/
|
||||
void Shutdown() override;
|
||||
* @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown.
|
||||
*/
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Connect to a remote system using the connection string to contact the system. Overload of
|
||||
@@ -122,7 +122,6 @@ public:
|
||||
void Disconnect(sdv::com::TConnectionID tConnectionID);
|
||||
|
||||
private:
|
||||
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
|
||||
std::mutex m_mtxRepositoryProxies; ///< Protect access to the remnote repository map.
|
||||
std::map<sdv::com::TConnectionID, CRepositoryProxy> m_mapRepositoryProxies; ///< map of remote repositories.
|
||||
};
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "listener.h"
|
||||
#include <support/toml.h>
|
||||
#include <interfaces/com.h>
|
||||
@@ -65,84 +78,63 @@ sdv::u8string CChannelBroker::RequestChannel(/*in*/ const sdv::u8string& /*ssCon
|
||||
CListener::CListener() : m_broker(*this)
|
||||
{}
|
||||
|
||||
void CListener::Initialize(const sdv::u8string& ssObjectConfig)
|
||||
bool CListener::OnInitialize()
|
||||
{
|
||||
const sdv::app::IAppContext* pContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (!pContext)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get application context!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
|
||||
if (!pConnectionControl)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get communication control!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
sdv::ipc::ICreateEndpoint* pEndpoint = nullptr;
|
||||
std::string ssConfig;
|
||||
try
|
||||
if (m_ssType == "Local")
|
||||
{
|
||||
// Determine whether the service is running as server or as client.
|
||||
sdv::toml::CTOMLParser config(ssObjectConfig);
|
||||
std::string ssType = config.GetDirect("Listener.Type").GetValue();
|
||||
if (ssType == "Local")
|
||||
m_bLocalListener = true;
|
||||
pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>("LocalChannelControl");
|
||||
if (!pEndpoint)
|
||||
{
|
||||
uint32_t uiInstanceID = config.GetDirect("Listener.Instance").GetValue();
|
||||
m_bLocalListener = true;
|
||||
pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>("LocalChannelControl");
|
||||
if (!pEndpoint)
|
||||
{
|
||||
SDV_LOG_ERROR("No local channel control!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
}
|
||||
SDV_LOG_ERROR("No local channel control!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Request the instance ID from the app control
|
||||
ssConfig = std::string(R"code([IpcChannel]
|
||||
Name = "LISTENER_)code") + std::to_string(uiInstanceID ? uiInstanceID : pContext->GetInstanceID()) + R"code("
|
||||
// Request the instance ID from the app control
|
||||
ssConfig = std::string(R"code([IpcChannel]
|
||||
Name = "LISTENER_)code") + std::to_string(m_uiInstanceID ? m_uiInstanceID : pContext->GetInstanceID()) + R"code("
|
||||
Size = 2048
|
||||
)code";
|
||||
}
|
||||
else if (ssType == "Remote")
|
||||
{
|
||||
m_bLocalListener = false;
|
||||
std::string ssInterface = config.GetDirect("Listener.Interface").GetValue();
|
||||
uint32_t uiPort = config.GetDirect("Listener.Interface").GetValue();
|
||||
if (ssInterface.empty() || !uiPort)
|
||||
{
|
||||
SDV_LOG_ERROR("Missing interface or port number to initialize a remote listener!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
}
|
||||
pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>("RemoteChannelControl");
|
||||
if (!pEndpoint)
|
||||
{
|
||||
SDV_LOG_ERROR("No remote channel control!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
}
|
||||
|
||||
ssConfig = R"code([IpcChannel]
|
||||
Interface = ")code" + ssInterface + R"code(
|
||||
Port = ")code" + std::to_string(uiPort) + R"code(
|
||||
)code";
|
||||
}
|
||||
else
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid or missing listener configuration for listener service!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
else if (m_ssType == "Remote")
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid service configuration for listener service: ", rexcept.what(), "!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
m_bLocalListener = false;
|
||||
if (m_ssInterface.empty() || !m_uiPort)
|
||||
{
|
||||
SDV_LOG_ERROR("Missing interface or port number to initialize a remote listener!");
|
||||
return false;
|
||||
}
|
||||
pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>("RemoteChannelControl");
|
||||
if (!pEndpoint)
|
||||
{
|
||||
SDV_LOG_ERROR("No remote channel control!");
|
||||
return false;
|
||||
}
|
||||
|
||||
ssConfig = R"code([IpcChannel]
|
||||
Interface = ")code" + m_ssInterface + R"code(
|
||||
Port = ")code" + std::to_string(m_uiPort) + R"code(
|
||||
)code";
|
||||
}
|
||||
else
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid or missing listener configuration for listener service!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the endpoint
|
||||
@@ -150,8 +142,7 @@ Port = ")code" + std::to_string(uiPort) + R"code(
|
||||
if (!sEndpoint.pConnection)
|
||||
{
|
||||
SDV_LOG_ERROR("Could not create the endpoint for listener service!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
sdv::TObjectPtr ptrEndpoint(sEndpoint.pConnection); // Does automatic destruction if failure happens.
|
||||
|
||||
@@ -161,39 +152,13 @@ Port = ")code" + std::to_string(uiPort) + R"code(
|
||||
if (!m_tConnection)
|
||||
{
|
||||
SDV_LOG_ERROR("Could not assign the server endpoint!");
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CListener::GetStatus() const
|
||||
void CListener::OnShutdown()
|
||||
{
|
||||
return m_eObjectStatus;
|
||||
}
|
||||
|
||||
void CListener::SetOperationMode(sdv::EOperationMode eMode)
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::configuring;
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::configuring || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::running;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CListener::Shutdown()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
|
||||
// Shutdown the listener...
|
||||
if (m_tConnection != sdv::com::TConnectionID{})
|
||||
{
|
||||
@@ -206,8 +171,6 @@ void CListener::Shutdown()
|
||||
}
|
||||
|
||||
m_ptrConnection.Clear();
|
||||
|
||||
m_eObjectStatus = sdv::EObjectStatus::destruction_pending;
|
||||
}
|
||||
|
||||
bool CListener::IsLocalListener() const
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 LISTENER_H
|
||||
#define LISTENER_H
|
||||
|
||||
@@ -40,7 +53,7 @@ private:
|
||||
/**
|
||||
* @brief Listener object
|
||||
*/
|
||||
class CListener : public sdv::CSdvObject, public sdv::IObjectControl
|
||||
class CListener : public sdv::CSdvObject
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@@ -48,17 +61,22 @@ public:
|
||||
*/
|
||||
CListener();
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declaration
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("ConnectionListenerService")
|
||||
|
||||
// Parameter map
|
||||
BEGIN_SDV_PARAM_MAP()
|
||||
SDV_PARAM_ENABLE_LOCKING()
|
||||
SDV_PARAM_GROUP("Listener")
|
||||
SDV_PARAM_ENTRY(m_ssType, "Type", "Local", "", "The type of listener \"Local\" or \"Remote\".")
|
||||
SDV_PARAM_ENTRY(m_uiInstanceID, "Instance", 0, "", "The instance ID to listen for.")
|
||||
SDV_PARAM_ENTRY(m_ssInterface, "Interface", "", "", "Interface identification.")
|
||||
SDV_PARAM_ENTRY(m_uiPort, "Port", 0, "", "Port number for connection.")
|
||||
END_SDV_PARAM_MAP()
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize.
|
||||
* @details The object configuration contains the information needed to start the listener. The following configuration is
|
||||
* available for the local listener:
|
||||
* @code
|
||||
@@ -73,26 +91,14 @@ public:
|
||||
* Interface = "127.0.0.1"
|
||||
* Port = 2000
|
||||
* @endcode
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
* @return Returns 'true' when the initialization was successful, 'false' when not.
|
||||
*/
|
||||
void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
virtual bool OnInitialize() override;
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
* @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown.
|
||||
*/
|
||||
sdv::EObjectStatus GetStatus() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
*/
|
||||
void Shutdown() override;
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief When set, the listener is configured to be a local listener. Otherwise the listerner is configured as remote listener.
|
||||
@@ -101,7 +107,10 @@ public:
|
||||
bool IsLocalListener() const;
|
||||
|
||||
private:
|
||||
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< To update the object status when it changes.
|
||||
sdv::u8string m_ssType; ///< Listener type: "Local" or "Remote"
|
||||
uint32_t m_uiInstanceID = 0; ///< Instance ID to listen for.
|
||||
std::string m_ssInterface; ///< Interface string for remote listener.
|
||||
uint32_t m_uiPort = 0; ///< Port for remote listener.
|
||||
sdv::TObjectPtr m_ptrConnection; ///< The connection object.
|
||||
CChannelBroker m_broker; ///< Channel broker, used to request new channels
|
||||
bool m_bLocalListener = true; ///< When set, the listener is a local listener; otherwise a remote listener.
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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
|
||||
#*******************************************************************************
|
||||
|
||||
# Define project
|
||||
project(ipc_shared_mem VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
|
||||
@@ -1,45 +1,23 @@
|
||||
/********************************************************************************
|
||||
* 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 "channel_mgnt.h"
|
||||
#include "connection.h"
|
||||
#include <support/toml.h>
|
||||
|
||||
void CSharedMemChannelMgnt::Initialize(const sdv::u8string& /*ssObjectConfig*/)
|
||||
void CSharedMemChannelMgnt::OnShutdown()
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::initialization_pending)
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||||
return;
|
||||
}
|
||||
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialized;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CSharedMemChannelMgnt::GetStatus() const
|
||||
{
|
||||
return m_eObjectStatus;
|
||||
}
|
||||
|
||||
void CSharedMemChannelMgnt::SetOperationMode(sdv::EOperationMode eMode)
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::configuring;
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::configuring || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::running;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CSharedMemChannelMgnt::Shutdown()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
m_watchdog.Clear();
|
||||
m_eObjectStatus = sdv::EObjectStatus::destruction_pending;
|
||||
}
|
||||
|
||||
sdv::ipc::SChannelEndpoint CSharedMemChannelMgnt::CreateEndpoint(/*in*/ const sdv::u8string& ssEndpointConfig)
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 CHANNEL_MGNT_H
|
||||
#define CHANNEL_MGNT_H
|
||||
|
||||
@@ -9,8 +22,8 @@
|
||||
#include "watchdog.h"
|
||||
#include "connection.h"
|
||||
|
||||
#define TEST_DECLARE_OBJECT_CLASS_ALIAS(...) \
|
||||
static sdv::sequence<sdv::u8string> GetClassAliasesStaticMyTest() \
|
||||
#define TEST_DECLARE_OBJECT_CLASS_ALIAS(...) \
|
||||
static sdv::sequence<sdv::u8string> GetClassAliasesStaticMyTest() \
|
||||
{ \
|
||||
return sdv::sequence<sdv::u8string>({__VA_ARGS__}); \
|
||||
}
|
||||
@@ -19,46 +32,26 @@
|
||||
/**
|
||||
* @brief IPC channel management class for the shared memory communication.
|
||||
*/
|
||||
class CSharedMemChannelMgnt : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::ipc::ICreateEndpoint,
|
||||
public sdv::ipc::IChannelAccess
|
||||
class CSharedMemChannelMgnt : public sdv::CSdvObject, public sdv::ipc::ICreateEndpoint, public sdv::ipc::IChannelAccess
|
||||
{
|
||||
public:
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IChannelAccess)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::ICreateEndpoint)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("DefaultSharedMemoryChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
* @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown.
|
||||
*/
|
||||
void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
*/
|
||||
sdv::EObjectStatus GetStatus() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
*/
|
||||
void Shutdown() override;
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Create IPC connection object and return the endpoint information. Overload of
|
||||
@@ -111,7 +104,6 @@ private:
|
||||
CSharedMemBufferRx bufferTargetRx; ///< Target Rx channel
|
||||
};
|
||||
|
||||
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
|
||||
std::map<std::string, std::unique_ptr<SChannel>> m_mapChannels; ///< Map with channels.
|
||||
CWatchDog m_watchdog; ///< Process monitor for connections.
|
||||
};
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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.h"
|
||||
#include "watchdog.h"
|
||||
#include <numeric>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
/**
|
||||
* @file connection.h
|
||||
* @author Erik Verhoeven FRD DISDS1 (mailto:erik.verhoeven@zf.com)
|
||||
* @brief Implementation of connection class.
|
||||
* @version 2.0
|
||||
* @date 2024-06-24
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* @copyright Copyright ZF Friedrichshafen AG (c) 2023-2025
|
||||
* 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 CHANNEL_H
|
||||
#define CHANNEL_H
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 IN_PROCESS_MEM_BUFFER_H
|
||||
#define IN_PROCESS_MEM_BUFFER_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "mem_buffer_accessor.h"
|
||||
#include <cassert>
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 MEM_BUFFER_ACCESSOR_H
|
||||
#define MEM_BUFFER_ACCESSOR_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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
|
||||
********************************************************************************/
|
||||
|
||||
#if !defined POSIX_SHARED_MEM_BUFFER_H && defined __unix__
|
||||
#define POSIX_SHARED_MEM_BUFFER_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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
|
||||
********************************************************************************/
|
||||
|
||||
#if !defined WINDOWS_SHARED_MEM_BUFFER_H && defined _WIN32
|
||||
#define WINDOWS_SHARED_MEM_BUFFER_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "watchdog.h"
|
||||
#include "connection.h"
|
||||
#include <vector>
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 WATCH_DOG_H
|
||||
#define WATCH_DOG_H
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#*******************************************************************************
|
||||
# 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
|
||||
#*******************************************************************************
|
||||
|
||||
# Define project
|
||||
project(manifest_util VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/********************************************************************************
|
||||
* 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 "manifest_util.h"
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
/**
|
||||
* @file process_control.h
|
||||
* @author Sudipta Babu Durjoy FRD DISS21 (mailto:sudipta.durjoy@zf.com)
|
||||
* @brief
|
||||
* @version 1.0
|
||||
* @date 2023-10-23
|
||||
*
|
||||
* @copyright Copyright ZF Friedrichshafen AG (c) 2023
|
||||
*
|
||||
*/
|
||||
/********************************************************************************
|
||||
* 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 MANIFEST_UTIL_H
|
||||
#define MANIFEST_UTIL_H
|
||||
@@ -31,7 +33,7 @@ public:
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Utility)
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::utility)
|
||||
DECLARE_OBJECT_CLASS_NAME("ManifestHelperUtility")
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user