diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e2f1f1..7bfc06f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 +#******************************************************************************* + # Enforce CMake version 3.20 cmake_minimum_required (VERSION 3.20) cmake_policy (VERSION 3.20) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 63ec82b..69aee23 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Enforce CMake version 3.20 cmake_minimum_required (VERSION 3.20) cmake_policy (VERSION 3.20) diff --git a/examples/_howto_examples/CMakeLists.txt b/examples/_howto_examples/CMakeLists.txt index 91193ca..2a1d2dc 100644 --- a/examples/_howto_examples/CMakeLists.txt +++ b/examples/_howto_examples/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + project(HowToExamples) # Use new policy for project version settings and default warning level diff --git a/examples/_howto_examples/example_config_file_parsing.cfg b/examples/_howto_examples/example_config_file_parsing.cfg index 86ccb89..436abfc 100644 --- a/examples/_howto_examples/example_config_file_parsing.cfg +++ b/examples/_howto_examples/example_config_file_parsing.cfg @@ -6,7 +6,6 @@ class DemoConfigurationComponent : public sdv::CSdvObject, public sdv::IObjectCo { public: BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) END_SDV_INTERFACE_MAP() DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) diff --git a/examples/_howto_examples/source/docu_app_examples.toml b/examples/_howto_examples/source/docu_app_examples.toml index 5000cac..41f1568 100644 --- a/examples/_howto_examples/source/docu_app_examples.toml +++ b/examples/_howto_examples/source/docu_app_examples.toml @@ -8,5 +8,6 @@ Class = "Hello_Component" [[Component]] Path = "example_general_component.sdv" Class = "Hello_Component" +[Component.Parameters] number = 42 diff --git a/examples/_howto_examples/source/docu_examples.toml b/examples/_howto_examples/source/docu_examples.toml index e4492fc..4bfb33d 100644 --- a/examples/_howto_examples/source/docu_examples.toml +++ b/examples/_howto_examples/source/docu_examples.toml @@ -8,6 +8,7 @@ Class = "Hello_Component" [[Component]] Path = "example_general_component_with_initialization.sdv" Class = "Hello_Component_With_Initialization" +[Component.Parameters] number = 42 [[Component]] diff --git a/examples/_howto_examples/source/example_access_to_other_components.cpp b/examples/_howto_examples/source/example_access_to_other_components.cpp index c36c069..b04810a 100644 --- a/examples/_howto_examples/source/example_access_to_other_components.cpp +++ b/examples/_howto_examples/source/example_access_to_other_components.cpp @@ -28,7 +28,7 @@ public: END_SDV_INTERFACE_MAP() DECLARE_OBJECT_CLASS_NAME("Access_Component") - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::BasicService); + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::basic_service); /** * @brief Show messages, implements the function of IShowExample diff --git a/examples/_howto_examples/source/example_basic_service.cpp b/examples/_howto_examples/source/example_basic_service.cpp index 6f7d8cc..5ac123d 100644 --- a/examples/_howto_examples/source/example_basic_service.cpp +++ b/examples/_howto_examples/source/example_basic_service.cpp @@ -52,7 +52,7 @@ public: END_SDV_INTERFACE_MAP() DECLARE_OBJECT_CLASS_NAME("BasicService_Component") - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::BasicService); + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::basic_service); /** * @brief Set brake force diff --git a/examples/_howto_examples/source/example_complex_service.cpp b/examples/_howto_examples/source/example_complex_service.cpp index 2ebd92e..544b55d 100644 --- a/examples/_howto_examples/source/example_complex_service.cpp +++ b/examples/_howto_examples/source/example_complex_service.cpp @@ -54,7 +54,7 @@ public: END_SDV_INTERFACE_MAP() DECLARE_OBJECT_CLASS_NAME("ComplexService_Component") - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService); + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::complex_service); /** * @brief Set speed diff --git a/examples/_howto_examples/source/example_general_component.cpp b/examples/_howto_examples/source/example_general_component.cpp index b4e600c..0e12d29 100644 --- a/examples/_howto_examples/source/example_general_component.cpp +++ b/examples/_howto_examples/source/example_general_component.cpp @@ -23,7 +23,7 @@ public: SDV_INTERFACE_ENTRY(ISayGoodbye) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("Hello_Component") /** diff --git a/examples/_howto_examples/source/example_general_component_with_initialization.cpp b/examples/_howto_examples/source/example_general_component_with_initialization.cpp index 279e5a4..994496b 100644 --- a/examples/_howto_examples/source/example_general_component_with_initialization.cpp +++ b/examples/_howto_examples/source/example_general_component_with_initialization.cpp @@ -5,7 +5,6 @@ class CTestComponentWithInitialization : public sdv::CSdvObject - , public sdv::IObjectControl , public ISayHello , public ISayGoodbye { @@ -22,63 +21,30 @@ public: BEGIN_SDV_INTERFACE_MAP() SDV_INTERFACE_ENTRY(ISayHello) SDV_INTERFACE_ENTRY(ISayGoodbye) - SDV_INTERFACE_ENTRY(sdv::IObjectControl) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("Hello_Component_With_Initialization") - /** - * @brief Initialize the object. Overload of sdv::IObjectControl::Initialize. - * @param[in] ssObjectConfig Optional configuration string. - */ - inline virtual void Initialize(const sdv::u8string& ssObjectConfig) override - { - if (!ParseConfigurationString(ssObjectConfig)) - { - m_status = sdv::EObjectStatus::initialization_failure; - return; - } - m_status = sdv::EObjectStatus::initialized; - }; + // Parameter map + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_Number, "number", -1, "", "A number") + END_SDV_PARAM_MAP() /** - * @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode. - * @param[in] eMode The operation mode, the component should run in. - */ - void SetOperationMode(/*in*/ sdv::EOperationMode eMode) override + * @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 bool OnInitialize() override { - switch (eMode) - { - case sdv::EOperationMode::configuring: - if (m_status == sdv::EObjectStatus::running || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::configuring; - break; - case sdv::EOperationMode::running: - if (m_status == sdv::EObjectStatus::configuring || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::running; - break; - default: - break; - } + return true; } /** - * @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. */ - inline virtual sdv::EObjectStatus GetStatus() const override - { - return m_status; - }; - - /** - * @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown. - */ - inline virtual void Shutdown() override - { - m_status = sdv::EObjectStatus::destruction_pending; - } + virtual void OnShutdown() override + {} /** * @brief Show messages, implements the function of IShowExample @@ -97,31 +63,7 @@ public: } private: - bool ParseConfigurationString(const sdv::u8string& objectConfig) - { - try - { - sdv::toml::CTOMLParser config(objectConfig.c_str()); - - // get any settings from the configuration - auto nodeNumber = config.GetDirect("number"); - if (nodeNumber.GetType() == sdv::toml::ENodeType::node_integer) - { - m_Number = static_cast(nodeNumber.GetValue()); - } - } - catch (const sdv::toml::XTOMLParseException& e) - { - std::cout << "Configuration could not be read:" << e.what() << std::endl; - return false; - } - - std::cout << "Initialization number: " << std::to_string(m_Number) << std::endl; - return true; - } - int32_t m_Number = -1; - std::atomic m_status = { sdv::EObjectStatus::initialization_pending }; //!< To update the object status when it changes. }; DEFINE_SDV_OBJECT(CTestComponentWithInitialization) diff --git a/examples/_howto_examples/source/example_vehicle_device.cpp b/examples/_howto_examples/source/example_vehicle_device.cpp index 275aeac..4789f8c 100644 --- a/examples/_howto_examples/source/example_vehicle_device.cpp +++ b/examples/_howto_examples/source/example_vehicle_device.cpp @@ -38,7 +38,7 @@ public: SDV_INTERFACE_ENTRY(vss::Device::ITransferSignalBrakeForce) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_bus) DECLARE_OBJECT_CLASS_NAME("VehicleDevice_Component") ~CVehicleDevice() diff --git a/examples/auto_headlamp_example/CMakeLists.txt b/examples/auto_headlamp_example/CMakeLists.txt index b284449..4dd0dbd 100644 --- a/examples/auto_headlamp_example/CMakeLists.txt +++ b/examples/auto_headlamp_example/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + project(AutoHeadlight) # Use new policy for project version settings and default warning level @@ -128,7 +138,7 @@ file (COPY ${PROJECT_SOURCE_DIR}/config/autoheadlight_cs.toml DESTINATION ${CMAK # # What cannot be created automatically is the method OpenAPILoad(const std::string& resources) in file model.cpp # The method must load all required components -# Therfore here the file is copied and overwritten the auto generated file +# Therefore here the file is copied and overwritten the auto generated file # # The required toml files need to be copied to the folder: # generated/fmu_DemoExampleFMU/DemoExampleFMU/resources diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_app.cpp b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_app.cpp index 88592de..d8fdff9 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_app.cpp +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_app.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp index 72ff1ef..d305d9c 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include "autoheadlight_console.h" #ifdef _WIN32 diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h index 929b0b0..2afc01c 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifndef CONSOLE_OUTPUT_H #define CONSOLE_OUTPUT_H diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.cpp b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.cpp index 738a323..221ac8d 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.cpp +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include "autoheadlight_simulate.h" #ifdef _WIN32 diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.h b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.h index b854acc..e5ef69a 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.h +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include diff --git a/examples/auto_headlamp_example/autoheadlight_app/signal_names.h b/examples/auto_headlamp_example/autoheadlight_app/signal_names.h index 742dc56..153b2d5 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/signal_names.h +++ b/examples/auto_headlamp_example/autoheadlight_app/signal_names.h @@ -1,4 +1,14 @@ -/** + /******************************************************************************** + * 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 + ********************************************************************************/ + + /** * namespace for the signal names * in case /generated/vss_files/signal_identifier.h * exists, use the file, otherwise define the namespace diff --git a/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs.cpp b/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs.cpp index ee924eb..94e0454 100644 --- a/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs.cpp +++ b/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs.cpp @@ -1,27 +1,31 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include "autoheadlight_cs.h" CAutoHeadlightService::CAutoHeadlightService() -{ -} +{} CAutoHeadlightService::~CAutoHeadlightService() -{ - Shutdown(); -} +{} -void CAutoHeadlightService::Initialize(const sdv::u8string& ssObjectConfig) +bool CAutoHeadlightService::OnInitialize() { - m_eStatus = sdv::EObjectStatus::initializing; - // Request the basic service for the headlight. m_pHeadlightSvc = sdv::core::GetObject("Vehicle.Body.Light.Front.LowBeam_Service").GetInterface(); if (!m_pHeadlightSvc) { SDV_LOG_ERROR("Could not get interface 'IVSS_SetHeadlightLowBeam': [CAutoHeadlightService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } // Request the basic service for the steering wheel. @@ -29,8 +33,7 @@ void CAutoHeadlightService::Initialize(const sdv::u8string& ssObjectConfig) if (!pCurrentLatitudeSvc) { SDV_LOG_ERROR("Could not get interface 'IVSS_GetCurrentLatitude': [CAutoHeadlightService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } // Request the basic service for the vehicle speed. @@ -38,8 +41,7 @@ void CAutoHeadlightService::Initialize(const sdv::u8string& ssObjectConfig) if (!pCurrentLongitudeSvc) { SDV_LOG_ERROR("Could not get interface 'IVSS_GetCurrentLongitude': [CAutoHeadlightService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } // Register Current Latitude change event handler. @@ -49,32 +51,17 @@ void CAutoHeadlightService::Initialize(const sdv::u8string& ssObjectConfig) pCurrentLongitudeSvc->RegisterOnSignalChangeOfFCurrentLongitude(static_cast (this)); - if(LoadGPSBounds(ssObjectConfig)) - { - SDV_LOG_INFO("AutoHeadlightService: GPS bounds loaded Successfully"); - m_eStatus = sdv::EObjectStatus::initialized; - } - else - { - SDV_LOG_ERROR("AutoHeadlightService: GPS bounds could not be loaded"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; - } - + // Swap the bounding box the make certain that min is less than max + if (m_SGPSBoundingBox.fTunnelMinLat > m_SGPSBoundingBox.fTunnelMaxLat) + std::swap(m_SGPSBoundingBox.fTunnelMinLat, m_SGPSBoundingBox.fTunnelMaxLat); + if (m_SGPSBoundingBox.fTunnelMinLon > m_SGPSBoundingBox.fTunnelMaxLon) + std::swap(m_SGPSBoundingBox.fTunnelMinLon, m_SGPSBoundingBox.fTunnelMaxLon); + SDV_LOG_INFO("AutoHeadlightService: Initialized Successfully"); + return true; } -sdv::EObjectStatus CAutoHeadlightService::GetStatus() const -{ - return m_eStatus; -} - -void CAutoHeadlightService::SetOperationMode(sdv::EOperationMode /*eMode*/) -{ - // Not applicable -} - -void CAutoHeadlightService::Shutdown() +void CAutoHeadlightService::OnShutdown() { // Unregister the Current latitude event handler. auto pCurrentLatitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLatitude_Service").GetInterface(); @@ -139,57 +126,6 @@ void CAutoHeadlightService::ProcessHeadlightBasedOnEgoPosition() } } - -bool CAutoHeadlightService::LoadGPSBounds(const sdv::u8string& rssObjectConfig) -{ - try - { - sdv::toml::CTOMLParser config(rssObjectConfig.c_str()); - - sdv::toml::CNode fStartLatNode = config.GetDirect("tunnel_start_lat"); - float fTunnelStartLat = 0.0; ///< Tunnel Start Latitude - if (fStartLatNode.GetType() == sdv::toml::ENodeType::node_floating_point) - { - fTunnelStartLat = static_cast(fStartLatNode.GetValue()); - } - - sdv::toml::CNode fStartLonNode = config.GetDirect("tunnel_start_lon"); - float fTunnelStartLon = 0.0; ///< Tunnel Start Longitude - if (fStartLonNode.GetType() == sdv::toml::ENodeType::node_floating_point) - { - fTunnelStartLon = static_cast(fStartLonNode.GetValue()); - } - - sdv::toml::CNode fEndLatNode = config.GetDirect("tunnel_end_lat"); - float fTunnelEndLat = 0.0; ///< Tunnel End Latitude - if (fEndLatNode.GetType() == sdv::toml::ENodeType::node_floating_point) - { - fTunnelEndLat = static_cast(fEndLatNode.GetValue()); - } - - sdv::toml::CNode fEndLonNode = config.GetDirect("tunnel_end_lon"); - float fTunnelEndLon = 0.0; ///< Tunnel End Longitude - if (fEndLonNode.GetType() == sdv::toml::ENodeType::node_floating_point) - { - fTunnelEndLon = static_cast(fEndLonNode.GetValue()); - } - - // Calculate bounding box - m_SGPSBoundingBox.fTunnelMinLat = std::min(fTunnelStartLat, fTunnelEndLat); - m_SGPSBoundingBox.fTunnelMaxLat = std::max(fTunnelStartLat, fTunnelEndLat); - m_SGPSBoundingBox.fTunnelMinLon = std::min(fTunnelStartLon, fTunnelEndLon); - m_SGPSBoundingBox.fTunnelMaxLon = std::max(fTunnelStartLon, fTunnelEndLon); - } - catch (const sdv::toml::XTOMLParseException& e) - { - SDV_LOG_ERROR("Parsing error: ", e.what()); - return false; - } - - return true; -} - - IAutoheadlightService::SGPSBoundBox CAutoHeadlightService::GetGPSBoundBox() const { SGPSBoundBox tunnel; diff --git a/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs.h b/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs.h index b220f66..df36f22 100644 --- a/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs.h +++ b/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifndef COMPLEX_SERVICE_EXAMPLE_H #define COMPLEX_SERVICE_EXAMPLE_H @@ -35,7 +45,6 @@ */ class CAutoHeadlightService : public sdv::CSdvObject, - public sdv::IObjectControl, public IAutoheadlightService, public vss::Vehicle::Position::CurrentLatitudeService::IVSS_SetCurrentLatitude_Event, public vss::Vehicle::Position::CurrentLongitudeService::IVSS_SetCurrentLongitude_Event @@ -53,39 +62,35 @@ public: // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(vss::Vehicle::Position::CurrentLatitudeService::IVSS_SetCurrentLatitude_Event) SDV_INTERFACE_ENTRY(vss::Vehicle::Position::CurrentLongitudeService::IVSS_SetCurrentLongitude_Event) SDV_INTERFACE_ENTRY(IAutoheadlightService) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_function) DECLARE_OBJECT_CLASS_NAME("Auto Headlight Service") 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; + // Parameter map + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENABLE_LOCKING() + SDV_PARAM_ENTRY(m_SGPSBoundingBox.fTunnelMinLat, "tunnel_start_lat", 0.0f, u8"°", "Tunnel Start Latitude") + SDV_PARAM_ENTRY(m_SGPSBoundingBox.fTunnelMinLon, "tunnel_start_lon", 0.0f, u8"°", "Tunnel Start Longitude") + SDV_PARAM_ENTRY(m_SGPSBoundingBox.fTunnelMaxLat, "tunnel_end_lat", 0.0f, u8"°", "Tunnel End Latitude") + SDV_PARAM_ENTRY(m_SGPSBoundingBox.fTunnelMaxLon, "tunnel_end_lon", 0.0f, u8"°", "Tunnel End Longitude") + 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. */ - 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. - */ - void Shutdown() override; + virtual void OnShutdown() override; private: @@ -129,13 +134,11 @@ private: */ bool LoadGPSBounds(const sdv::u8string& rssObjectConfig); - sdv::EObjectStatus m_eStatus = sdv::EObjectStatus::initialization_pending; ///< Current object status volatile float m_fCurrentLatitude = 0.0; ///< Current Latitude volatile float m_fCurrentLongitude = 0.0; ///< Current Longitude volatile bool m_bHeadlight = false; ///< Headlight status SGPSBoundBox m_SGPSBoundingBox; ///< Tunnel bounding box coordinates - ///< Headlight interface. vss::Vehicle::Body::Light::Front::LowBeamService::IVSS_SetHeadLightLowBeam* m_pHeadlightSvc = nullptr; }; diff --git a/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs_ifc.idl b/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs_ifc.idl index 2f2f63e..a95d2db 100644 --- a/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs_ifc.idl +++ b/examples/auto_headlamp_example/autoheadlight_service/autoheadlight_cs_ifc.idl @@ -1,11 +1,16 @@ -/******************************************************************************* - * @file AutoHeadlight.idl - * @details Auto Headlight example service interface definition. - */ - + /******************************************************************************** + * 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 + ********************************************************************************/ /** * @brief AutoHeadlight example service interface. The interface provides functions for the + * @details Auto Headlight example service interface definition. * Auto Headlight example complex service. */ interface IAutoheadlightService diff --git a/examples/auto_headlamp_example/config/autoheadlight_cs.toml b/examples/auto_headlamp_example/config/autoheadlight_cs.toml index 981e77e..be18ec7 100644 --- a/examples/auto_headlamp_example/config/autoheadlight_cs.toml +++ b/examples/auto_headlamp_example/config/autoheadlight_cs.toml @@ -4,6 +4,7 @@ Version = 100 [[Component]] Path = "autoheadlight_service.sdv" Class = "Auto Headlight Service" +[Component.Parameters] tunnel_start_lat = 47.6500 tunnel_start_lon = 9.4700 tunnel_end_lat = 47.6506 diff --git a/examples/auto_headlamp_example/fmu_files/model.cpp b/examples/auto_headlamp_example/fmu_files/model.cpp index f6dc77f..ad6b52c 100644 --- a/examples/auto_headlamp_example/fmu_files/model.cpp +++ b/examples/auto_headlamp_example/fmu_files/model.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * @file model.cpp * @date 2025-09-12 15:01:57 diff --git a/examples/auto_headlamp_example/fmu_files/resources/fmu_autoheadlight_cs.toml b/examples/auto_headlamp_example/fmu_files/resources/fmu_autoheadlight_cs.toml index 6a091df..8515dcc 100644 --- a/examples/auto_headlamp_example/fmu_files/resources/fmu_autoheadlight_cs.toml +++ b/examples/auto_headlamp_example/fmu_files/resources/fmu_autoheadlight_cs.toml @@ -4,6 +4,7 @@ Version = 100 [[Component]] Path = "autoheadlight_service.sdv" Class = "Auto Headlight Service" +[Component.Parameters] tunnel_start_lat = 31.300 tunnel_start_lon = 3.756 tunnel_end_lat = 21.168 diff --git a/examples/configuration_component_example/CMakeLists.txt b/examples/configuration_component_example/CMakeLists.txt index 89e0e88..b1b1398 100644 --- a/examples/configuration_component_example/CMakeLists.txt +++ b/examples/configuration_component_example/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Use new policy for project version settings and default warning level cmake_policy(SET CMP0048 NEW) # requires CMake 3.14 cmake_policy(SET CMP0092 NEW) # requires CMake 3.15 diff --git a/examples/configuration_component_example/src/configuration_comp.cpp b/examples/configuration_component_example/src/configuration_comp.cpp index 086e084..06456f8 100644 --- a/examples/configuration_component_example/src/configuration_comp.cpp +++ b/examples/configuration_component_example/src/configuration_comp.cpp @@ -1,27 +1,32 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include -class DemoConfigurationComponent : public sdv::CSdvObject, public sdv::IObjectControl +class DemoConfigurationComponent : public sdv::CSdvObject { - public: - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) +public: + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("Configuration_Example") /** - * @brief initialize function to register, access the task timer interface from platform abstraction. - * After initialization 'CreateTimer' function is called to execute the task periodically. - * @param[in] rssObjectConfig An object configuration is currently not used by this demo component. - */ - virtual void Initialize([[maybe_unused]] const sdv::u8string& rssObjectConfig) override + * @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 bool OnInitialize() override { try { - sdv::toml::CTOMLParser config(rssObjectConfig.c_str()); + sdv::toml::CTOMLParser config(GetObjectConfig()); sdv::toml::CNode messageNode = config.GetDirect("Message"); if (messageNode.GetType() == sdv::toml::ENodeType::node_string) @@ -89,53 +94,19 @@ class DemoConfigurationComponent : public sdv::CSdvObject, public sdv::IObjectCo { SDV_LOG_ERROR("Parsing error: ", e.what()); - m_status = sdv::EObjectStatus::initialization_failure; - return; + return false; } PrintAllVariables(); - m_status = sdv::EObjectStatus::initialized; + return true; }; /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object - */ - virtual sdv::EObjectStatus GetStatus() const override - { - return m_status; - }; - - /** - * @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(/*in*/ sdv::EOperationMode eMode) - { - switch (eMode) - { - case sdv::EOperationMode::configuring: - if (m_status == sdv::EObjectStatus::running || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::configuring; - break; - case sdv::EOperationMode::running: - if (m_status == sdv::EObjectStatus::configuring || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::running; - break; - default: - break; - } - } - - /** - * @brief Shutdown function is to shutdown the execution of periodic task. - * Timer ID of the task is used to shutdown the specific task. - */ - virtual void Shutdown() override - { - m_status = sdv::EObjectStatus::destruction_pending; - } + virtual void OnShutdown() override + {} /** * @brief Print all global variables to console @@ -160,8 +131,6 @@ class DemoConfigurationComponent : public sdv::CSdvObject, public sdv::IObjectCo } private: - std::atomic m_status = {sdv::EObjectStatus::initialization_pending}; //!< To update the object status when it changes. - std::string m_Message { "" }; std::string m_JSONConfig { "" }; int32_t m_Id { -1 }; diff --git a/examples/configuration_example/CMakeLists.txt b/examples/configuration_example/CMakeLists.txt index 5579f6a..fd1eef3 100644 --- a/examples/configuration_example/CMakeLists.txt +++ b/examples/configuration_example/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + project(ConfigurationTestApp) # Use new policy for project version settings and default warning level diff --git a/examples/configuration_example/config/test_configuration_example.toml b/examples/configuration_example/config/test_configuration_example.toml index 6b0d099..cfb1229 100644 --- a/examples/configuration_example/config/test_configuration_example.toml +++ b/examples/configuration_example/config/test_configuration_example.toml @@ -4,6 +4,7 @@ Version = 100 [[Component]] Path = "configuration_component_example.sdv" Class = "Configuration_Example" +[Component.Parameters] Message = "It's me" ### the following is a valid JSON structure in a muliline string JSONConfig = """{ diff --git a/examples/configuration_example/src/configuration_example.cpp b/examples/configuration_example/src/configuration_example.cpp index 6133c14..be14b53 100644 --- a/examples/configuration_example/src/configuration_example.cpp +++ b/examples/configuration_example/src/configuration_example.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include diff --git a/examples/door_demo_example/CMakeLists.txt b/examples/door_demo_example/CMakeLists.txt index 1b8df59..cbe207d 100644 --- a/examples/door_demo_example/CMakeLists.txt +++ b/examples/door_demo_example/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + project(DoorDemoExample) # Use new policy for project version settings and default warning level @@ -19,7 +29,7 @@ file (COPY ${PROJECT_SOURCE_DIR}/config/front_left_door_example.toml DESTINATION file (COPY ${PROJECT_SOURCE_DIR}/config/front_right_door_example.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) file (COPY ${PROJECT_SOURCE_DIR}/config/rear_left_door_example.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) file (COPY ${PROJECT_SOURCE_DIR}/config/rear_right_door_example.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) -file (COPY ${PROJECT_SOURCE_DIR}/config/door_comple_service.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) +file (COPY ${PROJECT_SOURCE_DIR}/config/door_complex_service.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) file (COPY ${PROJECT_SOURCE_DIR}/config/data_link_door.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) # Copy the ASC files used by can_com_sim.sdv which includes the CAN messages @@ -32,77 +42,85 @@ file (COPY ${PROJECT_SOURCE_DIR}/door_example_receiver.asc DESTINATION ${CMAKE_B # preparation ###################################################################################################################################################################### +# Execute sdv_vss_util to create IDL files for devices and basic services. +message("Create interface code for devices and basic services of doors example.") +execute_process(COMMAND "${SDV_VSS_UTIL}" "${PROJECT_SOURCE_DIR}/vss_doors_example.csv" "-O${PROJECT_SOURCE_DIR}/generated/" --prefixdoors --version1.0.0.1 --enable_components) + + message("Compiling Interface Door01Left RX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle01left_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle01left_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01left_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01left_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) message("Compiling Interface Door01Right RX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle01right_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle01right_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01right_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01right_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) message("Compiling Interface Door02Left RX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle02left_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle02left_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02left_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02left_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) message("Compiling Interface Door02Right RX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle02right_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle02right_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02right_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02right_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) message("Compiling Interface Door01Left TX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle01left_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle01left_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01left_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01left_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) message("Compiling Interface Door01Right TX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle01right_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle01right_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01right_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01right_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) message("Compiling Interface Door02Left TX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle02left_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle02left_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02left_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02left_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) message("Compiling Interface Door02Right TX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle02right_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/interfaces/vss_vehiclechassisdooraxle02right_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/interfaces/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02right_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02right_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) # Execute the IDL compiler for the complex service to digest interface code. message("Compiling door_ifc.idl") execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/door_service/door_ifc.idl" "-O${PROJECT_SOURCE_DIR}/generated/door_service/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Iexample_service/ --ps_lib_namedoors_service_proxystub) -add_subdirectory(interfaces/ps) +add_subdirectory(generated/door_service/ps) +add_subdirectory(generated/vss_files/ps) -add_subdirectory(vd/vd_front_door_left) -add_subdirectory(vd/vd_front_door_right) -add_subdirectory(vd/vd_rear_door_left) -add_subdirectory(vd/vd_rear_door_right) +add_subdirectory(generated/vss_files/vd_frontdoorleft) +add_subdirectory(generated/vss_files/vd_frontdoorright) +add_subdirectory(generated/vss_files/vd_reardoorleft) +add_subdirectory(generated/vss_files/vd_reardoorright) -add_subdirectory(bs/bs_front_door_left) -add_subdirectory(bs/bs_front_door_right) -add_subdirectory(bs/bs_rear_door_left) -add_subdirectory(bs/bs_rear_door_right) +add_subdirectory(generated/vss_files/bs_frontdoorleft) +add_subdirectory(generated/vss_files/bs_frontdoorright) +add_subdirectory(generated/vss_files/bs_reardoorleft) +add_subdirectory(generated/vss_files/bs_reardoorright) -add_subdirectory(can_dl_door) +# Execute sdv_dbc_util to create data link code & FMU code. +message("Create data link for doors example") +execute_process(COMMAND ${SDV_DBC_UTIL} "${PROJECT_SOURCE_DIR}/datalink_4doors_example.dbc" "-O${PROJECT_SOURCE_DIR}/generated/" --nodesdoors --version1.0.0.1 --moduleDoorsExampleFMU --dl_lib_namecan_dl_doors) + +add_subdirectory(generated/can_dl) ###################################################################################################################################################################### # complex service ###################################################################################################################################################################### -include_directories(interfaces) include_directories(${CMAKE_CURRENT_LIST_DIR}/door_app/include) message("Include: proxy/stub for complex doors service") include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/door_service) -add_subdirectory(generated/door_service/ps) -add_library(door_complex_service SHARED +add_library(doors_complex_service SHARED door_service/complex_service.h door_service/complex_service.cpp door_service/lock_doors_thread.h) -set_target_properties(door_complex_service PROPERTIES OUTPUT_NAME "door_complex_service") -set_target_properties(door_complex_service PROPERTIES PREFIX "") -set_target_properties(door_complex_service PROPERTIES SUFFIX ".sdv") +set_target_properties(doors_complex_service PROPERTIES OUTPUT_NAME "doors_complex_service") +set_target_properties(doors_complex_service PROPERTIES PREFIX "") +set_target_properties(doors_complex_service PROPERTIES SUFFIX ".sdv") ###################################################################################################################################################################### # executable @@ -152,19 +170,18 @@ else() endif() - ###################################################################################################################################################################### # create instance 3002 of door demo example ###################################################################################################################################################################### add_custom_target(vehicle_device_doors_config ALL DEPENDS - can_dl_door - doors_vd_frontdoorleft - doors_vd_frontdoorright - doors_vd_reardoorleft - doors_vd_reardoorright - COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL VehicleDeviceDoors --instance3002 can_dl_door.sdv doors_vd_frontdoorleft.sdv doors_vd_frontdoorright.sdv doors_vd_reardoorleft.sdv doors_vd_reardoorright.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --interface_config --overwrite + can_dl_doors + doors_vd_frontdoorleft_rx + doors_vd_frontdoorright_rx + doors_vd_reardoorleft_rx + doors_vd_reardoorright_rx + COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL VehicleDeviceDoors --instance3002 can_dl_doors.sdv doors_vd_frontdoorleft_rx.sdv doors_vd_frontdoorright_rx.sdv doors_vd_reardoorleft_rx.sdv doors_vd_reardoorright_rx.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --interface_config --overwrite VERBATIM ) @@ -172,31 +189,29 @@ add_custom_target(basic_service_doors_config ALL DEPENDS doors_proxystub - doors_bs_frontdoorleft - doors_bs_frontdoorright - doors_bs_reardoorleft - doors_bs_reardoorright - COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL BasicServiceDoors --instance3002 doors_proxystub.sdv doors_bs_frontdoorleft.sdv doors_bs_frontdoorright.sdv doors_bs_reardoorleft.sdv doors_bs_reardoorright.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --abstract_config --overwrite + doors_bs_frontdoorleft_rx + doors_bs_frontdoorright_rx + doors_bs_reardoorleft_rx + doors_bs_reardoorright_rx + COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL BasicServiceDoors --instance3002 doors_proxystub.sdv doors_bs_frontdoorleft_rx.sdv doors_bs_frontdoorright_rx.sdv doors_bs_reardoorleft_rx.sdv doors_bs_reardoorright_rx.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --abstract_config --overwrite VERBATIM ) add_custom_target(door_complex_service_config ALL DEPENDS - door_complex_service + doors_complex_service doors_service_proxystub - COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL DoorComplexService --instance3002 door_complex_service.sdv doors_service_proxystub.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --user_config --overwrite + COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL DoorsComplexService --instance3002 doors_complex_service.sdv doors_service_proxystub.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --user_config --overwrite + VERBATIM +) + +add_custom_target(platform_doors_config + ALL + COMMAND "${SDV_PACKAGER}" CONFIGURE ${PROJECT_SOURCE_DIR}/coreconfig/platform.toml --instance3002 --platform_config VERBATIM ) -###################################################################################################################################################################### -# TODO: SDV_PACKAGER does not create the toml files, therefore we need to copy them -###################################################################################################################################################################### -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/door_demo.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3002) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/platform.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3002) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/settings.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3002) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/vehicle_abstract.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3002) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/vehicle_ifc.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3002) ###################################################################################################################################################################### # the FMU files have been created via the dbc file @@ -211,5 +226,5 @@ file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/vehicle_ifc.toml DESTINATION ${SDV_F # ...\fmu_Doors2ExampleFMU\Doors2ExampleFMU\model.cpp # ...\fmu_Doors2ExampleFMU\Doors4ExampleFMU\model.cpp ###################################################################################################################################################################### -add_subdirectory(fmu_Doors2ExampleFMU) -add_subdirectory(fmu_Doors4ExampleFMU) +add_subdirectory(fmu_Doors2ExampleFMU) +add_subdirectory(fmu_Doors4ExampleFMU) diff --git a/examples/door_demo_example/config/data_link_door.toml b/examples/door_demo_example/config/data_link_door.toml index 69e2b94..6d58f51 100644 --- a/examples/door_demo_example/config/data_link_door.toml +++ b/examples/door_demo_example/config/data_link_door.toml @@ -4,6 +4,7 @@ Version = 100 [[Component]] Path = "can_com_sim.sdv" Class = "CAN_Com_Sim" +[Component.Parameters] Source="door_example_receiver.asc" Target="door_example_writer.asc" diff --git a/examples/door_demo_example/config/door_complex_service.toml b/examples/door_demo_example/config/door_complex_service.toml new file mode 100644 index 0000000..124545f --- /dev/null +++ b/examples/door_demo_example/config/door_complex_service.toml @@ -0,0 +1,10 @@ +[Configuration] +Version = 100 + +[[Component]] +Path = "doors_complex_service.sdv" +Class = "Doors Example Service" + + + + diff --git a/examples/door_demo_example/config/front_left_door_example.toml b/examples/door_demo_example/config/front_left_door_example.toml index 2c6dcb7..dd6699c 100644 --- a/examples/door_demo_example/config/front_left_door_example.toml +++ b/examples/door_demo_example/config/front_left_door_example.toml @@ -2,13 +2,12 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Left_Device" +Path = "doors_vd_frontdoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Left_Device" [[Component]] -Path = "doors_bs_frontdoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Left_Service" - +Path = "doors_bs_frontdoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Left_Service" diff --git a/examples/door_demo_example/config/front_right_door_example.toml b/examples/door_demo_example/config/front_right_door_example.toml index 28e517c..68f14bd 100644 --- a/examples/door_demo_example/config/front_right_door_example.toml +++ b/examples/door_demo_example/config/front_right_door_example.toml @@ -2,9 +2,12 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Right_Device" +Path = "doors_vd_frontdoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Right_Device" [[Component]] -Path = "doors_bs_frontdoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Right_Service" +Path = "doors_bs_frontdoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Right_Service" + + + diff --git a/examples/door_demo_example/config/rear_left_door_example.toml b/examples/door_demo_example/config/rear_left_door_example.toml index 19be3a6..2da09dd 100644 --- a/examples/door_demo_example/config/rear_left_door_example.toml +++ b/examples/door_demo_example/config/rear_left_door_example.toml @@ -2,12 +2,12 @@ Version = 100 [[Component]] -Path = "doors_vd_reardoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle02.Left_Device" +Path = "doors_vd_reardoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._02.Left_Device" [[Component]] -Path = "doors_bs_reardoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle02.Left_Service" +Path = "doors_bs_reardoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._02.Left_Service" diff --git a/examples/door_demo_example/config/rear_right_door_example.toml b/examples/door_demo_example/config/rear_right_door_example.toml index 1fcfa28..4f2069e 100644 --- a/examples/door_demo_example/config/rear_right_door_example.toml +++ b/examples/door_demo_example/config/rear_right_door_example.toml @@ -2,9 +2,9 @@ Version = 100 [[Component]] -Path = "doors_vd_reardoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle02.Right_Device" +Path = "doors_vd_reardoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._02.Right_Device" [[Component]] -Path = "doors_bs_reardoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle02.Right_Service" +Path = "doors_bs_reardoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._02.Right_Service" diff --git a/examples/door_demo_example/coreconfig/platform.toml b/examples/door_demo_example/coreconfig/platform.toml index 01de007..523bc44 100644 --- a/examples/door_demo_example/coreconfig/platform.toml +++ b/examples/door_demo_example/coreconfig/platform.toml @@ -4,6 +4,7 @@ Version = 100 [[Component]] Path = "can_com_sim.sdv" Class = "CAN_Com_Sim" +[Component.Parameters] Source="door_example_receiver.asc" Target="door_example_writer.asc" diff --git a/examples/door_demo_example/door_app/console.cpp b/examples/door_demo_example/door_app/console.cpp index bcc164e..63b12a3 100644 --- a/examples/door_demo_example/door_app/console.cpp +++ b/examples/door_demo_example/door_app/console.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include "include/console.h" #ifdef _WIN32 @@ -131,7 +141,7 @@ bool CConsole::PrepareDataConsumers() } } - auto basicServiceL1 = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Left_Service").GetInterface(); + auto basicServiceL1 = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Service").GetInterface(); if (!basicServiceL1) { SDV_LOG_ERROR("Could not get interface 'LeftService::IVSS_IsOpen': [CConsole]"); @@ -142,29 +152,29 @@ bool CConsole::PrepareDataConsumers() /* Interface exists -> Clean the line for Console window */ PrintText(g_sBasicServiceL1, " "); } - basicServiceL1->RegisterOnSignalChangeOfLeftDoorIsOpen01(dynamic_cast (this)); + basicServiceL1->RegisterOnSignalChangeOfLeftDoorIsOpen01(dynamic_cast (this)); bool value = false; PrintValue(g_sFrontLeftDoorIsLocked, "Front Left Latch:", value, (value ? "locked" : "unlocked")); // all other doors are optional - auto basicServiceR1 = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Right_Service").GetInterface(); + auto basicServiceR1 = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Right_Service").GetInterface(); if (basicServiceR1) { - basicServiceR1->RegisterOnSignalChangeOfRightDoorIsOpen01(dynamic_cast (this)); + basicServiceR1->RegisterOnSignalChangeOfRightDoorIsOpen01(dynamic_cast (this)); PrintValue(g_sFrontRightDoorIsLocked, "Front Right Latch:", value, (value ? "locked" : "unlocked")); } - auto basicServiceL2 = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Left_Service").GetInterface(); + auto basicServiceL2 = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Left_Service").GetInterface(); if (basicServiceL2) { - basicServiceL2->RegisterOnSignalChangeOfLeftDoorIsOpen02(dynamic_cast (this)); + basicServiceL2->RegisterOnSignalChangeOfLeftDoorIsOpen02(dynamic_cast (this)); PrintValue(g_sRearLeftDoorIsLocked, "Rear Left Latch:", value, (value ? "locked" : "unlocked")); } - auto basicServiceR2 = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Right_Service").GetInterface(); + auto basicServiceR2 = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Right_Service").GetInterface(); if (basicServiceR2) { - basicServiceR2->RegisterOnSignalChangeOfRightDoorIsOpen02(dynamic_cast (this)); + basicServiceR2->RegisterOnSignalChangeOfRightDoorIsOpen02(dynamic_cast (this)); PrintValue(g_sRearRightDoorIsLocked, "Rear Right Latch:", value, (value ? "locked" : "unlocked")); } @@ -196,25 +206,26 @@ void CConsole::ResetSignals() SetCursorPos(g_sCursor); // Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional - auto vehicleDevice = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Left_Device").GetInterface(); + auto vehicleDevice = + sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface < vss::Vehicle::Body::Door::Axle::_01 ::LeftDevice::IVSS_IsOpen > (); if (vehicleDevice) - vehicleDevice->UnregisterIsOpenEvent(dynamic_cast (this)); + vehicleDevice->UnregisterIsOpenEvent(dynamic_cast (this)); - auto basicServiceL1 = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Left_Service").GetInterface(); + auto basicServiceL1 = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Service").GetInterface(); if (basicServiceL1) - basicServiceL1->UnregisterOnSignalChangeOfLeftDoorIsOpen01(dynamic_cast (this)); + basicServiceL1->UnregisterOnSignalChangeOfLeftDoorIsOpen01(dynamic_cast (this)); - auto basicServiceR1 = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Right_Service").GetInterface(); + auto basicServiceR1 = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Right_Service").GetInterface(); if (basicServiceR1) - basicServiceR1->UnregisterOnSignalChangeOfRightDoorIsOpen01(dynamic_cast (this)); + basicServiceR1->UnregisterOnSignalChangeOfRightDoorIsOpen01(dynamic_cast (this)); - auto basicServiceL2 = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Left_Service").GetInterface(); + auto basicServiceL2 = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Left_Service").GetInterface(); if (basicServiceL2) - basicServiceL2->UnregisterOnSignalChangeOfLeftDoorIsOpen02(dynamic_cast (this)); + basicServiceL2->UnregisterOnSignalChangeOfLeftDoorIsOpen02(dynamic_cast (this)); - auto basicServiceR2 = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Right_Service").GetInterface(); + auto basicServiceR2 = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Right_Service").GetInterface(); if (basicServiceR2) - basicServiceR2->UnregisterOnSignalChangeOfRightDoorIsOpen02(dynamic_cast (this)); + basicServiceR2->UnregisterOnSignalChangeOfRightDoorIsOpen02(dynamic_cast (this)); // Unregister the data link signalss if (m_SignalFrontLeftDoorIsOpen) @@ -327,13 +338,13 @@ bool CConsole::PrepareDataConsumersForStandAlone() } // Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional - auto vehicleDevice = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Left_Device").GetInterface(); + auto vehicleDevice = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface(); if (!vehicleDevice) { SDV_LOG_ERROR("Could not get interface 'LeftDevice::IVSS_IsOpen': [CConsole]"); return false; } - vehicleDevice->RegisterIsOpenEvent(dynamic_cast (this)); + vehicleDevice->RegisterIsOpenEvent(dynamic_cast (this)); return true; } diff --git a/examples/door_demo_example/door_app/door_application.cpp b/examples/door_demo_example/door_app/door_application.cpp index 0898d49..8eece56 100644 --- a/examples/door_demo_example/door_app/door_application.cpp +++ b/examples/door_demo_example/door_app/door_application.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include "../door_app/include/door_application.h" #include "../door_app/include/signal_names.h" @@ -126,7 +136,7 @@ bool CDoorControl::Initialize(const uint32_t numberOfDoors) } } - bResult &= LoadConfigFile("Load door service (complex service): ", "door_comple_service.toml"); + bResult &= LoadConfigFile("Load door service (complex service): ", "door_complex_service.toml"); if (!bResult) { diff --git a/examples/door_demo_example/door_app/door_demo_example.cpp b/examples/door_demo_example/door_app/door_demo_example.cpp index faa4aaa..861e140 100644 --- a/examples/door_demo_example/door_app/door_demo_example.cpp +++ b/examples/door_demo_example/door_app/door_demo_example.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include "../door_app/include/door_application.h" #include "../door_app/include/console.h" diff --git a/examples/door_demo_example/door_app/door_extern_application.cpp b/examples/door_demo_example/door_app/door_extern_application.cpp index ca24e28..22f96ca 100644 --- a/examples/door_demo_example/door_app/door_extern_application.cpp +++ b/examples/door_demo_example/door_app/door_extern_application.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include "../door_app/include/door_extern_application.h" #include "../door_app/include/signal_names.h" diff --git a/examples/door_demo_example/door_app/door_extern_example.cpp b/examples/door_demo_example/door_app/door_extern_example.cpp index 200b76b..aea1e0c 100644 --- a/examples/door_demo_example/door_app/door_extern_example.cpp +++ b/examples/door_demo_example/door_app/door_extern_example.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include "../door_app/include/door_extern_application.h" #include "../door_app/include/console.h" diff --git a/examples/door_demo_example/door_app/include/console.h b/examples/door_demo_example/door_app/include/console.h index 4045a95..0f55358 100644 --- a/examples/door_demo_example/door_app/include/console.h +++ b/examples/door_demo_example/door_app/include/console.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifndef CONSOLE_OUTPUT_H #define CONSOLE_OUTPUT_H @@ -12,29 +22,30 @@ #include "signal_names.h" #include #include -#include "../interfaces/vss_vehiclechassisdooraxle01left_vd_rx.h" -#include "../interfaces/vss_vehiclechassisdooraxle01left_bs_rx.h" -#include "../interfaces/vss_vehiclechassisdooraxle01right_bs_rx.h" -#include "../interfaces/vss_vehiclechassisdooraxle02left_bs_rx.h" -#include "../interfaces/vss_vehiclechassisdooraxle02right_bs_rx.h" + +#include "../../generated/vss_files/vss_vehiclebodydooraxle_01left_vd_rx.h" +#include "../../generated/vss_files/vss_vehiclebodydooraxle_01left_bs_rx.h" +#include "../../generated/vss_files/vss_vehiclebodydooraxle_01right_bs_rx.h" +#include "../../generated/vss_files/vss_vehiclebodydooraxle_02left_bs_rx.h" +#include "../../generated/vss_files/vss_vehiclebodydooraxle_02right_bs_rx.h" #ifdef __unix__ #include // Needed for tcgetattr and fcntl #include #endif -#include "../generated/door_service/door_ifc.h" +#include "../../generated/door_service/door_ifc.h" /** * @brief Console operation class. * @details This class retrieves RX data from the data dispatch service, vehicle device & basic service of front left door on event change. * Furthermore, it shows TX value by polling the RX signals. */ -class CConsole : public vss::Vehicle::Chassis::Door::Axle01::LeftDevice::IVSS_WriteIsOpen_Event - , public vss::Vehicle::Chassis::Door::Axle01::LeftService::IVSS_SetIsOpen_Event - , public vss::Vehicle::Chassis::Door::Axle01::RightService::IVSS_SetIsOpen_Event - , public vss::Vehicle::Chassis::Door::Axle02::LeftService::IVSS_SetIsOpen_Event - , public vss::Vehicle::Chassis::Door::Axle02::RightService::IVSS_SetIsOpen_Event +class CConsole : public vss::Vehicle::Body::Door::Axle::_01::LeftDevice::IVSS_WriteIsOpen_Event + , public vss::Vehicle::Body::Door::Axle::_01::LeftService::IVSS_SetIsOpenL1_Event + , public vss::Vehicle::Body::Door::Axle::_01::RightService::IVSS_SetIsOpenR1_Event + , public vss::Vehicle::Body::Door::Axle::_02::LeftService::IVSS_SetIsOpenL2_Event + , public vss::Vehicle::Body::Door::Axle::_02::RightService::IVSS_SetIsOpenR2_Event { public: /** diff --git a/examples/door_demo_example/door_app/include/door_application.h b/examples/door_demo_example/door_app/include/door_application.h index 86b546e..e660329 100644 --- a/examples/door_demo_example/door_app/include/door_application.h +++ b/examples/door_demo_example/door_app/include/door_application.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include diff --git a/examples/door_demo_example/door_app/include/door_extern_application.h b/examples/door_demo_example/door_app/include/door_extern_application.h index 56582b7..af33be3 100644 --- a/examples/door_demo_example/door_app/include/door_extern_application.h +++ b/examples/door_demo_example/door_app/include/door_extern_application.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include diff --git a/examples/door_demo_example/door_app/include/signal_names.h b/examples/door_demo_example/door_app/include/signal_names.h index f6ad9b4..d01b038 100644 --- a/examples/door_demo_example/door_app/include/signal_names.h +++ b/examples/door_demo_example/door_app/include/signal_names.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * namespace for the signal names * in case /interfaces/signal_identifier.h @@ -9,14 +19,14 @@ namespace doors { // Data Dispatch Service signal names to dbc variable names C-type RX/TX vss name space - static std::string dsLeftDoorIsOpen01 = "CAN_Input_L1.Door01LeftIsOpen"; ///< bool RX Vehicle.Chassis.Door.Axle01.Left - static std::string dsRightDoorIsOpen01 = "CAN_Input_R1.Door01RightIsOpen"; ///< bool RX Vehicle.Chassis.Door.Axle01.Right - static std::string dsLeftDoorIsOpen02 = "CAN_Input_L2.Door02LeftIsOpen"; ///< bool RX Vehicle.Chassis.Door.Axle02.Left - static std::string dsRightDoorIsOpen02 = "CAN_Input_R2.Door02RightIsOpen"; ///< bool RX Vehicle.Chassis.Door.Axle02.Right - static std::string dsLeftLatch01 = "CAN_Output.LockDoor01Left"; ///< bool TX Vehicle.Chassis.TX.Door.Axle01.Left - static std::string dsRightLatch01 = "CAN_Output.LockDoor01Right"; ///< bool TX Vehicle.Chassis.TX.Door.Axle01.Right - static std::string dsLeftLatch02 = "CAN_Output.LockDoor02Left"; ///< bool TX Vehicle.Chassis.TX.Door.Axle02.Left - static std::string dsRightLatch02 = "CAN_Output.LockDoor02Right"; ///< bool TX Vehicle.Chassis.TX.Door.Axle02.Left + static std::string dsLeftDoorIsOpen01 = "CAN_Input_L1.Door01LeftIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._01.Left + static std::string dsRightDoorIsOpen01 = "CAN_Input_R1.Door01RightIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._01.Right + static std::string dsLeftDoorIsOpen02 = "CAN_Input_L2.Door02LeftIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._02.Left + static std::string dsRightDoorIsOpen02 = "CAN_Input_R2.Door02RightIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._02.Right + static std::string dsLeftLatch01 = "CAN_Output.LockDoor01Left"; ///< bool TX Vehicle.Body.Door.Axle._01.Left + static std::string dsRightLatch01 = "CAN_Output.LockDoor01Right"; ///< bool TX Vehicle.Body.Door.Axle._01.Right + static std::string dsLeftLatch02 = "CAN_Output.LockDoor02Left"; ///< bool TX Vehicle.Body.Door.Axle._02.Left + static std::string dsRightLatch02 = "CAN_Output.LockDoor02Right"; ///< bool TX Vehicle.Body.Door.Axle._02.Right } // doors diff --git a/examples/door_demo_example/door_service/complex_service.cpp b/examples/door_demo_example/door_service/complex_service.cpp index 9bed77e..0423538 100644 --- a/examples/door_demo_example/door_service/complex_service.cpp +++ b/examples/door_demo_example/door_service/complex_service.cpp @@ -1,119 +1,113 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include "complex_service.h" -void CDoorsExampleService::Initialize(const sdv::u8string& /*ssObjectConfig*/) +bool CDoorsExampleService::OnInitialize() { - m_eStatus = sdv::EObjectStatus::initializing; - // Request the basic service for front left door. - auto pFrontLeftDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Left_Service").GetInterface(); + auto pFrontLeftDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Service").GetInterface(); if (pFrontLeftDoorSvc) { // Register front left door change event handler. - pFrontLeftDoorSvc->RegisterOnSignalChangeOfLeftDoorIsOpen01(static_cast (this)); + pFrontLeftDoorSvc->RegisterOnSignalChangeOfLeftDoorIsOpen01(static_cast (this)); } // Request the basic service for front right door. - auto pFrontRightDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Right_Service").GetInterface(); + auto pFrontRightDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Right_Service").GetInterface(); if (pFrontRightDoorSvc) { // Register front right door change event handler. - pFrontRightDoorSvc->RegisterOnSignalChangeOfRightDoorIsOpen01(static_cast (this)); + pFrontRightDoorSvc->RegisterOnSignalChangeOfRightDoorIsOpen01(static_cast (this)); } // Request the basic service for rear left door. - auto pRearLeftDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Left_Service").GetInterface(); + auto pRearLeftDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Left_Service").GetInterface(); if (pRearLeftDoorSvc) { // Register rear left door change event handler. - pRearLeftDoorSvc->RegisterOnSignalChangeOfLeftDoorIsOpen02(static_cast (this)); + pRearLeftDoorSvc->RegisterOnSignalChangeOfLeftDoorIsOpen02(static_cast (this)); } // Request the basic service for front right door. - auto pRearRightDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Right_Service").GetInterface(); + auto pRearRightDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Right_Service").GetInterface(); if (pRearRightDoorSvc) { // Register rear right door change event handler. - pRearRightDoorSvc->RegisterOnSignalChangeOfRightDoorIsOpen02(static_cast (this)); + pRearRightDoorSvc->RegisterOnSignalChangeOfRightDoorIsOpen02(static_cast (this)); } // Request the basic service for locking the front left door. - m_pFrontLeftDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Left_Service").GetInterface(); + m_pFrontLeftDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Service").GetInterface(); // Request the basic service for locking the front right door. - m_pFrontRightDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Right_Service").GetInterface(); + m_pFrontRightDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Right_Service").GetInterface(); // Request the basic service for locking the rear left door. - m_pRearLeftDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Left_Service").GetInterface(); + m_pRearLeftDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Left_Service").GetInterface(); // Request the basic service for locking the rear right door. - m_pRearRightDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Right_Service").GetInterface(); + m_pRearRightDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Right_Service").GetInterface(); // Validate if we have the Open/Closed signal and the Lock/Unlock door signal, both must exist together or both must not exist - // Front left door is an exception, it isalways required + // Front left door is an exception, it is always required if ((!pFrontLeftDoorSvc) || (!m_pFrontLeftDoorSvc)) { SDV_LOG_ERROR("Could not get interfaces for 'Front left door': [CDoorsExampleService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } if ((pFrontRightDoorSvc == nullptr) != (m_pFrontRightDoorSvc == nullptr)) { SDV_LOG_ERROR("Could not get both interfaces for 'Front right door': [CDoorsExampleService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } if ((pRearLeftDoorSvc == nullptr) != (m_pRearLeftDoorSvc == nullptr)) { SDV_LOG_ERROR("Could not get both interfaces for 'Rear left door': [CDoorsExampleService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } if ((pRearRightDoorSvc == nullptr) != (m_pRearRightDoorSvc == nullptr)) { SDV_LOG_ERROR("Could not get both interfaces for 'Rear right door': [CDoorsExampleService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } m_doorsThread.start(m_Interval); - m_eStatus = sdv::EObjectStatus::initialized; + return true; } -sdv::EObjectStatus CDoorsExampleService::GetStatus() const -{ - return m_eStatus; -} - -void CDoorsExampleService::SetOperationMode(sdv::EOperationMode /*eMode*/) -{ - // Not applicable -} - -void CDoorsExampleService::Shutdown() +void CDoorsExampleService::OnShutdown() { // Unregister front left door change event handler. - auto pFrontLeftDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Left_Service").GetInterface(); + auto pFrontLeftDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Service").GetInterface(); if (pFrontLeftDoorSvc) - pFrontLeftDoorSvc->UnregisterOnSignalChangeOfLeftDoorIsOpen01(static_cast (this)); + pFrontLeftDoorSvc->UnregisterOnSignalChangeOfLeftDoorIsOpen01(static_cast (this)); // Unregister front right door change event handler. - auto pFrontRightDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle01.Right_Service").GetInterface(); + auto pFrontRightDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Right_Service").GetInterface(); if (pFrontRightDoorSvc) - pFrontRightDoorSvc->UnregisterOnSignalChangeOfRightDoorIsOpen01(static_cast (this)); + pFrontRightDoorSvc->UnregisterOnSignalChangeOfRightDoorIsOpen01(static_cast (this)); // Unregister rear left door change event handler. - auto pRearLeftDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Left_Service").GetInterface(); + auto pRearLeftDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Left_Service").GetInterface(); if (pRearLeftDoorSvc) - pRearLeftDoorSvc->UnregisterOnSignalChangeOfLeftDoorIsOpen02(static_cast (this)); + pRearLeftDoorSvc->UnregisterOnSignalChangeOfLeftDoorIsOpen02(static_cast (this)); // Unregister rear right door change event handler. - auto pRearRightDoorSvc = sdv::core::GetObject("Vehicle.Chassis.Door.Axle02.Right_Service").GetInterface(); + auto pRearRightDoorSvc = sdv::core::GetObject("Vehicle.Body.Door.Axle._02.Right_Service").GetInterface(); if (pRearRightDoorSvc) - pRearRightDoorSvc->UnregisterOnSignalChangeOfRightDoorIsOpen02(static_cast (this)); + pRearRightDoorSvc->UnregisterOnSignalChangeOfRightDoorIsOpen02(static_cast (this)); m_doorsThread.stop(); } diff --git a/examples/door_demo_example/door_service/complex_service.h b/examples/door_demo_example/door_service/complex_service.h index 22c431c..c3b56fd 100644 --- a/examples/door_demo_example/door_service/complex_service.h +++ b/examples/door_demo_example/door_service/complex_service.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifndef DOORS_COMPLEX_SERVICE_EXAMPLE_H #define DOORS_COMPLEX_SERVICE_EXAMPLE_H @@ -8,15 +18,15 @@ #include #include -// VSS interfaces - located in ../interfaces -#include "vss_vehiclechassisdooraxle01left_bs_rx.h" -#include "vss_vehiclechassisdooraxle01left_bs_tx.h" -#include "vss_vehiclechassisdooraxle01right_bs_rx.h" -#include "vss_vehiclechassisdooraxle01right_bs_tx.h" -#include "vss_vehiclechassisdooraxle02left_bs_rx.h" -#include "vss_vehiclechassisdooraxle02left_bs_tx.h" -#include "vss_vehiclechassisdooraxle02right_bs_rx.h" -#include "vss_vehiclechassisdooraxle02right_bs_tx.h" +// VSS interfaces - located in ../generated/vss_files/ +#include "../generated/vss_files/vss_vehiclebodydooraxle_01left_bs_rx.h" +#include "../generated/vss_files/vss_vehiclebodydooraxle_01left_bs_tx.h" +#include "../generated/vss_files/vss_vehiclebodydooraxle_01right_bs_rx.h" +#include "../generated/vss_files/vss_vehiclebodydooraxle_01right_bs_tx.h" +#include "../generated/vss_files/vss_vehiclebodydooraxle_02left_bs_rx.h" +#include "../generated/vss_files/vss_vehiclebodydooraxle_02left_bs_tx.h" +#include "../generated/vss_files/vss_vehiclebodydooraxle_02right_bs_rx.h" +#include "../generated/vss_files/vss_vehiclebodydooraxle_02right_bs_tx.h" #include "lock_doors_thread.h" @@ -26,11 +36,10 @@ * @brief Doors example service: locks/unlocks doors after closing/opening doors */ class CDoorsExampleService : public sdv::CSdvObject - , public sdv::IObjectControl - , public vss::Vehicle::Chassis::Door::Axle01::LeftService::IVSS_SetIsOpen_Event - , public vss::Vehicle::Chassis::Door::Axle01::RightService::IVSS_SetIsOpen_Event - , public vss::Vehicle::Chassis::Door::Axle02::LeftService::IVSS_SetIsOpen_Event - , public vss::Vehicle::Chassis::Door::Axle02::RightService::IVSS_SetIsOpen_Event + , public vss::Vehicle::Body::Door::Axle::_01::LeftService::IVSS_SetIsOpenL1_Event + , public vss::Vehicle::Body::Door::Axle::_01::RightService::IVSS_SetIsOpenR1_Event + , public vss::Vehicle::Body::Door::Axle::_02::LeftService::IVSS_SetIsOpenL2_Event + , public vss::Vehicle::Body::Door::Axle::_02::RightService::IVSS_SetIsOpenR2_Event , public IDoorService { public: @@ -43,48 +52,32 @@ public: * @brief Destructor */ ~CDoorsExampleService() - { - // Just in case... - Shutdown(); - } + {} // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - SDV_INTERFACE_ENTRY(vss::Vehicle::Chassis::Door::Axle01::LeftService::IVSS_SetIsOpen_Event) - SDV_INTERFACE_ENTRY(vss::Vehicle::Chassis::Door::Axle01::RightService::IVSS_SetIsOpen_Event) - SDV_INTERFACE_ENTRY(vss::Vehicle::Chassis::Door::Axle02::LeftService::IVSS_SetIsOpen_Event) - SDV_INTERFACE_ENTRY(vss::Vehicle::Chassis::Door::Axle02::RightService::IVSS_SetIsOpen_Event) + SDV_INTERFACE_ENTRY(vss::Vehicle::Body::Door::Axle::_01::LeftService::IVSS_SetIsOpenL1_Event) + SDV_INTERFACE_ENTRY(vss::Vehicle::Body::Door::Axle::_01::RightService::IVSS_SetIsOpenR1_Event) + SDV_INTERFACE_ENTRY(vss::Vehicle::Body::Door::Axle::_02::LeftService::IVSS_SetIsOpenL2_Event) + SDV_INTERFACE_ENTRY(vss::Vehicle::Body::Door::Axle::_02::RightService::IVSS_SetIsOpenR2_Event) SDV_INTERFACE_ENTRY(IDoorService) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_function) DECLARE_OBJECT_CLASS_NAME("Doors Example Service") DECLARE_OBJECT_SINGLETON() /** - * @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 Set leftDoorIsOpen signal (front door) @@ -166,8 +159,6 @@ private: */ void LockDoors(const bool lock) const; - sdv::EObjectStatus m_eStatus = sdv::EObjectStatus::initialization_pending; ///< Current object status - bool m_bFrontLeftDoorIsOpen = false; ///< Front Left Door Status bool m_bFrontRightDoorIsOpen = false; ///< Front Right Door Status bool m_bRearLeftDoorIsOpen = false; ///< Rear Left Door Status @@ -176,10 +167,10 @@ private: bool m_bAllDoorsAreLocked = false; ///< state for locked/unlocked of all doors ///< Door lock interfaces. - vss::Vehicle::Chassis::Door::Axle01::LeftService::IVSS_SetLock* m_pFrontLeftDoorSvc = nullptr; ///< Front Left Door - vss::Vehicle::Chassis::Door::Axle01::RightService::IVSS_SetLock* m_pFrontRightDoorSvc = nullptr; ///< Front Right Door - vss::Vehicle::Chassis::Door::Axle02::LeftService::IVSS_SetLock* m_pRearLeftDoorSvc = nullptr; ///< Rear Left Door - vss::Vehicle::Chassis::Door::Axle02::RightService::IVSS_SetLock* m_pRearRightDoorSvc = nullptr; ///< Rear Right Door + vss::Vehicle::Body::Door::Axle::_01::LeftService::IVSS_SetLock* m_pFrontLeftDoorSvc = nullptr; ///< Front Left Door + vss::Vehicle::Body::Door::Axle::_01::RightService::IVSS_SetLock* m_pFrontRightDoorSvc = nullptr; ///< Front Right Door + vss::Vehicle::Body::Door::Axle::_02::LeftService::IVSS_SetLock* m_pRearLeftDoorSvc = nullptr; ///< Rear Left Door + vss::Vehicle::Body::Door::Axle::_02::RightService::IVSS_SetLock* m_pRearRightDoorSvc = nullptr; ///< Rear Right Door LockDoorsThread m_doorsThread; ///< timer thread uint32_t m_Interval = 18; ///< interval value * 100 = x milliseconds diff --git a/examples/door_demo_example/door_service/door_ifc.idl b/examples/door_demo_example/door_service/door_ifc.idl index 43e307c..ccdeb90 100644 --- a/examples/door_demo_example/door_service/door_ifc.idl +++ b/examples/door_demo_example/door_service/door_ifc.idl @@ -1,10 +1,17 @@ -/******************************************************************************* - * @file door_ifc.idl - * @details Door service interface definition. - */ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * @brief DoorService example service interface. The interface provides functions for the + * @details Door service interface definition. * Doors example complex service. */ interface IDoorService diff --git a/examples/door_demo_example/door_service/lock_doors_thread.h b/examples/door_demo_example/door_service/lock_doors_thread.h index fb6d933..510c138 100644 --- a/examples/door_demo_example/door_service/lock_doors_thread.h +++ b/examples/door_demo_example/door_service/lock_doors_thread.h @@ -1,3 +1,12 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ #ifndef LOCK_DOORS_THREAD_H #define LOCK_DOORS_THREAD_H diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/CMakeLists.txt b/examples/door_demo_example/fmu_Doors2ExampleFMU/CMakeLists.txt index a7392f5..33fdba3 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/CMakeLists.txt +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/CMakeLists.txt @@ -1,9 +1,12 @@ - -# @file CMakeLists.txt -# This file is cmake project file. -# This file was generated by the DBC utility from: -# datalink_2doors_example.dbc -# DBC file version: 1.0.0.1 +#******************************************************************************* +# 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 +#******************************************************************************* # Based on CMakeLists.txt from https://github.com/modelica/Reference-FMUs diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/config.h b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/config.h index e6a2dca..6aa1ae2 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/config.h +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/config.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * @file config.h * @date 2025-09-06 15:15:34 diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/model.cpp b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/model.cpp index e6e6f4c..fa4b743 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/model.cpp +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/model.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * @file model.cpp * @date 2025-09-06 15:15:34 @@ -137,7 +147,7 @@ bool OpenAPILoad(const std::string& resources) success &= g_appcontrol->LoadConfig("front_left_door_example.toml") == sdv::core::EConfigProcessResult::successful; success &= g_appcontrol->LoadConfig("front_right_door_example.toml") == sdv::core::EConfigProcessResult::successful; - success &= g_appcontrol->LoadConfig("door_comple_service.toml") == sdv::core::EConfigProcessResult::successful; + success &= g_appcontrol->LoadConfig("door_complex_service.toml") == sdv::core::EConfigProcessResult::successful; g_appcontrol->SetRunningMode(); return success; diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/door_complex_service.toml b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/door_complex_service.toml new file mode 100644 index 0000000..124545f --- /dev/null +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/door_complex_service.toml @@ -0,0 +1,10 @@ +[Configuration] +Version = 100 + +[[Component]] +Path = "doors_complex_service.sdv" +Class = "Doors Example Service" + + + + diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_left_door_example.toml b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_left_door_example.toml index 2c6dcb7..63c92da 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_left_door_example.toml +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_left_door_example.toml @@ -2,12 +2,12 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Left_Device" +Path = "doors_vd_frontdoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Left_Device" [[Component]] -Path = "doors_bs_frontdoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Left_Service" +Path = "doors_bs_frontdoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Left_Service" diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_right_door_example.toml b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_right_door_example.toml index 28e517c..2aca0cd 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_right_door_example.toml +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_right_door_example.toml @@ -2,9 +2,10 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Right_Device" +Path = "doors_vd_frontdoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Right_Device" [[Component]] -Path = "doors_bs_frontdoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Right_Service" +Path = "doors_bs_frontdoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Right_Service" + diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/signal_identifier.h b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/signal_identifier.h index c4ea389..74d691c 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/signal_identifier.h +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/signal_identifier.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** @file signal_identifier.h @date 2025-09-06 15:15:34 diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/include/model.h b/examples/door_demo_example/fmu_Doors2ExampleFMU/include/model.h index 2971e84..483a6d1 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/include/model.h +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/include/model.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #pragma once #ifdef __cplusplus extern "C" { diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/CMakeLists.txt b/examples/door_demo_example/fmu_Doors4ExampleFMU/CMakeLists.txt index 62f9099..07cadf2 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/CMakeLists.txt +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/CMakeLists.txt @@ -1,9 +1,12 @@ - -# @file CMakeLists.txt -# This file is cmake project file. -# This file was generated by the DBC utility from: -# datalink_4doors_example.dbc -# DBC file version: 1.0.0.1 +#******************************************************************************* +# 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 +#******************************************************************************* # Based on CMakeLists.txt from https://github.com/modelica/Reference-FMUs diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/config.h b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/config.h index 475658d..eccc742 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/config.h +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/config.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * @file config.h * @date 2025-09-06 14:55:42 diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/model.cpp b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/model.cpp index 3680207..7dd9d35 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/model.cpp +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/model.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * @file model.cpp * @date 2025-09-06 14:55:42 @@ -164,7 +174,7 @@ bool OpenAPILoad(const std::string& resources) success &= g_appcontrol->LoadConfig("front_right_door_example.toml") == sdv::core::EConfigProcessResult::successful; success &= g_appcontrol->LoadConfig("rear_left_door_example.toml") == sdv::core::EConfigProcessResult::successful; success &= g_appcontrol->LoadConfig("rear_right_door_example.toml") == sdv::core::EConfigProcessResult::successful; - success &= g_appcontrol->LoadConfig("door_comple_service.toml") == sdv::core::EConfigProcessResult::successful; + success &= g_appcontrol->LoadConfig("door_complex_service.toml") == sdv::core::EConfigProcessResult::successful; g_appcontrol->SetRunningMode(); return success; diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/door_complex_service.toml b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/door_complex_service.toml new file mode 100644 index 0000000..124545f --- /dev/null +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/door_complex_service.toml @@ -0,0 +1,10 @@ +[Configuration] +Version = 100 + +[[Component]] +Path = "doors_complex_service.sdv" +Class = "Doors Example Service" + + + + diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_left_door_example.toml b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_left_door_example.toml index 2c6dcb7..e3162ea 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_left_door_example.toml +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_left_door_example.toml @@ -2,12 +2,13 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Left_Device" +Path = "doors_vd_frontdoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Left_Device" [[Component]] -Path = "doors_bs_frontdoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Left_Service" +Path = "doors_bs_frontdoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Left_Service" + diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_right_door_example.toml b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_right_door_example.toml index 28e517c..2aca0cd 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_right_door_example.toml +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_right_door_example.toml @@ -2,9 +2,10 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Right_Device" +Path = "doors_vd_frontdoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Right_Device" [[Component]] -Path = "doors_bs_frontdoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle01.Right_Service" +Path = "doors_bs_frontdoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._01.Right_Service" + diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_left_door_example.toml b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_left_door_example.toml index 19be3a6..23e6935 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_left_door_example.toml +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_left_door_example.toml @@ -2,12 +2,13 @@ Version = 100 [[Component]] -Path = "doors_vd_reardoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle02.Left_Device" +Path = "doors_vd_reardoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._02.Left_Device" [[Component]] -Path = "doors_bs_reardoorleft.sdv" -Class = "Vehicle.Chassis.Door.Axle02.Left_Service" +Path = "doors_bs_reardoorleft_rx.sdv" +Class = "Vehicle.Body.Door.Axle._02.Left_Service" + diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_right_door_example.toml b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_right_door_example.toml index 1fcfa28..4f2069e 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_right_door_example.toml +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_right_door_example.toml @@ -2,9 +2,9 @@ Version = 100 [[Component]] -Path = "doors_vd_reardoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle02.Right_Device" +Path = "doors_vd_reardoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._02.Right_Device" [[Component]] -Path = "doors_bs_reardoorright.sdv" -Class = "Vehicle.Chassis.Door.Axle02.Right_Service" +Path = "doors_bs_reardoorright_rx.sdv" +Class = "Vehicle.Body.Door.Axle._02.Right_Service" diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/signal_identifier.h b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/signal_identifier.h index 2df8504..aa4c410 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/signal_identifier.h +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/signal_identifier.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** @file signal_identifier.h @date 2025-09-06 14:55:42 diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/include/model.h b/examples/door_demo_example/fmu_Doors4ExampleFMU/include/model.h index 2971e84..483a6d1 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/include/model.h +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/include/model.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #pragma once #ifdef __cplusplus extern "C" { diff --git a/examples/open_trunk_example/CMakeLists.txt b/examples/open_trunk_example/CMakeLists.txt index 7cede28..646eafd 100644 --- a/examples/open_trunk_example/CMakeLists.txt +++ b/examples/open_trunk_example/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + project(SystemDemoExample) # Use new policy for project version settings and default warning level @@ -96,7 +106,7 @@ set_target_properties(trunk_complex_service PROPERTIES PREFIX "") set_target_properties(trunk_complex_service PROPERTIES SUFFIX ".sdv") ###################################################################################################################################################################### -# basic_system trunk application +# open trunk application ###################################################################################################################################################################### # Define the executable @@ -118,32 +128,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") else() target_link_libraries(open_trunk_example Rpcrt4.lib) endif() -#[[ -###################################################################################################################################################################### -# open trunk application -###################################################################################################################################################################### - -# Define the executable -add_executable(system_trunk_example - example_app/system_trunk_example.cpp - example_app/control.h - example_app/control.cpp - example_app/console.h - example_app/console.cpp - example_app/signal_names.h - ) - -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - if (WIN32) - target_link_libraries(system_trunk_example Ws2_32 Winmm Rpcrt4.lib) - else() - target_link_libraries(system_trunk_example ${CMAKE_DL_LIBS} rt ${CMAKE_THREAD_LIBS_INIT}) - endif() -else() - target_link_libraries(system_trunk_example Rpcrt4.lib) -endif() -#]] # Copy the config files file (COPY ${PROJECT_SOURCE_DIR}/config/can_com_simulation_trunk.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) file (COPY ${PROJECT_SOURCE_DIR}/config/complex_service_trunk.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) @@ -197,14 +182,11 @@ add_custom_target(trunk_user_config VERBATIM ) -###################################################################################################################################################################### -# TODO: SDV_PACKAGER does not create the toml files, therefore we need to copy them -###################################################################################################################################################################### -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/trunk.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3005) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/platform.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3005) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/settings.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3005) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/vehicle_abstract.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3005) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/vehicle_ifc.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3005) +add_custom_target(trunk_platform_config + ALL + COMMAND "${SDV_PACKAGER}" CONFIGURE ${PROJECT_SOURCE_DIR}/coreconfig/platform.toml --instance3005 --platform_config + VERBATIM +) diff --git a/examples/open_trunk_example/config/can_com_simulation_trunk.toml b/examples/open_trunk_example/config/can_com_simulation_trunk.toml index 35a525d..bc4f38c 100644 --- a/examples/open_trunk_example/config/can_com_simulation_trunk.toml +++ b/examples/open_trunk_example/config/can_com_simulation_trunk.toml @@ -4,5 +4,6 @@ Version = 100 [[Component]] Path = "can_com_sim.sdv" Class = "CAN_Com_Sim" +[Component.Parameters] Source="open_trunk_receiver.asc" Target="open_trunk_writer.asc" diff --git a/examples/open_trunk_example/coreconfig/platform.toml b/examples/open_trunk_example/coreconfig/platform.toml index 35a525d..bc4f38c 100644 --- a/examples/open_trunk_example/coreconfig/platform.toml +++ b/examples/open_trunk_example/coreconfig/platform.toml @@ -4,5 +4,6 @@ Version = 100 [[Component]] Path = "can_com_sim.sdv" Class = "CAN_Com_Sim" +[Component.Parameters] Source="open_trunk_receiver.asc" Target="open_trunk_writer.asc" diff --git a/examples/open_trunk_example/open_trunk_app/console.cpp b/examples/open_trunk_example/open_trunk_app/console.cpp index 1e9ac6c..62ec835 100644 --- a/examples/open_trunk_example/open_trunk_app/console.cpp +++ b/examples/open_trunk_example/open_trunk_app/console.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include "console.h" #ifdef _WIN32 @@ -138,10 +148,14 @@ bool CConsole::PrepareDataConsumers() m_pTrunkSvc = sdv::core::GetObject("Vehicle.Body.Trunk_Service").GetInterface(); if (m_pTrunkSvc) PrintText(g_sComplexServcie1, "Basic Service available"); + else + PrintText(g_sComplexServcie1, "Basic Service NOT available"); - m_pITrunkComplexService = sdv::core::GetObject("Open Trunk Service").GetInterface(); - if (m_pITrunkComplexService) + m_pTrunkComplexService = sdv::core::GetObject("Open Trunk Service").GetInterface(); + if (m_pTrunkComplexService) PrintText(g_sComplexServcie2, "Complex Service available"); + else + PrintText(g_sComplexServcie2, "Complex Service NOT available"); return true; } @@ -290,9 +304,9 @@ void CConsole::RunUntilBreak() } break; case '2': - if (m_pITrunkComplexService) + if (m_pTrunkComplexService) { - if (m_pITrunkComplexService->PopTrunk()) + if (m_pTrunkComplexService->PopTrunk()) PrintText(g_sComplexServcie2, "Safety open trunk via complex service."); else PrintText(g_sComplexServcie2, "Safety open trunk via complex service failed, car is moving"); diff --git a/examples/open_trunk_example/open_trunk_app/console.h b/examples/open_trunk_example/open_trunk_app/console.h index 48daa97..f9d4567 100644 --- a/examples/open_trunk_example/open_trunk_app/console.h +++ b/examples/open_trunk_example/open_trunk_app/console.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifndef CONSOLE_OUTPUT_H #define CONSOLE_OUTPUT_H @@ -152,7 +162,7 @@ private: float m_SpeedBS = 0.0; ///< Speed Data Link vss::Vehicle::Body::TrunkService::IVSS_SetOpen* m_pTrunkSvc = nullptr; ///< Front Left Door - ITrunkKitService* m_pITrunkComplexService = nullptr; ///< Trunk Service interface pointer. + ITrunkKitService* m_pTrunkComplexService = nullptr; ///< Trunk Service interface pointer. #ifdef _WIN32 diff --git a/examples/open_trunk_example/open_trunk_app/signal_names.h b/examples/open_trunk_example/open_trunk_app/signal_names.h index 27da59e..d74c1de 100644 --- a/examples/open_trunk_example/open_trunk_app/signal_names.h +++ b/examples/open_trunk_example/open_trunk_app/signal_names.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * namespace for the signal names * in case /interfaces/signal_identifier.h diff --git a/examples/open_trunk_example/open_trunk_app/trunk_application.cpp b/examples/open_trunk_example/open_trunk_app/trunk_application.cpp index b632987..bbfee95 100644 --- a/examples/open_trunk_example/open_trunk_app/trunk_application.cpp +++ b/examples/open_trunk_example/open_trunk_app/trunk_application.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include "trunk_application.h" #include "signal_names.h" diff --git a/examples/open_trunk_example/open_trunk_app/trunk_application.h b/examples/open_trunk_example/open_trunk_app/trunk_application.h index d386043..6e77103 100644 --- a/examples/open_trunk_example/open_trunk_app/trunk_application.h +++ b/examples/open_trunk_example/open_trunk_app/trunk_application.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include diff --git a/examples/open_trunk_example/open_trunk_app/trunk_example.cpp b/examples/open_trunk_example/open_trunk_app/trunk_example.cpp index 40c94df..c55f6e6 100644 --- a/examples/open_trunk_example/open_trunk_app/trunk_example.cpp +++ b/examples/open_trunk_example/open_trunk_app/trunk_example.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include // for std::strtol #include "trunk_application.h" diff --git a/examples/open_trunk_example/trunk_service/complex_service.cpp b/examples/open_trunk_example/trunk_service/complex_service.cpp index c56b5fe..2810f50 100644 --- a/examples/open_trunk_example/trunk_service/complex_service.cpp +++ b/examples/open_trunk_example/trunk_service/complex_service.cpp @@ -1,11 +1,19 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include "complex_service.h" -void CTrunkExampleService::Initialize(const sdv::u8string& /*ssObjectConfig*/) +bool CTrunkExampleService::OnInitialize() { - m_eStatus = sdv::EObjectStatus::initializing; - // Request the basic service for speed. auto pSpeedSvc = sdv::core::GetObject("Vehicle.Speed_Service").GetInterface(); if (pSpeedSvc) @@ -20,24 +28,12 @@ void CTrunkExampleService::Initialize(const sdv::u8string& /*ssObjectConfig*/) if ((!pSpeedSvc) || (!m_pTrunkSvc)) { SDV_LOG_ERROR("Could not get interfaces : [CTrunkExampleService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } - - m_eStatus = sdv::EObjectStatus::initialized; + return true; } -sdv::EObjectStatus CTrunkExampleService::GetStatus() const -{ - return m_eStatus; -} - -void CTrunkExampleService::SetOperationMode(sdv::EOperationMode /*eMode*/) -{ - // Not applicable -} - -void CTrunkExampleService::Shutdown() +void CTrunkExampleService::OnShutdown() { // Unregister trunk change event handler. auto pSpeedSvc = sdv::core::GetObject("Vehicle.Speed_Service").GetInterface(); diff --git a/examples/open_trunk_example/trunk_service/complex_service.h b/examples/open_trunk_example/trunk_service/complex_service.h index 2759cc0..4d63fde 100644 --- a/examples/open_trunk_example/trunk_service/complex_service.h +++ b/examples/open_trunk_example/trunk_service/complex_service.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifndef TRUNK_COMPLEX_SERVICE_EXAMPLE_H #define TRUNK_COMPLEX_SERVICE_EXAMPLE_H @@ -22,7 +32,6 @@ * Output call for basic service: opening the trunk */ class CTrunkExampleService : public sdv::CSdvObject - , public sdv::IObjectControl , public vss::Vehicle::SpeedService::IVSS_SetSpeed_Event , public ITrunkKitService { @@ -36,45 +45,29 @@ public: * @brief Destructor */ ~CTrunkExampleService() - { - // Just in case... - Shutdown(); - } + {} // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(vss::Vehicle::SpeedService::IVSS_SetSpeed_Event) SDV_INTERFACE_ENTRY(ITrunkKitService) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_function) DECLARE_OBJECT_CLASS_NAME("Open Trunk Service") DECLARE_OBJECT_SINGLETON() /** - * @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 Set vehicleSpeed signal @@ -89,9 +82,6 @@ public: virtual bool PopTrunk() override; private: - - sdv::EObjectStatus m_eStatus = sdv::EObjectStatus::initialization_pending; ///< Current object status - float m_Speed = 0.0; ///< Speed vss::Vehicle::Body::TrunkService::IVSS_SetOpen* m_pTrunkSvc = nullptr; ///< Trunk }; diff --git a/examples/open_trunk_example/trunk_service/trunkkit.idl b/examples/open_trunk_example/trunk_service/trunkkit.idl index a92481a..a21287c 100644 --- a/examples/open_trunk_example/trunk_service/trunkkit.idl +++ b/examples/open_trunk_example/trunk_service/trunkkit.idl @@ -1,10 +1,16 @@ -/******************************************************************************* - * @file trunkkit.idl - * @details Service interface definition to open the trunk. - */ + /******************************************************************************** + * 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 + ********************************************************************************/ /** * @brief Service to open the trunk + * @details Service interface definition to open the trunk. */ interface ITrunkKitService { diff --git a/examples/system_demo_example/CMakeLists.txt b/examples/system_demo_example/CMakeLists.txt index b8b5396..d4adf7e 100644 --- a/examples/system_demo_example/CMakeLists.txt +++ b/examples/system_demo_example/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + project(SystemDemoExample) # Use new policy for project version settings and default warning level @@ -205,16 +215,11 @@ add_custom_target(example_user_config VERBATIM ) -###################################################################################################################################################################### -# TODO: SDV_PACKAGER does not create the toml files, therefore we need to copy them -###################################################################################################################################################################### -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/demo.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3001) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/platform.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3001) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/settings.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3001) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/vehicle_abstract.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3001) -file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/vehicle_ifc.toml DESTINATION ${SDV_FRAMEWORK_RUNTIME}/3001) - - +add_custom_target(example_platform_config + ALL + COMMAND "${SDV_PACKAGER}" CONFIGURE ${PROJECT_SOURCE_DIR}/coreconfig/platform.toml --instance3001 --platform_config + VERBATIM +) ###################################################################################################################################################################### # system demo fmu for OpenXilEnv @@ -224,7 +229,7 @@ file (COPY ${PROJECT_SOURCE_DIR}/coreconfig/vehicle_ifc.toml DESTINATION ${SDV_F # # What cannot be created automatically is the method OpenAPILoad(const std::string& resources) in file model.cpp # The method must load all required components -# Therfore here the file is copied and overwritten the auto generated file +# Therefore here the file is copied and overwritten the auto generated file # # The required toml files need to be copied to the folder: # generated/fmu_DemoExampleFMU/DemoExampleFMU/resources diff --git a/examples/system_demo_example/config/can_com_simulation.toml b/examples/system_demo_example/config/can_com_simulation.toml index 5703f72..5295c16 100644 --- a/examples/system_demo_example/config/can_com_simulation.toml +++ b/examples/system_demo_example/config/can_com_simulation.toml @@ -4,5 +4,6 @@ Version = 100 [[Component]] Path = "can_com_sim.sdv" Class = "CAN_Com_Sim" +[Component.Parameters] Source="system_demo_receiver.asc" Target="system_demo_writer.asc" diff --git a/examples/system_demo_example/coreconfig/platform.toml b/examples/system_demo_example/coreconfig/platform.toml index 0540097..f81a75e 100644 --- a/examples/system_demo_example/coreconfig/platform.toml +++ b/examples/system_demo_example/coreconfig/platform.toml @@ -4,6 +4,7 @@ Version = 100 [[Component]] Path = "can_com_sim.sdv" Class = "CAN_Com_Sim" +[Component.Parameters] Source="system_demo_receiver.asc" Target="system_demo_writer.asc" diff --git a/examples/system_demo_example/example_app/console.cpp b/examples/system_demo_example/example_app/console.cpp index c6bde02..3a163e5 100644 --- a/examples/system_demo_example/example_app/console.cpp +++ b/examples/system_demo_example/example_app/console.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include "console.h" #ifdef _WIN32 diff --git a/examples/system_demo_example/example_app/console.h b/examples/system_demo_example/example_app/console.h index ed63d9c..28ccf45 100644 --- a/examples/system_demo_example/example_app/console.h +++ b/examples/system_demo_example/example_app/console.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifndef CONSOLE_OUTPUT_H #define CONSOLE_OUTPUT_H diff --git a/examples/system_demo_example/example_app/control.cpp b/examples/system_demo_example/example_app/control.cpp index a058af3..5090b88 100644 --- a/examples/system_demo_example/example_app/control.cpp +++ b/examples/system_demo_example/example_app/control.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include "control.h" CExampleControl::~CExampleControl() diff --git a/examples/system_demo_example/example_app/control.h b/examples/system_demo_example/example_app/control.h index e6be83f..b97d435 100644 --- a/examples/system_demo_example/example_app/control.h +++ b/examples/system_demo_example/example_app/control.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifndef EXMAPLE_UTILITY_H #define EXMAPLE_UTILITY_H diff --git a/examples/system_demo_example/example_app/signal_names.h b/examples/system_demo_example/example_app/signal_names.h index 322fb53..3000ebe 100644 --- a/examples/system_demo_example/example_app/signal_names.h +++ b/examples/system_demo_example/example_app/signal_names.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * namespace for the signal names * in case /generated/vss_files/signal_identifier.h diff --git a/examples/system_demo_example/example_app/system_demo_example.cpp b/examples/system_demo_example/example_app/system_demo_example.cpp index 6a43d91..25be729 100644 --- a/examples/system_demo_example/example_app/system_demo_example.cpp +++ b/examples/system_demo_example/example_app/system_demo_example.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifdef __unix__ #include #include diff --git a/examples/system_demo_example/example_app/system_extern_example.cpp b/examples/system_demo_example/example_app/system_extern_example.cpp index 83ddc98..ce8345e 100644 --- a/examples/system_demo_example/example_app/system_extern_example.cpp +++ b/examples/system_demo_example/example_app/system_extern_example.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include diff --git a/examples/system_demo_example/example_service/complex_service.cpp b/examples/system_demo_example/example_service/complex_service.cpp index ebc3358..c3b0bb9 100644 --- a/examples/system_demo_example/example_service/complex_service.cpp +++ b/examples/system_demo_example/example_service/complex_service.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include "complex_service.h" @@ -8,22 +18,17 @@ CCounterSteeringExampleService::CCounterSteeringExampleService() } CCounterSteeringExampleService::~CCounterSteeringExampleService() -{ - // Just in case... - Shutdown(); -} +{} -void CCounterSteeringExampleService::Initialize(const sdv::u8string& /*ssObjectConfig*/) +bool CCounterSteeringExampleService::OnInitialize() { - m_eStatus = sdv::EObjectStatus::initializing; - // Request the basic service for monitoring the alive counter. m_pAliveCounterSvc = sdv::core::GetObject("Vehicle.Software.Application.IsActiveCounter_Service").GetInterface(); if (!m_pAliveCounterSvc) { SDV_LOG_ERROR("Could not get interface 'IVSS_SetCounter': [CCounterSteeringExampleService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; + ; } // Request the basic service for the rear axle. @@ -31,8 +36,7 @@ void CCounterSteeringExampleService::Initialize(const sdv::u8string& /*ssObjectC if (!m_pRearAxleSvc) { SDV_LOG_ERROR("Could not get interface 'IVSS_SetAngle': [CCounterSteeringExampleService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } // Request the basic service for the steering wheel. @@ -40,8 +44,7 @@ void CCounterSteeringExampleService::Initialize(const sdv::u8string& /*ssObjectC if (!pSteeringWheelSvc) { SDV_LOG_ERROR("Could not get interface 'IVSS_SetSteeringAngle': [CCounterSteeringExampleService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } // Request the basic service for the vehicle speed. @@ -49,8 +52,7 @@ void CCounterSteeringExampleService::Initialize(const sdv::u8string& /*ssObjectC if (!pVehSpeedSvc) { SDV_LOG_ERROR("Could not get interface 'IVSS_SetSpeed': [CCounterSteeringExampleService]"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } // Register steering wheel change event handler. @@ -64,26 +66,14 @@ void CCounterSteeringExampleService::Initialize(const sdv::u8string& /*ssObjectC if (!m_Timer) { SDV_LOG_ERROR("CCounterSteeringExampleService: tasktimer with 10 milliseconds could not be created."); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } SDV_LOG_INFO("CCounterSteeringExampleService: tasktimer created with 10 milliseconds"); - - m_eStatus = sdv::EObjectStatus::initialized; + return true; } -sdv::EObjectStatus CCounterSteeringExampleService::GetStatus() const -{ - return m_eStatus; -} - -void CCounterSteeringExampleService::SetOperationMode(sdv::EOperationMode /*eMode*/) -{ - // Not applicable -} - -void CCounterSteeringExampleService::Shutdown() +void CCounterSteeringExampleService::OnShutdown() { // Terminate the alive counter m_Timer.Reset(); diff --git a/examples/system_demo_example/example_service/complex_service.h b/examples/system_demo_example/example_service/complex_service.h index 60e649f..df13a11 100644 --- a/examples/system_demo_example/example_service/complex_service.h +++ b/examples/system_demo_example/example_service/complex_service.h @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #ifndef COMPLEX_SERVICE_EXAMPLE_H #define COMPLEX_SERVICE_EXAMPLE_H @@ -35,7 +45,6 @@ */ class CCounterSteeringExampleService : public sdv::CSdvObject, - public sdv::IObjectControl, public vss::Vehicle::Chassis::SteeringWheel::AngleService::IVSS_SetSteeringWheel_Event, public vss::Vehicle::SpeedService::IVSS_SetSpeed_Event, public ICounterSteeringService @@ -53,39 +62,26 @@ public: // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(vss::Vehicle::Chassis::SteeringWheel::AngleService::IVSS_SetSteeringWheel_Event) SDV_INTERFACE_ENTRY(vss::Vehicle::SpeedService::IVSS_SetSpeed_Event) SDV_INTERFACE_ENTRY(ICounterSteeringService) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_function) DECLARE_OBJECT_CLASS_NAME("Counter Steering Example Service") DECLARE_OBJECT_SINGLETON() /** - * @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 Set steering angle event. Overload of @@ -136,7 +132,6 @@ private: */ void TimerFunction(); - sdv::EObjectStatus m_eStatus = sdv::EObjectStatus::initialization_pending; ///< Current object status volatile float m_fSteeringWheel = 0.0; ///< Steering wheel angle volatile float m_fVehSpeed = 0.0; ///< Vehicle speed volatile float m_fRearAxleAngle = 0.0; ///< Output rear angle diff --git a/examples/system_demo_example/example_service/countersteering.idl b/examples/system_demo_example/example_service/countersteering.idl index 77d97e1..9e178fd 100644 --- a/examples/system_demo_example/example_service/countersteering.idl +++ b/examples/system_demo_example/example_service/countersteering.idl @@ -1,10 +1,16 @@ -/******************************************************************************* - * @file countersteering.idl - * @details Counter steering example service interface definition. - */ + /******************************************************************************** + * 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 + ********************************************************************************/ /** * @brief Counter steering example service interface. The interface provides functions for the + * @details Counter steering example service interface definition. * counter steering example complex service. */ interface ICounterSteeringService diff --git a/examples/system_demo_example/fmu/model.cpp b/examples/system_demo_example/fmu/model.cpp index 6d3fb76..3983302 100644 --- a/examples/system_demo_example/fmu/model.cpp +++ b/examples/system_demo_example/fmu/model.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + /** * @file model.cpp * @date 2025-04-16 09:03:47 diff --git a/examples/tasktimer_component_example/CMakeLists.txt b/examples/tasktimer_component_example/CMakeLists.txt index ac6faca..830769f 100644 --- a/examples/tasktimer_component_example/CMakeLists.txt +++ b/examples/tasktimer_component_example/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Use new policy for project version settings and default warning level cmake_policy(SET CMP0048 NEW) # requires CMake 3.14 cmake_policy(SET CMP0092 NEW) # requires CMake 3.15 diff --git a/examples/tasktimer_component_example/src/tasktimer_comp.cpp b/examples/tasktimer_component_example/src/tasktimer_comp.cpp index d51b2d6..3402e24 100644 --- a/examples/tasktimer_component_example/src/tasktimer_comp.cpp +++ b/examples/tasktimer_component_example/src/tasktimer_comp.cpp @@ -1,98 +1,61 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include #include #include -class DemoTimerComponent : public sdv::CSdvObject, public sdv::IObjectControl +class DemoTimerComponent : public sdv::CSdvObject { public: - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("Timer_Example") + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_PeriodicValue, "Timer", 10, "ms", "Periodic timer duration.") + END_SDV_PARAM_MAP() + /** - * @brief initialize function to register, access the task timer interface from platform abstraction. + * @brief initialize function to register, access the task timer interface from platform abstraction. Overload of + * sdv::CSdvObject::OnInitialize. * After initialization 'CreateTimer' function is called to execute the task periodically. * @param[in] ssObjectConfig An object configuration is currently not used by this demo component. + * @return Returns 'true' when the initialization was successful, 'false' when not. */ - virtual void Initialize([[maybe_unused]] const sdv::u8string& ssObjectConfig) override + virtual bool OnInitialize() override { - try - { - sdv::toml::CTOMLParser config(ssObjectConfig.c_str()); - sdv::toml::CNode timerNode = config.GetDirect("Timer"); - if (timerNode.GetType() == sdv::toml::ENodeType::node_integer) - { - m_PeriodicValue = static_cast(timerNode.GetValue()); - } - } - catch (const sdv::toml::XTOMLParseException& e) - { - SDV_LOG_ERROR("Parser error: ", e.what()); - - m_status = sdv::EObjectStatus::initialization_failure; - return; - } - m_Timer = sdv::core::CTaskTimer(m_PeriodicValue, [&]() {CustomerExecute(); }); if (!m_Timer) { SDV_LOG_ERROR("Tasktimer with ", std::to_string(m_PeriodicValue), " milliseconds could not be created."); - m_status = sdv::EObjectStatus::initialization_failure; - return; + return false; } else { SDV_LOG_INFO("Tasktimer created with ", std::to_string(m_PeriodicValue), " milliseconds"); } - m_status = sdv::EObjectStatus::initialized; + return true; }; /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object - */ - virtual sdv::EObjectStatus GetStatus() const override - { - return m_status; - }; - - /** - * @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode. - * @param[in] eMode The operation mode, the component should run in. - */ - void SetOperationMode(/*in*/ sdv::EOperationMode eMode) - { - switch (eMode) - { - case sdv::EOperationMode::configuring: - if (m_status == sdv::EObjectStatus::running || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::configuring; - break; - case sdv::EOperationMode::running: - if (m_status == sdv::EObjectStatus::configuring || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::running; - break; - default: - break; - } - } - - /** - * @brief Shutdown function is to shutdown the execution of periodic task. + * @brief Shutdown function is to shutdown the execution of periodic task. Overload of sdv::CSdvObject::OnShutdown. * Timer ID of the task is used to shutdown the specific task. */ - virtual void Shutdown() override + virtual void OnShutdown() override { if (m_Timer) { m_Timer.Reset(); } - m_status = sdv::EObjectStatus::destruction_pending; } /** @@ -105,7 +68,6 @@ class DemoTimerComponent : public sdv::CSdvObject, public sdv::IObjectControl }; private: - std::atomic m_status = {sdv::EObjectStatus::initialization_pending}; //!< To update the object status when it changes. sdv::core::CTaskTimer m_Timer; ///< timer uint32_t m_PeriodicValue = 10; ///< periodix in milliseconds }; diff --git a/examples/tasktimer_example/CMakeLists.txt b/examples/tasktimer_example/CMakeLists.txt index 54c066c..bfcdc4e 100644 --- a/examples/tasktimer_example/CMakeLists.txt +++ b/examples/tasktimer_example/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + project(TaskTimerApp) # Use new policy for project version settings and default warning level diff --git a/examples/tasktimer_example/config/test_config_simulation_task_timer.toml b/examples/tasktimer_example/config/test_config_simulation_task_timer.toml index 3e0c1f1..f90b6bb 100644 --- a/examples/tasktimer_example/config/test_config_simulation_task_timer.toml +++ b/examples/tasktimer_example/config/test_config_simulation_task_timer.toml @@ -8,6 +8,7 @@ Class = "SimulationTaskTimerService" [[Component]] Path = "tasktimer_component_example.sdv" Class = "Timer_Example" +[Component.Parameters] Timer = 175 diff --git a/examples/tasktimer_example/config/test_config_task_timer.toml b/examples/tasktimer_example/config/test_config_task_timer.toml index ca3b8bf..0164b7c 100644 --- a/examples/tasktimer_example/config/test_config_task_timer.toml +++ b/examples/tasktimer_example/config/test_config_task_timer.toml @@ -8,6 +8,7 @@ Class = "TaskTimerService" [[Component]] Path = "tasktimer_component_example.sdv" Class = "Timer_Example" +[Component.Parameters] Timer = 175 diff --git a/examples/tasktimer_example/src/simulation_tasktimer_example.cpp b/examples/tasktimer_example/src/simulation_tasktimer_example.cpp index de705e9..ef8fb8d 100644 --- a/examples/tasktimer_example/src/simulation_tasktimer_example.cpp +++ b/examples/tasktimer_example/src/simulation_tasktimer_example.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include diff --git a/examples/tasktimer_example/src/tasktimer_example.cpp b/examples/tasktimer_example/src/tasktimer_example.cpp index 8832d9a..d57c4b8 100644 --- a/examples/tasktimer_example/src/tasktimer_example.cpp +++ b/examples/tasktimer_example/src/tasktimer_example.cpp @@ -1,3 +1,13 @@ + /******************************************************************************** + * 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 + ********************************************************************************/ + #include #include #include diff --git a/export/interfaces/app.idl b/export/interfaces/app.idl index 94c85a8..6fff9db 100644 --- a/export/interfaces/app.idl +++ b/export/interfaces/app.idl @@ -1,18 +1,22 @@ -/** -* @file app.idl -* @brief This file provides the core interface definitions SDV framework administration. -* @version 0.1 -* @date 2024.04.11 -* @author erik.verhoeven@zf.com -* @copyright Copyright ZF Friedrichshaven AG (c) 2023-2025 -*/ + /******************************************************************************** + * 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 "core.idl" #include "toml.idl" /** -* @brief Software Defined Vehicle framework. -*/ + * @brief Software Defined Vehicle framework. + */ module sdv { /** diff --git a/export/interfaces/can.idl b/export/interfaces/can.idl index eab92f3..b393239 100644 --- a/export/interfaces/can.idl +++ b/export/interfaces/can.idl @@ -1,19 +1,21 @@ -/** -* @file can.idl -* @author Erik Verhoeven FRD DISS2 (mailto:erik.verhoeven@zf.com) -* @brief This file provides the CAN abstraction interfaces -* @version 1.0 -* @date 2024-02-07 -* -* @copyright Copyright ZF Friedrichshafen AG (c) 2024 -* -*/ + /******************************************************************************** + * 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 "core.idl" /** -* @brief Software Defined Vehicle framework. -*/ + * @brief Software Defined Vehicle framework. + */ module sdv { /** diff --git a/export/interfaces/com.idl b/export/interfaces/com.idl index 1c542f5..2f2d392 100644 --- a/export/interfaces/com.idl +++ b/export/interfaces/com.idl @@ -1,12 +1,15 @@ -/** - * @file com.idl - * @author Erik Verhoeven FRD DISDS1 (mailto:erik.verhoeven@zf.com) & Steffen Altmeier FRD DISS21 (mailto:steffen.altmeier@zf.com) - * @brief This file includes all the interfaces used by the communication control service - * @version 1.0 - * @date 2024-08-12 + /******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG * - * @copyright Copyright ZF Friedrichshafen AG (c) 2024 - */ + * 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 "core.idl" #include "core_ps.idl" diff --git a/export/interfaces/config.idl b/export/interfaces/config.idl index 8b8f9df..5dc434a 100644 --- a/export/interfaces/config.idl +++ b/export/interfaces/config.idl @@ -1,24 +1,27 @@ -/** -* @file app.idl -* @brief This file provides the core interface definitions SDV framework administration. -* @version 0.1 -* @date 2024.04.11 -* @author erik.verhoeven@zf.com -* @copyright Copyright ZF Friedrichshaven AG (c) 2023-2025 -*/ + /******************************************************************************** + * 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 "core.idl" - /** - * @brief Software Defined Vehicle framework. - */ +#verbatim_begin +#include "../support/string.h" +#verbatim_end + +/** + * @brief Software Defined Vehicle framework. + */ module sdv { - #verbatim_begin -#include "../support/string.h" - #verbatim_end - - /** * @brief Failed to open a file. */ @@ -153,13 +156,27 @@ module sdv /** * @brief Save a configuration file pointed to by the provided file path. All components are saved that were added after * the last baseline with the configuration specific settings. - * @attention Configuration changes can only occur when the system is in configuration mode. + * @remarks The function will only save when the configuration has changed. * @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. */ boolean SaveConfig(in u8string ssConfigPath) const; + /** + * @brief Generate the configuration TOML string. + * @return The generated configuration string. + */ + u8string GenerateConfigString() const; + + /** + * @brief Close the current configuration. + * @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. + */ + void CloseConfig(); + /** * @brief Add a search path to a folder where a config file can be found. * @param[in] ssDir Relative or absolute path to an existing folder. @@ -174,7 +191,7 @@ module sdv */ void ResetConfigBaseline(); }; - }; // module core + }; // module core /** * @brief Installation module. diff --git a/export/interfaces/core.idl b/export/interfaces/core.idl index 6374be4..3685736 100644 --- a/export/interfaces/core.idl +++ b/export/interfaces/core.idl @@ -1,11 +1,17 @@ -/** - * @file core.idl - * @brief This file provides the core interface definitions of the core SDV framework. - * @version 0.1 - * @date 2023.04.26 - * @author erik.verhoeven@zf.com - * @copyright Copyright ZF Friedrichshaven 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 + * + * This file provides the core interface definitions of the core SDV framework. + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ #include "core_types.idl" @@ -25,8 +31,8 @@ #verbatim_end /** -* @brief Software Defined Vehicle framework. -*/ + * @brief Software Defined Vehicle framework. + */ module sdv { /** @@ -61,14 +67,20 @@ module sdv */ enum EObjectType : uint32 { - SystemObject = 0, ///< System object - Device = 10, ///< Abstract device - BasicService = 20, ///< Basic service - ComplexService = 21, ///< Complex service - Application = 30, ///< Application - Proxy = 100, ///< Proxy object - Stub = 101, ///< Stub object - Utility = 1000, ///< Utility object + undefined = 0, ///< Not defined + system_object = 1, ///< System object + device = 10, ///< Device category 10..19 + platform_abstraction = 11, ///< Platform abstraction object + vehicle_bus = 12, ///< Vehicle bus object + basic_service = 20, ///< Basic service category 20..29 + sensor = 21, ///< Sensor object + actuator = 22, ///< Actuator object + complex_service = 30, ///< Complex service category 30..39 + vehicle_function = 31, ///< Vehicle function + application = 50, ///< Application + proxy = 100, ///< Proxy object + stub = 101, ///< Stub object + utility = 1000, ///< Utility object }; /** @@ -85,9 +97,10 @@ module sdv struct SClassInfo { u8string ssModulePath; ///< Path to the module that contains the class. - u8string ssClassName; ///< String representing the class name. + u8string ssName; ///< String representing the class name. sequence seqClassAliases; ///< Sequence with class name aliases. u8string ssDefaultObjectName; ///< The default object name. + u8string ssDefaultConfig; ///< The configuration TOML. Currently only "Parameters" table is supported. EObjectType eType; ///< Type of object. uint32 uiFlags; ///< Zero or more object flags from EObjectFlags. sequence seqDependencies; ///< Sequence with object class names this object is dependent on. @@ -98,6 +111,19 @@ module sdv */ interface IObjectFactory { + /** + * @brief The object class names implemented in the object. + * @return Sequence with object class names string. + */ + sequence GetClassNames() const; + + /** + * @brief Get the class information. + * @param[in] ssClassName The name of the class object to get the class information for. + * @return Returns the class information struct. + */ + SClassInfo GetClassInfo(in u8string ssClassName) const; + /** * @brief Create or get the object using the name from the object class info. * @attention The objects lifetime is ended by a call to the DestroyObject function or the unloading of the module. @@ -119,9 +145,9 @@ module sdv }; /** - * @brief Object status enumeration + * @brief Object state enumeration */ - enum EObjectStatus : uint32 + enum EObjectState : uint32 { initialization_pending = 0, ///< constructor called, but no call to initialize yet initializing = 10, ///< Initialize was called and is being executed. @@ -158,10 +184,10 @@ module sdv void Initialize(in u8string ssObjectConfig); /** - * @brief Get the current status of the object. - * @return Return the current status of the object. + * @brief Get the current state of the object. + * @return Return the current state of the object. */ - EObjectStatus GetStatus() const; + EObjectState GetObjectState() const; /** * @brief Set the component operation mode. @@ -169,12 +195,18 @@ module sdv */ void SetOperationMode(in EOperationMode eMode); + /** + * @brief Get the object configuration for persistence. + * @return The object configuration as TOML string. + */ + u8string GetObjectConfig() const; + /** * @brief Shutdown called before the object is destroyed. * @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 */ void Shutdown(); }; diff --git a/export/interfaces/core_idl.idl b/export/interfaces/core_idl.idl index 8d28f3d..8b372c9 100644 --- a/export/interfaces/core_idl.idl +++ b/export/interfaces/core_idl.idl @@ -1,16 +1,20 @@ -/** - * @file core_idl.idl - * @brief This file provides all interface definitions for generator of the IDL compiler as part of the core SDV framework. - * @version 0.1 - * @date 2023.06.29 - * @author erik.verhoeven@zf.com - * @copyright Copyright ZF Friedrichshaven AG (c) 2022-2025 - */ + /******************************************************************************** + * 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 "core.idl" #include "mem.idl" - /** +/** * @brief Software Defined Vehicle framework. */ module sdv diff --git a/export/interfaces/core_ps.idl b/export/interfaces/core_ps.idl index d5bf736..04d4df3 100644 --- a/export/interfaces/core_ps.idl +++ b/export/interfaces/core_ps.idl @@ -1,16 +1,20 @@ -/** - * @file core_ps.idl - * @brief This file provides the marshalling interface definitions of the core SDV framework. - * @version 0.1 - * @date 2023.05.10 - * @author erik.verhoeven@zf.com - * @copyright Copyright ZF Friedrichshaven 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 + ********************************************************************************/ #include "core.idl" #include "process.idl" - /** +/** * @brief Software Defined Vehicle framework. */ module sdv diff --git a/export/interfaces/core_types.idl b/export/interfaces/core_types.idl index bdf94e6..caaca89 100644 --- a/export/interfaces/core_types.idl +++ b/export/interfaces/core_types.idl @@ -1,10 +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 + ********************************************************************************/ + /** - * @file core_types.idl * @brief This file provides all data types for the core SDV framework. - * @version 0.1 - * @date 2023.05.08 - * @author erik.verhoeven@zf.com - * @copyright Copyright ZF Friedrichshaven AG (c) 2023 */ /** @@ -56,8 +64,8 @@ const uint32 SDVFrameworkSubbuildVersion = 100; #verbatim_end /** -* @brief Software Defined Vehicle framework. -*/ + * @brief Software Defined Vehicle framework. + */ module sdv { /** diff --git a/export/interfaces/dispatch.idl b/export/interfaces/dispatch.idl index 63de613..894eb4a 100644 --- a/export/interfaces/dispatch.idl +++ b/export/interfaces/dispatch.idl @@ -1,13 +1,16 @@ -/** -* -* @file dispatch.idl -* @brief This file provides interfaces related to the data dispatch service. -* @version 1.0 -* @date 2024.01.12 -* @author Erik Verhoeven -* @copyright Copyright ZF Friedrichshaven AG (c) 2024 -* -*/ + /******************************************************************************** + * 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 "core.idl" /** diff --git a/export/interfaces/hw_ident.idl b/export/interfaces/hw_ident.idl index 2d64ea2..3bd7208 100644 --- a/export/interfaces/hw_ident.idl +++ b/export/interfaces/hw_ident.idl @@ -1,18 +1,21 @@ -/** - * @file hw_ident.idl - * @author Erik Verhoeven DISDS1 (mailto:erik.verhoeven@zf.com) - * @brief This file includes all the interfaces used for hardware identification. - * @version 1.0 - * @date 2024-05-09 + /******************************************************************************** + * 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 + ********************************************************************************/ #include "core.idl" - /** - * @brief Software Defined Vehicle framework. - */ +/** + * @brief Software Defined Vehicle framework. + */ module sdv { /** diff --git a/export/interfaces/ipc.idl b/export/interfaces/ipc.idl index 09f05c2..a9db13c 100644 --- a/export/interfaces/ipc.idl +++ b/export/interfaces/ipc.idl @@ -1,12 +1,15 @@ -/** - * @file ipc.idl - * @author Erik Verhoeven DISDS1 (mailto:erik.verhoeven@zf.com) - * @brief This file includes all the interfaces used for interpprocess communication. - * @version 1.0 - * @date 2024-05-09 + /******************************************************************************** + * 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 + ********************************************************************************/ #include "core.idl" diff --git a/export/interfaces/log.idl b/export/interfaces/log.idl index bc5a0ed..4d694d4 100644 --- a/export/interfaces/log.idl +++ b/export/interfaces/log.idl @@ -1,12 +1,15 @@ -/** - * @file log.idl - * @author Erik Verhoeven DISDS1 (mailto:erik.verhoeven@zf.com) - * @brief This file includes all the interfaces used for logging information. - * @version 1.0 - * @date 2024-05-09 + /******************************************************************************** + * 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 + ********************************************************************************/ #include "core.idl" #include "process.idl" diff --git a/export/interfaces/mem.idl b/export/interfaces/mem.idl index 270baf4..c6c7854 100644 --- a/export/interfaces/mem.idl +++ b/export/interfaces/mem.idl @@ -1,15 +1,19 @@ -/** - * @file mem.idl - * @brief This file provides the memory management interface definitions of the core SDV framework. - * @version 0.1 - * @date 2023.05.22 - * @author erik.verhoeven@zf.com - * @copyright Copyright ZF Friedrichshaven 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 + ********************************************************************************/ #include "core.idl" - /** +/** * @brief Software Defined Vehicle framework. */ module sdv diff --git a/export/interfaces/module.idl b/export/interfaces/module.idl index 5723d57..2d2f034 100644 --- a/export/interfaces/module.idl +++ b/export/interfaces/module.idl @@ -1,19 +1,21 @@ -/** - * @file module.idl - * @author Erik Verhoeven FRD DISDS1 (mailto:erik.verhoeven@zf.com) - * @brief This file includes all the interfaces used for module management - * @version 1.0 - * @date 2024-05-02 + /******************************************************************************** + * 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 + ********************************************************************************/ #include "core.idl" - /** - * @brief Software Defined Vehicle framework. - */ +/** + * @brief Software Defined Vehicle framework. + */ module sdv { /** diff --git a/export/interfaces/param.idl b/export/interfaces/param.idl new file mode 100644 index 0000000..60d16ca --- /dev/null +++ b/export/interfaces/param.idl @@ -0,0 +1,177 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 Contributors to the Eclipse Foundation + * + * 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 "core.idl" + +/** + * @brief Software Defined Vehicle framework. + */ +module sdv +{ + /** + * @brief Parameter bitmask flags. + */ + enum EParamFlags : uint32 + { + mask = 0xff, ///< Flags for the setting the parameter attributes + read_write = 0x00, ///< Read/write access to the parameter. + read_only = 0x01, ///< The parameter is read only. + temporary = 0x20, ///< The parameter is marked as a temporary parameter. + + state_mask = 0xff00, ///< Flags for the parameter states + dirty = 0x0100, ///< Parameter has changed. + locked = 0x0400, ///< When set, the parameter is not writable. + }; + + /** + * @brief Parameter type. + */ + enum EParamType + { + boolean_param, ///< The parameter is a boolean. + number_param, ///< The parameter is a number (integral or floating point). + string_param, ///< The parameter is a string. + enum_param, ///< The parameter is an enumeration value. + bitmask_param ///< The parameter is a bitmask value. + }; + + /** + * @brief Specific number information. + */ + struct SNumberInfo + { + any anyLowerLimit; ///< Optional lower limit value. + boolean bIncludeLowerLinit; ///< When set, low <= val, otherwise low < val. + any anyUpperLimit; ///< Optional upper limit value. + boolean bIncludeUpperLimit; ///< When set, val >= upper, otherwise val > upper. + }; + + /** + * @brief Specific string information. + */ + struct SStringInfo + { + u8string ssPattern; ///< Regular expression pattern describing the allowed pattern. + }; + + /** + * @brief Specific label information for enumerations and bitmasks. + */ + struct SLabelInfo + { + /** + * @brief Each label is described by a value and label text. + */ + struct SLabel + { + any anyValue; ///< Label value (must be an integral number) + u8string ssLabel; ///< Label text + }; + sequence seqLabels; ///< Sequence of element labels + }; + + /** + * @brief Structure to represent parameter information. + */ + struct SParamInfo + { + EParamType eType; ///< Parameter type + uint32 uiFlags; ///< Parameter flags containing one or more values of the EParamFlags enumeration. + u8string ssName; ///< The name of the parameter. + u8string ssGroup; ///< Optional group name for this parameter. Can contain sub-groups separated by a dot. + u8string ssUnit; ///< Optional parameter unit. + u8string ssDescription; ///< Optional parameter description. + any anyDefaultVal; ///< Default value + + /// Union holding extended parameter information. + union switch (eType) + { + case EParamType::number_param: + SNumberInfo sNumberInfo; ///< Additional information for number types + case EParamType::string_param: + SStringInfo sStringInfo; ///< Additional information for string types. + case EParamType::enum_param: + SLabelInfo sEnumInfo; ///< Additional information for enumeration types. + case EParamType::bitmask_param: + SLabelInfo sBitmaskInfo; ///< Additional information for bitmask types. + } uExtInfo; ///< Extended parameter information. + }; + + /** + * @brief Interface to access parameters. + */ + interface IParameters + { + /** + * @brief Return a sequence with parameter paths. Each path is unique and can be used to get and set the parameter value. + * @return Sequence containing parameter paths. Parameter paths are composed from group/sub-groups and the parameter name, + * separated by a dot. + */ + sequence GetParamPaths() const; + + /** + * @brief Returns the parameter value. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + * @return Returns the parameter value. Returns an 'empty' parameter value when not successful or the parameter is not set. + */ + any GetParam(in u8string ssPath) const; + + /** + * @brief Set the parameter value. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + * @param[in] anyValue Reference to the parameter value to set. + * @return Returns 'true' on success or 'false' when parameter could not be set (e.g. the parameter is read-only) or the + * index is larger than the amount of parameters being available. + */ + boolean SetParam(in u8string ssPath, in any anyValue); + + /** + * @brief Get parameter information. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + * @return Return the parameter information for the requested parameter or an empty structure when no parameter information + * is available or the parameter is not available. + */ + SParamInfo GetParamInfo(in u8string ssPath) const; + + /** + * @brief Is the parameter dirty (was it changed)? Checks the parameter dirty flag. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + * @return Returns whether the parameter was changed either by an explicit call to the SetParam function or internally by + * the + * object itself. + */ + boolean IsParamDirty(in u8string ssPath) const; + + /** + * @brief Reset the dirty flag for the parameter. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + */ + void ResetParamDirtyFlag(in u8string ssPath); + + /** + * @brief Is there at least one parameter with a dirty flag? + * @return Returns whether at least one parameter has its dirty flag enabled. + */ + boolean IsParamMapDirty() const; + + /** + * @brief Reset the dirty flag for all parameters. + */ + void ResetParamMapDirtyFlags(); + }; +}; // sdv \ No newline at end of file diff --git a/export/interfaces/process.idl b/export/interfaces/process.idl index a191ee4..f7fdb85 100644 --- a/export/interfaces/process.idl +++ b/export/interfaces/process.idl @@ -1,12 +1,15 @@ -/** - * @file process.idl - * @author Erik Verhoeven DISDS1 (mailto:erik.verhoeven@zf.com) - * @brief This file includes all the interfaces used for process creation. - * @version 1.0 - * @date 2024-05-09 + /******************************************************************************** + * 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 + ********************************************************************************/ #include "core.idl" diff --git a/export/interfaces/repository.idl b/export/interfaces/repository.idl index 5229bbe..6965a02 100644 --- a/export/interfaces/repository.idl +++ b/export/interfaces/repository.idl @@ -1,19 +1,22 @@ -/** + /******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG * - * @file repository.idl - * @brief This file provides interfaces related to the repository service - * @version 0.1 - * @date 2022.11.21 - * @author steffen.altmeier@zf.com - * @copyright Copyright ZF Friedrichshaven AG (c) 2022 + * 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 "core.idl" #include "module.idl" - /** - * @brief Software Defined Vehicle framework. - */ +/** + * @brief Software Defined Vehicle framework. + */ module sdv { /** @@ -110,6 +113,7 @@ module sdv /** * @brief Interface used to control object creation and destruction. + * @remarks This interface is exposed by the repository on for standalone, essential and isolated applications. */ interface IRepositoryControl { @@ -128,8 +132,7 @@ module sdv * @param[in] ssObjectName Name of the object, required to be unique. For standalone and essential applications, the * name string can be empty, in which case the object might either provide a name proposal or the name is the same as * the class name. Use the returned object ID to request the name of the object. - * @param[in] ssObjectConfig Optional configuration handed over to the object upon creation via IObjectControl. Only - * valid for standalone, essential and isolated applications. + * @param[in] ssObjectConfig Optional configuration handed over to the object upon creation via IObjectControl. * @return Returns the object ID when the object creation was successful or 0 when not. On success the object is * available through the IObjectAccess interface. If the object already exists (class and object names are identical), * the object ID of the existing object is returned. diff --git a/export/interfaces/timer.idl b/export/interfaces/timer.idl index cc3f5f0..fa76bbd 100644 --- a/export/interfaces/timer.idl +++ b/export/interfaces/timer.idl @@ -1,12 +1,15 @@ -/** - * @file timer.idl - * @author Erik Verhoeven DISDS1 (mailto:erik.verhoeven@zf.com) - * @brief This file includes all the interfaces used for timer creation. - * @version 1.0 - * @date 2024-05-09 + /******************************************************************************** + * 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 + ********************************************************************************/ #include "core.idl" diff --git a/export/interfaces/toml.idl b/export/interfaces/toml.idl index 8fe881d..0f94af6 100644 --- a/export/interfaces/toml.idl +++ b/export/interfaces/toml.idl @@ -1,13 +1,16 @@ -/** -* -* @file dispatch.idl -* @brief This file provides interfaces related to the data dispatch service. -* @version 1.0 -* @date 2024.01.12 -* @author Erik Verhoeven -* @copyright Copyright ZF Friedrichshaven AG (c) 2024 -* -*/ + /******************************************************************************** + * 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 "core.idl" /** @@ -102,13 +105,9 @@ module sdv u8string GetTOML() const; /** - * @brief Comment access flags. - * @details The comment access flags contain the comment code snippet index (0..15) as well as flags to receive the - * comments in interpreted or raw form. For comment code snippets are defined for all nodes, the comments before and - * after the node not related to the node and the comments before and after the node related to the node. Comment or - * whitespace in between the node tokens are part of the other 11 indices (each node interprets this differently). + * @brief Node comment type. */ - enum ECommentFlags + enum ECommentType { comment_before = 0, ///< The comment before the node. This will insert a newline and the comment ///< text preceded by the '#' character. @@ -118,169 +117,47 @@ module sdv ///< node and is separated by an extra newline. out_of_scope_comment_behind = 3, ///< An independent comment behind the node. This comment is not part of the ///< node and is separated by an extra newline. - comment_index_mask = 15, ///< Comment type mask to be used ot filter the comment type. - raw_comment = 8, ///< Store the comment exactly as provided (whitespace and comments). - replace_whitespace = 16, ///< When set, the comment will replace the current whitespace as well. - ///< Used with SetComment function. Automatically enabled for raw comments. }; /** * @brief Set or replace a comment for the node. - * @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. */ - void SetComment(in u8string ssComment, in uint32 uiFlags); + void SetComment(in ECommentType eType, in u8string ssComment); /** * Get the current comment for the node. - * @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. */ - u8string GetComment(in uint32 uiFlags); + u8string GetComment(in ECommentType eType); /** * @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments * will not be changed. */ void AutomaticFormat(); - }; - - /** - * @brief Interface allowing access to table and array nodes. - */ - interface INodeCollection - { - /** - * @brief Returns the amount of nodes. - * @return The amount of nodes. - */ - uint32 GetCount() const; /** - * @brief Get the node. - * @param[in] uiIndex Index of the node to get. - * @return Interface to the node object. + * @brief Is the node inline? + * @return Returns whether the node is defined as inline node. */ - IInterfaceAccess GetNode(in uint32 uiIndex) const; + boolean IsInline() const; /** - * @brief Searches a node by its key in the parse tree - * @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child - * name. E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be - * accessed and traversed by using the index number in brackets. E.g. 'array[3]' would access the fourth element of - * the array 'array'. These access conventions can also be chained like 'table.array[2][1].subtable.integerElement'. - * @attention Array indexing starts with 0! - * @param[in] ssPath The path of the Node to searched for. - * @return Returns an interface the requested node if available. + * @brief Is the node defined as standard node? + * @return Returns whether the node is defined as standard node. */ - IInterfaceAccess GetNodeDirect(in u8string ssPath) const; - }; - - /** - * @brief Extend the collection node with values, arrays and tables. - */ - interface INodeCollectionInsert - { - /** - * @brief Insert a value into the collection at the location before the supplied index. - * @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. Value nodes cannot be inserted behind external - * tables and table arrays. If the index is referencing a position behind an external table or a table array, the index - * is automatically corrected. - * @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. - * 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. - */ - IInterfaceAccess InsertValue(in uint32 uiIndex, in u8string ssName, in any anyValue); - - /** - * @brief Insert an array into the collection at the location before the supplied index. - * @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. Array nodes cannot be inserted behind external - * tables and table arrays. If the index is referencing a position behind an external table or a table array, the index - * is automatically corrected. - * @param[in] ssName Name of the array node to insert. Will be ignored if the current node is also 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. - * @return On success the interface to the newly inserted node is returned or NULL otherwise. - */ - IInterfaceAccess InsertArray(in uint32 uiIndex, in u8string ssName); - - /** - * @brief Insert a table into the collection at the location before the supplied index. - * @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 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. - * 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. - * @return On success the interface to the newly inserted node is returned or NULL otherwise. - */ - IInterfaceAccess InsertTable(in uint32 uiIndex, in u8string ssKeyName); - - /** - * @brief Insert a table array into the collection at the location before the supplied index. - * @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] ssName Name of the array node to insert. Will be ignored if the current node is also 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. - * @return On success the interface to the newly inserted node is returned or NULL otherwise. - */ - IInterfaceAccess InsertTableArray(in uint32 uiIndex, in u8string ssName); - - /** - * @brief The result of the TOML string to insert. - */ - enum EInsertResult : uint32 - { - invalid_TOML, ///< The TOML string was invalid or didn't fit the collection node it was to be inserted. - insert_partly_success, ///< Part, but not all nodes could be inserted (duplicate nodes are not inserted). - insert_success, ///< All nodes could be inserted or the TOML didn't contain any nodes. - }; - - /** - * @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. - * @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. - */ - EInsertResult InsertTOML(in u8string ssTOML, in boolean bRollbackOnPartly); - }; - - /** - * @brief Remove the current node. - * @remarks The root node cannot be deleted. - */ - interface INodeDelete - { - /** - * @brief Delete the current node. - * @attention A successful deletion will cause all interfaces to the current node to become inoperable. - * @return Returns whether the deletion was successful. - */ - boolean DeleteNode(); + boolean IsStandard() const; }; /** @@ -322,6 +199,176 @@ module sdv * @return Returns whether the move was successful. */ boolean MoveDown(); + + /** + * @brief Delete the current node. + * @attention A successful deletion will cause all interfaces to the current node to become inoperable. + * @return Returns whether the deletion was successful. + */ + boolean DeleteNode(); + }; + + /** + * @brief Interface allowing access to table and array nodes. + */ + interface INodeCollection + { + /** + * @brief Returns the amount of nodes. + * @return The amount of nodes. + */ + uint32 GetCount() const; + + /** + * @brief Get the node. + * @param[in] uiIndex Index of the node to get. + * @return Interface to the node object. + */ + IInterfaceAccess GetNode(in uint32 uiIndex) const; + + /** + * @brief Searches a node by its key in the parse tree + * @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child + * name. E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be + * accessed and traversed by using the index number in brackets. E.g. 'array[3]' would access the fourth element of + * the array 'array'. These access conventions can also be chained like 'table.array[2][1].subtable.integerElement'. + * @attention Array indexing starts with 0! + * @param[in] ssPath The path of the Node to searched for. + * @return Returns an interface the requested node if available. + */ + IInterfaceAccess GetNodeDirect(in u8string ssPath) const; + }; + + /** + * @brief Convert between inline and standard definitions. + */ + interface INodeCollectionConvert + { + /** + * @brief Can the node convert to an inline definition? + * @return Returns whether the conversion to inline is possible. Returns 'true' when the node is already inline. + */ + boolean CanMakeInline() const; + + /** + * @brief Convert the node to an inline node. + * @return Returns whether the conversion was successful. Returns 'true' when the node was already inline. + */ + boolean MakeInline(); + + /** + * @brief Can the node convert to a standard definition? + * @return Returns whether the conversion to standard is possible. Returns 'true' when the node is already defined as + * standard node. + */ + boolean CanMakeStandard() const; + + /** + * @brief Convert the node to a standard node. + * @return Returns whether the conversion was successful. Returns 'true' when the node was already defined as standard + * node. + */ + boolean MakeStandard(); + }; + + /** + * @brief Extend the collection node with values, arrays and tables. + */ + interface INodeCollectionInsert + { + /** + * @brief Insert a value into the collection at the location before the supplied index. + * @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. Value nodes cannot be inserted behind external + * tables and table arrays. If the index is referencing a position behind an external table or a table array, the index + * is automatically corrected. + * @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. + * 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. + */ + IInterfaceAccess InsertValue(in uint32 uiIndex, in u8string ssName, in any anyValue); + + /** + * @brief Insert an array into the collection at the location before the supplied index. + * @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. Array nodes cannot be inserted behind external + * tables and table arrays. If the index is referencing a position behind an external table or a table array, the index + * is automatically corrected. + * @param[in] ssName Name of the array node to insert. Will be ignored if the current node is also 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. + * @return On success the interface to the newly inserted node is returned or NULL otherwise. + */ + IInterfaceAccess InsertArray(in uint32 uiIndex, in u8string ssName); + + /** + * @brief Insertion preference for tables and table arrays, being standard or inline. + */ + enum EInsertPreference + { + prefer_standard = 0, ///< When the parent node is not inline, the node will be inserted as standard node. + prefer_inline = 1, ///< The node will be inserted as inline node. + }; + + /** + * @brief Insert a table into the collection at the location before the supplied index. + * @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 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] ssName Name of the table node to insert. Will be ignored if the parent 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. + */ + IInterfaceAccess InsertTable(in uint32 uiIndex, in u8string ssName, in EInsertPreference ePreference); + + /** + * @brief Insert a table array into the collection at the location before the supplied index. + * @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] ssName Name of the array node to insert. Will be ignored if the parent node is also 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. + */ + IInterfaceAccess InsertTableArray(in uint32 uiIndex, in u8string ssName, in EInsertPreference ePreference); + + /** + * @brief The result of the TOML string to insert. + */ + enum EInsertResult : uint32 + { + insert_fail, ///< The TOML string was invalid or didn't fit the collection node it was to be inserted. + insert_partly_success, ///< Part, but not all nodes could be inserted (duplicate nodes are not inserted). + insert_success, ///< All nodes could be inserted or the TOML didn't contain any nodes. + }; + + /** + * @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. + * @attention Even though the TOML might be defining the node(s) in standard form, if the parent node is an inline node, + * the node will be inserted as inline node. + * @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. This string can hold one or more nodes that should be inserted. + * @param[in] bRollbackOnFailure If only part of the nodes could be inserted, no node will be inserted. + * @return The result of the insertion. + */ + EInsertResult InsertTOML(in uint32 uiIndex, in u8string ssTOML, in boolean bRollbackOnFailure); }; /** diff --git a/export/support/any.h b/export/support/any.h index 267ad17..b650426 100644 --- a/export/support/any.h +++ b/export/support/any.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 SDV_ANY_H #define SDV_ANY_H @@ -93,62 +106,148 @@ namespace sdv /** * @brief Assignment constructor. - * @param[in] tVal The value to assign. + * @param[in] bVal The value to assign. + */ + any_t(bool bVal); + + /** + * @{ + * @brief Assignment constructor. + * @param[in] iVal The value to assign. + */ + any_t(int8_t iVal); + any_t(int16_t iVal); + any_t(int32_t iVal); +#ifdef _WIN32 + any_t(long iVal); +#endif + any_t(int64_t iVal); +#ifdef __linux__ + any_t(long long int iVal); +#endif + /** + * @} + */ + + /** + * @{ + * @brief Assignment constructor. + * @param[in] uiVal The value to assign. + */ + any_t(uint8_t uiVal); + any_t(uint16_t uiVal); + any_t(uint32_t uiVal); +#ifdef _WIN32 + any_t(unsigned long uiVal); +#endif + any_t(uint64_t uiVal); +#ifdef __linux__ + any_t(unsigned long long int uiVal); +#endif + /** + * @} + */ + + /** + * @{ + * @brief Assignment constructor. + * @param[in] cVal The value to assign. + */ + any_t(char cVal); + any_t(char16_t cVal); + any_t(char32_t cVal); + any_t(wchar_t cVal); + /** + * @} + */ + + /** + * @{ + * @brief Assignment constructor. + * @param[in] fVal The value to assign. + */ + any_t(float fVal); + any_t(double fVal); + any_t(long double fVal); + /** + * @} */ - template - explicit any_t(TType tVal); /** * @{ * @brief SDV string constructors. * @param[in] rssVal Reference to the string object. */ - explicit any_t(const string& rssVal); - explicit any_t(const u8string& rssVal); - explicit any_t(const u16string& rssVal); - explicit any_t(const u32string& rssVal); - explicit any_t(const wstring& rssVal); + any_t(const string& rssVal); + any_t(const u8string& rssVal); + any_t(const u16string& rssVal); + any_t(const u32string& rssVal); + any_t(const wstring& rssVal); /** - * @} - */ + * @} + */ /** + * @{ * @brief C-style string constructors. - * @param[in] sz Zero terminated string. + * @param[in] szVal Zero terminated string. */ - any_t(const char* sz); - + any_t(const char* szVal); + any_t(const char16_t* szVal); + any_t(const char32_t* szVal); + any_t(const wchar_t* szVal); /** - * @brief C-style string constructors. - * @param[in] sz Zero terminated string. + * @} */ - any_t(const char16_t* sz); - - /** - * @brief C-style string constructors. - * @param[in] sz Zero terminated string. - */ - any_t(const char32_t* sz); - - /** - * @brief C-style string constructors. - * @param[in] sz Zero terminated string. - */ - any_t(const wchar_t* sz); /** * @{ * @brief STD string constructors. * @param[in] rssVal Reference to the string object. */ - explicit any_t(const std::string& rssVal); - explicit any_t(const std::u16string& rssVal); - explicit any_t(const std::u32string& rssVal); - explicit any_t(const std::wstring& rssVal); + any_t(const std::string& rssVal); + any_t(const std::u16string& rssVal); + any_t(const std::u32string& rssVal); + any_t(const std::wstring& rssVal); /** * @} */ + /** + * @brief STD path constructor (will be stored as u8string). + * @param[in] rpathVal Reference to the path object. + */ + any_t(const std::filesystem::path& rpathVal); + + /** + * @brief Interface constructor. + * @param[in] ifcVal Interface pointer. + */ + any_t(interface_t ifcVal); + + // Assignment already covered by any_t(uint64) + ///** + // * brief Interface ID constructor. + // * param[in] idIfcVal Interface ID. + // */ + //any_t(interface_id idIfcVal); + + // Assignment already covered by any_t(uint64) + ///** + // * brief Exception ID constructor. + // * param[in] idExceptVal Exception ID. + // */ + //any_t(exception_id idExceptVal); + + /** + * @brief Enum constructor. + * @tparam TEnum Type of enum. + * @tparam TEnable Enable the function if the TType is an enum. + * @param eVal The eunm value. + */ + template >> + explicit any_t(TEnum eVal); + /** * @brief Assignment constructor. * @param[in] tVal The value to assign. @@ -171,11 +270,158 @@ namespace sdv /** * @brief Assignment operator. - * @param[in] tVal The value to assign. + * @param[in] bVal The value to assign. * @return Reference to this class. */ - template - any_t& operator=(TType tVal); + any_t& operator=(bool bVal); + + /** + * @{ + * @brief Assignment operator. + * @param[in] iVal The value to assign. + * @return Reference to this class. + */ + any_t& operator=(int8_t iVal); + any_t& operator=(int16_t iVal); + any_t& operator=(int32_t iVal); +#ifdef _WIN32 + any_t& operator=(long iVal); +#endif + any_t& operator=(int64_t iVal); +#ifdef __linux__ + any_t& operator=(long long int iVal); +#endif + /** + * @} + */ + + /** + * @{ + * @brief Assignment operator. + * @param[in] uiVal The value to assign. + * @return Reference to this class. + */ + any_t& operator=(uint8_t uiVal); + any_t& operator=(uint16_t uiVal); + any_t& operator=(uint32_t uiVal); +#ifdef _WIN32 + any_t& operator=(unsigned long uiVal); +#endif + any_t& operator=(uint64_t uiVal); +#ifdef __linux__ + any_t& operator=(unsigned long long int uiVal); +#endif + /** + * @} + */ + + /** + * @{ + * @brief Assignment operator. + * @param[in] cVal The value to assign. + * @return Reference to this class. + */ + any_t& operator=(char cVal); + any_t& operator=(char16_t cVal); + any_t& operator=(char32_t cVal); + any_t& operator=(wchar_t cVal); + /** + * @} + */ + + /** + * @{ + * @brief Assignment operator. + * @param[in] fVal The value to assign. + * @return Reference to this class. + */ + any_t& operator=(float fVal); + any_t& operator=(double fVal); + any_t& operator=(long double fVal); + /** + * @} + */ + + /** + * @{ + * @brief SDV string assignment operator. + * @param[in] rssVal Reference to the string object. + * @return Reference to this class. + */ + any_t& operator=(const string& rssVal); + any_t& operator=(const u8string& rssVal); + any_t& operator=(const u16string& rssVal); + any_t& operator=(const u32string& rssVal); + any_t& operator=(const wstring& rssVal); + /** + * @} + */ + + /** + * @{ + * @brief C-style string assignment operator. + * @param[in] szVal Zero terminated string. + * @return Reference to this class. + */ + any_t& operator=(const char* szVal); + any_t& operator=(const char16_t* szVal); + any_t& operator=(const char32_t* szVal); + any_t& operator=(const wchar_t* szVal); + /** + * @} + */ + + /** + * @{ + * @brief STD string assignment operator. + * @param[in] rssVal Reference to the string object. + * @return Reference to this class. + */ + any_t& operator=(const std::string& rssVal); + any_t& operator=(const std::u16string& rssVal); + any_t& operator=(const std::u32string& rssVal); + any_t& operator=(const std::wstring& rssVal); + /** + * @} + */ + + /** + * @brief STD path assignment operator (will be stored as u8string). + * @param[in] rpathVal Reference to the path object. + * @return Reference to this class. + */ + any_t& operator=(const std::filesystem::path& rpathVal); + + /** + * @brief Interface assignment operator. + * @param[in] ifcVal Interface pointer. + * @return Reference to this class. + */ + any_t& operator=(interface_t ifcVal); + + // Assignment already covered by operator=(uint64) + ///** + // * brief Interface ID operator. + // * param[in] idIfcVal Interface ID. + // */ + //any_t& operator=(interface_id idIfcVal); + + // Assignment already covered by operator=(uint64) + ///** + // * brief Exception ID operator. + // * param[in] idExceptVal Exception ID. + // */ + //any_t& operator=(exception_id idExceptVal); + + /** + * @brief Enum assignment operator. + * tparam TEnum Enum type. + * @tparam TEnable Enable the function if the TType is an enum. + * @param[in] eVal Enum value. + * @return Reference to this class. + */ + template >> + any_t& operator=(TEnum eVal); /** * @brief Copy assignment operator. @@ -231,6 +477,7 @@ namespace sdv operator std::u16string() const; operator std::u32string() const; operator std::wstring() const; + operator std::filesystem::path() const; /** * @} */ @@ -247,12 +494,148 @@ namespace sdv void clear(); /** - * @brief Assign the value to the any. The any takes the value type based on the value. - * @tparam TType The type of the value to set. - * @param[in] tVal The value to set. + * @brief Assignment function. + * @param[in] bVal The value to assign. */ - template - void set(TType tVal); + void set(bool bVal); + + /** + * @{ + * @brief Assignment function. + * @param[in] iVal The value to assign. + */ + void set(int8_t iVal); + void set(int16_t iVal); + void set(int32_t iVal); +#ifdef _WIN32 + void set(long iVal); +#endif + void set(int64_t iVal); +#ifdef __linux__ + void set(long long int iVal); +#endif + /** + * @} + */ + + /** + * @{ + * @brief Assignment function. + * @param[in] uiVal The value to assign. + */ + void set(uint8_t uiVal); + void set(uint16_t uiVal); + void set(uint32_t uiVal); +#ifdef _WIN32 + void set(unsigned long uiVal); +#endif + void set(uint64_t uiVal); +#ifdef __linux__ + void set(unsigned long long int uiVal); +#endif + /** + * @} + */ + + /** + * @{ + * @brief Assignment function. + * @param[in] cVal The value to assign. + */ + void set(char cVal); + void set(char16_t cVal); + void set(char32_t cVal); + void set(wchar_t cVal); + /** + * @} + */ + + /** + * @{ + * @brief Assignment function. + * @param[in] fVal The value to assign. + */ + void set(float fVal); + void set(double fVal); + void set(long double fVal); + /** + * @} + */ + + /** + * @{ + * @brief SDV string assignment function. + * @param[in] rssVal Reference to the string object. + */ + void set(const string& rssVal); + void set(const u8string& rssVal); + void set(const u16string& rssVal); + void set(const u32string& rssVal); + void set(const wstring& rssVal); + /** + * @} + */ + + /** + * @{ + * @brief C-style string assignment function. + * @param[in] szVal Zero terminated string. + */ + void set(const char* szVal); + void set(const char16_t* szVal); + void set(const char32_t* szVal); + void set(const wchar_t* szVal); + /** + * @} + */ + + /** + * @{ + * @brief STD string assignment function. + * @param[in] rssVal Reference to the string object. + */ + void set(const std::string& rssVal); + void set(const std::u16string& rssVal); + void set(const std::u32string& rssVal); + void set(const std::wstring& rssVal); + /** + * @} + */ + + /** + * @brief STD path assignment function (will be stored as u8string). + * @param[in] rpathVal Reference to the path object. + */ + void set(const std::filesystem::path& rpathVal); + + /** + * @brief Interface assignment function. + * @param[in] ifcVal Interface pointer. + */ + void set(interface_t ifcVal); + + // Assignment already covered by set(uint64) + ///** + // * brief Interface ID assignment function. + // * param[in] idIfcVal Interface ID. + // */ + //void set(interface_id idIfcVal); + + // Assignment already covered by set(uint64) + ///** + // * brief Exception ID assignment function. + // * param[in] idExceptVal Exception ID. + // */ + //void set(exception_id idExceptVal); + + /** + * @brief Enum assignment function. + * tparam TEnum Enum type. + * @tparam TEnable Enable the function if the TType is an enum. + * @param[in] eVal Enum value. + */ + template >> + void set(TEnum eVal); /** * @brief Assign the value to the any. The value will be converted to the provided value type. @@ -273,7 +656,7 @@ namespace sdv /** * @brief Comparison type. - */ + */ enum class ECompareType { compare_equal, diff --git a/export/support/any.inl b/export/support/any.inl index e1d6085..0442a91 100644 --- a/export/support/any.inl +++ b/export/support/any.inl @@ -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 SDV_ANY_INL #define SDV_ANY_INL @@ -29,16 +42,138 @@ namespace sdv clear(); } - template - inline any_t::any_t(TType tVal) : any_t() + inline any_t::any_t(bool bValParam) : any_t() { - set(tVal); + eValType = EValType::val_type_bool; + bVal = bValParam; + } + + inline any_t::any_t(int8_t iVal) : any_t() + { + eValType = EValType::val_type_int8; + i8Val = iVal; + } + + inline any_t::any_t(int16_t iVal) : any_t() + { + eValType = EValType::val_type_int16; + i16Val = iVal; + } + + inline any_t::any_t(int32_t iVal) : any_t() + { + eValType = EValType::val_type_int32; + i32Val = iVal; + } + +#ifdef _WIN32 + inline any_t::any_t(long iVal) : any_t() + { + eValType = EValType::val_type_int32; + i32Val = iVal; + } +#endif + + inline any_t::any_t(int64_t iVal) : any_t() + { + eValType = EValType::val_type_int64; + i64Val = iVal; + } + +#ifdef __linux__ + inline any_t::any_t(long long int iVal) : any_t() + { + eValType = EValType::val_type_int64; + i64Val = iVal; + } +#endif + + inline any_t::any_t(uint8_t uiVal) : any_t() + { + eValType = EValType::val_type_uint8; + ui8Val = uiVal; + } + + inline any_t::any_t(uint16_t uiVal) : any_t() + { + eValType = EValType::val_type_uint16; + ui16Val = uiVal; + } + + inline any_t::any_t(uint32_t uiVal) : any_t() + { + eValType = EValType::val_type_uint32; + ui32Val = uiVal; + } + +#ifdef _WIN32 + inline any_t::any_t(unsigned long uiVal) : any_t() + { + eValType = EValType::val_type_uint32; + ui32Val = uiVal; + } +#endif + + inline any_t::any_t(uint64_t uiVal) : any_t() + { + eValType = EValType::val_type_uint64; + ui64Val = uiVal; + } + +#ifdef __linux__ + inline any_t::any_t(unsigned long long int uiVal) : any_t() + { + eValType = EValType::val_type_uint64; + ui64Val = uiVal; + } +#endif + + inline any_t::any_t(char cValParam) : any_t() + { + eValType = EValType::val_type_char; + cVal = cValParam; + } + + inline any_t::any_t(char16_t cValParam) : any_t() + { + eValType = EValType::val_type_char16; + c16Val = cValParam; + } + + inline any_t::any_t(char32_t cValParam) : any_t() + { + eValType = EValType::val_type_char32; + c32Val = cValParam; + } + + inline any_t::any_t(wchar_t cValParam) : any_t() + { + eValType = EValType::val_type_wchar; + cwVal = cValParam; + } + + inline any_t::any_t(float fValParam) : any_t() + { + eValType = EValType::val_type_float; + fVal = fValParam; + } + + inline any_t::any_t(double fValParam) : any_t() + { + eValType = EValType::val_type_double; + dVal = fValParam; + } + + inline any_t::any_t(long double fValParam) : any_t() + { + eValType = EValType::val_type_long_double; + ldVal = fValParam; } inline any_t::any_t(const string& rssVal) : any_t() { - eValType = EValType::val_type_string; - new (&ssVal) string(rssVal); + eValType = EValType::val_type_string; + new (&ssVal) string(rssVal); } inline any_t::any_t(const u8string& rssVal) : any_t() @@ -65,17 +200,29 @@ namespace sdv new (&sswVal) wstring(rssVal); } - inline any_t::any_t(const char* sz) : any_t(sdv::u8string(sz)) - {} + inline any_t::any_t(const char* szVal) : any_t() + { + eValType = EValType::val_type_u8string; + new (&ss8Val) u8string(szVal); + } - inline any_t::any_t(const char16_t* sz) : any_t(sdv::u16string(sz)) - {} + inline any_t::any_t(const char16_t* szVal) : any_t() + { + eValType = EValType::val_type_u16string; + new (&ss16Val) u16string(szVal); + } - inline any_t::any_t(const char32_t* sz) : any_t(sdv::u32string(sz)) - {} + inline any_t::any_t(const char32_t* szVal) : any_t() + { + eValType = EValType::val_type_u32string; + new (&ss32Val) u32string(szVal); + } - inline any_t::any_t(const wchar_t* sz) : any_t(sdv::wstring(sz)) - {} + inline any_t::any_t(const wchar_t* szVal) : any_t() + { + eValType = EValType::val_type_wstring; + new (&sswVal) wstring(szVal); + } inline any_t::any_t(const std::string& rssVal) : any_t() { @@ -101,6 +248,36 @@ namespace sdv new (&sswVal) wstring(rssVal); } + inline any_t::any_t(const std::filesystem::path& rpathVal) : any_t(rpathVal.generic_u8string()) + {} + + inline any_t::any_t(interface_t ifcValParam) : any_t() + { + eValType = EValType::val_type_interface; + new (&ifcVal) interface_t(ifcValParam); + } + + // Assignment already covered by any_t(uint64) + //inline any_t::any_t(interface_id idIfcVal) : any_t() + //{ + // eValType = EValType::val_type_interface_id; + // new (&idIfcVal) interface_id(idIfcVal); + //} + + // Assignment already covered by any_t(uint64) + //inline any_t::any_t(exception_id idExceptVal) : any_t() + //{ + // eValType = EValType::val_type_exception_id; + // new (&idIfcVal) exception_id(idIfcValParam); + //} + + template + inline any_t::any_t(TEnum eVal) : any_t() + { + static_assert(std::is_enum_v, "The type is not supported to be converted to sdv::any_t!"); + set(static_cast>(eVal)); + } + template inline any_t::any_t(TType tVal, EValType eValTypeParam) : any_t() { @@ -188,10 +365,243 @@ namespace sdv rany.eValType = EValType::val_type_empty; } - template - inline any_t& any_t::operator=(TType tVal) + inline any_t& any_t::operator=(bool bValParam) { - set(tVal); + set(bValParam); + return *this; + } + + inline any_t& any_t::operator=(int8_t iVal) + { + set(iVal); + return *this; + } + + inline any_t& any_t::operator=(int16_t iVal) + { + set(iVal); + return *this; + } + + inline any_t& any_t::operator=(int32_t iVal) + { + set(iVal); + return *this; + } + +#ifdef _WIN32 + inline any_t& any_t::operator=(long iVal) + { + set(iVal); + return *this; + } +#endif + + inline any_t& any_t::operator=(int64_t iVal) + { + set(iVal); + return *this; + } + +#ifdef __linux__ + inline any_t& any_t::operator=(long long int iVal) + { + set(iVal); + return *this; + } +#endif + + inline any_t& any_t::operator=(uint8_t uiVal) + { + set(uiVal); + return *this; + } + + inline any_t& any_t::operator=(uint16_t uiVal) + { + set(uiVal); + return *this; + } + + inline any_t& any_t::operator=(uint32_t uiVal) + { + set(uiVal); + return *this; + } + +#ifdef _WIN32 + inline any_t& any_t::operator=(unsigned long uiVal) + { + set(uiVal); + return *this; + } +#endif + + inline any_t& any_t::operator=(uint64_t uiVal) + { + set(uiVal); + return *this; + } + +#ifdef __linux__ + inline any_t& any_t::operator=(unsigned long long int uiVal) + { + set(uiVal); + return *this; + } +#endif + + inline any_t& any_t::operator=(char cValParam) + { + set(cValParam); + return *this; + } + + inline any_t& any_t::operator=(char16_t cValParam) + { + set(cValParam); + return *this; + } + + inline any_t& any_t::operator=(char32_t cValParam) + { + set(cValParam); + return *this; + } + + inline any_t& any_t::operator=(wchar_t cValParam) + { + set(cValParam); + return *this; + } + + inline any_t& any_t::operator=(float fValParam) + { + set(fValParam); + return *this; + } + + inline any_t& any_t::operator=(double fValParam) + { + set(fValParam); + return *this; + } + + inline any_t& any_t::operator=(long double fValParam) + { + set(fValParam); + return *this; + } + + inline any_t& any_t::operator=(const string& rssVal) + { + set(rssVal); + return *this; + } + + inline any_t& any_t::operator=(const u8string& rssVal) + { + set(rssVal); + return *this; + } + + inline any_t& any_t::operator=(const u16string& rssVal) + { + set(rssVal); + return *this; + } + + inline any_t& any_t::operator=(const u32string& rssVal) + { + set(rssVal); + return *this; + } + + inline any_t& any_t::operator=(const wstring& rssVal) + { + set(rssVal); + return *this; + } + + inline any_t& any_t::operator=(const char* szVal) + { + set(szVal); + return *this; + } + + inline any_t& any_t::operator=(const char16_t* szVal) + { + set(szVal); + return *this; + } + + inline any_t& any_t::operator=(const char32_t* szVal) + { + set(szVal); + return *this; + } + + inline any_t& any_t::operator=(const wchar_t* szVal) + { + set(szVal); + return *this; + } + + inline any_t& any_t::operator=(const std::string& rssVal) + { + set(rssVal); + return *this; + } + + inline any_t& any_t::operator=(const std::u16string& rssVal) + { + set(rssVal); + return *this; + } + + inline any_t& any_t::operator=(const std::u32string& rssVal) + { + set(rssVal); + return *this; + } + + inline any_t& any_t::operator=(const std::wstring& rssVal) + { + set(rssVal); + return *this; + } + + inline any_t& any_t::operator=(const std::filesystem::path& rpathVal) + { + set(rpathVal); + return *this; + } + + inline any_t& any_t::operator=(interface_t ifcValParam) + { + set(ifcValParam); + return *this; + } + + // Assignment already covered by operator=(uint64) + //inline any_t& any_t::operator=(interface_id idIfcValParam) + //{ + // set(idIfcValParam); + // return *this; + //} + + // Assignment already covered by operator=(uint64) + //inline any_t& any_t::operator=(exception_id idExceptValParam) + //{ + // set(idExceptValParam); + // return *this; + //} + + template + inline any_t& any_t::operator=(TEnum eVal) + { + static_assert(std::is_enum_v, "The type is not supported to be converted to sdv::any_t!"); + set(static_cast>(eVal)); return *this; } @@ -392,6 +802,11 @@ namespace sdv return get(); } + inline any_t::operator std::filesystem::path() const + { + return get(); + } + inline any_t::operator interface_t() const { return get(); @@ -448,429 +863,250 @@ namespace sdv eValType = EValType::val_type_empty; } - /** - * @brief Set the boolean value (specialization) - * @param[in] bValParam The boolean value. - */ - template <> inline void any_t::set(bool bValParam) { clear(); eValType = EValType::val_type_bool; - bVal = bValParam; + bVal = bValParam; } - /** - * @brief Set the integer value (specialization) - * @param[in] iValParam The integer value. - */ - template <> - inline void any_t::set(int8_t iValParam) + inline void any_t::set(int8_t iVal) { clear(); eValType = EValType::val_type_int8; - i8Val = iValParam; + i8Val = iVal; } - /** - * @brief Set the unsigned integer value (specialization) - * @param[in] uiValParam The unsigned integer value. - */ - template <> - inline void any_t::set(uint8_t uiValParam) - { - clear(); - eValType = EValType::val_type_uint8; - ui8Val = uiValParam; - } - - /** - * @brief Set the integer value (specialization) - * @param[in] iValParam The integer value. - */ - template <> - inline void any_t::set(int16_t iValParam) + inline void any_t::set(int16_t iVal) { clear(); eValType = EValType::val_type_int16; - i16Val = iValParam; + i16Val = iVal; } - /** - * @brief Set the unsigned integer value (specialization) - * @param[in] uiValParam The unsigned integer value. - */ - template <> - inline void any_t::set(uint16_t uiValParam) + inline void any_t::set(int32_t iVal) + { + clear(); + eValType = EValType::val_type_int32; + i32Val = iVal; + } + +#ifdef _WIN32 + inline void any_t::set(long iVal) + { + clear(); + eValType = EValType::val_type_int32; + i32Val = iVal; + } +#endif + + inline void any_t::set(int64_t iVal) + { + clear(); + eValType = EValType::val_type_int64; + i64Val = iVal; + } + +#ifdef __linux__ + inline void any_t::set(long long int iVal) + { + clear(); + eValType = EValType::val_type_int64; + i64Val = iVal; + } +#endif + + inline void any_t::set(uint8_t uiVal) + { + clear(); + eValType = EValType::val_type_uint8; + ui8Val = uiVal; + } + + inline void any_t::set(uint16_t uiVal) { clear(); eValType = EValType::val_type_uint16; - ui16Val = uiValParam; + ui16Val = uiVal; } - /** - * @brief Set the integer value (specialization) - * @param[in] iValParam The integer value. - */ - template <> - inline void any_t::set(int32_t iValParam) - { - clear(); - eValType = EValType::val_type_int32; - i32Val = iValParam; - } - -#ifdef _WIN32 - /** - * @brief Set the long value (specialization) - * @param[in] iValParam The long value. - */ - template <> - inline void any_t::set(long lValParam) - { - clear(); - eValType = EValType::val_type_int32; - i32Val = static_cast(lValParam); - } -#endif - - /** - * @brief Set the unsigned integer value (specialization) - * @param[in] uiValParam The unsigned integer value. - */ - template <> - inline void any_t::set(uint32_t uiValParam) + inline void any_t::set(uint32_t uiVal) { clear(); eValType = EValType::val_type_uint32; - ui32Val = uiValParam; + ui32Val = uiVal; } #ifdef _WIN32 - /** - * @brief Set the unsigned long value (specialization) - * @param[in] uiValParam The unsigned long value. - */ - template <> - inline void any_t::set(unsigned long ulValParam) + inline void any_t::set(unsigned long uiVal) { clear(); eValType = EValType::val_type_uint32; - ui32Val = static_cast(ulValParam); + ui32Val = uiVal; } #endif - /** - * @brief Set the integer value (specialization) - * @param[in] iValParam The integer value. - */ - template <> - inline void any_t::set(int64_t iValParam) - { - clear(); - eValType = EValType::val_type_int64; - i64Val = iValParam; - } - -#ifdef __linux__ - /** - * @brief Set the long long value (specialization) - * @param[in] iValParam The integer value. - */ - template <> - inline void any_t::set(long long llValParam) - { - clear(); - eValType = EValType::val_type_int64; - i64Val = static_cast(llValParam); - } -#endif - - /** - * @brief Set the unsigned integer value (specialization) - * @param[in] uiValParam The unsigned integer value. - */ - template <> - inline void any_t::set(uint64_t uiValParam) + inline void any_t::set(uint64_t uiVal) { clear(); eValType = EValType::val_type_uint64; - ui64Val = uiValParam; + ui64Val = uiVal; } #ifdef __linux__ - /** - * @brief Set the unsigned long long value (specialization) - * @param[in] uiValParam The unsigned long long value. - */ - template <> - inline void any_t::set(unsigned long long ullValParam) + inline void any_t::set(unsigned long long int uiVal) { clear(); eValType = EValType::val_type_uint64; - ui64Val = static_cast(ullValParam); + ui64Val = uiVal; } #endif - /** - * @brief Set the character value (specialization) - * @param[in] cValParam The character value. - */ - template <> inline void any_t::set(char cValParam) { clear(); eValType = EValType::val_type_char; - cVal = cValParam; + cVal = cValParam; } - /** - * @brief Set the character value (specialization) - * @param[in] c16ValParam The character value. - */ - template <> - inline void any_t::set(char16_t c16ValParam) + inline void any_t::set(char16_t cValParam) { clear(); eValType = EValType::val_type_char16; - c16Val = c16ValParam; + c16Val = cValParam; } - /** - * @brief Set the character value (specialization) - * @param[in] c32ValParam The character value. - */ - template <> - inline void any_t::set(char32_t c32ValParam) + inline void any_t::set(char32_t cValParam) { clear(); eValType = EValType::val_type_char32; - c32Val = c32ValParam; + c32Val = cValParam; } - /** - * @brief Set the character value (specialization) - * @param[in] cwValParam The character value. - */ - template <> - inline void any_t::set(wchar_t cwValParam) + inline void any_t::set(wchar_t cValParam) { clear(); eValType = EValType::val_type_wchar; - cwVal = cwValParam; + cwVal = cValParam; } - /** - * @brief Set the float value (specialization) - * @param[in] fValParam The float value. - */ - template <> inline void any_t::set(float fValParam) { clear(); eValType = EValType::val_type_float; - fVal = fValParam; + fVal = fValParam; } - /** - * @brief Set the double value (specialization) - * @param[in] dValParam The double value. - */ - template <> - inline void any_t::set(double dValParam) + inline void any_t::set(double fValParam) { clear(); eValType = EValType::val_type_double; - dVal = dValParam; + dVal = fValParam; } - /** - * @brief Set the long double value (specialization) - * @param[in] ldValParam The long double value. - */ - template <> - inline void any_t::set(long double ldValParam) + inline void any_t::set(long double fValParam) { clear(); eValType = EValType::val_type_long_double; - ldVal = ldValParam; + ldVal = fValParam; } - /// @cond DOXYGEN_IGNORE - ///** - // * @brief Set the fixed value (specialization) - // * @param[in] fixValParam The SDV fixed value. - // */ - //template <> - //inline void any_t::set(fixed fixValParam) - //{ - // clear(); - // eValType = EValType::val_type_fixed; - // fixVal = fixValParam; - //} - /// @endcond - - /** - * @brief Set the string value (specialization) - * @param[in] rssValParam The string value. - */ - template <> - inline void any_t::set(const string& rssValParam) + inline void any_t::set(const string& rssVal) { - clear(); - eValType = EValType::val_type_string; - new (&ssVal) string(rssValParam); + clear(); + eValType = EValType::val_type_string; + new (&ssVal) string(rssVal); } - /** - * @brief Set the string value (specialization) - * @param[in] rss8ValParam The string value. - */ - template <> - inline void any_t::set(const u8string& rss8ValParam) + inline void any_t::set(const u8string& rssVal) { clear(); eValType = EValType::val_type_u8string; - new (&ss8Val) u8string(rss8ValParam); + new (&ss8Val) u8string(rssVal); } - /** - * @brief Set the string value (specialization) - * @param[in] rssValParam The string value. - */ - template <> - inline void any_t::set(const std::string& rssValParam) + inline void any_t::set(const u16string& rssVal) + { + clear(); + eValType = EValType::val_type_u16string; + new (&ss16Val) u16string(rssVal); + } + + inline void any_t::set(const u32string& rssVal) + { + clear(); + eValType = EValType::val_type_u32string; + new (&ss32Val) u32string(rssVal); + } + + inline void any_t::set(const wstring& rssVal) + { + clear(); + eValType = EValType::val_type_wstring; + new (&sswVal) wstring(rssVal); + } + + inline void any_t::set(const char* szVal) { clear(); eValType = EValType::val_type_u8string; - new (&ss8Val) u8string(rssValParam); + new (&ss8Val) u8string(szVal); } - /** - * @brief Set the string value (specialization) - * @param[in] pszValParam The string value. - */ - template <> - inline void any_t::set(const char* pszValParam) - { - clear(); - eValType = EValType::val_type_u8string; - new (&ss8Val) u8string(pszValParam ? pszValParam : ""); - } - - /** - * @brief Set the string value (specialization) - * @param[in] rss16ValParam The string value. - */ - template <> - inline void any_t::set(const u16string& rss16ValParam) + inline void any_t::set(const char16_t* szVal) { clear(); eValType = EValType::val_type_u16string; - new (&ss16Val) u16string(rss16ValParam); + new (&ss16Val) u16string(szVal); } - /** - * @brief Set the string value (specialization) - * @param[in] rss16ValParam The string value. - */ - template <> - inline void any_t::set(const std::u16string& rss16ValParam) + inline void any_t::set(const char32_t* szVal) + { + clear(); + eValType = EValType::val_type_u32string; + new (&ss32Val) u32string(szVal); + } + + inline void any_t::set(const wchar_t* szVal) + { + clear(); + eValType = EValType::val_type_wstring; + new (&sswVal) wstring(szVal); + } + + inline void any_t::set(const std::string& rssVal) + { + clear(); + eValType = EValType::val_type_string; + new (&ssVal) string(rssVal); + } + + inline void any_t::set(const std::u16string& rssVal) { clear(); eValType = EValType::val_type_u16string; - new (&ss16Val) u16string(rss16ValParam); + new (&ss16Val) u16string(rssVal); } - /** - * @brief Set the string value (specialization) - * @param[in] psz16ValParam The string value. - */ - template <> - inline void any_t::set(const char16_t* psz16ValParam) - { - clear(); - eValType = EValType::val_type_u16string; - new (&ss16Val) u16string(psz16ValParam ? psz16ValParam : u""); - } - - /** - * @brief Set the string value (specialization) - * @param[in] rss32ValParam The string value. - */ - template <> - inline void any_t::set(const u32string& rss32ValParam) + inline void any_t::set(const std::u32string& rssVal) { clear(); eValType = EValType::val_type_u32string; - new (&ss32Val) u32string(rss32ValParam); + new (&ss32Val) u32string(rssVal); } - /** - * @brief Set the string value (specialization) - * @param[in] rss32ValParam The string value. - */ - template <> - inline void any_t::set(const std::u32string& rss32ValParam) - { - clear(); - eValType = EValType::val_type_u32string; - new (&ss32Val) u32string(rss32ValParam); - } - - /** - * @brief Set the string value (specialization) - * @param[in] psz32ValParam The string value. - */ - template <> - inline void any_t::set(const char32_t* psz32ValParam) - { - clear(); - eValType = EValType::val_type_u32string; - new (&ss32Val) u32string(psz32ValParam ? psz32ValParam : U""); - } - - /** - * @brief Set the string value (specialization) - * @param[in] rsswValParam The string value. - */ - template <> - inline void any_t::set(const wstring& rsswValParam) + inline void any_t::set(const std::wstring& rssVal) { clear(); eValType = EValType::val_type_wstring; - new (&sswVal) wstring(rsswValParam); + new (&sswVal) wstring(rssVal); } - /** - * @brief Set the string value (specialization) - * @param[in] rsswValParam The string value. - */ - template <> - inline void any_t::set(const std::wstring& rsswValParam) + inline void any_t::set(const std::filesystem::path& rpathVal) { - clear(); - eValType = EValType::val_type_wstring; - new (&sswVal) wstring(rsswValParam); + set(rpathVal.generic_u8string()); } - /** - * @brief Set the string value (specialization) - * @param[in] pszwValParam The string value. - */ - template <> - inline void any_t::set(const wchar_t* pszwValParam) - { - clear(); - eValType = EValType::val_type_wstring; - new (&sswVal) wstring(pszwValParam ? pszwValParam : L""); - } - - /** - * @brief Set the interface value (specialization) - * @param[in] ifcValParam The interface value. - */ - template <> inline void any_t::set(interface_t ifcValParam) { clear(); @@ -878,8 +1114,7 @@ namespace sdv new (&ifcVal) interface_t(ifcValParam); } - // Assignment already covered by operator=(uint64) - //template <> + // Assignment already covered by set(uint64) //inline void any_t::set(interface_id idIfcValParam) //{ // clear(); @@ -887,8 +1122,7 @@ namespace sdv // idIfcVal = idIfcValParam; //} - // Assignment already covered by operator=(uint64) - //template <> + // Assignment already covered by set(uint64) //inline void any_t::set(exception_id idExceptValParam) //{ // clear(); @@ -896,6 +1130,13 @@ namespace sdv // idExceptVal = idExceptValParam; //} + template + void any_t::set(TEnum eVal) + { + static_assert(std::is_enum_v, "The type is not supported to be converted to sdv::any_t!"); + set(static_cast>(eVal)); + } + template inline void any_t::set(TType tVal, EValType eValTypeParam) { @@ -935,37 +1176,42 @@ namespace sdv template inline TType any_t::get() const { - TType tVal; - switch (eValType) + if constexpr (std::is_enum_v) + return static_cast(get>()); + else { - case EValType::val_type_bool: convert(bVal, tVal); break; - case EValType::val_type_int8: convert(i8Val, tVal); break; - case EValType::val_type_uint8: convert(ui8Val, tVal); break; - case EValType::val_type_int16: convert(i16Val, tVal); break; - case EValType::val_type_uint16: convert(ui16Val, tVal); break; - case EValType::val_type_int32: convert(i32Val, tVal); break; - case EValType::val_type_uint32: convert(ui32Val, tVal); break; - case EValType::val_type_int64: convert(i64Val, tVal); break; - case EValType::val_type_uint64: convert(ui64Val, tVal); break; - case EValType::val_type_char: convert(cVal, tVal); break; - case EValType::val_type_char16: convert(c16Val, tVal); break; - case EValType::val_type_char32: convert(c32Val, tVal); break; - case EValType::val_type_wchar: convert(cwVal, tVal); break; - case EValType::val_type_float: convert(fVal, tVal); break; - case EValType::val_type_double: convert(dVal, tVal); break; - case EValType::val_type_long_double: convert(ldVal, tVal); break; - //case EValType::val_type_fixed: convert(fixValue, tVal); break; - case EValType::val_type_string: convert(ssVal, tVal); break; - case EValType::val_type_u8string: convert(ss8Val, tVal); break; - case EValType::val_type_u16string: convert(ss16Val, tVal); break; - case EValType::val_type_u32string: convert(ss32Val, tVal); break; - case EValType::val_type_wstring: convert(sswVal, tVal); break; - case EValType::val_type_interface: convert(ifcVal, tVal); break; - case EValType::val_type_interface_id: convert(idIfcVal, tVal); break; - case EValType::val_type_exception_id: convert(idExceptVal, tVal); break; - default: tVal = TType(); break; + TType tVal; + switch (eValType) + { + case EValType::val_type_bool: convert(bVal, tVal); break; + case EValType::val_type_int8: convert(i8Val, tVal); break; + case EValType::val_type_uint8: convert(ui8Val, tVal); break; + case EValType::val_type_int16: convert(i16Val, tVal); break; + case EValType::val_type_uint16: convert(ui16Val, tVal); break; + case EValType::val_type_int32: convert(i32Val, tVal); break; + case EValType::val_type_uint32: convert(ui32Val, tVal); break; + case EValType::val_type_int64: convert(i64Val, tVal); break; + case EValType::val_type_uint64: convert(ui64Val, tVal); break; + case EValType::val_type_char: convert(cVal, tVal); break; + case EValType::val_type_char16: convert(c16Val, tVal); break; + case EValType::val_type_char32: convert(c32Val, tVal); break; + case EValType::val_type_wchar: convert(cwVal, tVal); break; + case EValType::val_type_float: convert(fVal, tVal); break; + case EValType::val_type_double: convert(dVal, tVal); break; + case EValType::val_type_long_double: convert(ldVal, tVal); break; + //case EValType::val_type_fixed: convert(fixValue, tVal); break; + case EValType::val_type_string: convert(ssVal, tVal); break; + case EValType::val_type_u8string: convert(ss8Val, tVal); break; + case EValType::val_type_u16string: convert(ss16Val, tVal); break; + case EValType::val_type_u32string: convert(ss32Val, tVal); break; + case EValType::val_type_wstring: convert(sswVal, tVal); break; + case EValType::val_type_interface: convert(ifcVal, tVal); break; + case EValType::val_type_interface_id: convert(idIfcVal, tVal); break; + case EValType::val_type_exception_id: convert(idExceptVal, tVal); break; + default: tVal = TType(); break; + } + return tVal; } - return tVal; } namespace internal @@ -994,7 +1240,8 @@ namespace sdv std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v) + std::is_same_v || std::is_same_v || + std::is_same_v) return ETypeClass::string; else return ETypeClass::other; @@ -1053,23 +1300,29 @@ namespace sdv */ static TDstType convert(TSrcType tVal) { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + return {}; + else if constexpr (std::is_same_v) { - char sz[] = { tVal, '\0' }; + char sz[] = {tVal, '\0'}; return MakeString(sz); - } else if constexpr (std::is_same_v) + } + else if constexpr (std::is_same_v) { - char16_t sz[] = { tVal, u'\0' }; + char16_t sz[] = {tVal, u'\0'}; return MakeString(sz); - } else if constexpr (std::is_same_v) + } + else if constexpr (std::is_same_v) { - char32_t sz[] = { tVal, U'\0' }; + char32_t sz[] = {tVal, U'\0'}; return MakeString(sz); - } else if constexpr (std::is_same_v) + } + else if constexpr (std::is_same_v) { - wchar_t sz[] = { tVal, L'\0' }; + wchar_t sz[] = {tVal, L'\0'}; return MakeString(sz); - } else + } + else return MakeString(std::to_string(tVal)); } }; @@ -1114,22 +1367,27 @@ namespace sdv { /** * @brief Convert from one string (SDV, C or C++) to another string (SDV). - * @param[in] tVal The source string. + * @param[in] rssVal The source string. * @return The SDV target string. */ - static TDstType convert(TSrcType tVal) + static TDstType convert(const TSrcType& rssVal) { if constexpr (std::is_pointer_v) { using TSrcCharType = std::remove_const_t>; - return MakeString(string_base(tVal)); + return MakeString(string_base(rssVal)); } + else if constexpr (std::is_same_v> && + std::is_same_v>) + return MakeString(rssVal); else if constexpr (std::is_same_v>) - return MakeString(tVal); + return MakeString(rssVal); else if constexpr (std::is_same_v>) - return MakeString(tVal); + return MakeString(rssVal); + else if constexpr (std::is_same_v) + return MakePath(rssVal); else - return MakeString(tVal); + return MakeString(rssVal); } }; } @@ -1181,9 +1439,14 @@ namespace sdv static constexpr bool bStdString = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; + /// Is the provided type a STD path? + /// @tparam The type to check. + template + static constexpr bool bStdPath = std::is_same_v; + /// Are both types a SDV or STD string or one of the type SDV or STD string and the other C string? - static constexpr bool bString = (bSdvString || bStdString) && - (bSdvString || bStdString); + static constexpr bool bString = (bSdvString || bStdString || bStdPath) && + (bSdvString || bStdString || bStdPath); /// Type to use when any of these conditions occur. using TType = std::conditional_t) + return Compare(rtVal1.generic_u8string(), rtVal2); + else if constexpr (std::is_same_v) + return Compare(rtVal1, rtVal2.generic_u8string()); else if constexpr (eType == any_t::ECompareType::compare_equal) return static_cast>(rtVal1) == static_cast>(rtVal2); @@ -1353,13 +1620,21 @@ namespace sdv template inline bool operator==(const sdv::any_t& ranyVal1, TType tVal2) { - return ranyVal1.Compare(tVal2); + if constexpr (std::is_enum_v) + return ranyVal1.Compare, any_t::ECompareType::compare_equal>( + static_cast>(tVal2)); + else + return ranyVal1.Compare(tVal2); } template inline bool operator==(TType tVal1, const sdv::any_t& ranyVal2) { - return ranyVal2.Compare(tVal1); + if constexpr (std::is_enum_v) + return ranyVal2.Compare, any_t::ECompareType::compare_equal>( + static_cast>(tVal1)); + else + return ranyVal2.Compare(tVal1); } inline bool operator==(const sdv::any_t& ranyVal1, const sdv::any_t& ranyVal2) @@ -1370,13 +1645,21 @@ namespace sdv template inline bool operator!=(const sdv::any_t& ranyVal1, TType tVal2) { - return ranyVal1.Compare(tVal2); + if constexpr (std::is_enum_v) + return ranyVal1.Compare, any_t::ECompareType::compare_inequal>( + static_cast>(tVal2)); + else + return ranyVal1.Compare(tVal2); } template inline bool operator!=(TType tVal1, const sdv::any_t& ranyVal2) { - return ranyVal2.Compare(tVal1); + if constexpr (std::is_enum_v) + return ranyVal2.Compare, any_t::ECompareType::compare_inequal>( + static_cast>(tVal1)); + else + return ranyVal2.Compare(tVal1); } inline bool operator!=(const sdv::any_t& ranyVal1, const sdv::any_t& ranyVal2) diff --git a/export/support/app_control.h b/export/support/app_control.h index a5d3cb8..77135ed 100644 --- a/export/support/app_control.h +++ b/export/support/app_control.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 SDV_APP_CONTROL_H #define SDV_APP_CONTROL_H @@ -18,8 +31,8 @@ namespace sdv { public: /** - * @brief Default constructor; doesn't start the application control. - */ + * @brief Default constructor; doesn't start the application control. + */ CAppControl() = default; /** @@ -256,7 +269,7 @@ namespace sdv /** * @brief Get the application context. * @return The application context. - */ + */ EAppContext GetAppContext() const { return m_eContext; @@ -265,7 +278,7 @@ namespace sdv /** * @brief Get the core instance ID. * @return The core instance ID. - */ + */ uint32_t GetInstanceID() const { return m_uiInstanceID; @@ -292,9 +305,9 @@ namespace sdv } /** - * @brief Set the operation to config mode. - * @pre The system is operating in running mode. - */ + * @brief Set the operation to config mode. + * @pre The system is operating in running mode. + */ void SetConfigMode() { IAppOperation* pAppOperation = core::GetObject("AppControlService"); @@ -330,7 +343,7 @@ namespace sdv * @param[in] ssFilename Path to the file containing the configuration (TOML). The path can be absolute as well as relative. * In case a relative path is provided, the current directory is searched as well as all directories supplied through * the AddConfigSearchDir function. - * @return Returns 'true' on success; 'false' otherwise. + * @return Returns a config process result enum value. */ core::EConfigProcessResult LoadConfig(/*in*/ const sdv::u8string& ssFilename) { @@ -346,6 +359,52 @@ namespace sdv return eResult; } + /** + * @brief Save a configuration file pointed to by the provided file path. All components are saved that were added after + * the last baseline with the configuration specific settings. + * @remarks The function will only save when the configuration has changed. + * @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 (or no changes detected); 'false' otherwise. + */ + bool SaveConfig(/*in*/ sdv::u8string ssConfigPath) const + { + const core::IConfig* pAppConfig = nullptr; + sdv::TInterfaceAccessPtr ptrConfigObj = core::GetObject("ConfigService"); + if (ptrConfigObj) pAppConfig = ptrConfigObj.GetInterface(); + if (!pAppConfig) return false; + return pAppConfig->SaveConfig(ssConfigPath); + } + + /** + * @brief Generate the configuration TOML string. + * @return The generated configuration string. + */ + sdv::u8string GenerateConfigString() const + { + const core::IConfig* pAppConfig = nullptr; + sdv::TInterfaceAccessPtr ptrConfigObj = core::GetObject("ConfigService"); + if (ptrConfigObj) pAppConfig = ptrConfigObj.GetInterface(); + if (!pAppConfig) return {}; + return pAppConfig->GenerateConfigString(); + } + + /** + * @brief Close the current configuration. + * @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. + */ + void CloseConfig() + { + core::IConfig* pAppConfig = nullptr; + sdv::TInterfaceAccessPtr ptrConfigObj = core::GetObject("ConfigService"); + if (ptrConfigObj) pAppConfig = ptrConfigObj.GetInterface(); + if (!pAppConfig) return; + pAppConfig->CloseConfig(); + } + /** * @brief Add a search path to a folder where a config file can be found. * @param[in] rpathDir Reference to the relative or absolute path to an existing folder. diff --git a/export/support/component_impl.h b/export/support/component_impl.h index 26e25a1..2ef9834 100644 --- a/export/support/component_impl.h +++ b/export/support/component_impl.h @@ -1,13 +1,16 @@ -/** +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG * - * @file component_impl.h - * @brief This file provides all necessary definitions to implement SDV object. - * @version 0.1 - * @date 2022.11.14 - * @author Thomas.pfleiderer@zf.com - * @copyright Copyright ZF Friedrichshaven AG (c) 2022 + * 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 COMPONENT_IMPL_H #define COMPONENT_IMPL_H @@ -15,12 +18,17 @@ #include #include #include +#include +#include #include "../interfaces/core_types.h" #include "../interfaces/core.h" #include "../interfaces/repository.h" +#include "sdv_core.h" #include "interface_ptr.h" #include "local_service_access.h" +#include "param_impl.h" +#include "toml.h" // NOLINTBEGIN(cppcoreguidelines-macro-usage) @@ -33,7 +41,7 @@ */ #define SDV_STRINGIZE_HELPER(txt) SDV_STRINGIZE_HELPER2(txt) - // NOLINTEND(cppcoreguidelines-macro-usage) +// NOLINTEND(cppcoreguidelines-macro-usage) #ifdef _WIN32 @@ -72,7 +80,6 @@ namespace sdv // Forward declarations class CSdvObject; - //////////////////////////////////// // Object control classes // //////////////////////////////////// @@ -88,7 +95,51 @@ namespace sdv * @brief Get the class information struct. * @return Returns the class information struct. */ - virtual sdv::SClassInfo GetClassInfo() const = 0; + virtual SClassInfo GetClassInfo() const = 0; + + /** + * @brief Get the object class name. + * @return Returns the class name of the object. + * @remarks Default implementation gets the information from the object class if not defined in the definition. + */ + virtual u8string GetClassName() const = 0; + + /** + * @brief Get the class name aliases. + * @return Returns a sequence containing zero or more class name aliases. + */ + virtual sequence GetClassAliases() const = 0; + + /** + * @brief Get the default object name. + * @return Returns the default name of the object if exists. + */ + virtual u8string GetDefaultObjectName() const = 0; + + /** + * @brief Get the default config TOML string. + * @param[in] rssTablePrefix Reference to the string containing a table prefix to insert as part of the table key. + * @return Returns the TOML string containing the default configuration of the object if exists. + */ + virtual u8string GetDefaultConfig(const u8string& rssTablePrefix = {}) const = 0; + + /** + * @brief Get the object type. + * @return Returns the type of the object + */ + virtual EObjectType GetObjectType() const = 0; + + /** + * @brief Get the object creation flags. + * @return Returns the current flags of the object + */ + virtual uint32_t GetObjectFlags() const = 0; + + /** + * @brief Get object dependencies. + * @return Returns a vector containing the class names of the objects this component is dependent on. + */ + virtual sequence GetObjectDependencies() const = 0; /** * @brief Create the SDV object. @@ -265,7 +316,7 @@ namespace sdv } /** - * @brief Gets the amount of active SDV objects. + * @brief Get the amount of active SDV objects. * @return Returns the number of active SDV objects. */ uint32_t GetActiveObjects() const @@ -284,6 +335,19 @@ namespace sdv return m_ssManifest.c_str(); } + /** + * @brief The object class names implemented in the object. Overload of sdv::IObjectFactory::GetClassNames. + * @return Sequence with object class names string. + */ + virtual sequence GetClassNames() const override; + + /** + * @brief Get the class information. Overload of sdv::IObjectFactory::GetClassInfo. + * @param[in] ssClassName The name of the class object to get the class information for. + * @return Returns the class information struct. + */ + virtual SClassInfo GetClassInfo(const u8string& ssClassName) const override; + /** * @brief Create or get the object using the name from the object class info. Overload of * sdv::IObjectFactory::CreateObject. @@ -291,7 +355,7 @@ namespace sdv * @param[in] ssClassName The name of the class object to instantiate. * @return Pointer to IInterfaceAccess interface of the object or NULL when the requested object doesn't exist. */ - virtual IInterfaceAccess* CreateObject(const sdv::u8string& ssClassName) override; + virtual IInterfaceAccess* CreateObject(const u8string& ssClassName) override; /** * @brief Destroy an instantiated object using the name of the object class info. Overload of @@ -309,147 +373,31 @@ namespace sdv /** * @brief Build the module manifest. */ - void BuildManifest() - { - std::stringstream sstream; - sstream << R"code(# Module manifest - -[Interface] -Version = )code" << SDVFrameworkInterfaceVersion - << R"code( - -)code"; - std::shared_lock lock(m_mtxObjectClasses); - for (const ISdvObjectClassInfo* pClassInfo : m_vecObjectClasses) - { - sdv::SClassInfo sInfo = pClassInfo->GetClassInfo(); - sstream << "[[Component]]" << std::endl; - sstream << "Class=\"" << sInfo.ssClassName << "\"" << std::endl; - if (!sInfo.seqClassAliases.empty()) - { - sstream << "Aliases=["; - bool bInitialAlias = true; - for (const sdv::u8string& rssAlias : sInfo.seqClassAliases) - { - if (!bInitialAlias) - sstream << ", "; - bInitialAlias = false; - sstream << "\"" << rssAlias << "\""; - } - sstream << "]" << std::endl; - } - if (!sInfo.ssDefaultObjectName.empty()) - sstream << "DefaultName=\"" << sInfo.ssDefaultObjectName << "\"" << std::endl; - bool bSkip = false; - switch (sInfo.eType) - { - case sdv::EObjectType::SystemObject: - sstream << "Type=\"" - << "System" - << "\"" << std::endl; - break; - case sdv::EObjectType::Device: - sstream << "Type=\"" - << "Device" - << "\"" << std::endl; - break; - case sdv::EObjectType::BasicService: - sstream << "Type=\"" - << "BasicService" - << "\"" << std::endl; - break; - case sdv::EObjectType::ComplexService: - sstream << "Type=\"" - << "ComplexService" - << "\"" << std::endl; - break; - case sdv::EObjectType::Application: - sstream << "Type=\"" - << "App" - << "\"" << std::endl; - break; - case sdv::EObjectType::Proxy: - sstream << "Type=\"" - << "Proxy" - << "\"" << std::endl; - break; - case sdv::EObjectType::Stub: - sstream << "Type=\"" - << "Stub" - << "\"" << std::endl; - break; - case sdv::EObjectType::Utility: - sstream << "Type=\"" - << "Utility" - << "\"" << std::endl; - break; - default: - bSkip = true; - break; - } - if (bSkip) - continue; - if (sInfo.uiFlags & static_cast(sdv::EObjectFlags::singleton)) - sstream << "Singleton=true" << std::endl; - if (!sInfo.seqDependencies.empty()) - { - sstream << "Dependencies=["; - bool bInitialDependency = true; - for (const sdv::u8string& rssDependsOn : sInfo.seqDependencies) - { - if (!bInitialDependency) - sstream << ", "; - bInitialDependency = false; - sstream << "\"" << rssDependsOn << "\""; - } - sstream << "]" << std::endl; - } - } - m_ssManifest = sstream.str(); - } + void BuildManifest(); /** * @brief Expose the object prototype by placing it into the object prototype list. * @details Expose the object class to allow class access. This function is called by the constructor of the * class object. - * @param[in] pObjectClassInfo The object class internal interface. + * @param[in] pClassInfo The object class internal interface. */ - void ExposeObjectClass(ISdvObjectClassInfo* pObjectClassInfo) - { - if (pObjectClassInfo) - { - std::unique_lock lock(m_mtxObjectClasses); - m_vecObjectClasses.push_back(pObjectClassInfo); - - // Attention: pObjectClass is a pointer to ISdvObjectClassInfo even if the class was derived. - // Virtual functions are not available yet at this stage. - } - } + void ExposeObjectClass(ISdvObjectClassInfo* pClassInfo); /** * @brief Revoke the object prototype from the object prototype list. - * @param[in] pObjectClassInfo The object class internal interface. + * @param[in] pClassInfo The object class internal interface. */ - void RevokeObjectClass(const ISdvObjectClassInfo* pObjectClassInfo) - { - std::unique_lock lock(m_mtxObjectClasses); - auto itObjectClass = std::find(m_vecObjectClasses.begin(), m_vecObjectClasses.end(), pObjectClassInfo); - if (itObjectClass != m_vecObjectClasses.end()) - m_vecObjectClasses.erase(itObjectClass); - // TODO EVE: Updated through cppcheck warning - //for (auto objectClassIter = m_vecObjectClasses.begin(); objectClassIter != m_vecObjectClasses.end(); - // objectClassIter++) - //{ - // if (*objectClassIter == pObjectClassInfo) - // { - // m_vecObjectClasses.erase(objectClassIter); - // break; - // } - //} - } + void RevokeObjectClass(const ISdvObjectClassInfo* pClassInfo); + + /** + * @brief Quote the provided text following the string rules of TOML. + * @param[in] rssText Reference to the text to quote. + * @return The quoted text string. + */ + static std::string QuoteText(const std::string& rssText); private: - std::atomic m_uiActiveObjectCount{0}; ///< The amount of active SDV objects. + std::atomic m_uiActiveObjectCount{0}; ///< The amount of active SDV objects. mutable std::mutex m_mtxActiveObjects; ///< Synchronize access to m_vecActiveObjects std::vector> m_vecActiveObjects; ///< List of objects actively kept alive @@ -471,9 +419,7 @@ Version = )code" << SDVFrameworkInterfaceVersion * The instance is created by the DEFINE_SDV_OBJECT macro and the macro also registers a helper capable of * instantiating the SDV object with the instance (see CObjectFactory for details) */ - class CModule - : public CSdvObjectAccess - , public CObjectFactory + class CModule : public CSdvObjectAccess, public CObjectFactory { public: /** @@ -530,8 +476,7 @@ Version = )code" << SDVFrameworkInterfaceVersion * @tparam TSdvObject Class type of the SDV object derived from CSdvObject. */ template - class CSdvObjectClass - : public ISdvObjectClassInfo + class CSdvObjectClass : public ISdvObjectClassInfo { public: /** @@ -557,117 +502,161 @@ Version = )code" << SDVFrameworkInterfaceVersion } /** - * @brief Gets the object class name. + * @brief Get the class information struct. Overload of ISdvObjectClassInfo::GetClassInfo. + * @return Returns the class information struct. + */ + SClassInfo GetClassInfo() const override + { + SClassInfo sClassInfo{}; + sClassInfo.ssName = GetClassName(); + sClassInfo.seqClassAliases = GetClassAliases(); + sClassInfo.ssDefaultObjectName = GetDefaultObjectName(); + sClassInfo.ssDefaultConfig = GetDefaultConfig("Parameters"); + sClassInfo.eType = GetObjectType(); + sClassInfo.uiFlags = GetObjectFlags(); + sClassInfo.seqDependencies = GetObjectDependencies(); + return sClassInfo; + } + + /** + * @brief Get the object class name. Overload of ISdvObjectClassInfo::GetClassName. * @return Returns the class name of the object. * @remarks Default implementation gets the information from the object class if not defined in the definition. */ - virtual sdv::u8string GetClassName() const + virtual u8string GetClassName() const override { return TSdvObject::GetClassNameStatic(); } /** - * @brief Gets the class name aliases. + * @brief Get the class name aliases. Overload of ISdvObjectClassInfo::GetClassAliases. * @return Returns a sequence containing zero or more class name aliases. * @remarks Default implementation gets the information from the object class if not defined in the definition. */ - virtual sdv::sequence GetClassAliases() const + virtual sequence GetClassAliases() const override { return TSdvObject::GetClassAliasesStatic(); } /** - * @brief Gets the default object name. + * @brief Get the default config TOML string. Overload of ISdvObjectClassInfo::GetDefaultConfig. + * @param[in] rssTablePrefix Reference to the string containing a table prefix to insert as part of the table key. + * @return Returns the TOML string containing the default configuration of the object if exists. + */ + virtual u8string GetDefaultConfig(const u8string& rssTablePrefix /*= {}*/) const override + { + std::vector> vecParaminfo = TSdvObject::GetParamMapInfoStatic(); + std::stringstream sstream; + u8string ssGroup; + for (const auto& ptrParam : vecParaminfo) + { + if (!ptrParam) continue; + + // Only describe readable and non-temporary parameters + if (ptrParam->ReadOnly() || ptrParam->Temporary()) continue; + + // Is there a value? If not, skip the parameter + if (ptrParam->DefaultVal().empty()) continue; + + // Need a new group? + if (ptrParam->Group() != ssGroup) + { + if (sstream.rdbuf()->in_avail()) + sstream << std::endl; + sstream << "["; + if (!rssTablePrefix.empty()) + sstream << rssTablePrefix << "."; + sstream << ptrParam->Group() << "]" << std::endl; + ssGroup = ptrParam->Group(); + } + + // Store the parameter + // Quotation of parameter name needed? + u8string ssName; + if (ptrParam->Name().find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-") + != u8string::npos) + ssName = CObjectFactory::QuoteText(ptrParam->Name()); + else + ssName = ptrParam->Name(); + sstream << ssName << " = "; + if (ptrParam->Numeric() || ptrParam->Bitmask() || ptrParam->Enum()) + { + std::string ssValue = ptrParam->DefaultVal(); + sstream << (ssValue.empty() ? "0" : ssValue); + } + else if (ptrParam->String()) + sstream << CObjectFactory::QuoteText(ptrParam->DefaultVal()); + else if (ptrParam->Boolean()) + sstream << (ptrParam->DefaultVal().get() ? "true" : "false"); + sstream << std::endl; + } + return sstream.str(); + } + + /** + * @brief Get the default object name. Overload of ISdvObjectClassInfo::GetDefaultObjectName. * @return Returns the default name of the object if exists. */ - virtual sdv::u8string GetDefaultObjectName() const + virtual u8string GetDefaultObjectName() const override { return TSdvObject::GetDefaultObjectNameStatic(); } /** - * @brief Is the object marked as singleton. - * @return Returns whether the object is a singleton object. - */ - virtual bool IsSingleton() const - { - return TSdvObject::IsSingletonStatic(); - } - - /** - * @brief Get object dependencies. - * @return Returns a vector containing the class names of the objects this component is dependent on. - */ - virtual sdv::sequence GetObjectDependencies() const - { - return TSdvObject::GetObjectDependenciesStatic(); - } - - /** - * @brief Gets the object type. + * @brief Get the object type. Overload of ISdvObjectClassInfo::GetObjectType. * @return Returns the type of the object * @remarks Default implementation gets the information from the object class if not defined in the definition. */ - virtual EObjectType GetObjectType() const + virtual EObjectType GetObjectType() const override { return TSdvObject::GetObjectType(); } - protected: /** - * @brief Helper function to retrieve the object creation flags. + * @brief Get the object creation flags. Overload of ISdvObjectClassInfo::GetObjectFlags. * @return Returns the current flags of the object */ - uint32_t GetObjectFlags() const + uint32_t GetObjectFlags() const override { - uint32_t flags = 0; + uint32_t uiFlags = 0; - if (IsSingleton()) flags |= static_cast(EObjectFlags::singleton); + if (TSdvObject::IsSingletonStatic()) + uiFlags |= static_cast(EObjectFlags::singleton); - // Currently no other flags known. + // Currently no other flags defined. - return flags; + return uiFlags; } /** - * @brief Get the class information struct. Overload of ISdvObjectClassInfo::GetClassInfo. - * @return Returns the class information struct. + * @brief Get object dependencies. Overload of ISdvObjectClassInfo::GetObjectDependencies. + * @return Returns a vector containing the class names of the objects this component is dependent on. */ - sdv::SClassInfo GetClassInfo() const override + virtual sequence GetObjectDependencies() const override { - sdv::SClassInfo classInfo{}; - classInfo.ssClassName = GetClassName(); - classInfo.seqClassAliases = GetClassAliases(); - classInfo.ssDefaultObjectName = GetDefaultObjectName(); - classInfo.eType = GetObjectType(); - classInfo.uiFlags = GetObjectFlags(); - classInfo.seqDependencies = GetObjectDependencies(); - return classInfo; + return TSdvObject::GetObjectDependenciesStatic(); } /** * @brief Create the SDV object. Overload of ISdvObjectClassInfo::CreateObject. * @return Returns an instance of the SDV object or nullptr when the object could not be created (exception thrown during construction). */ - std::unique_ptr CreateObject() override - { - std::unique_ptr ret; - try - { - ret = std::make_unique(); - } - catch(...) - { - SDV_LOG(core::ELogSeverity::error, "Failed to instantiate object of class ", GetClassName(), " - exception thrown during construction! "); - } - return ret; - } + virtual std::unique_ptr CreateObject() override; }; /** * @brief SDV object base class. + * @details The SDV object class implements all base functionality for is instantiating and controlling the SDV object. A SDV + * object implementation can derive from this class and overload the following events to control object behavior: + * - OnPreInitialize - prepare for initialization, no object configuration loaded yet. + * - OnInitialize - initialize; object configuration is loaded and stored in the parameters. + * - OnChangeToConfigMode - change to configuration mode; shutdown running instances. + * - OnChangeToRunningMode - change to running mode, interpret configuration parameters and start running instances. + * - OnShutdown - shutdown the object + * - OnParamChanged - parameter has changed + * - OnParamFlagChanged - parameter flag has changed */ - class CSdvObject : public CSdvObjectAccess + class CSdvObject : public CSdvParamMap, public CSdvObjectAccess, public IObjectControl { public: /** @@ -692,17 +681,271 @@ Version = )code" << SDVFrameworkInterfaceVersion ~CSdvObject() override {} + /** + * @brief Initialize the object. Overload of IObjectControl::Initialize. + * @param[in] ssObjectConfig Optional configuration string. + */ + virtual void Initialize(/*in*/ const u8string& ssObjectConfig) override + { + // Not started before or ended completely? + if (GetObjectState() != EObjectState::initialization_pending && GetObjectState() != EObjectState::destruction_pending) + { + m_eObjectState = EObjectState::runtime_error; + return; + } + + // Initializing + m_eObjectState = EObjectState::initializing; + + // Initialize the parameter map. + InitParamMap(); + + // Copy the configuration + m_ssObjectConfig = ssObjectConfig; + + // Prepare initialization + OnPreInitialize(); + + // Parse the object configuration if there is any. + if (!ssObjectConfig.empty()) + { + try + { + // Parse the config TOML. + toml::CTOMLParser parser(ssObjectConfig); + if (!parser.IsValid()) + { + m_eObjectState = EObjectState::config_error; + return; + } + + // Read group function (iterative) + u8string ssGroup; + std::function fnReadGroup; + fnReadGroup = [&](const toml::CNodeCollection& rTable) + { + // Iterate through the config. + for (size_t n = 0; n < rTable.GetCount(); n++) + { + const toml::CNode node = rTable.Get(n); + switch (node.GetType()) + { + case toml::ENodeType::node_integer: + case toml::ENodeType::node_floating_point: + case toml::ENodeType::node_boolean: + case toml::ENodeType::node_string: + // Ignore the result. If it is not possible to set the parameter, this is not a failure. + SetParam(ssGroup + node.GetName(), node.GetValue()); + break; + case toml::ENodeType::node_table: + { + const toml::CNodeCollection sub_table= node; + if (!sub_table.IsValid()) break; + ssGroup += sub_table.GetName() + "."; + fnReadGroup(sub_table); + } + default: + // Other node types cannot be processed. + break; + } + } + }; + + // Parse the root node + fnReadGroup(parser); + } catch (const toml::XTOMLParseException& rexcept) + { + SDV_LOG(core::ELogSeverity::error, "Cannot process object configuration TOML: ", rexcept.what()); + m_eObjectState = EObjectState::initialization_failure; + return; + } + } + + // Let the object initialize itself. + if (!OnInitialize()) + { + m_eObjectState = EObjectState::initialization_failure; + return; + } + + // Initialization is complete. + m_eObjectState = EObjectState::initialized; + } + + /** + * @brief Get the current state of the object. Overload of IObjectControl::GetObjectState. + * @return Return the current state of the object. + */ + virtual EObjectState GetObjectState() const override + { + return m_eObjectState; + } + + /** + * @brief Set the component operation mode. Overload of IObjectControl::SetOperationMode. + * @param[in] eMode The operation mode, the component should run in. + */ + virtual void SetOperationMode(/*in*/ EOperationMode eMode) override + { + switch (eMode) + { + case EOperationMode::configuring: + // Allowed? + if (GetObjectState() != EObjectState::initialized && GetObjectState() != EObjectState::running && + GetObjectState() != EObjectState::runtime_error && GetObjectState() != EObjectState::config_error) + break; + + // Inform derived class + OnChangeToConfigMode(); + + // Unlock the parameter map. + UnlockParamMap(); + + m_eObjectState = EObjectState::configuring; + break; + case EOperationMode::running: + // Allowed? + if (GetObjectState() != EObjectState::initialized && GetObjectState() != EObjectState::configuring && + GetObjectState() != EObjectState::config_error) + break; + + // Lock the parameter map. + LockParamMap(); + + // Inform the derived class + if (!OnChangeToRunningMode()) + { + // Cannot enter running mode. Reconfiguration needed. + m_eObjectState = EObjectState::config_error; + UnlockParamMap(); + break; + } + + m_eObjectState = EObjectState::running; + break; + default: + // Wrong operation mode. + m_eObjectState = EObjectState::runtime_error; + break; + } + } + + /** + * @brief Get the object configuration for persistence. + * @return The object configuration as TOML string. + */ + virtual u8string GetObjectConfig() const override + { + // During the initialization, return the stored object configuration. + if (m_eObjectState == EObjectState::initializing || m_eObjectState == EObjectState::initialization_pending) + return m_ssObjectConfig; + + // Split path function (splits the group from the parameter names) + auto fnSplitPath = [](const u8string& rssPath) -> std::pair + { + size_t nPos = rssPath.find_last_of('.'); + if (nPos == u8string::npos) + return std::make_pair("", rssPath); + return std::make_pair(rssPath.substr(0, nPos), rssPath.substr(nPos + 1)); + }; + + // Create a new configuration string from the parameters. + auto seqParameters = GetParamPaths(); + if (seqParameters.empty()) return {}; + + // Iterate through the list of parameter names and create the TOML entries for it. + u8string ssGroup; + toml::CTOMLParser parser(""); + toml::CNodeCollection table(parser); + for (auto ssParamPath : seqParameters) + { + // Get the parameter object + auto ptrParam = FindParamObject(ssParamPath); + + // Read only and temporary parameters are not stored + if ((!ptrParam->Locked() && ptrParam->ReadOnly()) || !ptrParam->Temporary()) + continue; + + // Get the value + any_t anyValue = ptrParam->Get(); + if (anyValue.empty()) + continue; + + // Split the path in group and parameter name + auto prParam = fnSplitPath(ssParamPath); + + // Need to add a group? + if (prParam.first != ssGroup) + { + table = parser.AddTable(prParam.first); + ssGroup = prParam.first; + } + + // Store the value + table.AddValue(prParam.second, anyValue); + } + + return parser.GetTOML(); + } + + /** + * @brief Shutdown called before the object is destroyed. Overload of 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 GetObjectState should return EObjectState::destruction_pending + */ + virtual void Shutdown() override + { + // Lock the parameter map - do not allow any more changes + LockParamMap(); + + // Set status + m_eObjectState = EObjectState::shutdown_in_progress; + + // Inform derived class + OnShutdown(); + + // Set status + m_eObjectState = EObjectState::destruction_pending; + + // Unlock parameter map to allow re-initialization. + UnlockParamMap(); + } + + /** + * @brief Set the object into configuration error state. + * @pre Works only when in configuration or initialized mode. + * @post Reset by switching to configuration mode again or by trying to switch to running mode. + */ + void SetObjectIntoConfigErrorState() + { + if (m_eObjectState == EObjectState::initialized || m_eObjectState == EObjectState::configuring) + m_eObjectState = EObjectState::config_error; + } + + /** + * @brief Set the onbject into runtime error state. + * @pre Works only when in running mode. + * @post Reset by switching to configuration mode. + */ + void SetObjectIntoRuntimeErrorState() + { + if (m_eObjectState == EObjectState::running) + m_eObjectState = EObjectState::runtime_error; + } + /** * @brief Class aliases sequence if not provided. * @return An empty sequence of class aliases. */ - static sdv::sequence GetClassAliasesStatic() { return {}; } + static sequence GetClassAliasesStatic() { return {}; } /** * @brief Default object name is not set. * @return The empty default object name */ - static sdv::u8string GetDefaultObjectNameStatic() { return {}; } + static u8string GetDefaultObjectNameStatic() { return {}; } /** * @brief Return whether the object is a singleton object (only one instance will be created of the object). @@ -714,12 +957,68 @@ Version = )code" << SDVFrameworkInterfaceVersion * @brief Get object dependencies. * @return Returns a vector containing the class names of the objects this component is dependent on. */ - static sdv::sequence GetObjectDependenciesStatic() { return sdv::sequence(); } + static sequence GetObjectDependenciesStatic() { return sequence(); } + + /** + * @brief Pre-initialization event, called before loading the object configuration. Overload this function to implement + * pre-initialization functionality. + */ + virtual void OnPreInitialize() {} + + /** + * @brief Initialization event, called after object configuration was loaded. Overload this function to implement object + * initialization. + * @return Returns 'true' when the initialization was successful, 'false' when not. + */ + virtual bool OnInitialize() { return true; } + + /** + * @brief Change to configuration mode event. After this a call to this function locked parameters can be changed again. + * Overload this function to shutdown any running instances and prepare for configuration. + */ + virtual void OnChangeToConfigMode() {} + + /** + * @brief Change to running mode event. Parameters were locked before the call to this event. Overload this function to + * interpret the configuration parameters and start the running instances. + * @return Returns 'true' when the configuration is valid and the running instances could be started. Otherwise returns + * 'false'. + */ + virtual bool OnChangeToRunningMode() { return true; } + + /** + * @brief Shutdown the object. Overload this function to terminate any running instances and prepare the object for + * termination or restarting. + */ + virtual void OnShutdown() {} /** * @brief Interface map */ BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(IParameters) + SDV_INTERFACE_ENTRY(IObjectControl) + END_SDV_INTERFACE_MAP() + + private: + std::atomic m_eObjectState = EObjectState::initialization_pending; ///< Object status + std::string m_ssObjectConfig; ///< Copy of the configuration TOML. + }; + + /** + * @brief The object control wrapper class ensures, that the CSdvObject interface map is always accessible. + * @tparam The class to instantiate. + */ + template + class CObjectInstance : public TSdvObject + { + static_assert(std::is_base_of_v, "TSdvObject must derive from sdv::CSdvObject!"); + + public: + // Interface map + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_CHAIN_BASE(CSdvObject) + SDV_INTERFACE_CHAIN_BASE(TSdvObject) END_SDV_INTERFACE_MAP() }; @@ -727,29 +1026,64 @@ Version = )code" << SDVFrameworkInterfaceVersion // Object control class implementation // ///////////////////////////////////////// - inline IInterfaceAccess* CObjectFactory::CreateObject(const sdv::u8string& ssClassName) + inline sequence CObjectFactory::GetClassNames() const { - if (ssClassName.empty()) + std::shared_lock lock(m_mtxObjectClasses); + sequence seqClassNames; + for (const ISdvObjectClassInfo* pClassInfo : m_vecObjectClasses) { - return nullptr; + if (!pClassInfo) continue; + seqClassNames.push_back(pClassInfo->GetClassName()); } + return seqClassNames; + } + + inline SClassInfo CObjectFactory::GetClassInfo(const u8string& ssClassName) const + { + if (ssClassName.empty()) return {}; std::shared_lock lock(m_mtxObjectClasses); - for (ISdvObjectClassInfo* pObjectClassInfo : m_vecObjectClasses) + for (ISdvObjectClassInfo* pClassInfo : m_vecObjectClasses) { - if (pObjectClassInfo == nullptr)continue; - sdv::SClassInfo sClassInfo = pObjectClassInfo->GetClassInfo(); + if (!pClassInfo) continue; // Check for the class name. - bool bFound = sClassInfo.ssClassName == ssClassName; + bool bFound = pClassInfo->GetClassName() == ssClassName; // If not found, check for all aliases. - for (auto itAlias = sClassInfo.seqClassAliases.begin(); !bFound && itAlias != sClassInfo.seqClassAliases.end(); itAlias++) + auto seqClassAliases = pClassInfo->GetClassAliases(); + for (auto itAlias = seqClassAliases.begin(); !bFound && itAlias != seqClassAliases.end(); ++itAlias) + bFound = *itAlias == ssClassName; + if (!bFound) continue; + + // Return the class information + return pClassInfo->GetClassInfo(); + } + + // No class information found + return {}; + } + + inline IInterfaceAccess* CObjectFactory::CreateObject(const u8string& ssClassName) + { + if (ssClassName.empty()) return nullptr; + + std::shared_lock lock(m_mtxObjectClasses); + for (ISdvObjectClassInfo* pClassInfo : m_vecObjectClasses) + { + if (!pClassInfo) continue; + + // Check for the class name. + bool bFound = pClassInfo->GetClassName() == ssClassName; + + // If not found, check for all aliases. + auto seqClassAliases = pClassInfo->GetClassAliases(); + for (auto itAlias = seqClassAliases.begin(); !bFound && itAlias != seqClassAliases.end(); ++itAlias) bFound = *itAlias == ssClassName; if (!bFound) continue; ++m_uiActiveObjectCount; - auto object = pObjectClassInfo->CreateObject(); + auto object = pClassInfo->CreateObject(); lock.unlock(); if(!object) { @@ -773,7 +1107,7 @@ Version = )code" << SDVFrameworkInterfaceVersion } std::unique_lock lockObjects(m_mtxActiveObjects); - for(auto iter = m_vecActiveObjects.begin(); iter!= m_vecActiveObjects.end();++iter) + for(auto iter = m_vecActiveObjects.begin(); iter != m_vecActiveObjects.end(); ++iter) { if(iter->get()->GetObjectAccess() == object) { @@ -799,18 +1133,295 @@ Version = )code" << SDVFrameworkInterfaceVersion if (m_uiActiveObjectCount) --m_uiActiveObjectCount; } } + + inline void CObjectFactory::BuildManifest() + { + // Start the manifest + std::stringstream sstream; + sstream << R"toml(# Module manifest + +[Interface] +Version = )toml" << SDVFrameworkInterfaceVersion + << std::endl + << std::endl; + + // Add all classes to the manifest + std::shared_lock lock(m_mtxObjectClasses); + for (const ISdvObjectClassInfo* pClassInfo : m_vecObjectClasses) + { + sstream << "[[Class]]" << std::endl; + sstream << "Name = " << QuoteText(pClassInfo->GetClassName()) << std::endl; + if (pClassInfo->GetClassName().empty()) + continue; + if (!pClassInfo->GetClassAliases().empty()) + { + sstream << "Aliases = ["; + bool bInitialAlias = true; + for (const u8string& rssAlias : pClassInfo->GetClassAliases()) + { + if (!bInitialAlias) + sstream << ", "; + bInitialAlias = false; + sstream << QuoteText(rssAlias); + } + sstream << "]" << std::endl; + } + if (!pClassInfo->GetDefaultObjectName().empty()) + sstream << "DefaultName = " << QuoteText(pClassInfo->GetDefaultObjectName()) << std::endl; + if (pClassInfo->GetObjectType() == EObjectType::undefined) + continue; + sstream << "Type = " << QuoteText(ObjectType2String(pClassInfo->GetObjectType())) << std::endl; + if (pClassInfo->GetObjectFlags() & static_cast(EObjectFlags::singleton)) + sstream << "Singleton = true" << std::endl; + if (!pClassInfo->GetObjectDependencies().empty()) + { + sstream << "Dependencies = ["; + bool bInitialDependency = true; + for (const u8string& rssDependsOn : pClassInfo->GetObjectDependencies()) + { + if (!bInitialDependency) + sstream << ", "; + bInitialDependency = false; + sstream << "\"" << rssDependsOn << "\""; + } + sstream << "]" << std::endl; + } + // Assign the parameter table to the class. Prepend [Class.Parameters] to every table. + std::string ssParameters = pClassInfo->GetDefaultConfig("Class.Parameters"); + if (!ssParameters.empty()) sstream << ssParameters; + } + m_ssManifest = sstream.str(); + } + + inline void CObjectFactory::ExposeObjectClass(ISdvObjectClassInfo* pClassInfo) + { + if (pClassInfo) + { + std::unique_lock lock(m_mtxObjectClasses); + m_vecObjectClasses.push_back(pClassInfo); + + // Attention: pObjectClass is a pointer to ISdvObjectClassInfo even if the class was derived. + // Virtual functions are not available yet at this stage. + } + } + + inline void CObjectFactory::RevokeObjectClass(const ISdvObjectClassInfo* pClassInfo) + { + std::unique_lock lock(m_mtxObjectClasses); + auto itObjectClass = std::find(m_vecObjectClasses.begin(), m_vecObjectClasses.end(), pClassInfo); + if (itObjectClass != m_vecObjectClasses.end()) + m_vecObjectClasses.erase(itObjectClass); + // TODO EVE: Updated through cppcheck warning + //for (auto objectClassIter = m_vecObjectClasses.begin(); objectClassIter != m_vecObjectClasses.end(); + // objectClassIter++) + //{ + // if (*objectClassIter == pClassInfo) + // { + // m_vecObjectClasses.erase(objectClassIter); + // break; + // } + //} + } + + inline std::string CObjectFactory::QuoteText(const std::string& rssText) + { + std::stringstream sstreamQuotedText; + sstreamQuotedText << "\""; + for (size_t nPos = 0; nPos < rssText.size(); nPos++) + { + uint8_t uiChar = static_cast(rssText[nPos]); + uint32_t uiUTFChar = 0; + switch (uiChar) + { + case '\'': + sstreamQuotedText << '\''; + break; // Single quote character + case '\b': + sstreamQuotedText << "\\b"; + break; // Escape backspace + case '\t': + sstreamQuotedText << "\\t"; + break; // Escape tab + case '\n': + sstreamQuotedText << "\\n"; + break; // Escape linefeed + case '\f': + sstreamQuotedText << "\\f"; + break; // Escape form feed + case '\r': + sstreamQuotedText << "\\r"; + break; // Escape carriage return + case '\"': + sstreamQuotedText << "\\\""; + break; // Escape quote + case '\\': + sstreamQuotedText << "\\\\"; + break; // Escape backslash + default: + // Check for ASCII character + if (uiChar >= 0x20 && uiChar < 0x7f) + { + // Standard ASCII + sstreamQuotedText << static_cast(uiChar); + break; + } + + // Use UNICODE escape character for the quoted text + if (uiChar <= 0x80) // One byte UTF-8 + uiUTFChar = static_cast(uiChar); + else if (uiChar <= 0xDF) // Two bytes UTF-8 + { + uiUTFChar = static_cast(uiChar & 0b00011111) << 6; + + // Expecting the next character to be between 0x80 and 0xBF + nPos++; + if (nPos >= rssText.size()) + break; + uiUTFChar |= static_cast(rssText[nPos] & 0b00111111); + } + else if (uiChar <= 0xEF) // Three bytes UTF-8 + { + uiUTFChar = static_cast(uiChar & 0b00001111) << 6; + + // Expecting the next character to be between 0x80 and 0xBF + nPos++; + if (nPos >= rssText.size()) + break; + uiUTFChar |= static_cast(rssText[nPos] & 0b00111111); + uiUTFChar <<= 6; + + // Expecting the next character to be between 0x80 and 0xBF + nPos++; + if (nPos >= rssText.size()) + break; + uiUTFChar |= static_cast(rssText[nPos] & 0b00111111); + } + else if (uiChar <= 0xF7) // Four bytes UTF-8 + { + uiUTFChar = static_cast(uiChar & 0b00000111) << 6; + + // Expecting the next character to be between 0x80 and 0xBF + nPos++; + if (nPos >= rssText.size()) + break; + uiUTFChar |= static_cast(rssText[nPos] & 0b00111111); + uiUTFChar <<= 6; + + // Expecting the next character to be between 0x80 and 0xBF + nPos++; + if (nPos >= rssText.size()) + break; + uiUTFChar |= static_cast(rssText[nPos] & 0b00111111); + uiUTFChar <<= 6; + + // Expecting the next character to be between 0x80 and 0xBF + nPos++; + if (nPos >= rssText.size()) + break; + uiUTFChar |= static_cast(rssText[nPos] & 0b00111111); + } + + // Stream the UTF character + if (uiUTFChar <= 0xFFFF) + sstreamQuotedText << "\\u" << std::uppercase << std::hex << std::setfill('0') << std::setw(4) << uiUTFChar; + else + sstreamQuotedText << "\\U" << std::uppercase << std::hex << std::setfill('0') << std::setw(8) << uiUTFChar; + break; + } + } + sstreamQuotedText << "\""; + return sstreamQuotedText.str(); + } + + template + inline std::unique_ptr CSdvObjectClass::CreateObject() + { + std::unique_ptr ret; + try + { + ret = std::make_unique>(); + } + catch (...) + { + SDV_LOG(core::ELogSeverity::error, + "Failed to instantiate object of class ", + GetClassName(), + " - exception thrown during construction! "); + } + return ret; + } + } // namespace sdv // NOLINTBEGIN(cppcoreguidelines-macro-usage) +#ifdef SDV_NO_CLASS_DEFINITION + +/** + * @brief Define the SDV component access functions, but do not define the SDV object. + * @param sdv_object_class The object class must derive from sdv::CSdvObject. + */ +#define DEFINE_SDV_OBJECT(sdv_object_class) \ + extern "C" bool HasActiveObjects(); \ + extern "C" sdv::IInterfaceAccess* GetModuleFactory(uint32_t uiInterfaceVersion); \ + extern "C" const char* GetManifest(); + +#elif defined SDV_NO_EXPORT_DEFINITION + +/** + * @brief Define the SDV object (derived from CSdvObject) to be exposed to the framework. Do not define export functions. + * @param sdv_object_class The object class must derive from sdv::CSdvObject. + */ +#define DEFINE_SDV_OBJECT(sdv_object_class) \ + /** \ + * @brief Object instantiation helper class. \ + */ \ + struct SObjectClassInstance_##sdv_object_class \ + { \ + public: \ + /** \ + * @brief Constructor \ + */ \ + SObjectClassInstance_##sdv_object_class() \ + { \ + /* Enforce derivation of sdv::CSdvObject. */ \ + static_assert(std::is_base_of::value, \ + "CSdvObject is not base of sdv_object_class"); \ + /* Call the static function once to instantiate the definition. */ \ + GetObjectClassInstance(); \ + } \ + /** \ + * @brief Get the creator class for the object instance. \ + * @return The global creator class. \ + */ \ + static sdv_object_class::TSdvObjectCreator& GetObjectClassInstance() \ + { \ + static sdv_object_class::TSdvObjectCreator object_class; \ + return object_class; \ + } \ + }; \ + /** Global instance of the object helper class. */ \ + static SObjectClassInstance_##sdv_object_class g_##sdv_object_class; \ + bool HasActiveObjects(); \ + sdv::IInterfaceAccess* GetModuleFactory(uint32_t uiInterfaceVersion); \ + const char* GetManifest(); + +#else + /** * @brief Define the SDV object (derived from CSdvObject) to be exposed to the framework. * @param sdv_object_class The object class must derive from sdv::CSdvObject. */ #define DEFINE_SDV_OBJECT(sdv_object_class) \ + /** \ + * @brief Object instantiation helper class. \ + */ \ struct SObjectClassInstance_##sdv_object_class \ { \ public: \ + /** \ + * @brief Constructor \ + */ \ SObjectClassInstance_##sdv_object_class() \ { \ /* Enforce derivation of sdv::CSdvObject. */ \ @@ -819,43 +1430,23 @@ Version = )code" << SDVFrameworkInterfaceVersion /* Call the static function once to instantiate the definition. */ \ GetObjectClassInstance(); \ } \ + /** \ + * @brief Get the creator class for the object instance. \ + * @return The global creator class. \ + */ \ static sdv_object_class::TSdvObjectCreator& GetObjectClassInstance() \ { \ static sdv_object_class::TSdvObjectCreator object_class; \ return object_class; \ } \ }; \ + /** Global instance of the object helper class. */ \ static SObjectClassInstance_##sdv_object_class g_##sdv_object_class; \ extern "C" SDV_SYMBOL_PUBLIC bool HasActiveObjects(); \ extern "C" SDV_SYMBOL_PUBLIC sdv::IInterfaceAccess* GetModuleFactory(uint32_t uiInterfaceVersion); \ extern "C" SDV_SYMBOL_PUBLIC const char* GetManifest(); -/** - * @brief Define the SDV object (derived from CSdvObject) to be exposed to the framework. Do not define export functions. - * @param sdv_object_class The object class must derive from sdv::CSdvObject. - */ -#define DEFINE_SDV_OBJECT_NO_EXPORT(sdv_object_class) \ - struct SObjectClassInstance_##sdv_object_class \ - { \ - public: \ - SObjectClassInstance_##sdv_object_class() \ - { \ - /* Enforce derivation of sdv::CSdvObject. */ \ - static_assert(std::is_base_of::value, \ - "CSdvObject is not base of sdv_object_class"); \ - /* Call the static function once to instantiate the definition. */ \ - GetObjectClassInstance(); \ - } \ - static sdv_object_class::TSdvObjectCreator& GetObjectClassInstance() \ - { \ - static sdv_object_class::TSdvObjectCreator object_class; \ - return object_class; \ - } \ - }; \ - static SObjectClassInstance_##sdv_object_class g_##sdv_object_class; \ - bool HasActiveObjects(); \ - sdv::IInterfaceAccess* GetModuleFactory(uint32_t uiInterfaceVersion); \ - const char* GetManifest(); +#endif // !defined SDV_NO_CLASS_DEFINITION && !defined SDV_NO_EXPORT_DEFINITION /** * @brief Declare the object class type. To be placed in the SDV object class derived from CSdvObject. @@ -863,7 +1454,7 @@ Version = )code" << SDVFrameworkInterfaceVersion */ #define DECLARE_OBJECT_CLASS_TYPE(class_type) \ /** \ - * @brief Declare the object class type. To be placed in the SDV object class derived from CSdvObject. \ + * @brief Get the object class type. \ * @return Returns the type of the object (EObjectType). \ */ \ constexpr static sdv::EObjectType GetObjectType() \ @@ -876,6 +1467,10 @@ Version = )code" << SDVFrameworkInterfaceVersion * @param class_name_string The UTF-8 string containing the name of the object. */ #define DECLARE_OBJECT_CLASS_NAME(class_name_string) \ + /** \ + * @brief Get the class name. \ + * @return Returns the string containing the class name. \ + */ \ static sdv::u8string GetClassNameStatic() \ { \ return class_name_string; \ @@ -886,6 +1481,10 @@ Version = )code" << SDVFrameworkInterfaceVersion * @param ... Multiple UTF-8 strings containing the alias names for the object class. */ #define DECLARE_OBJECT_CLASS_ALIAS(...) \ + /** \ + * @brief Get the list of class name aliases. \ + * @return Vector containing the class name alias strings. \ + */ \ static sdv::sequence GetClassAliasesStatic() \ { \ return sdv::sequence({__VA_ARGS__}); \ @@ -896,6 +1495,10 @@ Version = )code" << SDVFrameworkInterfaceVersion * @param object_name_string The UTF-8 string containing the name of the object. */ #define DECLARE_DEFAULT_OBJECT_NAME(object_name_string) \ + /** \ + * @brief Get the default name to be used during the object instantiation. \ + * @return String containing the default name. \ + */ \ static sdv::u8string GetDefaultObjectNameStatic() \ { \ return object_name_string; \ @@ -905,6 +1508,10 @@ Version = )code" << SDVFrameworkInterfaceVersion * @brief Declare the object to be a singleton object (only one instance will be created of the object). */ #define DECLARE_OBJECT_SINGLETON() \ + /** \ + * @brief Is the class defined as singleton (allows at the most one instance of the class)? \ + * @return Returns whether the class is defined as singleton. \ + */ \ static bool IsSingletonStatic() { return true; } /** @@ -912,6 +1519,10 @@ Version = )code" << SDVFrameworkInterfaceVersion * One or more strings with the class/default names of the objects the component is dependent on. */ #define DECLARE_OBJECT_DEPENDENCIES(...) \ + /** \ + * @brief Get the list of object dependencies. \ + * @return The sequence containing the dependency strings. \ + */ \ static sdv::sequence GetObjectDependenciesStatic() { return sdv::sequence({__VA_ARGS__}); } // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/export/support/crc.h b/export/support/crc.h index 5ac3f35..aa11eb3 100644 --- a/export/support/crc.h +++ b/export/support/crc.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 SDV_CRC_H #define SDV_CRC_H @@ -12,7 +25,7 @@ namespace sdv * @tparam T The type to use for bit reflection. * @param[in] tValue The value to reflect. Must be a arithmetic or boolean type. * @return The reflected value. - */ + */ template constexpr T reflect(T tValue) { @@ -53,7 +66,7 @@ namespace sdv * @param[in] pData Pointer to the value buffer. * @param[in] nCount Amount of values in the buffer. * @return The calculated CRC checksum. - */ + */ template TCRCType calc_checksum(const T* pData, size_t nCount) noexcept; @@ -62,25 +75,25 @@ namespace sdv * @remarks The CRC calculation occurs byte-wise regardless of the endianness of the the processor architecture. * @tparam T Type of the value buffer to calculate the CRC for. * @param[in] tValue The value to add to the calculation. - */ + */ template void add(T tValue) noexcept; /** * @brief Get the currently calculated checksum. * @return The CRC checksum. - */ + */ TCRCType get_checksum() const noexcept; /** * @brief Set a CRC checksum to continue the calculation with additional data. * @param[in] tCrcValue The previously calculated CRC value. - */ + */ void set_checksum(TCRCType tCrcValue) noexcept; /** * @brief Reset the current calculation (resets the CRC value to its initial value). - */ + */ void reset() noexcept; private: diff --git a/export/support/except.h b/export/support/except.h index 254523f..79750d8 100644 --- a/export/support/except.h +++ b/export/support/except.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 SDV_EXCEPT_H #define SDV_EXCEPT_H @@ -11,15 +24,15 @@ namespace sdv { /** - * @brief exception ID type - */ + * @brief exception ID type + */ using exception_id = uint64_t; /** - * @brief Get the exception ID. - * @tparam TException The exception type - * @return The ID of the exception - */ + * @brief Get the exception ID. + * @tparam TException The exception type + * @return The ID of the exception + */ template constexpr inline exception_id GetExceptionId() noexcept { diff --git a/export/support/interface.h b/export/support/interface.h index 07422d3..c6ae632 100644 --- a/export/support/interface.h +++ b/export/support/interface.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 SDV_INTERFACE_H #define SDV_INTERFACE_H @@ -8,6 +21,8 @@ #define interface struct #endif + + namespace sdv { /** diff --git a/export/support/interface.inl b/export/support/interface.inl index 8924863..04183df 100644 --- a/export/support/interface.inl +++ b/export/support/interface.inl @@ -1,8 +1,21 @@ +/******************************************************************************** + * 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 SDV_INTERFACE_INL #define SDV_INTERFACE_INL #ifndef SDV_INTERFACE_H -#error Do not include "interface.inl" directly. Include "interface.h" instead! +#include "interface.h" #endif //!defined SDV_INTERFACE_H #include diff --git a/export/support/interface_ptr.h b/export/support/interface_ptr.h index df8cf93..e10b40b 100644 --- a/export/support/interface_ptr.h +++ b/export/support/interface_ptr.h @@ -1,13 +1,16 @@ -/** +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG * - * @file interface_ptr.h - * @brief This file provides all necessary definitions to use and implement interface maps. - * @version 0.1 - * @date 2022.11.14 - * @author Thomas.pfleiderer@zf.com - * @copyright Copyright ZF Friedrichshaven AG (c) 2022 + * 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 INTERFACE_IMPL_H #define INTERFACE_IMPL_H @@ -79,10 +82,24 @@ namespace sdv int m_uiUseSection = -1; ///< The number of the section to process the interface table entries for. int m_uiSection = -1; ///< The number of the section the interface table entries belong to. }; + + /** + * @brief Helper struct for type reporting during static_assert. + * @tparam T Type to report for the always_false struct. + */ + template + struct always_false : std::false_type + {}; + + /** + * @brief Variable for the always_false struct. + * @tparam T Type to report for the always_false struct. + */ + template + constexpr bool always_false_v = always_false::value; } } - /** * @brief Interface map containing the supported interface definitions. * @pre The class using the interface map should derive from IInterfaceAccess. @@ -171,17 +188,16 @@ namespace sdv /** * @brief Conditional check; when true continue the checking for interfaces. - * @param condition The condition to be checked. When 'false' processing will be stopped; otherwise processing - * continues. Example: @code SDV_INTERFACE_CHECK_CONDITION(CheckFunc()) @endcode + * @param condition The condition to be checked. When 'false' processing will be stopped; otherwise processing continues. + * Example: @code SDV_INTERFACE_CHECK_CONDITION(CheckFunc()) @endcode */ #define SDV_INTERFACE_CHECK_CONDITION(condition) \ - if (!(condition)) \ - return nullptr; + if (!(condition)) return nullptr; /** * @brief Use the condition, to select a section to process. * @param condition Condition to be checked. When 'true' processing will be limited to the section. - * Example: @code SDV_INTERFACE_PROCESS_SECTION(CheckFunc(), 1) @endcode + * Example: @code SDV_INTERFACE_SET_SECTION_CONDITION(CheckFunc(), 1) @endcode * @param section_number The section to be processed when the condition is true. */ #define SDV_INTERFACE_SET_SECTION_CONDITION(condition, section_number) \ @@ -311,6 +327,26 @@ namespace sdv return *this; } + /** + * @brief Compare the interface pointer with an interface. + * @param[in] pInterface Pointer to the interface. + * @return Returns whether the interfaces are equal. + */ + bool operator==(const IInterfaceAccess* pInterface) const + { + return static_cast(m_pInterface) == pInterface; + } + + /** + * @brief Compare the interface pointer with an interface. + * @param[in] pInterface Pointer to the interface. + * @return Returns whether the interfaces are not equal. + */ + bool operator!=(const IInterfaceAccess* pInterface) const + { + return static_cast(m_pInterface) == pInterface; + } + /** * @brief Get a pointer to the interface */ @@ -392,7 +428,7 @@ namespace sdv return rtMember.GetInterface(idInterface); } - // Warning of cppchgeck for a potential const variable cannot be applied due to the non-const nature of interfaces. Suppress + // Warning of cppcheck for a potential const variable cannot be applied due to the non-const nature of interfaces. Suppress // warning. // cppcheck-suppress constParameterReference /** diff --git a/export/support/iterator.h b/export/support/iterator.h index d6bc54d..da54829 100644 --- a/export/support/iterator.h +++ b/export/support/iterator.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 SDV_ITERATOR_H #define SDV_ITERATOR_H @@ -15,7 +28,7 @@ namespace sdv * @tparam TContainer Container type to use for this iterator. * @tparam bConstIterator When set, the iterator is a const iterator not allowing to change values within the container. * @tparam bReserseIterator When set, the iterator starts at the end instead of at the beginning and decreases the location. - */ + */ template class index_iterator { diff --git a/export/support/local_service_access.h b/export/support/local_service_access.h index 5f3e7f8..3375315 100644 --- a/export/support/local_service_access.h +++ b/export/support/local_service_access.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_SERVICE_ACCESS_H #define LOCAL_SERVICE_ACCESS_H diff --git a/export/support/mem_access.h b/export/support/mem_access.h index 6a5fb6f..70487bb 100644 --- a/export/support/mem_access.h +++ b/export/support/mem_access.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 MEM_SUPPORT_H #define MEM_SUPPORT_H diff --git a/export/support/param_impl.h b/export/support/param_impl.h new file mode 100644 index 0000000..7344e5a --- /dev/null +++ b/export/support/param_impl.h @@ -0,0 +1,1541 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 Contributors to the Eclipse Foundation + * + * 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 PARAM_IMPL_H +#define PARAM_IMPL_H + +#include "../interfaces/param.h" +#include "interface_ptr.h" +#include +#include +#include +#include +#include +#include + +/** + * @brief Software Defined Vehicle framework. + */ +namespace sdv +{ + // Forward declaration + class CSdvParamMap; + + /** + * @brief Parameter information helper class; used for filling the parameter definition. + */ + class CSdvParamInfo : private SParamInfo + { + public: + /** + * @brief Constructor for automatic parameter type detection. + * @tparam TVar Type of the variable determining the type of information. + * @tparam TDefaultType Type of the variable of the default value. + * @param[in] rtVar Reference to the variable to extract the type from. + * @param[in] rssName Reference to the string containing the name of the parameter. + * @param[in] tDefaultVal The default value assigned to the parameter. + * @param[in] rssUnit Reference to the string containing the unit description. + * @param[in] rssCategory Reference to the string containing the name of the group. Can be empty when no group + * is provided. + * @param[in] rssDescription Reference to the string containing the description of the parameter. + * @param[in] uiFlags The flags to use with this parameter (extended with read-only for 'const' types). + */ + template + CSdvParamInfo(TVar& rtVar, const std::string& rssName, TDefaultType tDefaultVal, const std::string& rssUnit, + const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlags); + + /** + * @brief Constructor for numeric values. The parameter type is extracted from the variable type. + * @tparam TVar Type of the variable determining the type of information. + * @param[in] rtVar Reference to the variable to extract the type from. + * @param[in] rssName Reference to the string containing the name of the parameter. + * @param[in] tDefaultVal The default value assigned to the parameter. + * @param[in] ranyLimitLow Reference to the lower limit value or no limit if set to empty. + * @param[in] bIncludeLow If there is a lower limit set, the boolean defines how to interpret the limit: when set + * limit <= value, when not set limit < value. + * @param[in] ranyLimitHigh Reference to the higher limit value or no limit if set to empty. + * @param[in] bIncludeHigh If there is a higher limit set, the boolean defines how to interpret the limit: when set + * limit >= value, when not set limit > value. + * @param[in] rssUnit Reference to the string containing the unit description. + * @param[in] rssCategory Reference to the string containing the name of the group. Can be empty when no group + * is provided. + * @param[in] rssDescription Reference to the string containing the description of the parameter. + * @param[in] uiFlags The flags to use with this parameter (extended with read-only for 'const' types). + */ + template + CSdvParamInfo(TVar& rtVar, const std::string& rssName, TDefaultType tDefaultVal, const any_t& ranyLimitLow, + bool bIncludeLow, const any_t& ranyLimitHigh, bool bIncludeHigh, const std::string& rssUnit, + const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlags); + + /** + * @brief Construct function for string values. The numeric type is extracted from the variable type. + * @tparam TVar Type of the variable determining the type of information. + * @tparam TDefaultType Type of the variable of the default value. + * @param[in] rtVar Reference to the variable to extract the type from. + * @param[in] rssName Reference to the string containing the name of the parameter. + * @param[in] tDefaultVal The default value assigned to the parameter. + * @param[in] rssPattern Reference to the string containing the regular expression describing the pattern the value is + * allowed to have. + * @param[in] rssUnit Reference to the string containing the unit description. + * @param[in] rssCategory Reference to the string containing the name of the group. Can be empty when no group + * is provided. + * @param[in] rssDescription Reference to the string containing the description of the parameter. + * @param[in] uiFlags The flags to use with this parameter (extended with read-only for 'const' types). + */ + template + CSdvParamInfo(TVar& rtVar, const std::string& rssName, TDefaultType tDefaultVal, const std::string& rssPattern, + const std::string& rssUnit, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlags); + + /** + * @brief Constructor function for enumerator and bitmask parameters. The parameter type is extracted from the variable + * type. + * @tparam TVar Type of the variable determining the type of information. + * @tparam TDefaultType Type of the variable of the default value. + * @param[in] rtVar Reference to the variable to extract the type from. + * @param[in] rssName Reference to the string containing the name of the parameter. + * @param[in] tDefaultVal The default value assigned to the parameter. + * @param[in] rseqLabels Reference to the sequence of labels available for the enumerator or bitmask. + * @param[in] rssCategory Reference to the string containing the name of the group. Can be empty when no group + * is provided. + * @param[in] rssDescription Reference to the string containing the description of the parameter. + * @param[in] uiFlags The flags to use with this parameter (extended with read-only for 'const' types). + */ + template + CSdvParamInfo(TVar& rtVar, const std::string& rssName, TDefaultType tDefaultVal, + const sequence& rseqLabels, const std::string& rssCategory, const std::string& rssDescription, + uint32_t uiFlags); + + /** + * @brief Assignment constructor for parameter information structure. + * @param rInfo Reference to the parameter information structure. + */ + CSdvParamInfo(const SParamInfo& rInfo); + + /** + * @brief Move constructor for the parameter information structure. + * @param rInfo Reference to the parameter information structure. + */ + CSdvParamInfo(SParamInfo&& rInfo); + + /** + * @brief Default destructor. + */ + virtual ~CSdvParamInfo() = default; + + /** + * @brief Assignment operator for parameter information. + * @param rsInfo Reference to the information structure. + * @return Reference to this class. + */ + CSdvParamInfo& operator=(const SParamInfo& rsInfo); + + /** + * @brief Move operator for parameter information. + * @param rsInfo Reference to the information structure. + * @return Reference to this class. + */ + CSdvParamInfo& operator=(SParamInfo&& rsInfo); + + /** + * @brief Get the parameter information structure. + * @return Reference to the parameter information structure. + */ + virtual SParamInfo InfoStruct() const; + + /** + * @brief Get the name of the parameter. + * @return Reference to the parameter name. + */ + const u8string& Name() const; + + /** + * @brief Get the path of the parameter (name and grops together). + * @return Path to the parameter. + */ + u8string Path() const; + + /** + * @brief Get the default value of the parameter. + * @return Reference to the default value. + */ + const any_t& DefaultVal() const; + + /** + * @brief Get the group of the parameter. + * @return Reference to the parameter group. + */ + const u8string& Group() const; + + /** + * @brief Get the description of the parameter. + * @return Reference to the parameter description. + */ + const u8string& Description() const; + + /** + * @brief Get the unit for the parameter. + * @return Reference to the parameter unit. + */ + const u8string& Unit() const; + + /** + * @brief Get the parameter flags. + * @return The parameter flags. + */ + virtual uint32_t Flags() const; + + /** + * @brief Is this a read-only parameter? + * @return Returns whether the parameter has a read-only flag. + */ + bool ReadOnly() const; + + /** + * @brief Is this a Temporary parameter? + * @return Returns whether the parameter has a temporary flag. + */ + bool Temporary() const; + + /** + * @brief Is the parameter dirty (state value, not available in static param info)? + * @return Returns whether the parameter has a dirty flag set. + */ + virtual bool Dirty() const; + + /** + * @brief Is this parameter locked (state value, not available in static param info)? + * @return Returns whether the parameter has a locked flag set. + */ + virtual bool Locked() const; + + /** + * @brief Is the paratemer a boolean parameter. + * @return Returns whether the parameter is boolean. + */ + bool Boolean() const; + + /** + * @brief Is the paratemer a numeric parameter. + * @return Returns whether the parameter is numeric. + */ + bool Numeric() const; + + /** + * @brief Is the parameter a string parameter. + * @return Returns whether the parameter is a string. + */ + bool String() const; + + /** + * @brief Is the parameter an enum parameter. + * @return Returns whether the parameter is an enum. + */ + bool Enum() const; + + /** + * @brief Is the parameter a bitmask parameter. + * @return Returns whether the parameter is a bitmask. + */ + bool Bitmask() const; + + /** + * @brief Get the lower limit for a numeric parameter. + * @return Return a pair with the lower limit and a boolean whether the lower limit can be used as a value. The lower limit + * will be set to empty if the parameter is not numeric or if there is no lower limit set for the parameter. + */ + std::pair NumericLimitLow() const; + + /** + * @brief Get the higher limit for a numeric parameter. + * @return Return a pair with the upper limit and a boolean whether the upper limit can be used as a value. The upper limit + * will be set to empty if the parameter is not numeric or if there is no upper limit set for the parameter. + */ + std::pair NumericLimitHigh() const; + + /** + * @brief Get the string pattern for a string parameter. + * @return Return the string pattern or an empty string. + */ + std::string StringPattern() const; + + /** + * @brief Get the labels for an enumerator or bitmask parameter. + * @return Returns a sequence with the labels or an empty sequence when the parameter is not an enumerator or a bitmask or + * there are no labels defined. + */ + sequence EnumBitmaskLabels() const; + + protected: + /** + * @brief Check the type for being a string (standard C++ string, SDV string, character array, character pointer). + * @tparam TType Type to check for. + * @return Returns whether the type is a string. + */ + template + static constexpr bool TypeIsString(); + + /** + * @brief Check the type for being read only. + * @remarks Character arrays and character pointers are also identified as read only. + * @tparam TType Type to check for. + * @return Returns whether the type is read only. + */ + template + static constexpr bool TypeIsReadOnly(); + + private: + /** + * @brief Initialization function called by constructors. + * @param[in] eType The type of the parameter. + * @param[in] rssName Reference to the string containing the name of the parameter. + * @param[in] ranyDefaultVal Reference to the default value assigned to the parameter. Could be empty if no value was + * assigned. + * @param[in] rssUnit Reference to the string containing the unit description. + * @param[in] rssCategory Reference to the string containing the name of the group. Can be empty when no group + * is provided. + * @param[in] rssDescription Reference to the string containing the description of the parameter. + * @param[in] uiFlags The flags to use with this parameter (extended with read-only for 'const' types). + */ + void Init(EParamType eType, const std::string& rssName, const any_t& ranyDefaultVal, const std::string& rssUnit, + const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlags); + }; + + /** Internal namespace */ + namespace internal + { + /** + * @brief The parameter guardian class limits manages the parameter states and allows access to the parameter infos and + * values. + */ + class CParamGuardian : public CSdvParamInfo + { + public: + /** + * @brief Constructor. + * @tparam TInfoConstruct The type of the variables that are provided to the constructor function of the parameter + * information. + * @param[in] bLockable When set, the parameter is lockable. Only use with writable parameters. + * @param[in] bAutoDirty When set, the parameter dirty flag is detected automatically. Only use with writable parameters. + * @param[in] tConstruct The construct function arguments. + */ + template + CParamGuardian(bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct); + + /** + * @brief Get the parameter information structure. Overload of sdv::CSdvParamInfo::InfoStruct. + * @return Reference to the parameter information structure. + */ + virtual sdv::SParamInfo InfoStruct() const override; + + /** + * @brief Get the parameter flags. Overload of sdv::CSdvParamInfo::Flags. + * @return The parameter flags. + */ + virtual uint32_t Flags() const override; + + /** + * @brief Mark the parameter as dirty if the value has changed. This implies the automatic dirty detection to be + * enabled. + * @param ranyValue Reference to the value to be used to to check. + * @return Returns 'true' if the dirty flag has changed. + */ + bool UpdateDirty(const sdv::any_t& ranyValue); + + /** + * @brief Explicitly set the parameter to dirty. + * @return Returns 'true' if the dirty flag has changed. + */ + bool SetDirty(); + + /** + * @brief Reset the parameter dirty flag. + * @return Returns 'true' if the dirty flag has changed. + */ + bool ResetDirty(); + + /** + * @brief Is the parameter marked dirty? Overload of sdv::CSdvParamInfo::Dirty. + * @return Returns the dirty flag of the parameter. + */ + virtual bool Dirty() const override; + + /** + * @brief Is the parameter lockable? + * @return Returns whether the parameter can be locked. + */ + bool Lockable() const; + + /** + * @brief Lock the parameter. + * @return Returns 'true' if the lock flag has changed. + */ + bool Lock(); + + /** + * @brief Unlock the parameter. + * @return Returns 'true' if the lock flag has changed. + */ + bool Unlock(); + + /** + * @brief Is the parameter currently locked? Overload of sdv::CSdvParamInfo::Locked. + * @return Returns the lock flag for the parameter. + */ + virtual bool Locked() const override; + + /** + * @brief Set a value. + * @param[in] ranyValue Reference to the value. + * @return Returns whether the parameter is writable and can be set. + */ + virtual bool Set(const any_t& ranyValue) = 0; + + /** + * @brief Get a value. + * @return Returns the value. + */ + virtual any_t Get() const = 0; + + protected: + /** + * @brief Check the variable for compatibility the restrictions. + * @param[in] ranyValue Reference to the value. + * @return Returns whether the variable passes the restrictions. + */ + bool CheckRestrictions(const any_t& ranyValue); + + private: + bool m_bLockable = false; ///< When set, the parameter is lockable (only for writable parameters). + bool m_bLocked = false; ///< When set, the parameter is locked against writing (only for lockable parameters). + bool m_bAutoDirty = false; ///< When set, automatic dirty flag detection is enabled (only for writable parameters). + bool m_bDirty = false; ///< When set, the parameter is marked as dirty. + any_t m_anyStored; ///< Value of the parameter to detect for dirtiness. + }; + + /** + * @brief Parameter value class + * @tparam TVar Type of the parameter variable. + */ + template + class CParamValue : public CParamGuardian + { + public: + /** + * @brief Constructor. + * @tparam TInfoConstruct The type of the variables that are provided to the constructor function of the parameter + * information. + * @param[in] rtVar Reference to the parameter variable. + * @param[in] bLockable When set, the parameter is lockable. Only use with writable parameters. + * @param[in] bAutoDirty When set, the parameter dirty flag is detected automatically. Only use with writable parameters. + * @param[in] tConstruct The construct function arguments. + */ + template + CParamValue(TVar& rtVar, bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct); + + /** + * @brief Set a value. Overload of CParamGuardian::Set. + * @param[in] ranyValue Reference to the value. + * @return Returns whether the parameter is writable and can be set. + */ + virtual bool Set(const any_t& ranyValue) override; + + /** + * @brief Get a value. Overload of CParamGuardian::Get. + * @return Returns the value. + */ + virtual any_t Get() const override; + + private: + TVar& m_rtVar; ///< Reference to the parameter + }; + + /** + * @brief Label map helper class. This helper class collects the labels of each defined map. + */ + class CLabelMapHelper + { + public: + /** + * @brief Explicit clearing of the label map might be needed when a local memory manager is used. + * @remarks Call this function before the destruction of the memory manager. + */ + void Clear() + { + m_mapLabelCollections.clear(); + } + + /** + * @brief Local version of SLabel. + */ + struct SLabelLocal + { + sdv::any_t anyValue; ///< Label value (must be an integral number) + std::string ssLabel; ///< Label text + }; + + /** + * @brief Store a collection of labels for an enum type. + * @attention Since this function is called before the execution of main(), there is no SDV memory manager available + * yet. Use the C++ allocation instead (this means conversion needs to be done at a later stage). + * @tparam TEnum The enum type this label collection is describing. + * @param rseqLabels Reference to the sequence containing the label-value-pairs. + * @return Return 'true' to allow an assignment to a static variable during startup. + */ + template + bool StoreLabelMap(const std::vector& rseqLabels) + { + m_mapLabelCollections[typeid(TEnum).name()] = rseqLabels; + return true; + } + + /** + * @brief Get a previously stored label collection for an enum type. + * @tparam TEnum The enum type to get the labels for. + * @return Reference to a sequence containing the label-value-pairs or an empty sequence when no labels were stored for + * this enum type. + */ + template + sequence GetLabelMap() const + { + using TEnumLocal = std::remove_reference_t; + static const sequence seqEmpty; + auto itEnum = m_mapLabelCollections.find(typeid(TEnumLocal).name()); + if (itEnum == m_mapLabelCollections.end()) + return seqEmpty; + + // Create a copy pf the data using the SDV memory manager. + sequence seqLabels; + for (const SLabelLocal& rsLabel : itEnum->second) + seqLabels.push_back(SLabelInfo::SLabel{rsLabel.anyValue, rsLabel.ssLabel}); + + return seqLabels; + } + + private: + std::map> m_mapLabelCollections; ///< Map with label collections. + }; + + /** + * @brief Get access to the one instance of the label map helper class. + * @return Reference to the label map helper class instance. + */ + inline CLabelMapHelper& GetLabelMapHelper() + { + static CLabelMapHelper helper; + return helper; + } + + /** Limit type to use */ + enum class ELimitType + { + up_to_limit, + up_to_and_include_limit, + no_limit + }; + + /** + * @brief Determine the lower limit of the numeric value. + * @details The lower limit is defined by the operators > and >=. When no limit is required, the operator != is used + * (defined by the macro NO_LIMIT). + */ + struct SLowerLimit + { + /** + * @brief Operator larger than. + * @tparam TType Type to use for the comparison. + * @param[in] tLimit The limit value. + * @return A pair defining the limit. First the type, second the limit type. + */ + template + std::pair operator>(TType tLimit) const + { + return std::make_pair(tLimit, ELimitType::up_to_limit); + } + + /** + * @brief Operator larger than or equal. + * @tparam TType Type to use for the comparison. + * @param[in] tLimit The limit value. + * @return A pair defining the limit. First the type, second the limit type. + */ + template + std::pair operator>=(TType tLimit) const + { + return std::make_pair(tLimit, ELimitType::up_to_and_include_limit); + } + + /** + * @brief No limit operator. + * @param[in] n The value that was supplied. This is typically 0. + * @return A pair containing the value and the limit type, being no limit. + */ + std::pair operator!=(size_t n) const + { + return std::make_pair(n, ELimitType::no_limit); + } + }; + + /** + * @brief Determine the upper limit of the numeric value. + * @details The upper limit is defined by the operators < and <=. When no limit is required, the operator != is used + * (defined by the macro NO_LIMIT). + */ + struct SUpperLimit + { + /** + * @brief Operator smaller than. + * @tparam TType Type to use for the comparison. + * @param[in] tLimit The limit value. + * @return A pair defining the limit. First the type, second the limit type. + */ + template + std::pair operator<(TType tLimit) const + { + return std::make_pair(tLimit, ELimitType::up_to_limit); + } + + /** + * @brief Operator smaller than or equal. + * @tparam TType Type to use for the comparison. + * @param[in] tLimit The limit value. + * @return A pair defining the limit. First the type, second the limit type. + */ + template + std::pair operator<=(TType tLimit) const + { + return std::make_pair(tLimit, ELimitType::up_to_and_include_limit); + } + + /** + * @brief No limit operator. + * @param[in] n The value that was supplied. This is typically 0. + * @return A pair containing the value and the limit type, being no limit. + */ + std::pair operator!=(size_t n) const + { + return std::make_pair(n, ELimitType::no_limit); + } + }; + + /** + * @brief Helper structure to determine if the type is a shared pointer. + * @tparam T Type to test for. + */ + template + struct is_shared_ptr : std::false_type + {}; + + /** + * @brief Specialization of std::shared_ptr. + */ + template + struct is_shared_ptr> : std::true_type + {}; + + /** + * @brief Helper variable for is_shared_ptr. + */ + template + inline constexpr bool is_shared_ptr_v = is_shared_ptr::value; + + /** + * @brief Member access class. + */ + struct SMemberMapAccess + { + /** + * @brief Find the parameter object with the supplied name. + * @param[in] rssName Reference to the name of the parameter. + * @return Returns a smart pointer to the parameter object. + */ + virtual std::shared_ptr FindParamObject(const sdv::u8string& rssName) = 0; + + /** + * @{ + * @brief Get CSdvParamMap access. + * @return The parameter map pointer or a NULL pointer if there is no associated parameter map. + */ + virtual CSdvParamMap* Access() = 0; + virtual const CSdvParamMap* Access() const = 0; + /** + * @} + */ + }; + + /** + * @brief Struct to give access to a member map. + * @tparam TMember The member variable type. + */ + template + struct SMemberMap : SMemberMapAccess + { + static_assert(std::is_base_of_v, "The member type must derive from sdv::CSdvParamMap."); + + /// The type definition. + using TType = TMember; + + /** + * @brief Constructor + * @param[in] rMember Reference to the member. + * @param[in] pEventObject Pointer to the object receiving the parameter change notifications. + */ + SMemberMap(TMember& rMember, sdv::CSdvParamMap* pEventObject) : m_pEventObject(pEventObject), refMember(rMember) + {} + + /** + * @brief Default destructor. + */ + virtual ~SMemberMap() = default; + + /** + * @brief Build the parameter map (static build). + * @return Vector with parameter info classes. + */ + static std::vector> BuildStatic() + { + return TMember::BuildParamMap(nullptr, nullptr); + } + + /** + * @brief Build the parameter map. + */ + void BuildMap() + { + refMember.get().BuildParamMap(&refMember.get(), m_pEventObject); + } + + /** + * @brief Find the parameter object with the supplied name. Overload of SMemberMapAccess::FindParamObject. + * @param[in] rssName Reference to the name of the parameter. + * @return Returns a smart pointer to the parameter object. + */ + virtual std::shared_ptr FindParamObject(const sdv::u8string& rssName) override + { + return refMember.get().FindParamObject(rssName); + } + + /** + * @{ + * @brief Get CSdvParamMap access. Overload of SMemberMapAccess::Interface. + * @return The parameter map pointer or a NULL pointer if there is no associated parameter map. + */ + virtual CSdvParamMap* Access() override + { + return &refMember.get(); + } + virtual const CSdvParamMap* Access() const override + { + return &refMember.get(); + } + /** + * @} + */ + + private: + sdv::CSdvParamMap* m_pEventObject; ///< Pointer to the object to receive notifications. + std::reference_wrapper refMember; ///< Reference to the member map. + }; + + /** + * @brief Specialization for a pointer to the member. + * @tparam TMember The member variable type. Must derive from sdv::CSdvParamMap. + */ + template + struct SMemberMap : SMemberMapAccess + { + static_assert(std::is_base_of_v, "The member type must derive from sdv::CSdvParamMap."); + + /// The type definition. + using TType = TMember; + + /** + * @brief Constructor + * @param[in] rpMember Reference to the pointer to the member. + * @param[in] pEventObject Pointer to the object receiving the parameter change notifications. + */ + SMemberMap(TMember*& rpMember, sdv::CSdvParamMap* pEventObject) : m_pEventObject(pEventObject), refpMember(rpMember) + {} + + /** + * @brief Default destructor. + */ + virtual ~SMemberMap() = default; + + /** + * @brief Build the parameter map (static build). + * @return Vector with parameter info classes. + */ + static std::vector> BuildStatic() + { + return TMember::BuildParamMap(nullptr, nullptr); + } + + /** + * @brief Build the parameter map. + */ + void BuildMap() + { + if (refpMember.get()) + refpMember.get()->BuildParamMap(refpMember.get(), m_pEventObject); + } + + /** + * @brief Find the parameter object with the supplied name. Overload of SMemberMapAccess::FindParamObject. + * @param[in] rssName Reference to the name of the parameter. + * @return Returns a smart pointer to the parameter object. + */ + virtual std::shared_ptr FindParamObject(const sdv::u8string& rssName) override + { + if (!refpMember.get()) return {}; + return refpMember.get()->FindParamObject(rssName); + } + + /** + * @{ + * @brief Get the IParameters interface. Overload of SMemberMapAccess::Interface. + * @return The parameter map pointer or a NULL pointer if there is no associated parameter map. + */ + virtual CSdvParamMap* Access() override + { + // In case a change has happened, or the member was not build previously... + if (refpMember.get()) + refpMember.get()->BuildParamMap(refpMember.get(), m_pEventObject); + + return refpMember.get(); + } + virtual const CSdvParamMap* Access() const override + { + if (!refpMember.get()) return nullptr; + + // In case a change has happened, or the member was not build previously... + if (refpMember.get()) + refpMember.get()->BuildParamMap(refpMember.get(), m_pEventObject); + + return refpMember; + } + /** + * @} + */ + private: + sdv::CSdvParamMap* m_pEventObject; ///< Pointer to the object to receive notifications. + mutable std::reference_wrapper refpMember; ///< Reference to the pointer to the member map. + }; + + /** + * @brief Specialization for a shared pointer to the member. + * @tparam TMember The member variable type. Must derive from sdv::CSdvParamMap. + */ + template + struct SMemberMap> : SMemberMapAccess + { + static_assert(std::is_base_of_v, "The member type must derive from sdv::CSdvParamMap."); + + /// The type definition. + using TType = TMember; + + /** + * @brief Constructor + * @param[in] rptrMember Reference to the smart pointer holding the member. + * @param[in] pEventObject Pointer to the object receiving the parameter change notifications. + */ + // Warning of cppcheck 2.7 for missing m_pEventObject member variable instantiation. Suppress warning. + // cppcheck-suppress uninitMemberVar + SMemberMap(std::shared_ptr& rptrMember, sdv::CSdvParamMap* pEventObject) : + m_pEventObject(pEventObject), refptrMember(rptrMember) + {} + + /** + * @brief Default destructor. + */ + virtual ~SMemberMap() = default; + + /** + * @brief Build the parameter map (static build). + * @return Vector with parameter info classes. + */ + static std::vector> BuildStatic() + { + return TMember::BuildParamMap(nullptr, nullptr); + } + + /** + * @brief Build the parameter map. + */ + void BuildMap() + { + if (refptrMember.get()) + refptrMember.get()->BuildParamMap(refptrMember.get().get(), m_pEventObject); + } + + /** + * @brief Find the parameter object with the supplied name. Overload of SMemberMapAccess::FindParamObject. + * @param[in] rssName Reference to the name of the parameter. + * @return Returns a smart pointer to the parameter object. + */ + virtual std::shared_ptr FindParamObject(const sdv::u8string& rssName) override + { + if (!refptrMember.get()) return {}; + return refptrMember.get()->FindParamObject(rssName); + } + + /** + * @{ + * @brief Get the IParameters interface. Overload of SMemberMapAccess::Interface. + * @return The parameter map pointer or a NULL pointer if there is no associated parameter map. + */ + virtual CSdvParamMap* Access() override + { + if (!refptrMember.get()) return nullptr; + + // In case a change has happened, or the member was not build previously... + if (refptrMember.get()) + refptrMember.get()->BuildParamMap(refptrMember.get().get(), m_pEventObject); + + return refptrMember.get().get(); + } + virtual const CSdvParamMap* Access() const override + { + if (!refptrMember.get()) return nullptr; + + // In case a change has happened, or the member was not build previously... + if (refptrMember.get()) + refptrMember.get()->BuildParamMap(refptrMember.get().get(), m_pEventObject); + + return refptrMember.get().get(); + } + /** + * @} + */ + private: + sdv::CSdvParamMap* m_pEventObject; ///< Pointer to the object to receive notifications. + mutable std::reference_wrapper> refptrMember; ///< Reference to the smart pointer to the + ///< member map. + }; + } // namespace internal + + /** + * @brief Concatenate two variables together. + * @param a First variable + * @param b Second variable + */ +#define SDV_CONCAT_IMPL(a, b) a##b + + /** + * @brief Concatenate two variables together with additional indirection. + * @param a First variable + * @param b Second variable + */ +#define SDV_CONCAT(a, b) SDV_CONCAT_IMPL(a, b) + + /** + * @brief Create a unique variable by using the __COUNTER__ macro concatenated to a provided prefix. + * @param prefix The prefix to use for the unique variable. + */ +#define SDV_UNIQUE_VAR(prefix) SDV_CONCAT(prefix, __COUNTER__) + + /** + * @brief Begin an label map. + * @param enum_type The name of the enumeration declaration. + */ +#define BEGIN_SDV_LABEL_MAP(enum_type) \ + /** Global variable initialized at the beginning triggering the label map storage. */ \ + [[maybe_unused]] static inline bool SDV_UNIQUE_VAR(_bLabelMap) = sdv::internal::GetLabelMapHelper().StoreLabelMap({ + + /** + * @brief Label element entry. Associates the value to the label. + * @param enum_element The enumerator element. + * @param name_string The name of the element as a string. + */ +#define SDV_LABEL_ENTRY(enum_element, name_string) \ + { static_cast>(enum_element), name_string }, + + /** + * @brief End of enumerator description map. + */ +#define END_SDV_LABEL_MAP() \ + }); + + /** + * @brief Begin a parameter map. + */ +#define BEGIN_SDV_PARAM_MAP() \ + /** \ + * @brief Build the parameter map. If a pointer is supplied, registers the parameters in the map. \ + * @tparam TClass Type of the class containing the parameter map. This class must derive from sdv::CSdvParamMap or \ + * std::nullptr_t. \ + * @param[in] pObject Pointer to the class instance holding the parameter map. Can be NULL if only initial parameter \ + * information is requested. \ + * @param[in] pEventObject Pointer to the object receiving the parameter change notifications. \ + * @return Returns a vector with the parameter information structures. \ + */ \ + template \ + static std::vector> BuildParamMap([[maybe_unused]] TClassPtr pObject, \ + [[maybe_unused]] sdv::CSdvParamMap* pEventObject) \ + { \ + bool constexpr bStatic = std::is_same_v; \ + static_assert(bStatic || (std::is_pointer_v && \ + std::is_base_of_v>), \ + "The class needs to derive from sdv::CSdvParamMap."); \ + if constexpr (!bStatic) \ + { \ + /* Build only if necessary */ \ + if (!pObject->BuildNecessary()) return {}; \ + } \ + std::vector> vecParamInfo; \ + [[maybe_unused]] uint32_t uiFlags = 0; \ + [[maybe_unused]] std::string ssGroup; \ + [[maybe_unused]] bool bLockable = false; + + /** + * @brief End the parameter map. + */ +#define END_SDV_PARAM_MAP() \ + return vecParamInfo; \ + } \ + \ + /** \ + * @brief Return a vector with parameter information structures. \ + * @attention Only parameters that are statically available are available here. The returned list can differ from the list \ + * after instantiation. \ + * @return Vector containing parameter information structures. \ + */ \ + static std::vector> GetParamMapInfoStatic() \ + { \ + std::vector> vecParamInfo = BuildParamMap(nullptr, nullptr); \ + return vecParamInfo; \ + } \ + \ + /** \ + * @brief Initialize the parameter map. This function is called only once for initial map creation. Overload of \ + * sdv::CSdvParamMap::InitParamMap. \ + */ \ + virtual void InitParamMap() override \ + { \ + BuildParamMap(this, this); \ + } + + + /** + * @brief The following parameters belong to the defined group. + * @details Parameters cabn be grouped together. Within a group, they need to be unique. A group can have sub-groups, separated + * with a dot '.'. For example: "Sensor.ADAS.Radar". Each (sub-)group represents its own TOML table in the configuration. For + * the example this would be: + * @code + * [Sensor] + * [Sensor.ADAS] + * [Sensor.Radar] + * @endcode + * @param group_string Name of the group and sub-groups separated by dots. Group names are case-sensitive. + */ +#define SDV_PARAM_GROUP(group_string) \ + ssGroup = group_string; \ + + /** + * @brief The following parameters belong to the top level group again (not having any group name assigned to them). + */ +#define SDV_PARAM_NO_GROUP() \ + ssGroup.clear(); + + /** + * @brief Reset all attributes for all following parameters. + */ +#define SDV_PARAM_RESET_ATTRIBUTES() \ + uiFlags = 0; \ + bLockable = false; + + /** + * @brief Following parameters are not writable. + */ +#define SDV_PARAM_SET_READONLY() \ + uiFlags |= static_cast(::sdv::EParamFlags::read_only); + + /** + * @brief Following parameters can be written (if not defined as const). + */ +#define SDV_PARAM_RESET_READONLY() \ + uiFlags &= ~static_cast(::sdv::EParamFlags::read_only); + + /** + * @brief Following parameters will be protected against writing when locked (e.g. after initialization). + */ +#define SDV_PARAM_ENABLE_LOCKING() \ + bLockable = true; + + /** + * @brief Following parameters will not be protected against writing. + */ +#define SDV_PARAM_DISABLE_LOCKING() \ + bLockable = false; + + /** + * @brief Following parameters are marked temporary (will not be stored in the configuration). + */ +#define SDV_PARAM_SET_TEMPORARY() \ + uiFlags |= static_cast(::sdv::EParamFlags::temporary); + + /** + * @brief Following parameters are not marked temporary. + */ +#define SDV_PARAM_RESET_TEMPORARY() \ + uiFlags &= ~static_cast(::sdv::EParamFlags::temporary); + + /** + * @brief Define a parameter. + * @param var The member variable of the class containing this parameter map. + * @param name_string The name of the parameter. The name must be unique within the group. + * @param default_val The default value for the parameter. + * @param unit_string The unit of the parameter. + * @param description_string The description of the parameter. + */ +#define SDV_PARAM_ENTRY(var, name_string, default_val, unit_string, description_string) \ + if constexpr (bStatic) \ + { \ + decltype(var) temp{}; \ + std::shared_ptr ptrParamInfo = std::make_shared(temp, name_string, default_val, \ + unit_string, ssGroup, description_string, uiFlags); \ + if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ + } \ + else \ + { \ + std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, bLockable, true, name_string, \ + default_val, unit_string, ssGroup, description_string, uiFlags); \ + if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ + } + + /** No limit for the SDV_PARAM_NUMBER_ENTRY limitation definition. */ +#define NO_LIMIT != 0 + + /** + * @brief Define a number parameter with restriction. + * @param var The member variable of the class containing this parameter map. + * @param name_string The name of the parameter. The name must be unique within the group. + * @param default_val The default value for the parameter. + * @param low_limit The minimal value limit. Can be '> val' or '>= val' or NO_LIMIT for no limitation. + * @param high_limit The maximal value limit. Can be '< val' or '<= val' or NO_LIMIT for no limitation. + * @param unit_string The unit of the parameter. + * @param description_string The description of the parameter. + */ +#define SDV_PARAM_NUMBER_ENTRY(var, name_string, default_val, low_limit, high_limit, unit_string, description_string) \ + { \ + auto prLowLimit = sdv::internal::SLowerLimit() low_limit; \ + sdv::any_t anyLower; \ + if (prLowLimit.second != sdv::internal::ELimitType::no_limit) anyLower = prLowLimit.first; \ + auto prHighLimit = sdv::internal::SUpperLimit() high_limit; \ + sdv::any_t anyUpper; \ + if (prHighLimit.second != sdv::internal::ELimitType::no_limit) anyUpper = prHighLimit.first; \ + if constexpr (bStatic) \ + { \ + decltype(var) temp{}; \ + std::shared_ptr ptrParamInfo = std::make_shared(temp, name_string, \ + default_val, anyLower, prLowLimit.second != sdv::internal::ELimitType::up_to_limit, anyUpper, \ + prHighLimit.second != sdv::internal::ELimitType::up_to_limit, unit_string, ssGroup, description_string, \ + uiFlags); \ + if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ + } \ + else \ + { \ + std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, bLockable, true, \ + name_string, default_val, anyLower, prLowLimit.second != sdv::internal::ELimitType::up_to_limit, anyUpper, \ + prHighLimit.second != sdv::internal::ELimitType::up_to_limit, unit_string, ssGroup, description_string, \ + uiFlags); \ + if (ptrParamInfo)vecParamInfo.push_back(std::move(ptrParamInfo)); \ + } \ + } + + /** + * @brief Define an enum parameter. + * @param var The member variable of the class containing this parameter map. + * @param name_string The name of the parameter. The name must be unique within the group. + * @param default_val The default value for the parameter. + * @param description_string The description of the parameter. + */ +#define SDV_PARAM_ENUM_ENTRY(var, name_string, default_val, description_string) \ + SDV_PARAM_ENTRY(var, name_string, default_val, "", description_string) + + /** + * @brief Define a string parameter. + * @param var The member variable of the class containing this parameter map. + * @param name_string The name of the parameter. The name must be unique within the group. + * @param default_val The default value for the parameter. + * @param pattern_string The pattern the parameter should fit to. This is a regular expression. + * @param unit_string The unit of the parameter. + * @param description_string The description of the parameter. + */ +#define SDV_PARAM_STRING_ENTRY(var, name_string, default_val, pattern_string, unit_string, description_string) \ + if constexpr (bStatic) \ + { \ + decltype(var) temp{}; \ + std::shared_ptr ptrParamInfo = std::make_shared(temp, name_string, default_val, \ + pattern_string, unit_string, ssGroup, description_string, uiFlags); \ + if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ + } \ + else \ + { \ + std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, bLockable, true, name_string, \ + default_val, pattern_string, unit_string, ssGroup, description_string, uiFlags); \ + if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ + } + + /** + * @brief Define a path parameter. + * @param var The member variable of the class containing this parameter map. + * @param name_string The name of the parameter. The name must be unique within the group. + * @param default_val The default value for the parameter. + * @param description_string The description of the parameter. + */ +#define SDV_PARAM_PATH_ENTRY(var, name_string, default_val, description_string) \ + SDV_PARAM_ENTRY(var, name_string, default_val, "", description_string) + + /** + * @brief Define a bitmask parameter. + * @param TEnum Name of the enum definition defining the bits through BEGIN_SDV_ENUM_DEF_MAP macro. + * @param var The member variable of the class containing this parameter map. + * @param name_string The name of the parameter. The name must be unique within the group. + * @param description_string The description of the parameter. + */ +#define SDV_PARAM_BITMASK_ENTRY(TEnum, var, name_string, default_val, description_string) \ + if constexpr (bStatic) \ + { \ + decltype(var) temp{}; \ + std::shared_ptr ptrParamInfo = std::make_shared(temp, name_string, default_val, \ + sdv::internal::GetLabelMapHelper().GetLabelMap(), ssGroup, description_string, uiFlags); \ + if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ + } \ + else \ + { \ + std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, bLockable, true, name_string, \ + default_val, sdv::internal::GetLabelMapHelper().GetLabelMap(), ssGroup, description_string, uiFlags); \ + if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ + } + + /** + * @brief Chain the parameter map of a base class. + * @param base_class Name of the base class. + */ +#define SDV_PARAM_CHAIN_BASE(base_class) \ + { \ + auto vecBaseParamInfo = base_class::BuildParamMap(pObject, pEventObject); \ + vecParamInfo.insert(vecParamInfo.end(), vecBaseParamInfo.begin(), vecBaseParamInfo.end()); \ + } + + /** + * @brief Call the parameter map a member variable containing a parameter map. + * @param member Name of the member variable containing a parameter map. This can be a member instance, a pointer or a smart + * pointer to the instance. + */ +#define SDV_PARAM_CHAIN_MEMBER(member) \ + { \ + std::vector> vecParamInfoMember; \ + if constexpr (bStatic) \ + { \ + vecParamInfoMember = sdv::internal::SMemberMap::BuildStatic(); \ + } \ + else \ + { \ + auto ptrMemberMap = std::make_shared>(pObject->member, pObject); \ + if (ptrMemberMap) \ + { \ + ptrMemberMap->BuildMap(); \ + pObject->RegisterMemberMap(ptrMemberMap); \ + } \ + } \ + vecParamInfo.insert(vecParamInfo.end(), vecParamInfoMember.begin(), vecParamInfoMember.end()); \ + } + + /** + * @brief Parameter map base class implementing parameter support using the parameter map macros. + * @details The parameter map base class implements the support for the parameter map macros as well as parameter state + * management. A class using parameters can derive from this class and overload the following events to control its behavior: + * - OnParamChanged - parameter has changed + * - OnParamFlagChanged - parameter flag has changed + */ + class CSdvParamMap : public IParameters + { + public: + + /** + * @brief Default destructor. + */ + virtual ~CSdvParamMap() = default; + + /** + * @{ + * @brief Find a parameter object by name. + * @param[in] rssName Reference to the name of the parameter. + * @return Smart pointer to the parameter guardian object or an empty pointer when the parameter was not found. + */ + std::shared_ptr FindParamObject(const sdv::u8string& rssName); + std::shared_ptr FindParamObject(const sdv::u8string& rssName) const; + /** + * @} + */ + + /** + * Return a sequence with parameter paths. Each path is unique and can be used to get and set the parameter value. Overload + * of sdv::IParameters::GetParamPaths. + * @return Sequence containing parameter paths. Parameter paths are composed from group/sub-groups and the parameter name, + * separated by a dot. + */ + virtual sequence GetParamPaths() const override; + + /** + * @brief Returns the parameter value. Overload of sdv::IParameters::GetParam. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + * @return Returns the parameter value. Returns an 'empty' parameter value when not successful or the parameter is not set. + */ + virtual any_t GetParam(/*in*/ const u8string& ssPath) const override; + + /** + * @brief Set the parameter value. Overload of sdv::IParameters::SetParam. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + * @param[in] anyValue Reference to the parameter value to set. + * @return Returns 'true' on success or 'false' when parameter could not be set (e.g. the parameter is read-only) or the + * index is larger than the amount of parameters being available. + */ + virtual bool SetParam(/*in*/ const u8string& ssPath, /*in*/ any_t anyValue) override; + + /** + * @brief Get parameter information.Overload of sdv::IParameters::GetParamInfo. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + * @return Return the parameter information for the requested parameter or an empty structure when no parameter information + * is available or the parameter is not available. + */ + virtual SParamInfo GetParamInfo(/*in*/ const u8string& ssPath) const override; + + /** + * @brief Is the parameter dirty (was it changed)? Checks the parameter dirty flag. Overload of sdv::IParameters::IsDirty. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + * @return Returns whether the parameter was changed either by an explicit call to the SetParam function or internally by + * the object itself. + */ + virtual bool IsParamDirty(/*in*/ const u8string& ssPath) const override; + + /** + * @brief Set dirty flag for one parameter (or all properties for the function without parameters). + * @param[in] rssPath Reference to the path of the parameter. + */ + void SetParamDirtyFlag(const std::string& rssPath); + + /** + * @brief Reset the dirty flag. Overload of sdv::IParameters::ResetParamDirtyFlag. + * @param[in] ssPath Path of the parameter. The parameter path is composed from group/sub-groups and the parameter name, + * separated by a dot. + */ + virtual void ResetParamDirtyFlag(/*in*/ const u8string& ssPath) override; + + /** + * @brief Is the parameter map dirty? Overload of sdv::IParameters::IsParamMapDirty. + * @return Returns true when any one of the parameters is flagged as dirty. + */ + virtual bool IsParamMapDirty() const override; + + /** + * @brief Reset the dirty flag for all parameters. Overload of sdv::IParameters::ResetParamMapDirtyFlags. + */ + virtual void ResetParamMapDirtyFlags() override; + + /** + * @brief Lock the parameter map. + */ + void LockParamMap(); + + /** + * @brief Unlock the parameter map. + */ + void UnlockParamMap(); + + /** + * @brief Parameter changed event. Overload this function to implement parameter event handling. + * @param[in] rssPath Reference to the string containing the path of the parameter that changed. + * @param[in] rvarOld Reference to the any holding the value of the parameter. + * @param[in] rvarNew Reference to the any holding the value of the parameter. + * @param[in] rptrParamInfo Reference to the smart pointer to the parameter information object. + */ + virtual void OnParamChanged([[maybe_unused]] const std::string& rssPath, [[maybe_unused]] const any_t& rvarOld, + [[maybe_unused]] const any_t& rvarNew, [[maybe_unused]] const std::shared_ptr& rptrParamInfo) {} + + /** + * @brief Parameter dirty flag changed event. Overload this function to implement parameter dirty flag event + * handling. + * @param[in] rssPath Reference to the string containing the path of the parameter that changed. + * @param[in] uiOldFlags The old value of the flags. + * @param[in] uiNewFlags The new value of the flags. + * @param[in] uiMask The mask of the flags that are changed by the new value. + * @param[in] rvar Reference to the any holding the value of the parameter. + * @param[in] rptrParamInfo Reference to the smart pointer to the parameter information object. + */ + virtual void OnParamFlagChanged([[maybe_unused]] const std::string& rssPath, [[maybe_unused]] uint32_t uiOldFlags, + [[maybe_unused]] uint32_t uiNewFlags, [[maybe_unused]] uint32_t uiMask, [[maybe_unused]] const any_t& rvar, + [[maybe_unused]] const std::shared_ptr& rptrParamInfo) {} + + protected: + /** + * @brief Register the parameter in the parameter map. + * @tparam TVar Type of the variable. + * @tparam TConstruct The arguments for the parameter info construct function. + * @param[in] rtVar Reference to the parameter. + * @param[in] bLockable When set, the parameter is lockable. Only use with writable parameters. + * @param[in] bAutoDirty When set, the parameter dirty flag is detected automatically. Only use with writable parameters. + * @param[in] tConstruct The construct function arguments. + * @return Smart pointer to the parameter information structure. + */ + template + std::shared_ptr RegisterParameter(TVar& rtVar, bool bLockable, bool bAutoDirty, TConstruct... tConstruct); + + /** + * @brief Register a member parameter map into this parameter map. + * @param[in] rptrMember Reference to the smart pointer pointing to the member map. + */ + void RegisterMemberMap(const std::shared_ptr& rptrMember); + + /** + * @brief Initialize the parameter map. This function is called only once for initial map creation. This implementation + * doesn't do anything. The parameter map macros overload this function to allow map building. + */ + virtual void InitParamMap(); + + /** + * @brief Is a parameter map build necessary? + * @return Returns whether parameter map building is required. + */ + bool BuildNecessary() const; + + public: + /** + * @brief Return a vector with parameter information structures. Empty function called when the derived class doesn't + * implement its own parameter map. + * @return Vector containing parameter information structures. + */ + static std::vector> GetParamMapInfoStatic() + { + return {}; + } + + private: + + /** + * @brief Member registration. + */ + struct SParamRegistration + { + /** + * @brief Constructor for a parameter. + * @tparam TVar Type of the variable. + * @param[in] rptrParameter Reference to the smart pointer containing the parameter. + */ + template + SParamRegistration(const std::shared_ptr>& rptrParameter) : + eType(EType::parameter), ptrParameter(rptrParameter) + {} + + /** + * @brief Constructor for a member parameter map + * @param[in] rptrMember Reference to the smart pointer pointing to the member map. + */ + SParamRegistration(const std::shared_ptr& rptrMember) : + eType(EType::member_map), ptrMemberMap(rptrMember) + {} + + /** + * @brief Copy constructor + */ + SParamRegistration(const SParamRegistration& rRegistration) + { + eType = rRegistration.eType; + switch (rRegistration.eType) + { + case EType::parameter: + new (&ptrParameter) std::shared_ptr(rRegistration.ptrParameter); + break; + case EType::member_map: + new (&ptrMemberMap) std::shared_ptr(rRegistration.ptrMemberMap); + break; + } + } + + /** + * @brief Move constructor + */ + SParamRegistration(SParamRegistration&& rRegistration) + { + eType = rRegistration.eType; + switch (rRegistration.eType) + { + case EType::parameter: + new (&ptrParameter) std::shared_ptr(std::move(rRegistration.ptrParameter)); + break; + case EType::member_map: + new (&ptrMemberMap) std::shared_ptr(std::move(rRegistration.ptrMemberMap)); + break; + } + } + + /** + * @brief Destructor + */ + ~SParamRegistration() + { + switch (eType) + { + case EType::parameter: + ptrParameter.~shared_ptr(); + break; + case EType::member_map: + ptrMemberMap.~shared_ptr(); + break; + } + } + + /** + * @brief Get the parameter if this structure is defined as parameter. + * @return Smart pointer to the parameter or empty when there is no parameter. + */ + std::shared_ptr Parameter() const + { + if (eType == EType::parameter) + return ptrParameter; + return {}; + } + + /** + * @brief Get the member parameter map if this structure is defined as member parameter map. + * @return Smart pointer to the member map or empty when there is no member map. + */ + std::shared_ptr MemberMap() const + { + if (eType == EType::member_map) + return ptrMemberMap; + return {}; + } + + private: + enum class EType {parameter, member_map} eType; ///< Type of registration + union + { + std::shared_ptr ptrParameter; ///< Smart pointer to parameter of this map. + std::shared_ptr ptrMemberMap; ///< Smart pointer to a member containing another + ///< parameter map. + }; + }; + + mutable std::vector m_vecParamMapRegistration; ///< Vector with parameters. + }; +}; // namespace sdv + +#include "param_impl.inl" + +#endif // !defined PARAM_IMPL_H \ No newline at end of file diff --git a/export/support/param_impl.inl b/export/support/param_impl.inl new file mode 100644 index 0000000..83a6d67 --- /dev/null +++ b/export/support/param_impl.inl @@ -0,0 +1,732 @@ +/******************************************************************************** + * 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 PARAM_IMPL_INL +#define PARAM_IMPL_INL + +#ifndef PARAM_IMPL_H + #include "param_impl.h" +#endif //! defined PARAM_IMPL_H + +#include +#include + +/** + * @brief Software Defined Vehicle framework. + */ +namespace sdv +{ + template + inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const std::string& rssUnit, + const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam) + { + EParamType eTypeLocal = EParamType::boolean_param; + + if constexpr (std::is_same_v) + eTypeLocal = EParamType::boolean_param; + else if constexpr (std::is_arithmetic_v) + eTypeLocal = EParamType::number_param; // No number limitation + else if constexpr (TypeIsString()) + eTypeLocal = EParamType::string_param; // No pattern + else if constexpr (std::is_enum_v) + eTypeLocal = EParamType::enum_param; // No enumerator labels + else + static_assert(sdv::internal::always_false_v, "The parameter type is not supported!"); + + any_t anyDefaultValLocal; + if constexpr (std::is_enum_v) + { + static_assert(std::is_same_v>, TDefaultType>, + "The enum types of the value and the default value must be identical!"); + anyDefaultValLocal = static_cast>(tDefaultVal); + } + else + anyDefaultValLocal = tDefaultVal; + + uint32_t uiFlagsLocal = uiFlagsParam; + if (TypeIsReadOnly()) + uiFlagsLocal |= static_cast(EParamFlags::read_only); + if (uiFlagsLocal & static_cast(EParamFlags::read_only)) + uiFlagsLocal &= ~static_cast(EParamFlags::locked); // Readonly cannot write + Init(eTypeLocal, rssName, anyDefaultValLocal, rssUnit, rssCategory, rssDescription, uiFlagsLocal); + + if constexpr (std::is_enum_v) + uExtInfo.sEnumInfo.seqLabels = internal::GetLabelMapHelper().GetLabelMap(); + } + + template + inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const any_t& ranyLimitLow, + bool bIncludeLow, const any_t& ranyLimitHigh, bool bIncludeHigh, const std::string& rssUnit, const std::string& rssCategory, + const std::string& rssDescription, uint32_t uiFlagsParam) + { + static_assert(std::is_arithmetic_v && !std::is_same_v, + "The construct function for numeric types expects the type to be numeric."); + uint32_t uiFlagsLocal = uiFlagsParam; + if (TypeIsReadOnly()) + uiFlagsLocal |= static_cast(EParamFlags::read_only); + if (uiFlagsLocal & static_cast(EParamFlags::read_only)) + uiFlagsLocal &= ~static_cast(EParamFlags::locked); // Readonly cannot write + Init(EParamType::number_param, rssName, tDefaultVal, rssUnit, rssCategory, rssDescription, uiFlagsLocal); + uExtInfo.sNumberInfo.anyLowerLimit = ranyLimitLow; + uExtInfo.sNumberInfo.bIncludeLowerLinit = bIncludeLow; + uExtInfo.sNumberInfo.anyUpperLimit = ranyLimitHigh; + uExtInfo.sNumberInfo.bIncludeUpperLimit = bIncludeHigh; + } + + template + inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, const std::string& rssPattern, + const std::string& rssUnit, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam) + { + static_assert(TypeIsString(), "The construct function for string types expects the type to be a string."); + uint32_t uiFlagsLocal = uiFlagsParam; + if (TypeIsReadOnly()) + uiFlagsLocal |= static_cast(EParamFlags::read_only); + if (uiFlagsLocal & static_cast(EParamFlags::read_only)) + uiFlagsLocal &= ~static_cast(EParamFlags::locked); // Readonly cannot write + Init(EParamType::string_param, rssName, tDefaultVal, rssUnit, rssCategory, rssDescription, uiFlagsLocal); + uExtInfo.sStringInfo.ssPattern = rssPattern; + } + + template + inline CSdvParamInfo::CSdvParamInfo(TVar&, const std::string& rssName, TDefaultType tDefaultVal, + const sequence& rseqLabels, const std::string& rssCategory, const std::string& rssDescription, + uint32_t uiFlagsParam) + { + static_assert(std::is_enum_v || (std::is_integral_v && !std::is_same_v), + "The construct function for enumerators and bitmasks expects the type to be an enum or an integral type."); + uint32_t uiFlagsLocal = uiFlagsParam; + if (TypeIsReadOnly()) + uiFlagsLocal |= static_cast(EParamFlags::read_only); + if (uiFlagsLocal & static_cast(EParamFlags::read_only)) + uiFlagsLocal &= ~static_cast(EParamFlags::locked); // Readonly cannot write + if constexpr (std::is_enum_v) + { + Init(EParamType::enum_param, rssName, static_cast>(tDefaultVal), "", + rssCategory, rssDescription, uiFlagsLocal); + uExtInfo.sEnumInfo.seqLabels = rseqLabels; + } + else + { + Init(EParamType::bitmask_param, rssName, tDefaultVal, "", rssCategory, rssDescription, uiFlagsLocal); + uExtInfo.sBitmaskInfo.seqLabels = rseqLabels; + } + } + + inline CSdvParamInfo::CSdvParamInfo(const SParamInfo& rInfo) : SParamInfo(rInfo) + {} + + inline CSdvParamInfo::CSdvParamInfo(SParamInfo&& rInfo) : SParamInfo(std::move(rInfo)) + {} + + inline CSdvParamInfo& CSdvParamInfo::operator=(const SParamInfo& rsInfo) + { + static_cast(*this) = rsInfo; + return *this; + } + + inline CSdvParamInfo& CSdvParamInfo::operator=(SParamInfo&& rsInfo) + { + static_cast(*this) = std::move(rsInfo); + return *this; + } + + inline SParamInfo CSdvParamInfo::InfoStruct() const + { + return *this; + } + + inline const u8string& CSdvParamInfo::Name() const + { + return SParamInfo::ssName; + } + + inline u8string CSdvParamInfo::Path() const + { + sdv::u8string ssPath; + if (!SParamInfo::ssGroup.empty()) + ssPath += ssGroup + "."; + ssPath += ssName; + return ssPath; + } + + inline const any_t& CSdvParamInfo::DefaultVal() const + { + return SParamInfo::anyDefaultVal; + } + + inline const u8string& CSdvParamInfo::Group() const + { + return SParamInfo::ssGroup; + } + + inline const u8string& CSdvParamInfo::Description() const + { + return SParamInfo::ssDescription; + } + + inline const u8string& CSdvParamInfo::Unit() const + { + return SParamInfo::ssUnit; + } + + inline uint32_t CSdvParamInfo::Flags() const + { + return uiFlags; + } + + inline bool CSdvParamInfo::ReadOnly() const + { + return (uiFlags & static_cast(EParamFlags::read_only)) ? true : false; + } + + inline bool CSdvParamInfo::Temporary() const + { + return (uiFlags & static_cast(EParamFlags::temporary)) ? true : false; + } + + inline bool CSdvParamInfo::Dirty() const + { + return (uiFlags & static_cast(EParamFlags::dirty)) ? true : false; + } + + inline bool CSdvParamInfo::Locked() const + { + return (uiFlags & static_cast(EParamFlags::locked)) ? true : false; + } + + + inline bool CSdvParamInfo::Boolean() const + { + return get_switch() == EParamType::boolean_param; + } + + inline bool CSdvParamInfo::Numeric() const + { + return get_switch() == EParamType::number_param; + } + + inline bool CSdvParamInfo::String() const + { + return get_switch() == EParamType::string_param; + } + + inline bool CSdvParamInfo::Enum() const + { + return get_switch() == EParamType::enum_param; + } + + inline bool CSdvParamInfo::Bitmask() const + { + return get_switch() == EParamType::bitmask_param; + } + + inline std::pair CSdvParamInfo::NumericLimitLow() const + { + if (!Numeric()) return {}; + return std::make_pair(uExtInfo.sNumberInfo.anyLowerLimit, uExtInfo.sNumberInfo.bIncludeLowerLinit); + } + + inline std::pair CSdvParamInfo::NumericLimitHigh() const + { + if (!Numeric()) return {}; + return std::make_pair(uExtInfo.sNumberInfo.anyUpperLimit, uExtInfo.sNumberInfo.bIncludeUpperLimit); + } + + inline std::string CSdvParamInfo::StringPattern() const + { + if (!String()) return {}; + return uExtInfo.sStringInfo.ssPattern; + } + + inline sequence CSdvParamInfo::EnumBitmaskLabels() const + { + if (Enum()) + return uExtInfo.sEnumInfo.seqLabels; + else if (Bitmask()) + return uExtInfo.sBitmaskInfo.seqLabels; + else + return {}; + } + + inline void CSdvParamInfo::Init(EParamType eTypeParam, const std::string& rssName, const any_t& ranyDefaultVal, + const std::string& rssUnit, const std::string& rssCategory, const std::string& rssDescription, uint32_t uiFlagsParam) + { + switch_to(eTypeParam); + uiFlags = uiFlagsParam; + ssName = rssName; + anyDefaultVal = ranyDefaultVal; + ssUnit = rssUnit; + ssGroup = rssCategory; + ssDescription = rssDescription; + } + + namespace internal + { + template + inline CParamGuardian::CParamGuardian(bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct) : + CSdvParamInfo(tConstruct...), m_bLockable(bLockable), m_bAutoDirty(bAutoDirty) + {} + + inline SParamInfo CParamGuardian::InfoStruct() const + { + sdv::SParamInfo sInfo = CSdvParamInfo::InfoStruct(); + sInfo.uiFlags = Flags(); + return sInfo; + } + + inline uint32_t CParamGuardian::Flags() const + { + uint32_t uiFlagsTemp = CSdvParamInfo::Flags() & ~static_cast(EParamFlags::state_mask); + if (m_bLocked) + uiFlagsTemp |= static_cast(EParamFlags::locked); + if (m_bDirty) + uiFlagsTemp |= static_cast(EParamFlags::dirty); + return uiFlagsTemp; + } + + inline bool CParamGuardian::UpdateDirty(const sdv::any_t& ranyValue) + { + if (!m_bAutoDirty) return false; + bool bDirty = m_bDirty; + m_bDirty |= ranyValue != m_anyStored; + m_anyStored = ranyValue; + return m_bDirty != bDirty; + } + + inline bool CParamGuardian::SetDirty() + { + bool bDirty = m_bDirty; + m_bDirty = true; + return m_bDirty != bDirty; + } + + inline bool CParamGuardian::ResetDirty() + { + bool bDirty = m_bDirty; + m_bDirty = false; + return m_bDirty != bDirty; + } + + inline bool CParamGuardian::Dirty() const + { + return m_bDirty; + } + + inline bool CParamGuardian::Lockable() const + { + return m_bLockable; + } + + inline bool CParamGuardian::Lock() + { + if (!m_bLockable) + return false; + bool bLocked = m_bLocked; + m_bLocked = true; + return m_bLocked != bLocked; + } + + inline bool CParamGuardian::Unlock() + { + if (!m_bLockable) + return false; + bool bLocked = m_bLocked; + m_bLocked = false; + return m_bLocked != bLocked; + } + + inline bool CParamGuardian::Locked() const + { + return m_bLocked; + } + + inline bool CParamGuardian::CheckRestrictions(const any_t& ranyValue) + { + if (Numeric()) + { + // Check for lower limit + auto prLimitLow = NumericLimitLow(); + if (!prLimitLow.first.empty()) + { + if (prLimitLow.second) + { + if (ranyValue < prLimitLow.first) return false; + } else + { + if (ranyValue <= prLimitLow.first) return false; + } + } + // Check for higher limit + auto prLimitHigh = NumericLimitHigh(); + if (!prLimitHigh.first.empty()) + { + if (prLimitHigh.second) + { + if (ranyValue > prLimitHigh.first) return false; + } + else + { + if (ranyValue >= prLimitHigh.first) return false; + } + } + } + else if (String()) + { + // Check for compatibility to the regular expression pattern. + if (!StringPattern().empty()) + { + std::regex regexPattern(StringPattern()); + if (!std::regex_match(static_cast(ranyValue), regexPattern)) return false; + } + } + else if (Enum()) + { + // Check for occurrence in the label sequence + auto seqLabels = EnumBitmaskLabels(); + if (std::find_if(seqLabels.begin(), seqLabels.end(), [&](const auto& rsLabel) + { return ranyValue == rsLabel.anyValue; }) == seqLabels.end()) return false; + } + else if (Bitmask()) + { + // Check for the occurence of each set bit in the label sequence + auto seqLabels = EnumBitmaskLabels(); + uint64_t uiBitmask = ranyValue; + for (const auto& rsLabel : seqLabels) + uiBitmask &= ~static_cast(rsLabel.anyValue); + if (uiBitmask) return false; + } + + // All okay; restrictions were checked + return true; + } + + template + template + inline CParamValue::CParamValue(TVar& rtVar, bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct) : + CParamGuardian(bLockable, bAutoDirty, rtVar, tConstruct...), m_rtVar(rtVar) + { + // Assign the default value + if constexpr (!CSdvParamInfo::TypeIsReadOnly()) + { + m_rtVar = DefaultVal().get(); + UpdateDirty(DefaultVal()); + ResetDirty(); + } + } + + template + inline bool CParamValue::Set(const any_t& ranyValue) + { + // Assign the value + if constexpr (!CSdvParamInfo::TypeIsReadOnly()) + { + if (!Locked() && !ReadOnly() && CheckRestrictions(ranyValue)) + { + m_rtVar = ranyValue.get(); + UpdateDirty(ranyValue); + return true; + } + } + return false; + } + + template + inline any_t CParamValue::Get() const + { + // Remarks: in case of an enum, an explicit construction is needed. + return sdv::any_t(m_rtVar); + } + } // namespace internal + + inline std::shared_ptr CSdvParamMap::FindParamObject(const sdv::u8string& rssPath) + { + // Iterate through the registrations + for (const SParamRegistration& rsRegistration : m_vecParamMapRegistration) + { + // Member parameter map entry + if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) + { + auto ptrParameter = rsRegistration.MemberMap()->Access()->FindParamObject(rssPath); + if (ptrParameter) return ptrParameter; + } + + // Parameter entry + if (rsRegistration.Parameter() && rsRegistration.Parameter()->Path() == rssPath) + return rsRegistration.Parameter(); + } + return {}; + } + + inline std::shared_ptr CSdvParamMap::FindParamObject(const sdv::u8string& rssPath) const + { + // Iterate through the registrations + for (const SParamRegistration& rsRegistration : m_vecParamMapRegistration) + { + // Member parameter map entry + if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) + { + auto ptrParameter = rsRegistration.MemberMap()->Access()->FindParamObject(rssPath); + if (ptrParameter) + return ptrParameter; + } + + // Parameter entry + if (rsRegistration.Parameter() && rsRegistration.Parameter()->Path() == rssPath) + return rsRegistration.Parameter(); + } + return {}; + } + + inline sequence CSdvParamMap::GetParamPaths() const + { + // Split path function (splits the group from the parameter names) + auto fnSplitPath = [](const sdv::u8string& rssPath) -> std::pair + { + size_t nPos = rssPath.find_last_of('.'); + if (nPos == sdv::u8string::npos) + return std::make_pair("", rssPath); + return std::make_pair(rssPath.substr(0, nPos), rssPath.substr(nPos + 1)); + }; + + std::set setOneTime; + sequence seqPaths; + for (const auto& rsRegistration : m_vecParamMapRegistration) + { + // Add a path to the sequence. + auto fnAddPath = [&](const u8string& rssPath) + { + // Check if already present in the list + if (setOneTime.find(rssPath) != setOneTime.end()) return; + setOneTime.insert(rssPath); + + // Sort the paths per group, so all paths belonging to one group is returned together. + // Find thee first location where the group is larger than the one of this path. + auto prPath = fnSplitPath(rssPath); + auto itPos = seqPaths.begin(); + while (itPos != seqPaths.end()) + { + auto prStoredPath = fnSplitPath(*itPos); + if (prPath.first.compare(prStoredPath.first) < 0) break; + ++itPos; + } + + // Insert into the sequence + seqPaths.insert(itPos, rssPath); + }; + + // Add the chained member to the list + if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) + { + auto seqNamesMemberMap = rsRegistration.MemberMap()->Access()->GetParamPaths(); + for (const auto& rssPath : seqNamesMemberMap) + fnAddPath(rssPath); + } + + // Add the parameter to the list + if (rsRegistration.Parameter()) + fnAddPath(rsRegistration.Parameter()->Path()); + } + + return seqPaths; + } + + inline any_t CSdvParamMap::GetParam(const u8string& rssPath) const + { + auto ptrParam = FindParamObject(rssPath); + if (!ptrParam) return {}; + return ptrParam->Get(); + } + + inline bool CSdvParamMap::SetParam(const u8string& rssPath, any_t ranyValue) + { + auto ptrParam = FindParamObject(rssPath); + if (!ptrParam) return false; + return ptrParam->Set(ranyValue); + } + + inline SParamInfo CSdvParamMap::GetParamInfo(const u8string& rssPath) const + { + auto ptrParamInfo = FindParamObject(rssPath); + if (!ptrParamInfo) return {}; + return ptrParamInfo.get()->InfoStruct(); + } + + inline bool CSdvParamMap::IsParamDirty(const u8string& rssPath) const + { + auto ptrParam = FindParamObject(rssPath); + if (!ptrParam) return false; + return ptrParam->Dirty(); + } + + inline void CSdvParamMap::SetParamDirtyFlag(const std::string& rssPath) + { + auto ptrParam = FindParamObject(rssPath); + if (!ptrParam) return; + ptrParam->SetDirty(); + } + + inline void CSdvParamMap::ResetParamDirtyFlag(const u8string& rssPath) + { + auto ptrParam = FindParamObject(rssPath); + if (!ptrParam) return; + ptrParam->ResetDirty(); + } + + inline bool CSdvParamMap::IsParamMapDirty() const + { + std::set setOneTime; + for (const auto& rsRegistration : m_vecParamMapRegistration) + { + // Member parameter map entry + if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) + { + if (rsRegistration.MemberMap()->Access()->IsParamMapDirty()) + return true; + continue; + } + + // Parameter entry + if (rsRegistration.Parameter()) + { + // Check if already present in the list + if (setOneTime.find(rsRegistration.Parameter()->Name()) != setOneTime.end()) + continue; + setOneTime.insert(rsRegistration.Parameter()->Name()); + + // Check for dirtiness + if (rsRegistration.Parameter()->Dirty()) + return true; + } + } + return false; + } + + inline void CSdvParamMap::ResetParamMapDirtyFlags() + { + std::set setOneTime; + for (const auto& rsRegistration : m_vecParamMapRegistration) + { + // Member parameter map entry + if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) + { + rsRegistration.MemberMap()->Access()->ResetParamMapDirtyFlags(); + continue; + } + + // Parameter entry + if (rsRegistration.Parameter()) + { + // Check if already present in the list + if (setOneTime.find(rsRegistration.Parameter()->Name()) != setOneTime.end()) + continue; + setOneTime.insert(rsRegistration.Parameter()->Name()); + + // Reset dirtiness + rsRegistration.Parameter()->ResetDirty(); + } + } + } + + inline void CSdvParamMap::LockParamMap() + { + for (const auto& rsRegistration : m_vecParamMapRegistration) + { + // Member parameter map entry + if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) + { + rsRegistration.MemberMap()->Access()->LockParamMap(); + continue; + } + + // Parameter entry + if (rsRegistration.Parameter()) + { + // Lock + rsRegistration.Parameter()->Lock(); + } + } + } + + inline void CSdvParamMap::UnlockParamMap() + { + for (const auto& rsRegistration : m_vecParamMapRegistration) + { + // Member parameter map entry + if (rsRegistration.MemberMap() && rsRegistration.MemberMap()->Access()) + { + rsRegistration.MemberMap()->Access()->UnlockParamMap(); + continue; + } + + // Parameter entry + if (rsRegistration.Parameter()) + { + // Lock + rsRegistration.Parameter()->Unlock(); + } + } + } + + template + inline std::shared_ptr CSdvParamMap::RegisterParameter(TVar& rtVar, bool bLockable, bool bAutoDirty, + TConstruct... tConstruct) + { + auto ptrParam = std::make_shared>(rtVar, bLockable, bAutoDirty, tConstruct...); + m_vecParamMapRegistration.push_back(SParamRegistration(ptrParam)); + return ptrParam; + } + + inline void CSdvParamMap::RegisterMemberMap(const std::shared_ptr& rptrMember) + { + m_vecParamMapRegistration.push_back(SParamRegistration(rptrMember)); + } + + inline void CSdvParamMap::InitParamMap() + { + // Nothing to do here. + } + + inline bool CSdvParamMap::BuildNecessary() const + { + return m_vecParamMapRegistration.empty(); + } + + template + constexpr bool CSdvParamInfo::TypeIsString() + { + using TType2 = std::remove_cv_t; + using TRoot = std::remove_cv_t>>; + constexpr bool bArray = std::is_array_v; + constexpr bool bPointer = std::is_pointer_v; + constexpr bool bCharacter = std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v; + constexpr bool bStdString = std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v; + constexpr bool bSdvString = std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || std::is_same_v; + constexpr bool bStdPath = std::is_same_v; + return bStdString || bSdvString || (bArray && bCharacter) || (bPointer && bCharacter) || bStdPath; + } + + template + constexpr bool CSdvParamInfo::TypeIsReadOnly() + { + using TType2 = std::remove_cv_t; + constexpr bool bArray = std::is_array_v; + constexpr bool bPointer = std::is_pointer_v; + return bArray || bPointer || std::is_const_v; + } +}; // namespace sdv + + +#endif // !defined PARAM_IMPL_INL \ No newline at end of file diff --git a/export/support/pointer.h b/export/support/pointer.h index 682d0ee..42c8f71 100644 --- a/export/support/pointer.h +++ b/export/support/pointer.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 SDV_PTR_H #define SDV_PTR_H diff --git a/export/support/pointer.inl b/export/support/pointer.inl index 02c646a..ab22969 100644 --- a/export/support/pointer.inl +++ b/export/support/pointer.inl @@ -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 SDV_PTR_INL #define SDV_PTR_INL @@ -6,8 +19,8 @@ #endif #ifndef SDV_PTR_H -#error Do not include "pointer.inl" directly. Include "pointer.h" instead! -#endif //!defined SDV_PTR_H +#include "pointer.h" +#endif //! defined SDV_PTR_H #include "pointer.h" #include diff --git a/export/support/pssup.h b/export/support/pssup.h index b0bc594..aa12c60 100644 --- a/export/support/pssup.h +++ b/export/support/pssup.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 SDV_PS_SUPPORT_H #define SDV_PS_SUPPORT_H @@ -330,13 +343,12 @@ namespace sdv // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_CHAIN_BASE(sdv::CSdvObject) SDV_INTERFACE_ENTRY(IMarshallObjectIdent) SDV_INTERFACE_ENTRY(IMarshallLink) END_SDV_INTERFACE_MAP() // Object class type - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Proxy) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::proxy) /** * @brief Set the identification. Overload of IMarshallObjectIdent::SetIdentification. @@ -396,13 +408,12 @@ namespace sdv // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_CHAIN_BASE(sdv::CSdvObject) SDV_INTERFACE_ENTRY(IMarshallObjectIdent) SDV_INTERFACE_ENTRY(IMarshall) END_SDV_INTERFACE_MAP() // Object class type - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Stub) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::stub) protected: /** diff --git a/export/support/pssup.inl b/export/support/pssup.inl index 8931122..ded58f0 100644 --- a/export/support/pssup.inl +++ b/export/support/pssup.inl @@ -1,9 +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 SDV_PS_SUPPORT_INL #define SDV_PS_SUPPORT_INL #ifndef SDV_PS_SUPPORT_H -#error Do not include "pssup.inl" directly. Include "pssup.h" instead! -#endif //!defined SDV_PS_SUPPORT_H +#include "pssup.h" +#endif //! defined SDV_PS_SUPPORT_H #include "../interfaces/core_ps.h" #include "../interfaces/serdes/core_ps_serdes.h" @@ -23,8 +36,8 @@ namespace serdes { /** - * @brief Specialization of serializer/deserializer class for sdv::interface_t. - */ + * @brief Specialization of serializer/deserializer class for sdv::interface_t. + */ template <> class CSerdes { diff --git a/export/support/sdv_core.h b/export/support/sdv_core.h index 8d4024b..470f517 100644 --- a/export/support/sdv_core.h +++ b/export/support/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 SDV_CORE_H #define SDV_CORE_H @@ -48,6 +61,87 @@ namespace sdv { + /** + * @brief Get the object type string from the type + * @param[in] eType The object type to get the string from. + * @return The object type string. + */ + inline std::string ObjectType2String(sdv::EObjectType eType) + { + switch (eType) + { + case EObjectType::system_object: + return "SystemObject"; + case EObjectType::device: + return "Device"; + case EObjectType::platform_abstraction: + return "PlatformAbstraction"; + case EObjectType::vehicle_bus: + return "VehicleBus"; + case EObjectType::basic_service: + return "BasicService"; + case EObjectType::sensor: + return "Sensor"; + case EObjectType::actuator: + return "Actuator"; + case EObjectType::complex_service: + return "ComplexService"; + case EObjectType::vehicle_function: + return "VehicleFunction"; + case EObjectType::application: + return "Application"; + case EObjectType::proxy: + return "Proxy"; + case EObjectType::stub: + return "Stub"; + case EObjectType::utility: + return "Utility"; + default: + return "Unknown"; + } + } + + /** + * @brief Get the object type from the string + * @param[in] rssType Reference to the string identifying the object type. + * @return The object type or sdv::EObjectType::Undefined. + */ + inline EObjectType String2ObjectType(const std::string& rssType) + { + if (rssType == "SystemObject") + return EObjectType::system_object; + if (rssType == "Device") + return EObjectType::device; + if (rssType == "PlatformAbstraction") + return EObjectType::platform_abstraction; + if (rssType == "VehicleBus") + return EObjectType::vehicle_bus; + if (rssType == "BasicService") + return EObjectType::basic_service; + if (rssType == "Sensor") + return EObjectType::sensor; + if (rssType == "Actuator") + return EObjectType::actuator; + if (rssType == "ComplexService") + return EObjectType::complex_service; + if (rssType == "VehicleFunction") + return EObjectType::complex_service; + if (rssType == "Application") + return EObjectType::application; + if (rssType == "Proxy") + return EObjectType::proxy; + if (rssType == "Stub") + return EObjectType::stub; + if (rssType == "Utility") + return EObjectType::utility; + return EObjectType::undefined; + } +} // namespace sdv + + +#ifndef SDV_NO_LOADER +namespace sdv{ + namespace core { namespace internal @@ -345,6 +439,6 @@ namespace sdv #endif } } - +#endif // !defined SDV_NO_LOADER #endif // !define SDV_CORE_H diff --git a/export/support/sequence.h b/export/support/sequence.h index 3b5c1ef..f6a96d8 100644 --- a/export/support/sequence.h +++ b/export/support/sequence.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 SDV_SEQUENCE_H #define SDV_SEQUENCE_H diff --git a/export/support/sequence.inl b/export/support/sequence.inl index 2de6c7f..00ce4f7 100644 --- a/export/support/sequence.inl +++ b/export/support/sequence.inl @@ -1,9 +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 SDV_SEQUENCE_INL #define SDV_SEQUENCE_INL #ifndef SDV_SEQUENCE_H -#error Do not include "sequence.inl" directly. Include "sequence.h" instead! -#endif //!defined SDV_SEQUENCE_H +#include "sequence.h" +#endif //! defined SDV_SEQUENCE_H namespace sdv { diff --git a/export/support/serdes.h b/export/support/serdes.h index f8fdf43..6f6b6d4 100644 --- a/export/support/serdes.h +++ b/export/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 SDV_SERDES_H #define SDV_SERDES_H diff --git a/export/support/serdes.inl b/export/support/serdes.inl index a755c15..5aefdd2 100644 --- a/export/support/serdes.inl +++ b/export/support/serdes.inl @@ -1,9 +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 SDV_SERDES_INL #define SDV_SERDES_INL #ifndef SDV_SERDES_H -#error Do not include "serdes.inl" directly. Include "serdes.h" instead! -#endif //!defined SDV_SERDES_H +#include "serdes.h" +#endif //! defined SDV_SERDES_H #include diff --git a/export/support/signal_support.h b/export/support/signal_support.h index 8d80901..2485105 100644 --- a/export/support/signal_support.h +++ b/export/support/signal_support.h @@ -1,13 +1,16 @@ -/** +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG * - * @file signal_support.h - * @brief This file provides base-implementations and helpers for signals and signal handling. - * @version 0.1 - * @date 2022.11.14 - * @author Thomas.pfleiderer@zf.com - * @copyright Copyright ZF Friedrichshaven AG (c) 2022 + * 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_SUPPORT_H #define SIGNAL_SUPPORT_H @@ -409,7 +412,7 @@ namespace sdv template void Write(TType tVal, const CTransaction& rTransaction = CTransaction()) { - if (m_pSignalWrite) m_pSignalWrite->Write(any_t(tVal), rTransaction.GetTransaction()); + if (m_pSignalWrite) m_pSignalWrite->Write(tVal, rTransaction.GetTransaction()); } /** @@ -747,7 +750,7 @@ namespace sdv CSignal signal; if (pRegister) signal = CSignal(*this, rssName, - pRegister->RegisterTxSignal(rssName, any_t(tDefVal)), + pRegister->RegisterTxSignal(rssName, tDefVal), true); return signal; } diff --git a/export/support/string.h b/export/support/string.h index c43d084..bce9d64 100644 --- a/export/support/string.h +++ b/export/support/string.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 SDV_STRING_H #define SDV_STRING_H diff --git a/export/support/string.inl b/export/support/string.inl index 5ced7d7..dafb668 100644 --- a/export/support/string.inl +++ b/export/support/string.inl @@ -1,9 +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 SDV_STRING_INL #define SDV_STRING_INL #ifndef SDV_STRING_H -#error Do not include "string.inl" directly. Include "string.h" instead! -#endif //!defined SDV_STRING_H +#include "string.h" +#endif //! defined SDV_STRING_H #include #include @@ -2926,7 +2939,7 @@ namespace sdv template inline std::filesystem::path MakePath(const string_base& rssPath) { - std::filesystem::path path(MakeWString(rssPath).c_str()); + std::filesystem::path path(static_cast(MakeWString(rssPath))); return path; } diff --git a/export/support/timer.h b/export/support/timer.h index 620d89c..e51812a 100644 --- a/export/support/timer.h +++ b/export/support/timer.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 VAPI_TASK_TIMER_H #define VAPI_TASK_TIMER_H @@ -25,20 +38,21 @@ namespace sdv * @brief Constructor * @param[in] uiPeriod The period to create a timer for. * @param[in] fnCallback Callback function to be called on task execution. + * @param[in] bUseSimTimer Use the simulated task timer instead of the actual task timer. */ - CTaskTimer(uint32_t uiPeriod, std::function fnCallback) : + CTaskTimer(uint32_t uiPeriod, std::function fnCallback, bool bUseSimTimer = false) : m_ptrCallback(std::make_unique(fnCallback)) { if (!m_ptrCallback) return; if (!uiPeriod) return; // Get the task timer service. - sdv::core::ITaskTimer* pTaskTimer = sdv::core::GetObject("TaskTimerService"); - if (!pTaskTimer) - { + sdv::core::ITaskTimer* pTaskTimer = nullptr; + if (bUseSimTimer) pTaskTimer = sdv::core::GetObject("SimulationTaskTimerService"); - if (!pTaskTimer) return; - } + else + pTaskTimer = sdv::core::GetObject("TaskTimerService"); + if (!pTaskTimer) return; // Create the timer m_pTimer = pTaskTimer->CreateTimer(uiPeriod, m_ptrCallback.get()); @@ -49,18 +63,20 @@ namespace sdv * @param[in] uiPeriod The period to create a timer for. * @param[in] pTask Pointer to the interface of the task object to be called. The object must expose * sdv::core::ITastExecute. - */ - CTaskTimer(uint32_t uiPeriod, sdv::IInterfaceAccess* pTask) + * @param[in] bUseSimTimer Use the simulated task timer instead of the actual task timer. + */ + CTaskTimer(uint32_t uiPeriod, sdv::IInterfaceAccess* pTask, bool bUseSimTimer = false) { if (!uiPeriod) return; // Get the task timer service. - sdv::core::ITaskTimer* pTaskTimer = sdv::core::GetObject("TaskTimerService"); - if (!pTaskTimer) - { + sdv::core::ITaskTimer* pTaskTimer = nullptr; + if (bUseSimTimer) pTaskTimer = sdv::core::GetObject("SimulationTaskTimerService"); - if (!pTaskTimer) return; - } + else + pTaskTimer = sdv::core::GetObject("TaskTimerService"); + if (!pTaskTimer) + return; // Create the timer m_pTimer = pTaskTimer->CreateTimer(uiPeriod, pTask); diff --git a/export/support/toml.h b/export/support/toml.h index ba8ba88..659ad17 100644 --- a/export/support/toml.h +++ b/export/support/toml.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 SDV_CONFIG_H #define SDV_CONFIG_H @@ -43,11 +56,17 @@ namespace sdv::toml */ virtual operator bool() const; + /** + * @brief Get the underlying interface. + * @return The interface pointer. + */ + TInterfaceAccessPtr GetInterface(); + /** * @brief Return the node name. * @return String containing the node name. */ - sdv::u8string GetName(); + sdv::u8string GetName() const; /** * @brief Retrurn the node qualified path including the parent path. @@ -55,19 +74,58 @@ namespace sdv::toml * to the node can be used to directly access the node. * @return String containing the qualified path to the node. */ - sdv::u8string GetQualifiedPath(); + sdv::u8string GetQualifiedPath() const; /** * @brief Get the node type. * @return The node type. */ - ENodeType GetType(); + ENodeType GetType() const; + + /** + * @brief Return any associated comment text for this node. + * @return The node comment or an empty string when there is not comment for this node. + */ + std::string GetComment() const; + + /** + * @brief Set comment for this node. Comments are placed before the node if this node is a table or an array and placed + * behind the node if this node is a value node. + * @param rssComment Reference to the comment string. + */ + void SetComment(const std::string& rssComment); /** * @brief Get the node value if the node contains a value. - * @return The node value. + * @return The node value or empty when the node doesn't have a value. */ - sdv::any_t GetValue(); + sdv::any_t GetValue() const; + + /** + * @brief Automatically cast the value to a string. + * @return String to return or an empty string. + */ + std::string GetValueAsString() const; + + /** + * @brief Automatically cast the value to a path (only for string values). + * @return Path to return or an empty path. + */ + std::filesystem::path GetValueAsPath() const; + + /** + * @brief Set the node value if the node is a value node. + * @remarks The value type depends on the node type and will be converted if necessary. + * @param[in] ranyValue Reference to the value to set. + * @return Returns 'true' on successful setting; 'false' otherwise. + */ + bool SetValue(const sdv::any_t& ranyValue); + + /** + * @brief Delete the node including all child nodes. + * @return Returns 'true' on successful setting; 'false' otherwise. + */ + bool Delete(); /** * @brief Clear the node class. @@ -76,7 +134,7 @@ namespace sdv::toml /** * @brief Get the TOML string from this node including all children. - * @return The TOMl string. + * @return The TOML string. */ sdv::u8string GetTOML() const; @@ -170,6 +228,140 @@ namespace sdv::toml */ CNode GetDirect(const sdv::u8string& rssNode) const; + /** + * @brief Insert a value node before the provided position. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as + * sdv::toml::npos when adding the node at the end. + * @param[in] rssName Reference to the name of the new value node. If this collection is an array, the name is ignored. + * Otherwise the name must be unique within this collection. + * @param[in] ranyValue The value to assign to the node. The value also determines the type of value node. + * @return Returns the node when successfully inserted or an empty node when not. + */ + CNode InsertValue(size_t nIndex, const std::string& rssName, const sdv::any_t& ranyValue); + + /** + * @brief Add a value node to the collection. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @param[in] rssName Reference to the name of the new array collection node. If this collection is an array, the name is + * ignored. Otherwise the name must be unique within this collection. + * @param[in] ranyValue The value to assign to the node. The value also determines the type of value node. + * @return Returns the node when successfully added or an empty node when not. + */ + CNode AddValue(const std::string& rssName, const sdv::any_t& ranyValue); + + /** + * @brief Insert an array collection before the provided position. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as + * sdv::toml::npos when adding the node at the end. + * @param[in] rssName Reference to the name of the new value node. If this collection is an array, the name is ignored. + * Otherwise the name must be unique within this collection. + * @return Returns the collection node when successfully inserted or an empty node when not. + */ + CNodeCollection InsertArray(size_t nIndex, const std::string& rssName); + + /** + * @brief Add an array collection to this collection. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @param[in] rssName Reference to the name of the new array collection node. If this collection is an array, the name is + * ignored. Otherwise the name must be unique within this collection. + * @return Returns the collection node when successfully added or an empty node when not. + */ + CNodeCollection AddArray(const std::string& rssName); + + /** + * @brief Insert a table collection before the provided position. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as + * sdv::toml::npos when adding the node at the end. + * @param[in] rssName Reference to the name of the new table collection node. If this collection is an array, the name is + * ignored. Otherwise the name must be unique within this collection. + * @param[in] bFavorInline When set, the node will be added as inline collection node. When not, the node will be inserted + * as inline collection if the this collection is also inline, as standard when not. + * @return Returns the collection node when successfully inserted or an empty node when not. + */ + CNodeCollection InsertTable(size_t nIndex, const std::string& rssName, bool bFavorInline = false); + + /** + * @brief Add a table collection to this collection. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @param[in] rssName Reference to the name of the new table collection node. If this collection is an array, the name is + * ignored. Otherwise the name must be unique within this collection. + * @param[in] bFavorInline When set, the node will be added as inline collection node. When not, the node will be inserted + * as inline collection if the this collection is also inline, as standard when not. + * @return Returns the collection node when successfully added or an empty node when not. + */ + CNodeCollection AddTable(const std::string& rssName, bool bFavorInline = false); + + /** + * @brief Insert a table array collection before the provided position. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @remarks A table array is an array with table inside. Inserting a table array node can also be done by creating an array, + * if not existing already, and adding a table to the array. + * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as + * sdv::toml::npos when adding the node at the end. + * @param[in] rssName Reference to the name of the new table array node. If this collection is an array, the name is + * ignored. Otherwise the name must be unique within this collection. + * @param[in] bFavorInline When set, the node will be added as inline collection node. When not, the node will be inserted + * as inline collection if the this collection is also inline, as standard when not. + * @return Returns the collection node when successfully inserted or an empty node when not. The collection node represents + * a table collection. + */ + CNodeCollection InsertTableArray(size_t nIndex, const std::string& rssName, bool bFavorInline = false); + + /** + * @brief Add a table array collection to this collection. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @remarks A table array is an array with table inside. Inserting a table array node can also be done by creating an array, + * if not existing already, and adding a table to the array. + * @param[in] rssName Reference to the name of the new table array node. If this collection is an array, the name is + * ignored. Otherwise the name must be unique within this collection. + * @param[in] bFavorInline When set, the node will be added as inline collection node. When not, the node will be inserted + * as inline collection if the this collection is also inline, as standard when not. + * @return Returns the collection node when successfully inserted or an empty node when not. The collection node represents + * a table collection. + */ + CNodeCollection AddTableArray(const std::string& rssName, bool bFavorInline = false); + + /** + * @brief Insert a TOML string to the collection. All nodes specified in the TOML will be added in the collection except + * when the nodes already exist. Comment and whitespace are preserved when possible. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as + * sdv::toml::npos when adding the node at the end. + * @param[in] rssTOML Reference to the TOML string containing the nodes. The TOML string can be empty, which is not an + * error. If required the TOML nodes are converted to inline nodes. + * @param[in] bAllowPartial When set, duplicate nodes (already present in this collection) will be ignored and do not + * cause an error. When not set, duplicate nodes will cause the function to return without inserting any node. + * @return Returns 1 if the complete TOMl could be inserted, 0 if no TOML could be inserted or -1 when the TOML could be + * partially inserted. + */ + int InsertTOML(size_t nIndex, const std::string& rssTOML, bool bAllowPartial = false); + + /** + * @brief Add a TOML string to this collection. All nodes specified in the TOML will be added in the collection except + * when the nodes already exist. Comment and whitespace are preserved when possible. + * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before + * standard nodes. + * @param[in] rssTOML Reference to the TOML string containing the nodes. The TOML string can be empty, which is not an + * error. If required the TOML nodes are converted to inline nodes. + * @param[in] bAllowPartial When set, duplicate nodes (already present in this collection) will be ignored and do not + * cause an error. When not set, duplicate nodes will cause the function to return without inserting any node. + * @return Returns 1 if the complete TOMl could be inserted, 0 if no TOML could be inserted or -1 when the TOML could be + * partially inserted. + */ + int AddTOML(const std::string& rssTOML, bool bAllowPartial = false); + private: INodeCollection* m_pCollection = nullptr; ///< Pointer to the node collection interface. }; @@ -225,8 +417,7 @@ namespace sdv::toml inline CNode::CNode(const TInterfaceAccessPtr& rptrNode) { m_pNodeInfo = rptrNode.GetInterface(); - if (!m_pNodeInfo) - return; + if (!m_pNodeInfo) return; m_ptrNode = rptrNode; } @@ -250,26 +441,95 @@ namespace sdv::toml return m_pNodeInfo ? true : false; } - inline sdv::u8string CNode::GetName() + inline TInterfaceAccessPtr CNode::GetInterface() + { + return m_ptrNode; + } + + inline sdv::u8string CNode::GetName() const { return m_pNodeInfo ? m_pNodeInfo->GetName() : sdv::u8string(); } - inline sdv::u8string CNode::GetQualifiedPath() + inline sdv::u8string CNode::GetQualifiedPath() const { return m_pNodeInfo ? m_pNodeInfo->GetPath(true) : sdv::u8string(); } - inline ENodeType CNode::GetType() + inline ENodeType CNode::GetType() const { return m_pNodeInfo ? m_pNodeInfo->GetType() : ENodeType::node_invalid; } - inline sdv::any_t CNode::GetValue() + inline std::string CNode::GetComment() const + { + if (!m_pNodeInfo) return {}; + std::string ssComment; + switch (GetType()) + { + case ENodeType::node_boolean: + case ENodeType::node_integer: + case ENodeType::node_floating_point: + case ENodeType::node_string: + ssComment = m_pNodeInfo->GetComment(INodeInfo::ECommentType::comment_behind); + if (ssComment.empty()) + ssComment = m_pNodeInfo->GetComment(INodeInfo::ECommentType::comment_before); + break; + default: + ssComment = m_pNodeInfo->GetComment(INodeInfo::ECommentType::comment_before); + if (ssComment.empty()) + ssComment = m_pNodeInfo->GetComment(INodeInfo::ECommentType::comment_behind); + break; + } + return ssComment; + } + + inline void CNode::SetComment(const std::string& rssComment) + { + if (!m_pNodeInfo) return; + switch (GetType()) + { + case ENodeType::node_boolean: + case ENodeType::node_integer: + case ENodeType::node_floating_point: + case ENodeType::node_string: + m_pNodeInfo->SetComment(INodeInfo::ECommentType::comment_behind, rssComment); + break; + default: + m_pNodeInfo->SetComment(INodeInfo::ECommentType::comment_before, rssComment); + break; + } + } + + inline sdv::any_t CNode::GetValue() const { return m_pNodeInfo ? m_pNodeInfo->GetValue() : sdv::any_t(); } + inline std::string CNode::GetValueAsString() const + { + return m_pNodeInfo ? m_pNodeInfo->GetValue().get() : std::string(); + } + + inline std::filesystem::path CNode::GetValueAsPath() const + { + return m_pNodeInfo ? m_pNodeInfo->GetValue().get() : std::filesystem::path(); + } + + inline bool CNode::SetValue(const sdv::any_t& ranyValue) + { + INodeUpdate* pNodeUpdate = m_ptrNode.GetInterface(); + if (!pNodeUpdate) return false; + return pNodeUpdate->ChangeValue(ranyValue); + } + + inline bool CNode::Delete() + { + INodeUpdate* pNodeUpdate = m_ptrNode.GetInterface(); + if (!pNodeUpdate) return false; + return pNodeUpdate->DeleteNode(); + } + inline void CNode::Clear() { m_ptrNode = nullptr; @@ -345,6 +605,105 @@ namespace sdv::toml return m_pCollection ? CNode(m_pCollection->GetNodeDirect(rssNode)) : CNode(); } + inline CNode CNodeCollection::InsertValue(size_t nIndex, const std::string& rssName, const sdv::any_t& ranyValue) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return {}; + return CNode(pInsert->InsertValue(static_cast(nIndex), rssName, ranyValue)); + } + + inline CNode CNodeCollection::AddValue(const std::string& rssName, const sdv::any_t& ranyValue) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return {}; + return CNode(pInsert->InsertValue(npos, rssName, ranyValue)); + } + + inline CNodeCollection CNodeCollection::InsertArray(size_t nIndex, const std::string& rssName) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return {}; + return CNodeCollection(pInsert->InsertArray(static_cast(nIndex), rssName)); + } + + inline CNodeCollection CNodeCollection::AddArray(const std::string& rssName) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return {}; + return CNodeCollection(pInsert->InsertArray(npos, rssName)); + } + + inline CNodeCollection CNodeCollection::InsertTable(size_t nIndex, const std::string& rssName, bool bFavorInline /*= false*/) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return {}; + return CNodeCollection(pInsert->InsertTable(static_cast(nIndex), rssName, + bFavorInline ? INodeCollectionInsert::EInsertPreference::prefer_inline : + INodeCollectionInsert::EInsertPreference::prefer_standard)); + } + + inline CNodeCollection CNodeCollection::AddTable(const std::string& rssName, bool bFavorInline /*= false*/) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return {}; + return CNodeCollection(pInsert->InsertTable(npos, rssName, + bFavorInline ? INodeCollectionInsert::EInsertPreference::prefer_inline : + INodeCollectionInsert::EInsertPreference::prefer_standard)); + } + + inline CNodeCollection CNodeCollection::InsertTableArray(size_t nIndex, const std::string& rssName, + bool bFavorInline /*= false*/) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return {}; + return CNodeCollection(pInsert->InsertTableArray(static_cast(nIndex), rssName, + bFavorInline ? INodeCollectionInsert::EInsertPreference::prefer_inline : + INodeCollectionInsert::EInsertPreference::prefer_standard)); + } + + inline CNodeCollection CNodeCollection::AddTableArray(const std::string& rssName, bool bFavorInline /*= false*/) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return {}; + return CNodeCollection(pInsert->InsertTableArray(npos, rssName, + bFavorInline ? INodeCollectionInsert::EInsertPreference::prefer_inline : + INodeCollectionInsert::EInsertPreference::prefer_standard)); + } + + inline int CNodeCollection::InsertTOML(size_t nIndex, const std::string& rssTOML, bool bAllowPartial /*= false*/) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return 0; + INodeCollectionInsert::EInsertResult eRet = pInsert->InsertTOML(static_cast(nIndex), rssTOML, !bAllowPartial); + switch (eRet) + { + case INodeCollectionInsert::EInsertResult::insert_success: + return 1; + case INodeCollectionInsert::EInsertResult::insert_partly_success: + return -1; + case INodeCollectionInsert::EInsertResult::insert_fail: + default: + return 0; + } + } + + inline int CNodeCollection::AddTOML(const std::string& rssTOML, bool bAllowPartial /*= false*/) + { + INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); + if (!pInsert) return 0; + INodeCollectionInsert::EInsertResult eRet = pInsert->InsertTOML(npos, rssTOML, !bAllowPartial); + switch (eRet) + { + case INodeCollectionInsert::EInsertResult::insert_success: + return 1; + case INodeCollectionInsert::EInsertResult::insert_partly_success: + return -1; + case INodeCollectionInsert::EInsertResult::insert_fail: + default: + return 0; + } + } + inline CTOMLParser::CTOMLParser(const std::string& rssConfig) { Process(rssConfig); diff --git a/framework_tests/CMakeLists.txt b/framework_tests/CMakeLists.txt index 873b7ff..5187346 100644 --- a/framework_tests/CMakeLists.txt +++ b/framework_tests/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Enforce CMake version 3.20 cmake_minimum_required (VERSION 3.20) cmake_policy (VERSION 3.20) diff --git a/framework_tests/dbc_util/CMakeLists.txt b/framework_tests/dbc_util/CMakeLists.txt index 9079e2a..491cf30 100644 --- a/framework_tests/dbc_util/CMakeLists.txt +++ b/framework_tests/dbc_util/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + set(DBC_CONFIGURATION_FAILED FALSE) # Execute sdv_dbc_util to create datalink component diff --git a/framework_tests/test_vss/CMakeLists.txt b/framework_tests/test_vss/CMakeLists.txt index 555cf61..24606c5 100644 --- a/framework_tests/test_vss/CMakeLists.txt +++ b/framework_tests/test_vss/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Define project project (VSSComponentsTests VERSION 1.0 LANGUAGES CXX) diff --git a/framework_tests/test_vss/load_components_test.cpp b/framework_tests/test_vss/load_components_test.cpp index 10c1fce..b154674 100644 --- a/framework_tests/test_vss/load_components_test.cpp +++ b/framework_tests/test_vss/load_components_test.cpp @@ -1,4 +1,14 @@ -#include + /******************************************************************************** + * 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 + ********************************************************************************/ + + #include #include #include #include diff --git a/framework_tests/test_vss_formula/CMakeLists.txt b/framework_tests/test_vss_formula/CMakeLists.txt index 5ac4ca6..35f6404 100644 --- a/framework_tests/test_vss_formula/CMakeLists.txt +++ b/framework_tests/test_vss_formula/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Define project project (VSSComponentsFormulaTests VERSION 1.0 LANGUAGES CXX) diff --git a/framework_tests/test_vss_formula/load_components_test.cpp b/framework_tests/test_vss_formula/load_components_test.cpp index 7ee6c3d..02417af 100644 --- a/framework_tests/test_vss_formula/load_components_test.cpp +++ b/framework_tests/test_vss_formula/load_components_test.cpp @@ -1,4 +1,14 @@ -#include + /******************************************************************************** + * 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 + ********************************************************************************/ + + #include #include #include #include diff --git a/framework_tests/vss_util/CMakeLists.txt b/framework_tests/vss_util/CMakeLists.txt index 393133b..1b475bf 100644 --- a/framework_tests/vss_util/CMakeLists.txt +++ b/framework_tests/vss_util/CMakeLists.txt @@ -1,4 +1,12 @@ - +#******************************************************************************* +# 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 +#******************************************************************************* set(VSS_CONFIGURATION_FAILED FALSE) diff --git a/global/ascformat/ascreader.cpp b/global/ascformat/ascreader.cpp index 7d34e49..deb5904 100644 --- a/global/ascformat/ascreader.cpp +++ b/global/ascformat/ascreader.cpp @@ -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 "ascreader.h" #include #include diff --git a/global/ascformat/ascreader.h b/global/ascformat/ascreader.h index 11a42c0..6502114 100644 --- a/global/ascformat/ascreader.h +++ b/global/ascformat/ascreader.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 ASC_FILE_READER_H #define ASC_FILE_READER_H diff --git a/global/ascformat/ascwriter.cpp b/global/ascformat/ascwriter.cpp index 5df17bf..6c1fb3d 100644 --- a/global/ascformat/ascwriter.cpp +++ b/global/ascformat/ascwriter.cpp @@ -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 "ascwriter.h" #include diff --git a/global/ascformat/ascwriter.h b/global/ascformat/ascwriter.h index 121c58c..e55d032 100644 --- a/global/ascformat/ascwriter.h +++ b/global/ascformat/ascwriter.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 ASC_FILE_WRITER_H #define ASC_FILE_WRITER_H diff --git a/global/base64.h b/global/base64.h index 8f5ce34..4d9d1d6 100644 --- a/global/base64.h +++ b/global/base64.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 BASE64_H #define BASE64_H diff --git a/global/cmdlnparser/cmdlnparser.cpp b/global/cmdlnparser/cmdlnparser.cpp index 409fef8..7a6e2d8 100644 --- a/global/cmdlnparser/cmdlnparser.cpp +++ b/global/cmdlnparser/cmdlnparser.cpp @@ -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 "cmdlnparser.h" #ifdef _WIN32 @@ -537,11 +550,33 @@ void CCommandLine::DumpArguments(std::ostream& rstream, bool bAll /*= true*/) co std::vector CCommandLine::IncompatibleArguments(size_t nArgumentGroup, bool bFull /*= true*/) const { - std::vector vecIncompatible; + // Create a copy of the list and remove all arguments that are available and fit. + auto lstSuppliedCopy = m_lstSupplied; for (auto& prArgument : m_lstSupplied) { // Only valid for options, not for default arguments - if (prArgument.first.get().CheckFlag(EArgumentFlags::default_argument)) continue; + if (prArgument.first.get().CheckFlag(EArgumentFlags::default_argument) || + prArgument.first.get().PartOfArgumentGroup(nArgumentGroup)) + { + // Remove the arguments from the supplied copy list + auto itArgumentCopy = lstSuppliedCopy.begin(); + while (itArgumentCopy != lstSuppliedCopy.end()) + { + if (itArgumentCopy->second == prArgument.second) + itArgumentCopy = lstSuppliedCopy.erase(itArgumentCopy); + else + ++itArgumentCopy; + } + } + } + + // Left over are the arguments that do not fit. + std::vector vecIncompatible; + for (auto& prArgument : lstSuppliedCopy) + { + // Only valid for options, not for default arguments + if (prArgument.first.get().CheckFlag(EArgumentFlags::default_argument)) + continue; // Is the argument compatible? if (prArgument.first.get().PartOfArgumentGroup(nArgumentGroup)) continue; diff --git a/global/cmdlnparser/cmdlnparser.h b/global/cmdlnparser/cmdlnparser.h index 07ed1ae..df414e6 100644 --- a/global/cmdlnparser/cmdlnparser.h +++ b/global/cmdlnparser/cmdlnparser.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 CMDLN_PARSER_H #define CMDLN_PARSER_H @@ -428,8 +441,8 @@ private: uint32_t m_uiParseFlags = 0; ///< The parse flags supplied to the parse function. std::shared_ptr m_ptrDefaultArg; ///< Default argument (if available). std::list> m_lstOptionArgs; ///< List of configured option arguments (in order of definition). - std::map> m_mapSortedOptions; ///< Map with sorted options. - std::map> m_mapSortedSubOptions; ///< Map with sorted sub-options. + std::multimap> m_mapSortedOptions; ///< Map with sorted options. + std::multimap> m_mapSortedSubOptions; ///< Map with sorted sub-options. std::shared_ptr m_ptrCurrentGroup; ///< Current group to assign the options to. std::list, std::string>> m_lstSupplied; ///< List of supplied arguments. size_t m_nFixedWidth = 0; ///< Fixed with limit (or 0 for dynamic width). @@ -2677,21 +2690,21 @@ inline void CCommandLine::Parse(size_t nArgs, const TCharType** rgszArgs) }; // Find the argument + // NOTE: Multiple arguments with the same name, but different processing can be defined (based on the argument groups they + // might or might not have relevance). Therefore, process all and do not stop after on argument. bool bFound = false; if (bOption) { for (auto& rvtOption : m_mapSortedOptions) { - bFound = fnFindAndAssign(rvtOption.second, rvtOption.first); - if (bFound) break; + bFound |= fnFindAndAssign(rvtOption.second, rvtOption.first); } } else if (bSubOption) { for (auto& rvtOption : m_mapSortedSubOptions) { - bFound = fnFindAndAssign(rvtOption.second, rvtOption.first); - if (bFound) break; + bFound |= fnFindAndAssign(rvtOption.second, rvtOption.first); } } else // Default argument bFound = m_ptrDefaultArg ? fnFindAndAssign(*m_ptrDefaultArg.get(), {}) : false; diff --git a/global/dbcparser/dbcparser.cpp b/global/dbcparser/dbcparser.cpp index a7e43e6..80410b6 100644 --- a/global/dbcparser/dbcparser.cpp +++ b/global/dbcparser/dbcparser.cpp @@ -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 "dbcparser.h" #include #include diff --git a/global/dbcparser/dbcparser.h b/global/dbcparser/dbcparser.h index f00ac19..ff3f83d 100644 --- a/global/dbcparser/dbcparser.h +++ b/global/dbcparser/dbcparser.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 DBCPARSER_H #define DBCPARSER_H diff --git a/global/debug_log.h b/global/debug_log.h index 20876fa..24cfc50 100644 --- a/global/debug_log.h +++ b/global/debug_log.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 DEBUG_LOG_H #define DEBUG_LOG_H @@ -18,7 +31,7 @@ /** * @brief Enable debug log by defining the ENABLE_DEBUG_LOG to a value other than zero. */ -#define ENABLE_DEBUG_LOG 1 +#define ENABLE_DEBUG_LOG 0 #endif #ifndef DECOUPLED_DEBUG_LOG @@ -46,9 +59,11 @@ namespace debug /** * @brief Constructor, creating the file. If the file exists, it will be overwritten. */ - CLogger() + CLogger() : m_pathLogFile(GetExecDirectory() / GetExecFilename().replace_extension(".log")) { - m_pathLogFile = GetExecDirectory() / GetExecFilename().replace_extension(".log"); +#if DECOUPLED_DEBUG_LOG == 0 + StartLog(); +#endif } /** @@ -67,6 +82,8 @@ namespace debug // Prevent the logger mutex to be still in use. std::unique_lock lock(m_mtxLogger); lock.unlock(); +#else + FinishLog(); #endif } @@ -89,7 +106,6 @@ namespace debug while (!m_threadLogger.joinable()) std::this_thread::sleep_for(std::chrono::milliseconds(10)); } - std::cout << rss << std::endl; // Add message to the log queue m_queueLogger.push(sMsg); @@ -123,12 +139,16 @@ namespace debug { std::ofstream fstream; fstream.open(m_pathLogFile, std::ios_base::out | std::ios_base::trunc); + + // Make one line out of it... prevents a cut in the middle when multiple streams are being used in parallel. + std::stringstream sstreamMsg; + sstreamMsg << "PID#" << getpid() << ": Starting log of " << GetExecFilename().generic_u8string() << std::endl; if (fstream.is_open()) { - fstream << "Starting log of " << GetExecFilename().generic_u8string() << std::endl; + fstream << sstreamMsg.str(); fstream.close(); } - std::cout << "Starting log of " << GetExecFilename().generic_u8string() << std::endl << std::flush; + std::cout << sstreamMsg.str() << std::flush; } /** @@ -139,12 +159,16 @@ namespace debug // Finish logging std::ofstream fstream; fstream.open(m_pathLogFile, std::ios_base::out | std::ios_base::app); + + // Make one line out of it... prevents a cut in the middle when multiple streams are being used in parallel. + std::stringstream sstreamMsg; + sstreamMsg << "PID#" << getpid() << ": Ending log of " << GetExecFilename().generic_u8string() << std::endl; if (fstream.is_open()) { - fstream << "End log of " << GetExecFilename().generic_u8string() << std::endl; + fstream << sstreamMsg.str(); fstream.close(); } - std::cout << "End log of " << GetExecFilename().generic_u8string() << std::endl << std::flush; + std::cout << sstreamMsg.str() << std::flush; } /** @@ -160,20 +184,24 @@ namespace debug /** * @brief Log a message. * @param[in] rfstream Reference to the stream to log to. - * @param[in9 rsMsg Reference to the message to log. + * @param[in] rsMsg Reference to the message to log. */ void LogMsg(std::ofstream& rfstream, const SLogMsg& rsMsg) { std::string ssIndent(rsMsg.nDepth, '>'); if (!ssIndent.empty()) ssIndent += ' '; + + // Make one line out of it... prevents a cut in the middle when multiple streams are being used in parallel. + std::stringstream sstreamMsg; + sstreamMsg << "PID#" << getpid() << " THREAD#" << rsMsg.id << ": " << ssIndent << rsMsg.ssMsg << std::endl; if (rfstream.is_open()) { - rfstream << rsMsg.id << ": " << ssIndent << rsMsg.ssMsg << std::endl; + rfstream << sstreamMsg.str(); rfstream.close(); } - std::cout << rsMsg.id << ": " << ssIndent << rsMsg.ssMsg << std::endl << std::flush; + std::cout << sstreamMsg.str() << std::flush; } #if DECOUPLED_DEBUG_LOG != 0 @@ -241,7 +269,7 @@ namespace debug return nDepth; } - std::filesystem::path m_pathLogFile; ///< Path to the log file. + std::filesystem::path m_pathLogFile; ///< Path to the log file. #if DECOUPLED_DEBUG_LOG != 0 std::mutex m_mtxLogger; ///< Protect against multiple log entries at the same time. std::thread m_threadLogger; ///< Logger thread @@ -271,10 +299,10 @@ namespace debug * @param[in] rssFunc Reference to the function name. * @param[in] rssFile Reference to the source file the function is implemented in. * @param[in] nLine Reference to the line the function logger object is created. - */ + */ CFuncLogger(const std::string& rssFunc, const std::string& rssFile, size_t nLine) : m_ssFunc(rssFunc) { - GetLogger().Log(std::string("Enter function:") + rssFunc + " - file " + rssFile + " - line " + std::to_string(nLine)); + GetLogger().Log(std::string("ENTER FUNCTION: ") + rssFunc + " - FILE " + rssFile + " - LINE " + std::to_string(nLine)); GetLogger().IncrDepth(); } @@ -284,25 +312,27 @@ namespace debug ~CFuncLogger() { GetLogger().DecrDepth(); - GetLogger().Log(std::string("Leave function:") + m_ssFunc); + GetLogger().Log(std::string("LEAVE FUNCTION: ") + m_ssFunc); } /** * @brief Log a function checkpoint. + * @param[in] rssFunc Reference to the function name. * @param[in] nLine The line number of this checkpoint. */ - void Checkpoint(size_t nLine) + void Checkpoint(const std::string& rssFunc, size_t nLine) { - GetLogger().Log(std::string("Checkpoint #") + std::to_string(m_nCounter++) + " - line " + std::to_string(nLine)); + GetLogger().Log("FUNCTION: " + rssFunc + " - LINE " + std::to_string(nLine) + " - CHECKPOINT #" + std::to_string(m_nCounter++)); } /** * @brief Log a string. + * @param[in] nLine The line number of this checkpoint. * @param[in] rss Reference to the string to log. */ - void Log(const std::string& rss) const + void Log(size_t nLine, const std::string& rss) const { - GetLogger().Log(rss); + GetLogger().Log("LINE " + std::to_string(nLine) + " - MSG: " + rss); } private: @@ -319,22 +349,29 @@ namespace debug * @brief Macro to create a function logger object using the function name, the file name and the line number from the compiler. */ #define FUNC_LOGGER() debug::CFuncLogger logger(__FUNCSIG__, __FILE__, __LINE__) + +/** + * @brief Macro to set a checkpoint providing the line number from the compiler. + */ +#define CHECKPOINT() logger.Checkpoint(__FUNCSIG__, __LINE__) + #else /** * @brief Macro to create a function logger object using the function name, the file name and the line number from the compiler. */ #define FUNC_LOGGER() debug::CFuncLogger logger(__PRETTY_FUNCTION__, __FILE__, __LINE__) -#endif /** * @brief Macro to set a checkpoint providing the line number from the compiler. */ -#define CHECKPOINT() logger.Checkpoint(__LINE__) +#define CHECKPOINT() logger.Checkpoint(__PRETTY_FUNCTION__, __LINE__) + +#endif /** * @brief Log a message */ -#define FUNC_LOG(msg) logger.Log(msg) +#define FUNC_LOG(msg) logger.Log(__LINE__, msg) #else /** diff --git a/global/exec_dir_helper.h b/global/exec_dir_helper.h index dfe495a..de380da 100644 --- a/global/exec_dir_helper.h +++ b/global/exec_dir_helper.h @@ -1,13 +1,17 @@ -/** +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG * - * @file exec_dir_helper.h - * @brief This file contains helper functions e.g. filesystem - * @version 0.1 - * @date 2022.11.14 - * @author Thomas.pfleiderer@zf.com - * @copyright Copyright ZF Friedrichshaven AG (c) 2022 + * 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 EXEC_DIR_HELPER_H #define EXEC_DIR_HELPER_H diff --git a/global/filesystem_helper.h b/global/filesystem_helper.h index d861e3b..358c44a 100644 --- a/global/filesystem_helper.h +++ b/global/filesystem_helper.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 FILESYSTEM_HELPER #define FILESYSTEM_HELPER diff --git a/global/flags.h b/global/flags.h index 196f0a9..a68b89d 100644 --- a/global/flags.h +++ b/global/flags.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 FLAGS_HELPER_H #define FLAGS_HELPER_H diff --git a/global/ipc_named_mutex.h b/global/ipc_named_mutex.h index 7952861..9c6392a 100644 --- a/global/ipc_named_mutex.h +++ b/global/ipc_named_mutex.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 IPC_NAMED_MUTEX_H #define IPC_NAMED_MUTEX_H @@ -51,9 +64,9 @@ namespace ipc /** * @brief Named mutex constructor. Opens or creates a named mutex. This mutex is unlocked. - * @param[in] szName Pointer to the zero terminated name of the mutex. Can be null to automatically generate a name. + * @param[in] rssName Reference to name of the mutex. Can be empty to automatically generate a name. */ - named_mutex(const char* szName = nullptr) noexcept; + named_mutex(const std::string& rssName = std::string()); /** * @brief Copy constructor is deleted. @@ -118,10 +131,10 @@ namespace ipc }; #ifdef _WIN32 - inline named_mutex::named_mutex(const char* szName) noexcept : m_handle(nullptr) + inline named_mutex::named_mutex(const std::string& rssName) : m_handle(nullptr) { - if (szName) - m_ssName = szName; + if (!rssName.empty()) + m_ssName = rssName; else { std::srand(static_cast(std::time(nullptr))); // Use current time as seed for random generator @@ -186,10 +199,10 @@ namespace ipc #elif defined __unix__ - inline named_mutex::named_mutex(const char* szName) noexcept : m_handle(nullptr) + inline named_mutex::named_mutex(const std::string& rssName) : m_handle(nullptr) { - if (szName) - m_ssName = szName; + if (!rssName.empty()) + m_ssName = rssName; else { std::srand(static_cast(std::time(nullptr))); // Use current time as seed for random generator diff --git a/global/localmemmgr.h b/global/localmemmgr.h index 91cd1a5..f2493f3 100644 --- a/global/localmemmgr.h +++ b/global/localmemmgr.h @@ -1,8 +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 + ********************************************************************************/ + #ifndef LOCAL_MEM_MGR_H #define LOCAL_MEM_MGR_H -#define SDV_CORE_H - +#define SDV_NO_LOADER #define NO_SDV_CORE_FUNC #include "../export/interfaces/core.h" #include "../export//support/interface_ptr.h" diff --git a/global/path_match.h b/global/path_match.h index cbb75ee..2621758 100644 --- a/global/path_match.h +++ b/global/path_match.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 WILDCARD_MATCH_H #define WILDCARD_MATCH_H @@ -32,8 +45,8 @@ * extension ending with "n" match. * - "subdir?_*" - all files from a directory starting with the name "subdir" followed by a single digit or character, followed * with a "_" and zero or more characters. - * - "**\\/file*.bin" - all files starting with the name "file" followed by zero or more characters and the extension ".bin" in this - * and any subdirectory. + * - "**\\/file*.bin" - all files starting with the name "file" followed by zero or more characters and the extension ".bin" in + * this and any subdirectory. * @param[in] rpathRel Reference to the relative path to check for a match. * @param[in] rssPattern Reference to the string containing the pattern to match. * @return Returns whether the path matches. diff --git a/global/process_watchdog.h b/global/process_watchdog.h index 4eb23ce..6572de1 100644 --- a/global/process_watchdog.h +++ b/global/process_watchdog.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 PROCESS_WATCHDOG_H #define PROCESS_WATCHDOG_H @@ -41,6 +54,7 @@ #else #error The OS is not supported! #endif +#include "exec_dir_helper.h" /** * @brief Process watchdog class; ends the process when runtime duration has superseded (as is the case when a deadlock has @@ -125,6 +139,8 @@ private: if (IsDebuggerPresent()) continue; std::cerr << "WATCHDOG TERMINATION ENFORCED!!!" << std::endl; + std::cerr << GetExecFilename().generic_u8string() << " will be terminated due to inactivity of " << + std::chrono::duration_cast(tpNow - tpStart).count() << " seconds... " << std::endl; std::cerr.flush(); #ifdef _WIN32 // Get the current process handle diff --git a/global/scheduler/scheduler.cpp b/global/scheduler/scheduler.cpp index 81311a0..274d688 100644 --- a/global/scheduler/scheduler.cpp +++ b/global/scheduler/scheduler.cpp @@ -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 "scheduler.h" #include diff --git a/global/scheduler/scheduler.h b/global/scheduler/scheduler.h index c094892..28de486 100644 --- a/global/scheduler/scheduler.h +++ b/global/scheduler/scheduler.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 THREAD_POOL_H #define THREAD_POOL_H diff --git a/global/timetracker.h b/global/timetracker.h index d62549d..1365f3c 100644 --- a/global/timetracker.h +++ b/global/timetracker.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 TIME_TRACKER_H #define TIME_TRACKER_H diff --git a/global/trace.h b/global/trace.h index 63e3d62..f1cde24 100644 --- a/global/trace.h +++ b/global/trace.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 TRACE_H #define TRACE_H diff --git a/global/tracefifo/trace_fifo.cpp b/global/tracefifo/trace_fifo.cpp index 3c6cec2..5f69c79 100644 --- a/global/tracefifo/trace_fifo.cpp +++ b/global/tracefifo/trace_fifo.cpp @@ -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 "trace_fifo.h" // Include the platform support diff --git a/global/tracefifo/trace_fifo.h b/global/tracefifo/trace_fifo.h index 95784ae..f664a06 100644 --- a/global/tracefifo/trace_fifo.h +++ b/global/tracefifo/trace_fifo.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 TRACE_FIFO_H #define TRACE_FIFO_H diff --git a/global/tracefifo/trace_fifo_posix.cpp b/global/tracefifo/trace_fifo_posix.cpp index a39d01e..c88a426 100644 --- a/global/tracefifo/trace_fifo_posix.cpp +++ b/global/tracefifo/trace_fifo_posix.cpp @@ -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 __unix__ #ifndef INCLUDE_TRACE_FIFO_PLATFORM diff --git a/global/tracefifo/trace_fifo_posix.h b/global/tracefifo/trace_fifo_posix.h index 6666c35..3124dd1 100644 --- a/global/tracefifo/trace_fifo_posix.h +++ b/global/tracefifo/trace_fifo_posix.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 TRACE_FIFO_POSIX_H && defined __unix__ #define TRACE_FIFO_POSIX_H diff --git a/global/tracefifo/trace_fifo_windows.cpp b/global/tracefifo/trace_fifo_windows.cpp index 700312f..5d8b020 100644 --- a/global/tracefifo/trace_fifo_windows.cpp +++ b/global/tracefifo/trace_fifo_windows.cpp @@ -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 _WIN32 #ifndef INCLUDE_TRACE_FIFO_PLATFORM diff --git a/global/tracefifo/trace_fifo_windows.h b/global/tracefifo/trace_fifo_windows.h index fbe0cf0..3343552 100644 --- a/global/tracefifo/trace_fifo_windows.h +++ b/global/tracefifo/trace_fifo_windows.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 TRACE_FIFO_WINDOWS_H && defined _WIN32 #define TRACE_FIFO_WINDOWS_H diff --git a/sdv_executables/CMakeLists.txt b/sdv_executables/CMakeLists.txt index f97e029..a724e9a 100644 --- a/sdv_executables/CMakeLists.txt +++ b/sdv_executables/CMakeLists.txt @@ -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) diff --git a/sdv_executables/error_msg.h b/sdv_executables/error_msg.h index 58ef04e..e5f4667 100644 --- a/sdv_executables/error_msg.h +++ b/sdv_executables/error_msg.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 EXECUTABLES_ERROR_MSG #define EXECUTABLES_ERROR_MSG @@ -87,17 +100,25 @@ MAKE_ERROR_MSG(-1013, CMDLN_SOURCE_FILE_MISSING, "No source file specified.", "T MAKE_ERROR_MSG(-1014, CMDLN_TOO_MANY_SOURCE_FILES, "Too many source files were specified.", "Too many source files were specified at the command line. Only one file is supported at the time.") MAKE_ERROR_MSG(-1015, CMDLN_INCOMPATIBLE_ARGUMENTS, "Incompatible arguments were supplied, not fitting the command.", "Some arguments cannot be combined with the command on the command line.") MAKE_ERROR_MSG(-1016, CMDLN_MISSING_SHOW_COMMAND, "Missing show command.", "The command SHOW was supplied on the command line, but not what to show.") -MAKE_ERROR_MSG(-1017, CMDLN_INVALID_CONFIG_STRING, "The configuration string is invalid.", "The configuration string should consist of path (only for local) followed by +component,component,....") -MAKE_ERROR_MSG(-1018, SAVE_INSTALL_MANIFEST_ERROR, "Failed to save the installation manifest.", "Saving the installation manifest returned with an error.") -MAKE_ERROR_MSG(-1019, SAVE_SETTINGS_FILE_ERROR, "Failed to save application settings file.", "Saving the application settings file returned with an error.") -MAKE_ERROR_MSG(-1020, SAVE_CONFIG_FILE_ERROR, "Failed to save the config file.", "Saving the configuration file returned with an error.") -MAKE_ERROR_MSG(-1021, CANNOT_REMOVE_INSTALL_DIR, "Cannot remove existing installation directory.", "Failed to remove an existing installation directory.") -MAKE_ERROR_MSG(-1022, CREATE_INSTALL_DIR_ERROR, "Cannot create installation directory.", "Failed to create the installation directory.") -MAKE_ERROR_MSG(-1023, CREATE_TARGET_DIR_ERROR, "Cannot create target root directory.", "Failed to create the target root directory.") -MAKE_ERROR_MSG(-1024, CREATE_CONFIG_DIR_ERROR, "Cannot create config target directory.", "Failed to create the config target directory.") -MAKE_ERROR_MSG(-1026, NO_SOURCE_FILES, "No source files were found.", "No source files were found to add to the package.") -MAKE_ERROR_MSG(-1027, PACKAGE_CREATION_ERROR, "A compose package error has occurred.", "An error has occurred during the package creation.") -MAKE_ERROR_MSG(-1028, PACKAGE_READ_ERROR, "A read package error has occurred.", "An error has occurred while reading the package.") +MAKE_ERROR_MSG(-1017, CMDLN_INVALID_CONFIG_STRING, "The configuration string is invalid.", "The configuration string should consist of path (only for local) followed by '+component,component,...'.") +MAKE_ERROR_MSG(-1018, CMDLN_INVALID_PARAM_STRING, "The configuration parameter string is invalid or could not be parsed.", "The configuration string should consist of an component, a colon separator ':' followed by multiple parameters 'param1=value1,param2=value2,...'.") +MAKE_ERROR_MSG(-1019, CMDLN_INVALID_PARAM_FILE, "The configuration parameter TOML file could not be read.", "The configuration parameter TOML file contains errors or cannot be read.") +MAKE_ERROR_MSG(-1020, CMDLN_TOO_MANY_CONFIG_TARGETS, "Too many configuration targets were selected.", "Only one configuration target can be selected for configuration merging.") +MAKE_ERROR_MSG(-1021, CMDLN_MISSING_TARGET, "No target was provided.", "Cannot continue without target.") +MAKE_ERROR_MSG(-1030, SAVE_INSTALL_MANIFEST_ERROR, "Failed to save the installation manifest.", "Saving the installation manifest returned with an error.") +MAKE_ERROR_MSG(-1031, SAVE_SETTINGS_FILE_ERROR, "Failed to save application settings file.", "Saving the application settings file returned with an error.") +MAKE_ERROR_MSG(-1035, SAVE_CONFIG_FILE_ERROR, "Failed to save the config file.", "Saving the configuration file returned with an error.") +MAKE_ERROR_MSG(-1036, PARAMETERS_CONFIG_ERROR, "No instance could be found in the configuration to assign the parameters to or no configuration was selected at the command line.", "Storing parameters inside the configuration doesn't work.") +MAKE_ERROR_MSG(-1040, CANNOT_REMOVE_INSTALL_DIR, "Cannot remove existing installation directory.", "Failed to remove an existing installation directory.") +MAKE_ERROR_MSG(-1041, CREATE_INSTALL_DIR_ERROR, "Cannot create installation directory.", "Failed to create the installation directory.") +MAKE_ERROR_MSG(-1042, CREATE_TARGET_DIR_ERROR, "Cannot create target root directory.", "Failed to create the target root directory.") +MAKE_ERROR_MSG(-1043, CREATE_CONFIG_DIR_ERROR, "Cannot create config target directory.", "Failed to create the config target directory.") +MAKE_ERROR_MSG(-1050, NO_SOURCE_FILES, "No source files were found.", "No source files were found to add to the package.") +MAKE_ERROR_MSG(-1055, PACKAGE_CREATION_ERROR, "A compose package error has occurred.", "An error has occurred during the package creation.") +MAKE_ERROR_MSG(-1058, PACKAGE_READ_ERROR, "A read package error has occurred.", "An error has occurred while reading the package.") +MAKE_ERROR_MSG(-1060, CANNOT_READ_CONFIG, "The configuration cannot be read.", "An error has occurred while reading the configuration TOML.") +MAKE_ERROR_MSG(-1061, FAILED_UPDATING_CONFIG, "The configuration could not be updated.", "An error has occurred while updating the configuration TOML.") +MAKE_ERROR_MSG(-1062, PARTLY_FAILED_UPDATING_CONFIG, "The configuration could only be updated.", "Could not merge all entities in the target configuration.") ////////// SDV TRACE MONITOR ERROR CODES //////////// MAKE_ERROR_MSG(-5500, TRACE_MON_REG_HNDLR_ERROR, "Failed to register application control handler.", "The OS returned an error during the registration of the application control handler.") diff --git a/sdv_executables/sdv_control/CMakeLists.txt b/sdv_executables/sdv_control/CMakeLists.txt index 7c7c2e1..9be7b01 100644 --- a/sdv_executables/sdv_control/CMakeLists.txt +++ b/sdv_executables/sdv_control/CMakeLists.txt @@ -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_control VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_executables/sdv_control/context.h b/sdv_executables/sdv_control/context.h index c559e85..49ad06b 100644 --- a/sdv_executables/sdv_control/context.h +++ b/sdv_executables/sdv_control/context.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 CONTEXT_H #define CONTEXT_H diff --git a/sdv_executables/sdv_control/installation.cpp b/sdv_executables/sdv_control/installation.cpp index df75c99..4dffd9e 100644 --- a/sdv_executables/sdv_control/installation.cpp +++ b/sdv_executables/sdv_control/installation.cpp @@ -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.h" #include "../../global/cmdlnparser/cmdlnparser.h" #include diff --git a/sdv_executables/sdv_control/installation.h b/sdv_executables/sdv_control/installation.h index f799280..9b035db 100644 --- a/sdv_executables/sdv_control/installation.h +++ b/sdv_executables/sdv_control/installation.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 INSTALLATION_H #define INSTALLATION_H diff --git a/sdv_executables/sdv_control/list_elements.cpp b/sdv_executables/sdv_control/list_elements.cpp index 688ae96..4a5bd7b 100644 --- a/sdv_executables/sdv_control/list_elements.cpp +++ b/sdv_executables/sdv_control/list_elements.cpp @@ -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 "list_elements.h" #include "print_table.h" #include @@ -211,23 +224,6 @@ int ListClasses(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository return MODULE_CONTROL_SERVICE_ACCESS_ERROR; } - // Class type - auto fnPrintClassType = [](sdv::EObjectType eType) - { - switch (eType) - { - case sdv::EObjectType::SystemObject: return "SystemObject"; - case sdv::EObjectType::Device: return "Device"; - case sdv::EObjectType::BasicService: return "BasicService"; - case sdv::EObjectType::ComplexService: return "ComplexService"; - case sdv::EObjectType::Application: return "Application"; - case sdv::EObjectType::Proxy: return "Proxy"; - case sdv::EObjectType::Stub: return "Stub"; - case sdv::EObjectType::Utility: return "Utility"; - default: return "Unknown"; - } - }; - // Class flags auto fnPrintClassFlags = [](uint32_t uiFlags) { @@ -263,7 +259,7 @@ int ListClasses(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository { sdv::sequence seqClasses = pModuleInfo->GetClassList(rsModuleInfo.tModuleID); for (const sdv::SClassInfo& rsClassInfo : seqClasses) - vecShortClassList.push_back({ rsClassInfo.ssClassName, fnPrintList(rsClassInfo.seqClassAliases) }); + vecShortClassList.push_back({ rsClassInfo.ssName, fnPrintList(rsClassInfo.seqClassAliases) }); } // Print the module list @@ -279,10 +275,10 @@ int ListClasses(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository sdv::sequence seqClasses = pModuleInfo->GetClassList(rsModuleInfo.tModuleID); for (const sdv::SClassInfo& rsClassInfo : seqClasses) vecClassList.push_back({ - rsClassInfo.ssClassName, + rsClassInfo.ssName, fnPrintList(rsClassInfo.seqClassAliases), rsClassInfo.ssDefaultObjectName, - fnPrintClassType(rsClassInfo.eType), + sdv::ObjectType2String(rsClassInfo.eType), fnPrintClassFlags(rsClassInfo.uiFlags), std::to_string(static_cast(rsModuleInfo.tModuleID)), fnPrintList(rsClassInfo.seqDependencies) }); @@ -309,23 +305,6 @@ int ListComponents(const SContext& rsContext, const sdv::TObjectPtr& rptrReposit return REPOSITORY_SERVICE_ACCESS_ERROR; } - // Class type - auto fnPrintClassType = [](sdv::EObjectType eType) - { - switch (eType) - { - case sdv::EObjectType::SystemObject: return "SystemObject"; - case sdv::EObjectType::Device: return "Device"; - case sdv::EObjectType::BasicService: return "BasicService"; - case sdv::EObjectType::ComplexService: return "ComplexService"; - case sdv::EObjectType::Application: return "Application"; - case sdv::EObjectType::Proxy: return "Proxy"; - case sdv::EObjectType::Stub: return "Stub"; - case sdv::EObjectType::Utility: return "Utility"; - default: return "Unknown"; - } - }; - // Object flags auto fnPrintObjectFlags = [](uint32_t uiFlags) { @@ -369,9 +348,9 @@ int ListComponents(const SContext& rsContext, const sdv::TObjectPtr& rptrReposit for (const sdv::core::SObjectInfo& rsObjectInfo : seqObjects) vecObjectList.push_back({ std::to_string(static_cast(rsObjectInfo.tModuleID)), - rsObjectInfo.sClassInfo.ssClassName, + rsObjectInfo.sClassInfo.ssName, rsObjectInfo.ssObjectName, - fnPrintClassType(rsObjectInfo.sClassInfo.eType), + sdv::ObjectType2String(rsObjectInfo.sClassInfo.eType), fnPrintObjectFlags(rsObjectInfo.uiFlags), std::to_string(static_cast(rsObjectInfo.tModuleID)) }); diff --git a/sdv_executables/sdv_control/list_elements.h b/sdv_executables/sdv_control/list_elements.h index 645185d..2515a18 100644 --- a/sdv_executables/sdv_control/list_elements.h +++ b/sdv_executables/sdv_control/list_elements.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 LIST_ELEMENTS_H #define LIST_ELEMENTS_H diff --git a/sdv_executables/sdv_control/main.cpp b/sdv_executables/sdv_control/main.cpp index b4c8e88..86ed273 100644 --- a/sdv_executables/sdv_control/main.cpp +++ b/sdv_executables/sdv_control/main.cpp @@ -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 "../../global/process_watchdog.h" #include #include "../../global/cmdlnparser/cmdlnparser.cpp" diff --git a/sdv_executables/sdv_control/print_table.h b/sdv_executables/sdv_control/print_table.h index 53f4d73..698118c 100644 --- a/sdv_executables/sdv_control/print_table.h +++ b/sdv_executables/sdv_control/print_table.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 TABLE_H #define TABLE_H diff --git a/sdv_executables/sdv_control/start_stop_service.cpp b/sdv_executables/sdv_control/start_stop_service.cpp index 271b969..879111e 100644 --- a/sdv_executables/sdv_control/start_stop_service.cpp +++ b/sdv_executables/sdv_control/start_stop_service.cpp @@ -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 "start_stop_service.h" #include "../../global/cmdlnparser/cmdlnparser.h" #include diff --git a/sdv_executables/sdv_control/start_stop_service.h b/sdv_executables/sdv_control/start_stop_service.h index 411cf62..d5995f9 100644 --- a/sdv_executables/sdv_control/start_stop_service.h +++ b/sdv_executables/sdv_control/start_stop_service.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 START_STOP_SERVICE_H #define START_STOP_SERVICE_H diff --git a/sdv_executables/sdv_control/startup_shutdown.cpp b/sdv_executables/sdv_control/startup_shutdown.cpp index 3191994..57c8c0f 100644 --- a/sdv_executables/sdv_control/startup_shutdown.cpp +++ b/sdv_executables/sdv_control/startup_shutdown.cpp @@ -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 "startup_shutdown.h" #include "../../global/cmdlnparser/cmdlnparser.h" #include diff --git a/sdv_executables/sdv_control/startup_shutdown.h b/sdv_executables/sdv_control/startup_shutdown.h index bed6f17..18bcee7 100644 --- a/sdv_executables/sdv_control/startup_shutdown.h +++ b/sdv_executables/sdv_control/startup_shutdown.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 STARTUP_SHUTDOWN_H #define STARTUP_SHUTDOWN_H diff --git a/sdv_executables/sdv_core/CMakeLists.txt b/sdv_executables/sdv_core/CMakeLists.txt index 79b5c16..73870b2 100644 --- a/sdv_executables/sdv_core/CMakeLists.txt +++ b/sdv_executables/sdv_core/CMakeLists.txt @@ -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_core VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_executables/sdv_core/main.cpp b/sdv_executables/sdv_core/main.cpp index cb4cbdd..012c37d 100644 --- a/sdv_executables/sdv_core/main.cpp +++ b/sdv_executables/sdv_core/main.cpp @@ -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 #include #include "../../global/cmdlnparser/cmdlnparser.cpp" diff --git a/sdv_executables/sdv_dbc_util/CMakeLists.txt b/sdv_executables/sdv_dbc_util/CMakeLists.txt index 0377485..76b11d9 100644 --- a/sdv_executables/sdv_dbc_util/CMakeLists.txt +++ b/sdv_executables/sdv_dbc_util/CMakeLists.txt @@ -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_dbc_util VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_executables/sdv_dbc_util/can_dl.cpp b/sdv_executables/sdv_dbc_util/can_dl.cpp index 4c68464..050ede0 100644 --- a/sdv_executables/sdv_dbc_util/can_dl.cpp +++ b/sdv_executables/sdv_dbc_util/can_dl.cpp @@ -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_dl.h" #include #include @@ -167,7 +180,7 @@ const char szHdrTemplate[] = R"code(/** /** * @brief Data link class. */ -class CDataLink : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::can::IReceive +class CDataLink : public sdv::CSdvObject, public sdv::can::IReceive { public: /** @@ -182,38 +195,25 @@ public: // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(sdv::can::IReceive) END_SDV_INTERFACE_MAP() // Declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_bus) DECLARE_OBJECT_CLASS_NAME("CAN_data_link") DECLARE_DEFAULT_OBJECT_NAME("DataLink") DECLARE_OBJECT_SINGLETON() /** - * @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 Process a receive a CAN message. Overload of sdv::can::IReceive::Receive. @@ -264,7 +264,6 @@ private: double dValue; ///< 64-bit double precision floating point number. }; %message_def% - sdv::EObjectStatus m_eStatus = sdv::EObjectStatus::initialization_pending; ///< Keep track of the object status. size_t m_nIfcIndex = %ifc_index%; ///< CAN Interface index. sdv::can::IRegisterReceiver* m_pRegister = nullptr; ///< CAN receiver registration interface. sdv::can::ISend* m_pSend = nullptr; ///< CAN sender interface. @@ -308,17 +307,14 @@ CDataLink::~CDataLink() Shutdown(); // Just in case } -void CDataLink::Initialize(const sdv::u8string& /*ssObjectConfig*/) +bool CDataLink::OnInitialize() { - if (m_eStatus != sdv::EObjectStatus::initialization_pending) return; - // Get the CAN communication object. sdv::TInterfaceAccessPtr ptrCANObject = sdv::core::GetObject("CAN_Communication_Object"); if (!ptrCANObject) { SDV_LOG_ERROR("CDataLink::Initialize() failure, 'CAN_Communication_Object' not found"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } %init_ifc_index%// Get the CAN receiver registration interface. @@ -326,8 +322,7 @@ void CDataLink::Initialize(const sdv::u8string& /*ssObjectConfig*/) if (!m_pRegister) { SDV_LOG_ERROR("CDataLink::Initialize() failure, 'sdv::can::IRegisterReceiver' interface not found"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } m_pRegister->RegisterReceiver(static_cast(this)); @@ -336,42 +331,17 @@ void CDataLink::Initialize(const sdv::u8string& /*ssObjectConfig*/) if (!m_pSend) { SDV_LOG_ERROR("CDataLink::Initialize() failure, 'sdv::can::ISend' interface not found"); - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } // Initialize messages bool bSuccess = true;%init_msg% - m_eStatus = bSuccess ? sdv::EObjectStatus::initialized : sdv::EObjectStatus::initialization_failure; + return bSuccess; } -sdv::EObjectStatus CDataLink::GetStatus() const +void CDataLink::OnShutdown() { - return m_eStatus; -} - -void CDataLink::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 CDataLink::Shutdown() -{ - m_eStatus = sdv::EObjectStatus::shutdown_in_progress; - // Unregister receiver interface. if (m_pRegister) m_pRegister->UnregisterReceiver(static_cast(this)); m_pRegister = nullptr; @@ -379,9 +349,6 @@ void CDataLink::Shutdown() m_pSend = nullptr; // Terminate messages%term_msg% - - // Update the status - m_eStatus = sdv::EObjectStatus::destruction_pending; } void CDataLink::Receive(/*in*/ [[maybe_unused]] const sdv::can::SMessage& sMsg, /*in*/ uint32_t uiIfcIndex) @@ -605,8 +572,7 @@ std::string CCanDataLinkGen::CodeInitInterfaceIndex(const std::string& rssIfcNam if (!pInfo) { // CAN information interface not found. - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } sdv::sequence seqInterfaces = pInfo->GetInterfaces(); size_t nIndex = 0; @@ -621,9 +587,10 @@ std::string CCanDataLinkGen::CodeInitInterfaceIndex(const std::string& rssIfcNam if (nIndex >= seqInterfaces.size()) { // Interface with supplied name not found. - m_eStatus = sdv::EObjectStatus::initialization_failure; - return; + return false; } + + return true; )code", mapKeywords); } @@ -696,8 +663,9 @@ std::string CCanDataLinkGen::CodeTxMessageDefinition(const dbc::SMessageDef& rsM /** * @brief Initialize the message by registering all signals. * @param[in] pSend The send-interface of the CAN. + * @param[in] nIfcIndex CAN Interface index. */ - bool Init(sdv::can::ISend* pSend); + bool Init(sdv::can::ISend* pSend, size_t nIfcIndex); /** * @brief Terminate the message by unregistering all signals. @@ -712,7 +680,8 @@ std::string CCanDataLinkGen::CodeTxMessageDefinition(const dbc::SMessageDef& rsM sdv::core::CDispatchService& m_rdispatch; ///< Reference to the dispatch service. sdv::core::CTrigger m_trigger; ///< Message trigger being called by the dispatch service. - sdv::can::ISend* m_pSend = nullptr; ///< Message sending interface. + sdv::can::ISend* m_pSend = nullptr; ///< Message sending interface. + size_t m_nIfcIndex = 0; ///< CAN Interface index. %sig_decl% } m_sTxMsg%msg_name%; )code", mapKeywords); @@ -759,7 +728,7 @@ std::string CCanDataLinkGen::CodeInitTxMessage(const dbc::SMessageDef& rsMsg) mapKeywords["msg_name"] = rsMsg.ssName; return ReplaceKeywords(R"code( - bSuccess &= m_sTxMsg%msg_name%.Init(m_pSend);)code", mapKeywords); + bSuccess &= m_sTxMsg%msg_name%.Init(m_pSend, m_nIfcIndex);)code", mapKeywords); } std::string CCanDataLinkGen::CodeTermRxMessage(const dbc::SMessageDef& rsMsg) @@ -975,10 +944,14 @@ CDataLink::STxMsg_%msg_name%::STxMsg_%msg_name%(sdv::core::CDispatchService& rdi m_rdispatch(rdispatch) {} -bool CDataLink::STxMsg_%msg_name%::Init(sdv::can::ISend* pSend) +bool CDataLink::STxMsg_%msg_name%::Init(sdv::can::ISend* pSend, size_t nIfcIndex) { - if (!pSend) return false; + if (!pSend) + { + return false; + } m_pSend = pSend; + m_nIfcIndex = nIfcIndex; // Register signals bool bSuccess = true;%sig_register% @@ -1020,8 +993,7 @@ void CDataLink::STxMsg_%msg_name%::Transmit() transaction.Finish(); // Transmit the message - // TODO: Determine CAN interface index... - m_pSend->Send(msg, 0); + m_pSend->Send(msg, static_cast(m_nIfcIndex)); } )code", mapKeywords); } diff --git a/sdv_executables/sdv_dbc_util/can_dl.h b/sdv_executables/sdv_dbc_util/can_dl.h index 223be09..01ee2a9 100644 --- a/sdv_executables/sdv_dbc_util/can_dl.h +++ b/sdv_executables/sdv_dbc_util/can_dl.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 CAN_DL_H #define CAN_DL_H diff --git a/sdv_executables/sdv_dbc_util/cmake_generator.cpp b/sdv_executables/sdv_dbc_util/cmake_generator.cpp index 3741cda..6cd0c8b 100644 --- a/sdv_executables/sdv_dbc_util/cmake_generator.cpp +++ b/sdv_executables/sdv_dbc_util/cmake_generator.cpp @@ -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 "cmake_generator.h" #include #include diff --git a/sdv_executables/sdv_dbc_util/cmake_generator.h b/sdv_executables/sdv_dbc_util/cmake_generator.h index f2a6d1b..a1e976c 100644 --- a/sdv_executables/sdv_dbc_util/cmake_generator.h +++ b/sdv_executables/sdv_dbc_util/cmake_generator.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 CMAKE_GENERATOR_H #define CMAKE_GENERATOR_H diff --git a/sdv_executables/sdv_dbc_util/codegen_base.cpp b/sdv_executables/sdv_dbc_util/codegen_base.cpp index 74710de..c08b2d0 100644 --- a/sdv_executables/sdv_dbc_util/codegen_base.cpp +++ b/sdv_executables/sdv_dbc_util/codegen_base.cpp @@ -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 "codegen_base.h" #include diff --git a/sdv_executables/sdv_dbc_util/codegen_base.h b/sdv_executables/sdv_dbc_util/codegen_base.h index dab1eab..6df7882 100644 --- a/sdv_executables/sdv_dbc_util/codegen_base.h +++ b/sdv_executables/sdv_dbc_util/codegen_base.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 CODEGEN_BASE_H #define CODEGEN_BASE_H diff --git a/sdv_executables/sdv_dbc_util/fmu.cpp b/sdv_executables/sdv_dbc_util/fmu.cpp index cced50b..3f083b5 100644 --- a/sdv_executables/sdv_dbc_util/fmu.cpp +++ b/sdv_executables/sdv_dbc_util/fmu.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "fmu_templates.h" #include "fmu_fmi_templates.h" #include "fmu.h" diff --git a/sdv_executables/sdv_dbc_util/fmu.h b/sdv_executables/sdv_dbc_util/fmu.h index 7c570aa..635ccae 100644 --- a/sdv_executables/sdv_dbc_util/fmu.h +++ b/sdv_executables/sdv_dbc_util/fmu.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef SOFTCAR_FMU_H #define SOFTCAR_FMU_H diff --git a/sdv_executables/sdv_dbc_util/fmu_fmi_templates.h b/sdv_executables/sdv_dbc_util/fmu_fmi_templates.h index 26ad613..965370c 100644 --- a/sdv_executables/sdv_dbc_util/fmu_fmi_templates.h +++ b/sdv_executables/sdv_dbc_util/fmu_fmi_templates.h @@ -1,3 +1,15 @@ +/******************************************************************************** + * 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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ /** * @brief CoSimulation header template. File 'cosimulation.h'. Code chunks are inserted at the keywords surrounded by %%. diff --git a/sdv_executables/sdv_dbc_util/fmu_templates.h b/sdv_executables/sdv_dbc_util/fmu_templates.h index 4371dfd..3505e95 100644 --- a/sdv_executables/sdv_dbc_util/fmu_templates.h +++ b/sdv_executables/sdv_dbc_util/fmu_templates.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + /** * @brief Build description template. File 'buildDescription.xml'. Code chunks are inserted at the keywords surrounded by %%. diff --git a/sdv_executables/sdv_dbc_util/main.cpp b/sdv_executables/sdv_dbc_util/main.cpp index b529057..6cfc0d0 100644 --- a/sdv_executables/sdv_dbc_util/main.cpp +++ b/sdv_executables/sdv_dbc_util/main.cpp @@ -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: +* Erik Verhoeven - initial API and implementation +* Thomas Pfleiderer - extended for FMU processing +********************************************************************************/ + #include "../../global/process_watchdog.h" #include #include diff --git a/sdv_executables/sdv_idl_compiler/CMakeLists.txt b/sdv_executables/sdv_idl_compiler/CMakeLists.txt index 9f47af5..2df2ecb 100644 --- a/sdv_executables/sdv_idl_compiler/CMakeLists.txt +++ b/sdv_executables/sdv_idl_compiler/CMakeLists.txt @@ -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_idl_compiler VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_executables/sdv_idl_compiler/codepos.cpp b/sdv_executables/sdv_idl_compiler/codepos.cpp index b1d8594..a83db7b 100644 --- a/sdv_executables/sdv_idl_compiler/codepos.cpp +++ b/sdv_executables/sdv_idl_compiler/codepos.cpp @@ -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 "codepos.h" #include "lexer.h" #include "exception.h" diff --git a/sdv_executables/sdv_idl_compiler/codepos.h b/sdv_executables/sdv_idl_compiler/codepos.h index ce40cf6..db26e21 100644 --- a/sdv_executables/sdv_idl_compiler/codepos.h +++ b/sdv_executables/sdv_idl_compiler/codepos.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 CODEPOS_H #define CODEPOS_H diff --git a/sdv_executables/sdv_idl_compiler/constvariant.cpp b/sdv_executables/sdv_idl_compiler/constvariant.cpp index 0aed75e..4c6fc75 100644 --- a/sdv_executables/sdv_idl_compiler/constvariant.cpp +++ b/sdv_executables/sdv_idl_compiler/constvariant.cpp @@ -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 "constvariant.h" #include "constvariant.inl" diff --git a/sdv_executables/sdv_idl_compiler/constvariant.h b/sdv_executables/sdv_idl_compiler/constvariant.h index f57cf7b..d538056 100644 --- a/sdv_executables/sdv_idl_compiler/constvariant.h +++ b/sdv_executables/sdv_idl_compiler/constvariant.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 VARIANT_H #define VARIANT_H diff --git a/sdv_executables/sdv_idl_compiler/constvariant.inl b/sdv_executables/sdv_idl_compiler/constvariant.inl index 66d6e8b..cac94d4 100644 --- a/sdv_executables/sdv_idl_compiler/constvariant.inl +++ b/sdv_executables/sdv_idl_compiler/constvariant.inl @@ -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 CONSTVARIANT_INL #define CONSTVARIANT_INL diff --git a/sdv_executables/sdv_idl_compiler/core_idl_backup.h b/sdv_executables/sdv_idl_compiler/core_idl_backup.h index abe3f5a..2526f91 100644 --- a/sdv_executables/sdv_idl_compiler/core_idl_backup.h +++ b/sdv_executables/sdv_idl_compiler/core_idl_backup.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 IDL_PARSER_INTERFACE_H #define IDL_PARSER_INTERFACE_H diff --git a/sdv_executables/sdv_idl_compiler/entities/attribute_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/attribute_entity.cpp index 83ce51f..53f5203 100644 --- a/sdv_executables/sdv_idl_compiler/entities/attribute_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/attribute_entity.cpp @@ -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 "attribute_entity.h" CAttributeEntity::CAttributeEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, bool bReadOnly) : diff --git a/sdv_executables/sdv_idl_compiler/entities/attribute_entity.h b/sdv_executables/sdv_idl_compiler/entities/attribute_entity.h index 3c6eb44..e952e4c 100644 --- a/sdv_executables/sdv_idl_compiler/entities/attribute_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/attribute_entity.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 ATTRIBUTE_ENTITY_H #define ATTRIBUTE_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/declaration_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/declaration_entity.cpp index dd1f31a..82409ae 100644 --- a/sdv_executables/sdv_idl_compiler/entities/declaration_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/declaration_entity.cpp @@ -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 "declaration_entity.h" #include "../exception.h" #include "../logger.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/declaration_entity.h b/sdv_executables/sdv_idl_compiler/entities/declaration_entity.h index f852ae6..a370442 100644 --- a/sdv_executables/sdv_idl_compiler/entities/declaration_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/declaration_entity.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 BASIC_TYPE_ENTITY_H #define BASIC_TYPE_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/definition_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/definition_entity.cpp index b79731c..0154b55 100644 --- a/sdv_executables/sdv_idl_compiler/entities/definition_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/definition_entity.cpp @@ -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 "definition_entity.h" #include "../exception.h" #include "../logger.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/definition_entity.h b/sdv_executables/sdv_idl_compiler/entities/definition_entity.h index f8b989f..58e2696 100644 --- a/sdv_executables/sdv_idl_compiler/entities/definition_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/definition_entity.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 DEFINITION_ENTITY_H #define DEFINITION_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/entity_base.cpp b/sdv_executables/sdv_idl_compiler/entities/entity_base.cpp index a6179b4..24e540a 100644 --- a/sdv_executables/sdv_idl_compiler/entities/entity_base.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/entity_base.cpp @@ -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 "entity_base.h" #include "../parser.h" #include "../exception.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/entity_base.h b/sdv_executables/sdv_idl_compiler/entities/entity_base.h index d8cc330..0db78ac 100644 --- a/sdv_executables/sdv_idl_compiler/entities/entity_base.h +++ b/sdv_executables/sdv_idl_compiler/entities/entity_base.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 ENTITY_BASE_H #define ENTITY_BASE_H diff --git a/sdv_executables/sdv_idl_compiler/entities/entity_value.cpp b/sdv_executables/sdv_idl_compiler/entities/entity_value.cpp index b9b92f6..fa15a8c 100644 --- a/sdv_executables/sdv_idl_compiler/entities/entity_value.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/entity_value.cpp @@ -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 "entity_value.h" #include "../exception.h" #include "variable_entity.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/entity_value.h b/sdv_executables/sdv_idl_compiler/entities/entity_value.h index b58a421..55937f1 100644 --- a/sdv_executables/sdv_idl_compiler/entities/entity_value.h +++ b/sdv_executables/sdv_idl_compiler/entities/entity_value.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 ENTITIY_VALUE_H #define ENTITIY_VALUE_H diff --git a/sdv_executables/sdv_idl_compiler/entities/enum_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/enum_entity.cpp index 810cc43..b5fbaa8 100644 --- a/sdv_executables/sdv_idl_compiler/entities/enum_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/enum_entity.cpp @@ -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 "enum_entity.h" #include "entity_value.h" #include "typedef_entity.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/enum_entity.h b/sdv_executables/sdv_idl_compiler/entities/enum_entity.h index b5024e8..788678b 100644 --- a/sdv_executables/sdv_idl_compiler/entities/enum_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/enum_entity.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 ENUM_ENTITY_H #define ENUM_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/exception_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/exception_entity.cpp index b499db6..f583d87 100644 --- a/sdv_executables/sdv_idl_compiler/entities/exception_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/exception_entity.cpp @@ -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 "exception_entity.h" #include "../exception.h" #include "../logger.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/exception_entity.h b/sdv_executables/sdv_idl_compiler/entities/exception_entity.h index 3021955..435be4f 100644 --- a/sdv_executables/sdv_idl_compiler/entities/exception_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/exception_entity.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 EXCEPTION_ENTITY_H #define EXCEPTION_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/hash_calc.cpp b/sdv_executables/sdv_idl_compiler/entities/hash_calc.cpp index c856f1e..921aaaa 100644 --- a/sdv_executables/sdv_idl_compiler/entities/hash_calc.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/hash_calc.cpp @@ -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 "hash_calc.h" CHashObject::CHashObject() diff --git a/sdv_executables/sdv_idl_compiler/entities/hash_calc.h b/sdv_executables/sdv_idl_compiler/entities/hash_calc.h index b74db39..004b7b5 100644 --- a/sdv_executables/sdv_idl_compiler/entities/hash_calc.h +++ b/sdv_executables/sdv_idl_compiler/entities/hash_calc.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 HASH_CALC_H #define HASH_CALC_H diff --git a/sdv_executables/sdv_idl_compiler/entities/interface_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/interface_entity.cpp index f19172c..e39f60b 100644 --- a/sdv_executables/sdv_idl_compiler/entities/interface_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/interface_entity.cpp @@ -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 "interface_entity.h" #include "../exception.h" #include "../logger.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/interface_entity.h b/sdv_executables/sdv_idl_compiler/entities/interface_entity.h index 0539b08..a2836d1 100644 --- a/sdv_executables/sdv_idl_compiler/entities/interface_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/interface_entity.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 INTERFACE_ENTITY_H #define INTERFACE_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/meta_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/meta_entity.cpp index ffbc3ad..3ba200d 100644 --- a/sdv_executables/sdv_idl_compiler/entities/meta_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/meta_entity.cpp @@ -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 "meta_entity.h" CMetaEntity::CMetaEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, const CToken& rtokenMeta, diff --git a/sdv_executables/sdv_idl_compiler/entities/meta_entity.h b/sdv_executables/sdv_idl_compiler/entities/meta_entity.h index 5ccfffe..3d1b2f9 100644 --- a/sdv_executables/sdv_idl_compiler/entities/meta_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/meta_entity.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 META_ENTITY_H #define META_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/module_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/module_entity.cpp index 63e6a05..6db165e 100644 --- a/sdv_executables/sdv_idl_compiler/entities/module_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/module_entity.cpp @@ -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 "module_entity.h" #include "typedef_entity.h" #include "struct_entity.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/module_entity.h b/sdv_executables/sdv_idl_compiler/entities/module_entity.h index 27c6583..d52ad9e 100644 --- a/sdv_executables/sdv_idl_compiler/entities/module_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/module_entity.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 MODULE_ENTITY_H #define MODULE_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/operation_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/operation_entity.cpp index 332c849..a41a5ec 100644 --- a/sdv_executables/sdv_idl_compiler/entities/operation_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/operation_entity.cpp @@ -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 "operation_entity.h" #include "interface_entity.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/operation_entity.h b/sdv_executables/sdv_idl_compiler/entities/operation_entity.h index 54227b6..f9a02da 100644 --- a/sdv_executables/sdv_idl_compiler/entities/operation_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/operation_entity.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 OPERATION_ENTITY_H #define OPERATION_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/parameter_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/parameter_entity.cpp index f016ada..933988d 100644 --- a/sdv_executables/sdv_idl_compiler/entities/parameter_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/parameter_entity.cpp @@ -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 "parameter_entity.h" #include "interface_entity.h" #include "operation_entity.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/parameter_entity.h b/sdv_executables/sdv_idl_compiler/entities/parameter_entity.h index d565a17..bed2ef8 100644 --- a/sdv_executables/sdv_idl_compiler/entities/parameter_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/parameter_entity.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 PARAMETER_ENTITY_H #define PARAMETER_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/root_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/root_entity.cpp index d90dd6c..ab9e4cd 100644 --- a/sdv_executables/sdv_idl_compiler/entities/root_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/root_entity.cpp @@ -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 "root_entity.h" #include "module_entity.h" #include "../exception.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/root_entity.h b/sdv_executables/sdv_idl_compiler/entities/root_entity.h index a3c7b00..fd554d7 100644 --- a/sdv_executables/sdv_idl_compiler/entities/root_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/root_entity.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 ROOT_ENTITY_H #define ROOT_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/struct_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/struct_entity.cpp index 57b565e..1ec4945 100644 --- a/sdv_executables/sdv_idl_compiler/entities/struct_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/struct_entity.cpp @@ -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 "struct_entity.h" #include "../exception.h" #include "../logger.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/struct_entity.h b/sdv_executables/sdv_idl_compiler/entities/struct_entity.h index 4123442..a3164b2 100644 --- a/sdv_executables/sdv_idl_compiler/entities/struct_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/struct_entity.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 STRUCT_ENTITY_H #define STRUCT_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/typedef_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/typedef_entity.cpp index 9434b97..56649c7 100644 --- a/sdv_executables/sdv_idl_compiler/entities/typedef_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/typedef_entity.cpp @@ -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 "typedef_entity.h" #include "../exception.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/typedef_entity.h b/sdv_executables/sdv_idl_compiler/entities/typedef_entity.h index 9790092..f9720e8 100644 --- a/sdv_executables/sdv_idl_compiler/entities/typedef_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/typedef_entity.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 TYPEDEF_ENTITY_H #define TYPEDEF_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/union_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/union_entity.cpp index 0c5b50e..416b6b1 100644 --- a/sdv_executables/sdv_idl_compiler/entities/union_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/union_entity.cpp @@ -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 "union_entity.h" #include "../exception.h" #include "../logger.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/union_entity.h b/sdv_executables/sdv_idl_compiler/entities/union_entity.h index 70375e2..49ada0c 100644 --- a/sdv_executables/sdv_idl_compiler/entities/union_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/union_entity.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 UNION_ENTITY_H #define UNION_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/entities/variable_entity.cpp b/sdv_executables/sdv_idl_compiler/entities/variable_entity.cpp index 40d6ff6..40245c1 100644 --- a/sdv_executables/sdv_idl_compiler/entities/variable_entity.cpp +++ b/sdv_executables/sdv_idl_compiler/entities/variable_entity.cpp @@ -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 "variable_entity.h" #include "typedef_entity.h" #include "../exception.h" diff --git a/sdv_executables/sdv_idl_compiler/entities/variable_entity.h b/sdv_executables/sdv_idl_compiler/entities/variable_entity.h index 41eee2f..a0f824b 100644 --- a/sdv_executables/sdv_idl_compiler/entities/variable_entity.h +++ b/sdv_executables/sdv_idl_compiler/entities/variable_entity.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 DECLARATOR_ENTITY_H #define DECLARATOR_ENTITY_H diff --git a/sdv_executables/sdv_idl_compiler/environment.cpp b/sdv_executables/sdv_idl_compiler/environment.cpp index 451a5d8..fa5d83b 100644 --- a/sdv_executables/sdv_idl_compiler/environment.cpp +++ b/sdv_executables/sdv_idl_compiler/environment.cpp @@ -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 "environment.h" #include "lexer.h" #include "codepos.h" diff --git a/sdv_executables/sdv_idl_compiler/environment.h b/sdv_executables/sdv_idl_compiler/environment.h index d33c4c0..dca7a5c 100644 --- a/sdv_executables/sdv_idl_compiler/environment.h +++ b/sdv_executables/sdv_idl_compiler/environment.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 ENV_H #define ENV_H diff --git a/sdv_executables/sdv_idl_compiler/exception.cpp b/sdv_executables/sdv_idl_compiler/exception.cpp index 55ef0bf..f11e63e 100644 --- a/sdv_executables/sdv_idl_compiler/exception.cpp +++ b/sdv_executables/sdv_idl_compiler/exception.cpp @@ -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 "exception.h" #include diff --git a/sdv_executables/sdv_idl_compiler/exception.h b/sdv_executables/sdv_idl_compiler/exception.h index cd3b1e5..f42d9fe 100644 --- a/sdv_executables/sdv_idl_compiler/exception.h +++ b/sdv_executables/sdv_idl_compiler/exception.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 EXCEPTION_H #define EXCEPTION_H diff --git a/sdv_executables/sdv_idl_compiler/generator/cmake_generator.cpp b/sdv_executables/sdv_idl_compiler/generator/cmake_generator.cpp index d79bcaa..85285dd 100644 --- a/sdv_executables/sdv_idl_compiler/generator/cmake_generator.cpp +++ b/sdv_executables/sdv_idl_compiler/generator/cmake_generator.cpp @@ -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 "context.h" #include "cmake_generator.h" #include "../exception.h" diff --git a/sdv_executables/sdv_idl_compiler/generator/cmake_generator.h b/sdv_executables/sdv_idl_compiler/generator/cmake_generator.h index bd7c115..5957144 100644 --- a/sdv_executables/sdv_idl_compiler/generator/cmake_generator.h +++ b/sdv_executables/sdv_idl_compiler/generator/cmake_generator.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 CMAKE_GENERATOR_H #define CMAKE_GENERATOR_H diff --git a/sdv_executables/sdv_idl_compiler/generator/context.cpp b/sdv_executables/sdv_idl_compiler/generator/context.cpp index 7aeeb38..1fcbf4f 100644 --- a/sdv_executables/sdv_idl_compiler/generator/context.cpp +++ b/sdv_executables/sdv_idl_compiler/generator/context.cpp @@ -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 "context.h" #include "../exception.h" #include diff --git a/sdv_executables/sdv_idl_compiler/generator/context.h b/sdv_executables/sdv_idl_compiler/generator/context.h index f83cc1f..94aa25a 100644 --- a/sdv_executables/sdv_idl_compiler/generator/context.h +++ b/sdv_executables/sdv_idl_compiler/generator/context.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 CONTEXT_H #define CONTEXT_H diff --git a/sdv_executables/sdv_idl_compiler/generator/definition_generator.cpp b/sdv_executables/sdv_idl_compiler/generator/definition_generator.cpp index aa0044b..4c1047a 100644 --- a/sdv_executables/sdv_idl_compiler/generator/definition_generator.cpp +++ b/sdv_executables/sdv_idl_compiler/generator/definition_generator.cpp @@ -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 "definition_generator.h" #include "../logger.h" #include "../exception.h" @@ -5,6 +18,76 @@ #include #include #include +#include +#include + +/** + * @brief Does the provided string represent a valid number - starting with a digit, 0x or 0b representing an hexa-decimal, + * decimal, octal or binary number comparable with C++? + * @remarks The original function used regular expressions. Two possible pattern came into question: + * @code + * ^(0x[0-9a-fA-F]+|0b[01]+|0[0-7]*|[1-9][0-9]*)$ + * ^(?:0x[0-9a-fA-F]+|0b[01]+|0[0-7]*|[1-9][0-9]*)$ + * @endcode + * Due to a bug in the MINGW libstd++ regex library, which hangs due to backtracing and stack-overflow issues while parsing these + * patterns, the implementation was replaced by a C++ function. + * @param[in] sv Sting view to the string to check. + * @return Returns whether the string view contains a valid number. + */ +inline bool IsValidIdlNumber(std::string_view sv) +{ + if (sv.empty()) return false; + + // 1. Hexadecimal: 0x... or 0X... + if (sv.size() > 2 && sv[0] == '0' && (sv[1] == 'x' || sv[1] == 'X')) + return sv.substr(2).find_first_not_of("0123456789abcdefABCDEF") == std::string_view::npos; + + // 2. Binary: 0b... or 0B... + if (sv.size() > 2 && sv[0] == '0' && (sv[1] == 'b' || sv[1] == 'B')) + return sv.substr(2).find_first_not_of("01") == std::string_view::npos; + + // 3. Octal or a zero: 0... + if (sv[0] == '0') + return sv.find_first_not_of("01234567") == std::string_view::npos; + + // 4. Decimal: starts with 1-9, followed by 0-9 + if (sv[0] >= '1' && sv[0] <= '9') + return sv.find_first_not_of("0123456789") == std::string_view::npos; + + // Not a valid number + return false; +} + + +/** + * @brief Add std::move to complex data types. + * @param rssName Reference to the variable name. + * @param eDeclType Declaration type. + * @return The string with or without std::move. + */ +inline std::string MoveVarDeclType(const std::string& rssName, sdv::idl::EDeclType eDeclType) +{ + switch (eDeclType) + { + case sdv::idl::EDeclType::decltype_string: + case sdv::idl::EDeclType::decltype_u8string: + case sdv::idl::EDeclType::decltype_u16string: + case sdv::idl::EDeclType::decltype_u32string: + case sdv::idl::EDeclType::decltype_wstring: + case sdv::idl::EDeclType::decltype_struct: + case sdv::idl::EDeclType::decltype_union: + case sdv::idl::EDeclType::decltype_pointer: + case sdv::idl::EDeclType::decltype_sequence: + case sdv::idl::EDeclType::decltype_map: + case sdv::idl::EDeclType::decltype_bitset: + case sdv::idl::EDeclType::decltype_bitfield: + case sdv::idl::EDeclType::decltype_bitmask: + case sdv::idl::EDeclType::decltype_any: + return "std::move(" + rssName + ")"; + default: + return rssName; + } +} CDefinitionContext::CDefinitionContext(const CGenContext& rGenContext, sdv::IInterfaceAccess* pEntity) : CDefEntityContext(rGenContext, pEntity), m_bPreface(true) @@ -647,11 +730,24 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I rcontext.GetDefCodeStream() << MapDeclType2CType(eBaseType); } + // Code streams + std::stringstream sstreamConstructorImpl; + std::stringstream sstreamCopyConstructImpl; + std::stringstream sstreamCopyConstructInitializers; + std::stringstream sstreamCopyOperatorImpl; + std::stringstream sstreamMoveConstructImpl; + std::stringstream sstreamMoveConstructInitializers; + std::stringstream sstreamMoveOperatorImpl; + std::stringstream sstreamDestructorImpl; + std::stringstream sstreamPrivateFunc; + std::stringstream sstreamPublicFunc; + bool bCopyMoveConstructNeeded = false; + // Start content rcontext.GetDefCodeStream() << std::endl << rcontext.GetIndent() << "{" << std::endl; rcontext.IncrIndent(); - // Run through the declarations and add each declaration. Add the definition before the declaration if there is one to be added. + // Run through the declarations and check for an exception definitions defining a description. sdv::idl::IEntityIterator* pChildIterator = pDefinition ? pDefinition->GetChildren() : nullptr; bool bContainsConstDescription = false; for (uint32_t uiIndex = 0; !bInline && pChildIterator && uiIndex < pChildIterator->GetCount(); uiIndex++) @@ -673,10 +769,10 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I // Check for const variable if (!pChildDeclarationEntity->IsReadOnly()) throw CCompileException("Exception definition declares '_description' member; this member must be a " - "const-variable."); + "const-variable."); if (pChildDeclType->GetBaseType() != sdv::idl::EDeclType::decltype_char || !pChildDeclarationEntity->HasArray()) throw CCompileException("Exception definition declares '_description' member; " - "this member must be of char array (char[]) type."); + "this member must be of char array (char[]) type."); bContainsConstDescription = true; } } @@ -751,6 +847,127 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I // NOTE: Compound definitions are all definitions except modules. if (pChildIterator && !ProcessEntities(sContentStreamContext, pChildIterator)) return; + // Get the switch code context list + auto vecSwitchCodeContext = sContentStreamContext.GetSwitchCodeContexts(); + + // Add copy and move construction code. + for (uint32_t uiIndex = 0; !bInline && pChildIterator && uiIndex < pChildIterator->GetCount(); uiIndex++) + { + sdv::IInterfaceAccess* pChildEntity = pChildIterator->GetEntityByIndex(uiIndex); + if (!pChildEntity) continue; + const sdv::idl::IEntityInfo* pChildEntityInfo = pChildEntity->GetInterface(); + if (!pChildEntityInfo) continue; + + // Is the entity a declaration? + sdv::idl::IDeclarationEntity* pChildDeclarationEntity = pChildEntity->GetInterface(); + if (!pChildDeclarationEntity) continue; + sdv::IInterfaceAccess* pChildDeclTypeObj = pChildDeclarationEntity->GetDeclarationType(); + if (!pChildDeclTypeObj) continue; + const sdv::idl::IDeclarationType* pChildDeclType = pChildDeclTypeObj->GetInterface(); + if (!pChildDeclType) continue; + if ((pChildDeclType->GetBaseType() != sdv::idl::EDeclType::decltype_union) && !pChildDeclarationEntity->IsReadOnly() && + std::find_if(vecSwitchCodeContext.begin(), vecSwitchCodeContext.end(), + [&](const std::shared_ptr& ptrSwitchCodeContext) + { + return ptrSwitchCodeContext && ptrSwitchCodeContext->ssSwitchVarName == pChildEntityInfo->GetName(); + }) == vecSwitchCodeContext.end()) + { + // Is this an array? + if (pChildDeclarationEntity->HasArray()) + { + // Automatic indentation + auto fnLocalIndent = [&](size_t nSteps) + { + std::string m_ssIndent = sContentStreamContext.GetIndent(); + for (size_t n = 0; n < nSteps; n++) + m_ssIndent += GetIndentChars(); + return m_ssIndent; + }; + + // Create the limiter string + auto fnLimiter = [&](const std::string& rssExpression) + { + if (IsValidIdlNumber(rssExpression)) return rssExpression; + return "static_cast(" + GetRelativeScopedName(rssExpression, sContentStreamContext.GetScope()) + ")"; + }; + + // Iterate through the array dimensions + auto seqDimensions = pChildDeclarationEntity->GetArrayDimensions(); + std::stringstream sstreamIteration, sstreamIndexRef; + size_t nDepth = 0; + for (auto sDimension : seqDimensions) + { + std::string ssArrayIterator = std::string("nIndex_") + pChildEntityInfo->GetName(); + if (seqDimensions.size() > 1) ssArrayIterator += std::to_string(nDepth++); + sstreamIteration << fnLocalIndent(nDepth) << "for (size_t " << ssArrayIterator << " = 0; " << + ssArrayIterator << " < " << fnLimiter(sDimension.ssExpression) << "; ++" << ssArrayIterator << ")" << + std::endl << fnLocalIndent(nDepth) << "{" << std::endl; + sstreamIndexRef << "[" << ssArrayIterator << "]"; + if (nDepth == seqDimensions.size()) + { + sstreamCopyConstructImpl << sstreamIteration.str(); + sstreamMoveConstructImpl << sstreamIteration.str(); + sstreamCopyOperatorImpl << sstreamIteration.str(); + sstreamMoveOperatorImpl << sstreamIteration.str(); + ++nDepth; + sstreamCopyConstructImpl << fnLocalIndent(nDepth) << pChildEntityInfo->GetName() << + sstreamIndexRef.str() << " = rvar." << pChildEntityInfo->GetName() << sstreamIndexRef.str() << ";" << + std::endl; + sstreamMoveConstructImpl << fnLocalIndent(nDepth) << pChildEntityInfo->GetName() << + sstreamIndexRef.str() << " = " << + MoveVarDeclType("rvar." + pChildEntityInfo->GetName() + sstreamIndexRef.str(), + pChildDeclType->GetBaseType()) << ";" << std::endl; + sstreamCopyOperatorImpl << fnLocalIndent(nDepth) << pChildEntityInfo->GetName() << + sstreamIndexRef.str() << " = rvar." << pChildEntityInfo->GetName() << sstreamIndexRef.str() << ";" << + std::endl; + sstreamMoveOperatorImpl << fnLocalIndent(nDepth) << pChildEntityInfo->GetName() << + sstreamIndexRef.str() << " = " << + MoveVarDeclType("rvar." + pChildEntityInfo->GetName() + sstreamIndexRef.str(), + pChildDeclType->GetBaseType()) << ";" << std::endl; + --nDepth; + } + } + + // Stop array iteration if the union was allocated using a single or multi dimensional array + while (nDepth) + { + sstreamCopyConstructImpl << fnLocalIndent(nDepth) << "}" << std::endl; + sstreamMoveConstructImpl << fnLocalIndent(nDepth) << "}" << std::endl; + sstreamCopyOperatorImpl << fnLocalIndent(nDepth) << "}" << std::endl; + sstreamMoveOperatorImpl << fnLocalIndent(nDepth) << "}" << std::endl; + --nDepth; + } + } + else if (pChildEntityInfo->GetType() != sdv::idl::EEntityType::type_case_entry) + { + // Copy constructor initialization + if (sstreamCopyConstructInitializers.str().empty()) + sstreamCopyConstructInitializers << " :" << std::endl; + else + sstreamCopyConstructInitializers << "," << std::endl; + sstreamCopyConstructInitializers << sContentStreamContext.GetIndent(true, true) << pChildEntityInfo->GetName() + << "(rvar." << pChildEntityInfo->GetName() << ")"; + + // Move constructor initialization + if (sstreamMoveConstructInitializers.str().empty()) + sstreamMoveConstructInitializers << " :" << std::endl; + else + sstreamMoveConstructInitializers << "," << std::endl; + sstreamMoveConstructInitializers << sContentStreamContext.GetIndent(true, true) << pChildEntityInfo->GetName() << + "(" << MoveVarDeclType("rvar." + pChildEntityInfo->GetName(), pChildDeclType->GetBaseType()) << ")"; + + // Copy operator + sstreamCopyOperatorImpl << sContentStreamContext.GetIndent(true, true) << pChildEntityInfo->GetName() << + " = rvar." << pChildEntityInfo->GetName() << ";" << std::endl; + + // Move operator + sstreamMoveOperatorImpl << sContentStreamContext.GetIndent(true, true) << pChildEntityInfo->GetName() << + " = " << MoveVarDeclType("rvar." + pChildEntityInfo->GetName(), pChildDeclType->GetBaseType()) << ";" << + std::endl; + } + } + } + // Add the what-function to the exception if (pEntityInfo->GetType() == sdv::idl::EEntityType::type_exception) { @@ -780,17 +997,10 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I } // Build the code streams from any switch variables - std::stringstream sstreamConstructorImpl; - std::stringstream sstreamCopyConstructImpl; - std::stringstream sstreamMoveConstructImpl; - std::stringstream sstreamDestructorImpl; - std::stringstream sstreamPrivateFunc; - std::stringstream sstreamPublicFunc; - for (const auto& rptrSwitchCodeContext : sContentStreamContext.GetSwitchCodeContexts()) + for (const auto& rptrSwitchCodeContext : vecSwitchCodeContext) { std::string ssSwitchVarName = rptrSwitchCodeContext->ssSwitchVarName; std::string ssSwitchVarType = rptrSwitchCodeContext->ptrSwitchVar->ssType; - //std::string ssSwitchVarValue; // Create a stream of constructor implementation code if (!rptrSwitchCodeContext->sstreamConstructorImpl.str().empty()) @@ -813,10 +1023,7 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I sstreamPrivateFunc << sContentStreamContext.GetIndent(false) << "void construct_" << QualifyName(ssSwitchVarName); sstreamPrivateFunc << "("; sstreamPrivateFunc << ssSwitchVarType << " val = "; - //if (!ssSwitchVarValue.empty()) - // sstreamPrivateFunc << ssSwitchVarValue; - //else - sstreamPrivateFunc << ssSwitchVarType << "{}"; + sstreamPrivateFunc << ssSwitchVarType << "{}"; sstreamPrivateFunc << ")" << std::endl << sContentStreamContext.GetIndent(false) << "{" << std::endl; sstreamPrivateFunc << sContentStreamContext.GetIndent(false, true) << ssSwitchVarName << " = val;" << std::endl; sstreamPrivateFunc << @@ -824,28 +1031,47 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I sstreamPrivateFunc << sContentStreamContext.GetIndent(false) << "}" << std::endl; } - // Create copy constructor impl for the switch type + // Create copy constructor/operator impl for the switch type if (!rptrSwitchCodeContext->sstreamCopyConstructHelperImpl.str().empty()) { + // Constructor if (!sstreamCopyConstructImpl.str().empty()) sstreamCopyConstructImpl << std::endl; sstreamCopyConstructImpl << sContentStreamContext.GetIndent(false, true) << "// Construct content based on " << ssSwitchVarName << std::endl; - sstreamCopyConstructImpl << - SmartIndent(rptrSwitchCodeContext->sstreamCopyConstructHelperImpl.str(), - sContentStreamContext.GetIndent(false, true)); + sstreamCopyConstructImpl << SmartIndent(rptrSwitchCodeContext->sstreamCopyConstructHelperImpl.str(), + sContentStreamContext.GetIndent(false, true)); + + // Operator + if (!sstreamCopyOperatorImpl.str().empty()) + sstreamCopyOperatorImpl << std::endl; + sstreamCopyOperatorImpl << sContentStreamContext.GetIndent(false, true) << "// Construct content based on " << + ssSwitchVarName << std::endl; + sstreamCopyOperatorImpl << SmartIndent(rptrSwitchCodeContext->sstreamCopyConstructHelperImpl.str(), + sContentStreamContext.GetIndent(false, true)); + bCopyMoveConstructNeeded = true; } - // Create move constructor impl for the switch type + // Create move constructor/operator impl for the switch type if (!rptrSwitchCodeContext->sstreamMoveConstructHelperImpl.str().empty()) { + // Constructor if (!sstreamMoveConstructImpl.str().empty()) sstreamMoveConstructImpl << std::endl; sstreamMoveConstructImpl << sContentStreamContext.GetIndent(false, true) << "// Construct content based on " << ssSwitchVarName << std::endl; - sstreamMoveConstructImpl << - SmartIndent(rptrSwitchCodeContext->sstreamMoveConstructHelperImpl.str(), + sstreamMoveConstructImpl << SmartIndent(rptrSwitchCodeContext->sstreamMoveConstructHelperImpl.str(), sContentStreamContext.GetIndent(false, true)); + bCopyMoveConstructNeeded = true; + + // Operator + if (!sstreamMoveOperatorImpl.str().empty()) + sstreamMoveOperatorImpl << std::endl; + sstreamMoveOperatorImpl << sContentStreamContext.GetIndent(false, true) << "// Construct content based on " << + ssSwitchVarName << std::endl; + sstreamMoveOperatorImpl << SmartIndent(rptrSwitchCodeContext->sstreamMoveConstructHelperImpl.str(), + sContentStreamContext.GetIndent(false, true)); + bCopyMoveConstructNeeded = true; } // Create destructor helper function @@ -894,9 +1120,6 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I sstreamPublicFunc << sContentStreamContext.GetIndent(false, true) << "// Anything to do?" << std::endl; sstreamPublicFunc << sContentStreamContext.GetIndent(false, true) << "if (" << ssSwitchVarName << " == val) return;" << std::endl; - //sstreamPublicFunc << sContentStreamContext.GetIndent(false, true) << "// Assign the new value..." << std::endl; - //sstreamPublicFunc << sContentStreamContext.GetIndent(false, true) << - // rptrSwitchCodeContext->ssSwitchVarName << " = val;" << std::endl; sstreamPublicFunc << SmartIndent(rptrSwitchCodeContext->sstreamCode.str(), sContentStreamContext.GetIndent(false, true)); sstreamPublicFunc << sContentStreamContext.GetIndent(false) << "}" << std::endl << std::endl; @@ -923,11 +1146,11 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I { if (!ssConstructorImpl.empty()) { - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) - << "// No initialization of variables is done in the constructor; suppress cppcheck warning." - << std::endl; - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) - << "// cppcheck-suppress uninitMemberVar" << std::endl; + //rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) + // << "// No initialization of variables is done in the constructor; suppress cppcheck warning." + // << std::endl; + //rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) + // << "// cppcheck-suppress uninitMemberVar" << std::endl; } rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "/** Constructor */" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << pEntityInfo->GetName() << "()"; @@ -943,17 +1166,20 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I rcontext.GetDefCodeStream() << std::endl; } - // Stream copy constructor function - std::string ssCopyConstructImpl = sstreamCopyConstructImpl.str(); + // Stream copy constructor function (if required) + std::string ssCopyConstructImpl; + std::string ssCopyConstructInitializers = sstreamCopyConstructInitializers.str(); + if (bCopyMoveConstructNeeded) + ssCopyConstructImpl = sstreamCopyConstructImpl.str(); if (sContentStreamContext.NeedsConstruction() || !ssCopyConstructImpl.empty()) { if (!ssCopyConstructImpl.empty()) { - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent( - false) << "// No initialization of variables is done in the constructor; suppress cppcheck warning." - << std::endl; - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "// cppcheck-suppress uninitMemberVar" - << std::endl; + //rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent( + // false) << "// No initialization of variables is done in the constructor; suppress cppcheck warning." + // << std::endl; + //rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "// cppcheck-suppress uninitMemberVar" + // << std::endl; } rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "/**" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << " * Copy constructor " << std::endl; @@ -964,12 +1190,20 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I if (ssCopyConstructImpl.empty()) rcontext.GetDefCodeStream() << "[[maybe_unused]] "; rcontext.GetDefCodeStream() << "const " << pEntityInfo->GetName() << "& rvar)"; - if (ssCopyConstructImpl.empty()) - rcontext.GetDefCodeStream() << " {}" << std::endl; + if (!ssCopyConstructInitializers.empty()) + rcontext.GetDefCodeStream() << ssCopyConstructInitializers << std::endl << sContentStreamContext.GetIndent(false); else { - rcontext.GetDefCodeStream() << std::endl; - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "{" << std::endl; + if (ssCopyConstructImpl.empty()) + rcontext.GetDefCodeStream() << " "; + else + rcontext.GetDefCodeStream() << std::endl; + } + if (ssCopyConstructImpl.empty()) + rcontext.GetDefCodeStream() << "{}" << std::endl; + else + { + rcontext.GetDefCodeStream() << "{" << std::endl; rcontext.GetDefCodeStream() << ssCopyConstructImpl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "}" << std::endl; } @@ -977,16 +1211,19 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I } // Stream move constructor function - std::string ssMoveConstructImpl = sstreamMoveConstructImpl.str(); + std::string ssMoveConstructImpl; + std::string ssMoveConstructInitializers = sstreamMoveConstructInitializers.str(); + if (bCopyMoveConstructNeeded) + ssMoveConstructImpl = sstreamMoveConstructImpl.str(); if (sContentStreamContext.NeedsConstruction() || !ssMoveConstructImpl.empty()) { if (!ssMoveConstructImpl.empty()) { - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent( - false) << "// No initialization of variables is done in the constructor; suppress cppcheck warning." - << std::endl; - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "// cppcheck-suppress uninitMemberVar" - << std::endl; + //rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent( + // false) << "// No initialization of variables is done in the constructor; suppress cppcheck warning." + // << std::endl; + //rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "// cppcheck-suppress uninitMemberVar" + // << std::endl; } rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "/**" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << " * Move constructor " << std::endl; @@ -997,12 +1234,20 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I if (ssMoveConstructImpl.empty()) rcontext.GetDefCodeStream() << "[[maybe_unused]] "; rcontext.GetDefCodeStream() << pEntityInfo->GetName() << "&& rvar) noexcept"; - if (ssMoveConstructImpl.empty()) - rcontext.GetDefCodeStream() << " {}" << std::endl; + if (!ssMoveConstructInitializers.empty()) + rcontext.GetDefCodeStream() << ssMoveConstructInitializers << std::endl << sContentStreamContext.GetIndent(false); else { - rcontext.GetDefCodeStream() << std::endl; - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "{" << std::endl; + if (ssMoveConstructImpl.empty()) + rcontext.GetDefCodeStream() << " "; + else + rcontext.GetDefCodeStream() << std::endl; + } + if (ssMoveConstructImpl.empty()) + rcontext.GetDefCodeStream() << "{}" << std::endl; + else + { + rcontext.GetDefCodeStream() << "{" << std::endl; rcontext.GetDefCodeStream() << ssMoveConstructImpl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "}" << std::endl; } @@ -1028,16 +1273,19 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I } // Stream assignment operator function - if (sContentStreamContext.NeedsConstruction() || !ssCopyConstructImpl.empty()) + std::string ssCopyOperatorImpl; + if (bCopyMoveConstructNeeded) + ssCopyOperatorImpl = sstreamCopyOperatorImpl.str(); + if (sContentStreamContext.NeedsConstruction() || !ssCopyOperatorImpl.empty()) { - if (!ssCopyConstructImpl.empty()) - { - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent( - false) << "// Assignment of variables is done in a member function; suppress cppcheck warning." - << std::endl; - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "// cppcheck-suppress operatorEqVarError" - << std::endl; - } + //if (!ssCopyOperatorImpl.empty()) + //{ + // rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent( + // false) << "// Assignment of variables is done in a member function; suppress cppcheck warning." + // << std::endl; + // rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "// cppcheck-suppress operatorEqVarError" + // << std::endl; + //} rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "/**" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << " * Assignment operator" << std::endl; @@ -1048,10 +1296,10 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << " */" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << pEntityInfo->GetName() << "& operator=("; - if (ssCopyConstructImpl.empty()) + if (ssCopyOperatorImpl.empty()) rcontext.GetDefCodeStream() << "[[maybe_unused]] "; rcontext.GetDefCodeStream() << "const " << pEntityInfo->GetName() << " & rvar)"; - if (ssCopyConstructImpl.empty()) + if (ssCopyOperatorImpl.empty()) rcontext.GetDefCodeStream() << " { return *this; }" << std::endl; else { @@ -1061,7 +1309,7 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I std::endl; rcontext.GetDefCodeStream() << ssDestructorImpl; rcontext.GetDefCodeStream() << std::endl; - rcontext.GetDefCodeStream() << ssCopyConstructImpl; + rcontext.GetDefCodeStream() << ssCopyOperatorImpl; rcontext.GetDefCodeStream() << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false, true) << "return *this;" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "}" << std::endl; @@ -1070,16 +1318,19 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I } // Stream move operator function - if (sContentStreamContext.NeedsConstruction() || !ssMoveConstructImpl.empty()) + std::string ssMoveOperatorImpl; + if (bCopyMoveConstructNeeded) + ssMoveOperatorImpl = sstreamMoveOperatorImpl.str(); + if (sContentStreamContext.NeedsConstruction() || !ssMoveOperatorImpl.empty()) { - if (!ssMoveConstructImpl.empty()) - { - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) - << "// Assignment of variables is done in a member function; suppress cppcheck warning." - << std::endl; - rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "// cppcheck-suppress operatorEqVarError" - << std::endl; - } + //if (!ssMoveOperatorImpl.empty()) + //{ + // rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) + // << "// Assignment of variables is done in a member function; suppress cppcheck warning." + // << std::endl; + // rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "// cppcheck-suppress operatorEqVarError" + // << std::endl; + //} rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "/**" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << " * Move operator" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << " * @param[in] rvar Reference to the " << @@ -1089,10 +1340,10 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << " */" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << pEntityInfo->GetName() << "& operator=("; - if (ssMoveConstructImpl.empty()) + if (ssMoveOperatorImpl.empty()) rcontext.GetDefCodeStream() << "[[maybe_unused]] "; rcontext.GetDefCodeStream() << pEntityInfo->GetName() << "&& rvar) noexcept"; - if (ssMoveConstructImpl.empty()) + if (ssMoveOperatorImpl.empty()) rcontext.GetDefCodeStream() << " { return *this; }" << std::endl; else { @@ -1102,7 +1353,7 @@ void CDefinitionGenerator::StreamDefinition(CDefinitionContext& rcontext, sdv::I std::endl; rcontext.GetDefCodeStream() << ssDestructorImpl; rcontext.GetDefCodeStream() << std::endl; - rcontext.GetDefCodeStream() << ssMoveConstructImpl; + rcontext.GetDefCodeStream() << ssMoveOperatorImpl; rcontext.GetDefCodeStream() << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false, true) << "return *this;" << std::endl; rcontext.GetDefCodeStream() << sContentStreamContext.GetIndent(false) << "}" << std::endl; @@ -1578,22 +1829,22 @@ void CDefinitionGenerator::ProcessUnionInContainerContext(CDefinitionContext& rc // Start array iteration if the union was allocated using a single or multi dimensional array for (const auto& rsArrayIndex : rvecArrayIndices) { - rsstreamConstructHelperImpl << fnLocalIndent() << "for (uint32_t " << rsArrayIndex.ssArrayIterator << " = 0; " << - rsArrayIndex.ssArrayIterator << " < " << - GetRelativeScopedName(rsArrayIndex.ssCountExpression, rcontext.GetScope()) << "; " << - rsArrayIndex.ssArrayIterator << "++)" << std::endl << fnLocalIndent() << "{" << std::endl; - rsstreamCopyConstructHelperImpl << fnLocalIndent() << "for (uint32_t " << rsArrayIndex.ssArrayIterator << " = 0; " << - rsArrayIndex.ssArrayIterator << " < " << - GetRelativeScopedName(rsArrayIndex.ssCountExpression, rcontext.GetScope()) << "; " << - rsArrayIndex.ssArrayIterator << "++)" << std::endl << fnLocalIndent() << "{" << std::endl; - rsstreamMoveConstructHelperImpl << fnLocalIndent() << "for (uint32_t " << rsArrayIndex.ssArrayIterator << " = 0; " << - rsArrayIndex.ssArrayIterator << " < " << - GetRelativeScopedName(rsArrayIndex.ssCountExpression, rcontext.GetScope()) << "; " << - rsArrayIndex.ssArrayIterator << "++)" << std::endl << fnLocalIndent() << "{" << std::endl; - rsstreamDestructHelperImpl << fnLocalIndent() << "for (uint32_t " << rsArrayIndex.ssArrayIterator << " = 0; " << - rsArrayIndex.ssArrayIterator << " < " << - GetRelativeScopedName(rsArrayIndex.ssCountExpression, rcontext.GetScope()) << "; " << - rsArrayIndex.ssArrayIterator << "++)" << std::endl << fnLocalIndent() << "{" << std::endl; + // Create the limiter string + auto fnLimiter = [&]() + { + if (IsValidIdlNumber(rsArrayIndex.ssCountExpression)) + return rsArrayIndex.ssCountExpression; + return "static_cast(" + GetRelativeScopedName(rsArrayIndex.ssCountExpression, rcontext.GetScope()) + ")"; + }; + + std::stringstream sstreamInteratorCode; + sstreamInteratorCode << fnLocalIndent() << "for (size_t " << rsArrayIndex.ssArrayIterator << " = 0; " << + rsArrayIndex.ssArrayIterator << " < " << fnLimiter() << "; ++" << rsArrayIndex.ssArrayIterator << ")" << std::endl << + fnLocalIndent() << "{" << std::endl; + rsstreamConstructHelperImpl << sstreamInteratorCode.str(); + rsstreamCopyConstructHelperImpl << sstreamInteratorCode.str(); + rsstreamMoveConstructHelperImpl << sstreamInteratorCode.str(); + rsstreamDestructHelperImpl << sstreamInteratorCode.str(); nLocalIndent++; } diff --git a/sdv_executables/sdv_idl_compiler/generator/definition_generator.h b/sdv_executables/sdv_idl_compiler/generator/definition_generator.h index e18ef3a..e71f57c 100644 --- a/sdv_executables/sdv_idl_compiler/generator/definition_generator.h +++ b/sdv_executables/sdv_idl_compiler/generator/definition_generator.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 DEFINITION_GENERATOR_H #define DEFINITION_GENERATOR_H diff --git a/sdv_executables/sdv_idl_compiler/generator/definition_generator_base.h b/sdv_executables/sdv_idl_compiler/generator/definition_generator_base.h index 20246d6..1540ecf 100644 --- a/sdv_executables/sdv_idl_compiler/generator/definition_generator_base.h +++ b/sdv_executables/sdv_idl_compiler/generator/definition_generator_base.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 DEFINITION_GENERATOR_BASE_H #define DEFINITION_GENERATOR_BASE_H diff --git a/sdv_executables/sdv_idl_compiler/generator/definition_generator_base.inl b/sdv_executables/sdv_idl_compiler/generator/definition_generator_base.inl index 3ee587e..30eba63 100644 --- a/sdv_executables/sdv_idl_compiler/generator/definition_generator_base.inl +++ b/sdv_executables/sdv_idl_compiler/generator/definition_generator_base.inl @@ -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 DEFINITION_GENERATOR_BASE_INL #define DEFINITION_GENERATOR_BASE_INL @@ -515,7 +528,7 @@ inline void CDefinitionGeneratorBase::DetectUnionContainerFor for (const sdv::idl::SArrayDimension& rsDimension : seqArrayDimensions) { // Create an index variable name - std::string ssIndexVarName = std::string("uiIndex_") + pEntityInfo->GetName(); + std::string ssIndexVarName = std::string("nIndex_") + pEntityInfo->GetName(); if (seqArrayDimensions.size() > 1) ssIndexVarName += std::to_string(nIndex++); vecNewInicesInfo.push_back(SArrayIterationInfo{ ssIndexVarName, rsDimension.ssExpression }); ssDeclMemberScope += std::string("[") + ssIndexVarName + "]"; diff --git a/sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp b/sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp index 43913a6..a53432b 100644 --- a/sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp +++ b/sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp @@ -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 "proxy_generator.h" #include "../exception.h" #include diff --git a/sdv_executables/sdv_idl_compiler/generator/proxy_generator.h b/sdv_executables/sdv_idl_compiler/generator/proxy_generator.h index bcca701..5a48c19 100644 --- a/sdv_executables/sdv_idl_compiler/generator/proxy_generator.h +++ b/sdv_executables/sdv_idl_compiler/generator/proxy_generator.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 PROXY_GENERATOR_H #define PROXY_GENERATOR_H diff --git a/sdv_executables/sdv_idl_compiler/generator/ps_class_generator_base.cpp b/sdv_executables/sdv_idl_compiler/generator/ps_class_generator_base.cpp index c8f4e85..d1da1dc 100644 --- a/sdv_executables/sdv_idl_compiler/generator/ps_class_generator_base.cpp +++ b/sdv_executables/sdv_idl_compiler/generator/ps_class_generator_base.cpp @@ -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 "ps_class_generator_base.h" #include "../exception.h" #include diff --git a/sdv_executables/sdv_idl_compiler/generator/ps_class_generator_base.h b/sdv_executables/sdv_idl_compiler/generator/ps_class_generator_base.h index 3b05f04..43658fa 100644 --- a/sdv_executables/sdv_idl_compiler/generator/ps_class_generator_base.h +++ b/sdv_executables/sdv_idl_compiler/generator/ps_class_generator_base.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 PS_CLASS_GENERATOR_BASE_H #define PS_CLASS_GENERATOR_BASE_H diff --git a/sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.cpp b/sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.cpp index 52d71da..cf66652 100644 --- a/sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.cpp +++ b/sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.cpp @@ -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 "context.h" #include "ps_cpp_generator.h" #include "../exception.h" diff --git a/sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.h b/sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.h index e382f78..f177c31 100644 --- a/sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.h +++ b/sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.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 PS_CPP_GENERATOR_H #define PS_CPP_GENERATOR_H diff --git a/sdv_executables/sdv_idl_compiler/generator/serdes_generator.cpp b/sdv_executables/sdv_idl_compiler/generator/serdes_generator.cpp index a3d950e..e41e4ad 100644 --- a/sdv_executables/sdv_idl_compiler/generator/serdes_generator.cpp +++ b/sdv_executables/sdv_idl_compiler/generator/serdes_generator.cpp @@ -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 "serdes_generator.h" #include "../logger.h" #include diff --git a/sdv_executables/sdv_idl_compiler/generator/serdes_generator.h b/sdv_executables/sdv_idl_compiler/generator/serdes_generator.h index 5ca9c0d..ba797c2 100644 --- a/sdv_executables/sdv_idl_compiler/generator/serdes_generator.h +++ b/sdv_executables/sdv_idl_compiler/generator/serdes_generator.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 SERDES_GENERATOR_H #define SERDES_GENERATOR_H diff --git a/sdv_executables/sdv_idl_compiler/generator/stub_generator.cpp b/sdv_executables/sdv_idl_compiler/generator/stub_generator.cpp index 077f7c5..96a6698 100644 --- a/sdv_executables/sdv_idl_compiler/generator/stub_generator.cpp +++ b/sdv_executables/sdv_idl_compiler/generator/stub_generator.cpp @@ -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 "stub_generator.h" #include "../exception.h" #include diff --git a/sdv_executables/sdv_idl_compiler/generator/stub_generator.h b/sdv_executables/sdv_idl_compiler/generator/stub_generator.h index 52e6093..67a2310 100644 --- a/sdv_executables/sdv_idl_compiler/generator/stub_generator.h +++ b/sdv_executables/sdv_idl_compiler/generator/stub_generator.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 STUB_GTENERATOR_H #define STUB_GTENERATOR_H diff --git a/sdv_executables/sdv_idl_compiler/includes.h b/sdv_executables/sdv_idl_compiler/includes.h index ecaee2a..8dfd99b 100644 --- a/sdv_executables/sdv_idl_compiler/includes.h +++ b/sdv_executables/sdv_idl_compiler/includes.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 INCLUDES_IDL_COMPILER_H #define INCLUDES_IDL_COMPILER_H diff --git a/sdv_executables/sdv_idl_compiler/lexer.cpp b/sdv_executables/sdv_idl_compiler/lexer.cpp index 0545a99..626686a 100644 --- a/sdv_executables/sdv_idl_compiler/lexer.cpp +++ b/sdv_executables/sdv_idl_compiler/lexer.cpp @@ -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 "lexer.h" #include "exception.h" #include "support.h" diff --git a/sdv_executables/sdv_idl_compiler/lexer.h b/sdv_executables/sdv_idl_compiler/lexer.h index 1519096..d64c26d 100644 --- a/sdv_executables/sdv_idl_compiler/lexer.h +++ b/sdv_executables/sdv_idl_compiler/lexer.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 LEXER_H #define LEXER_H diff --git a/sdv_executables/sdv_idl_compiler/lexerbase.h b/sdv_executables/sdv_idl_compiler/lexerbase.h index 70d2055..629888e 100644 --- a/sdv_executables/sdv_idl_compiler/lexerbase.h +++ b/sdv_executables/sdv_idl_compiler/lexerbase.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 LEXERBASE_H #define LEXERBASE_H diff --git a/sdv_executables/sdv_idl_compiler/logger.cpp b/sdv_executables/sdv_idl_compiler/logger.cpp index e849b5e..28e1dee 100644 --- a/sdv_executables/sdv_idl_compiler/logger.cpp +++ b/sdv_executables/sdv_idl_compiler/logger.cpp @@ -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 "logger.h" #ifdef _MSC_VER diff --git a/sdv_executables/sdv_idl_compiler/logger.h b/sdv_executables/sdv_idl_compiler/logger.h index 7e49598..f2406d5 100644 --- a/sdv_executables/sdv_idl_compiler/logger.h +++ b/sdv_executables/sdv_idl_compiler/logger.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 LOGGER_H #define LOGGER_H diff --git a/sdv_executables/sdv_idl_compiler/macro.cpp b/sdv_executables/sdv_idl_compiler/macro.cpp index 8a72571..7b8dc0e 100644 --- a/sdv_executables/sdv_idl_compiler/macro.cpp +++ b/sdv_executables/sdv_idl_compiler/macro.cpp @@ -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 "macro.h" #include "exception.h" #include "lexer.h" @@ -138,7 +151,7 @@ std::string CMacro::Expand(const CIdlCompilerEnvironment& renv, const CToken& rt // Circular references to macros within the expanded code are prevented for // - each paramerer separately with the used macro list provided by the function. // - macro expansion result with the used macros from all the parameters and the used macro list provided to the function. - // Macros used in the expansion of the paramters and the results are added to the used macro set provided to this function. + // Macros used in the expansion of the parameters and the results are added to the used macro set provided to this function. // Check whether the amount of provided params corresponds to the amount of param definitions. if (rvecParams.size() < m_vecParamDefs.size()) diff --git a/sdv_executables/sdv_idl_compiler/macro.h b/sdv_executables/sdv_idl_compiler/macro.h index 9e0ffa7..d13bde7 100644 --- a/sdv_executables/sdv_idl_compiler/macro.h +++ b/sdv_executables/sdv_idl_compiler/macro.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 MACRO_H #define MACRO_H diff --git a/sdv_executables/sdv_idl_compiler/main.cpp b/sdv_executables/sdv_idl_compiler/main.cpp index b176c86..4723265 100644 --- a/sdv_executables/sdv_idl_compiler/main.cpp +++ b/sdv_executables/sdv_idl_compiler/main.cpp @@ -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 "../../global/process_watchdog.h" #include "parser.h" #include "core_idl_backup.h" @@ -11,6 +24,7 @@ #include "generator/ps_cpp_generator.h" #include "generator/cmake_generator.h" #include +#include #include "../error_msg.h" #if defined(_WIN32) && defined(_UNICODE) diff --git a/sdv_executables/sdv_idl_compiler/parsecontext.cpp b/sdv_executables/sdv_idl_compiler/parsecontext.cpp index cbc4bb1..36c96f5 100644 --- a/sdv_executables/sdv_idl_compiler/parsecontext.cpp +++ b/sdv_executables/sdv_idl_compiler/parsecontext.cpp @@ -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 "parsecontext.h" CContext::CContext(const std::filesystem::path& rpath, diff --git a/sdv_executables/sdv_idl_compiler/parsecontext.h b/sdv_executables/sdv_idl_compiler/parsecontext.h index 1e2d34f..00ac741 100644 --- a/sdv_executables/sdv_idl_compiler/parsecontext.h +++ b/sdv_executables/sdv_idl_compiler/parsecontext.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 PARSE_CONTEXT_H #define PARSE_CONTEXT_H diff --git a/sdv_executables/sdv_idl_compiler/parser.cpp b/sdv_executables/sdv_idl_compiler/parser.cpp index dea8194..7648824 100644 --- a/sdv_executables/sdv_idl_compiler/parser.cpp +++ b/sdv_executables/sdv_idl_compiler/parser.cpp @@ -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 "parser.h" #include "exception.h" diff --git a/sdv_executables/sdv_idl_compiler/parser.h b/sdv_executables/sdv_idl_compiler/parser.h index fa0132c..f28bd7b 100644 --- a/sdv_executables/sdv_idl_compiler/parser.h +++ b/sdv_executables/sdv_idl_compiler/parser.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 PARSER_H #define PARSER_H diff --git a/sdv_executables/sdv_idl_compiler/preproc.cpp b/sdv_executables/sdv_idl_compiler/preproc.cpp index b9c9b31..9102303 100644 --- a/sdv_executables/sdv_idl_compiler/preproc.cpp +++ b/sdv_executables/sdv_idl_compiler/preproc.cpp @@ -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 "preproc.h" #include "lexer.h" #include "parser.h" diff --git a/sdv_executables/sdv_idl_compiler/preproc.h b/sdv_executables/sdv_idl_compiler/preproc.h index 26751cd..4ca2636 100644 --- a/sdv_executables/sdv_idl_compiler/preproc.h +++ b/sdv_executables/sdv_idl_compiler/preproc.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 PREPROC_H #define PREPROC_H diff --git a/sdv_executables/sdv_idl_compiler/source.cpp b/sdv_executables/sdv_idl_compiler/source.cpp index f03ed29..cbe7e0c 100644 --- a/sdv_executables/sdv_idl_compiler/source.cpp +++ b/sdv_executables/sdv_idl_compiler/source.cpp @@ -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 + ********************************************************************************/ + #ifdef _MSC_VER #pragma push_macro("interface") #undef interface diff --git a/sdv_executables/sdv_idl_compiler/source.h b/sdv_executables/sdv_idl_compiler/source.h index 8a5705a..0f4c621 100644 --- a/sdv_executables/sdv_idl_compiler/source.h +++ b/sdv_executables/sdv_idl_compiler/source.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 SOURCE_H #define SOURCE_H diff --git a/sdv_executables/sdv_idl_compiler/support.h b/sdv_executables/sdv_idl_compiler/support.h index 6e5af17..45f9772 100644 --- a/sdv_executables/sdv_idl_compiler/support.h +++ b/sdv_executables/sdv_idl_compiler/support.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 SUPPORT_H #define SUPPORT_H diff --git a/sdv_executables/sdv_idl_compiler/token.cpp b/sdv_executables/sdv_idl_compiler/token.cpp index 7fe5030..e502c02 100644 --- a/sdv_executables/sdv_idl_compiler/token.cpp +++ b/sdv_executables/sdv_idl_compiler/token.cpp @@ -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 "token.h" #include "support.h" #include "constvariant.inl" diff --git a/sdv_executables/sdv_idl_compiler/token.h b/sdv_executables/sdv_idl_compiler/token.h index 115ab15..d5046ee 100644 --- a/sdv_executables/sdv_idl_compiler/token.h +++ b/sdv_executables/sdv_idl_compiler/token.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 LOCATION_H #define LOCATION_H diff --git a/sdv_executables/sdv_idl_compiler/tokenlist.cpp b/sdv_executables/sdv_idl_compiler/tokenlist.cpp index 492910e..33b3f5f 100644 --- a/sdv_executables/sdv_idl_compiler/tokenlist.cpp +++ b/sdv_executables/sdv_idl_compiler/tokenlist.cpp @@ -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 "tokenlist.h" CTokenList::CTokenList() : m_itCurrent(end()) diff --git a/sdv_executables/sdv_idl_compiler/tokenlist.h b/sdv_executables/sdv_idl_compiler/tokenlist.h index a66c2c3..455d8fb 100644 --- a/sdv_executables/sdv_idl_compiler/tokenlist.h +++ b/sdv_executables/sdv_idl_compiler/tokenlist.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 TOKENLIST_H #define TOKENLIST_H diff --git a/sdv_executables/sdv_iso/CMakeLists.txt b/sdv_executables/sdv_iso/CMakeLists.txt index b350f8c..4f891b8 100644 --- a/sdv_executables/sdv_iso/CMakeLists.txt +++ b/sdv_executables/sdv_iso/CMakeLists.txt @@ -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_iso VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_executables/sdv_iso/main.cpp b/sdv_executables/sdv_iso/main.cpp index 6d7b9f7..aa559ab 100644 --- a/sdv_executables/sdv_iso/main.cpp +++ b/sdv_executables/sdv_iso/main.cpp @@ -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 #include #include @@ -274,7 +287,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) return REPOSITORY_SERVICE_ACCESS_ERROR; } sdv::SClassInfo sClassInfo = pRepositoryInfo->FindClass(parser.GetDirect("Isolation.Class").GetValue()); - if (sClassInfo.ssClassName.empty()) + if (sClassInfo.ssName.empty()) { if (!bSilent) std::cerr << "ERROR: " << CANNOT_FIND_OBJECT_MSG << " Class name: " << ssClassName << std::endl; @@ -284,13 +297,14 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) sdv::IInterfaceAccess* pObject = nullptr; switch (sClassInfo.eType) { - case sdv::EObjectType::ComplexService: + case sdv::EObjectType::complex_service: + case sdv::EObjectType::vehicle_function: { sdv::core::TObjectID tObjectID = pRepositoryControl->CreateObject(ssClassName, ssObjectName, ssConfig); pObject = pObjectAccess->GetObjectByID(tObjectID); break; } - case sdv::EObjectType::Utility: + case sdv::EObjectType::utility: pObject = pCreateUtility->CreateUtility(ssClassName, ssConfig); break; default: diff --git a/sdv_executables/sdv_local_shutdown/CMakeLists.txt b/sdv_executables/sdv_local_shutdown/CMakeLists.txt index af6c765..49bba69 100644 --- a/sdv_executables/sdv_local_shutdown/CMakeLists.txt +++ b/sdv_executables/sdv_local_shutdown/CMakeLists.txt @@ -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_local_shutdown VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_executables/sdv_local_shutdown/main.cpp b/sdv_executables/sdv_local_shutdown/main.cpp index 3651a3e..b103e16 100644 --- a/sdv_executables/sdv_local_shutdown/main.cpp +++ b/sdv_executables/sdv_local_shutdown/main.cpp @@ -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 "../../global/process_watchdog.h" #include #include diff --git a/sdv_executables/sdv_packager/CMakeLists.txt b/sdv_executables/sdv_packager/CMakeLists.txt index c0e8648..64d4d0a 100644 --- a/sdv_executables/sdv_packager/CMakeLists.txt +++ b/sdv_executables/sdv_packager/CMakeLists.txt @@ -1,30 +1,32 @@ +#******************************************************************************* +# 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_packager VERSION 1.0 LANGUAGES CXX) # build test application add_executable(sdv_packager "main.cpp" - "../../sdv_services/core/installation_manifest.h" - "../../sdv_services/core/installation_manifest.cpp" - "../../sdv_services/core/installation_composer.h" - "../../sdv_services/core/installation_composer.cpp" - "../../sdv_services/core/toml_parser/parser_toml.h" - "../../sdv_services/core/toml_parser/parser_toml.cpp" - "../../sdv_services/core/toml_parser/lexer_toml.h" - "../../sdv_services/core/toml_parser/lexer_toml.cpp" - "../../sdv_services/core/toml_parser/lexer_toml_token.cpp" - "../../sdv_services/core/toml_parser/parser_node_toml.h" - "../../sdv_services/core/toml_parser/parser_node_toml.cpp" - "../../sdv_services/core/toml_parser/character_reader_utf_8.h" - "../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" - "../../sdv_services/core/toml_parser/miscellaneous.h" - "../../sdv_services/core/toml_parser/miscellaneous.cpp" - "../../sdv_services/core/toml_parser/exception.h" "environment.cpp" "environment.h" "packager.cpp" "packager.h" - ) + "core_control.cpp" "core_control.h") + +# Compiler settings +add_compile_definitions(SDV_NO_CLASS_DEFINITION) + +# Linker settings if (WIN32) target_link_libraries(sdv_packager ${CMAKE_THREAD_LIBS_INIT} Ws2_32 Winmm Rpcrt4.lib) else() diff --git a/sdv_executables/sdv_packager/core_control.cpp b/sdv_executables/sdv_packager/core_control.cpp new file mode 100644 index 0000000..a58493a --- /dev/null +++ b/sdv_executables/sdv_packager/core_control.cpp @@ -0,0 +1,27 @@ +/******************************************************************************** + * 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 "core_control.h" +#include "../../sdv_services/core/app_settings.cpp" +#include "../../sdv_services/core/app_config.cpp" +#include "../../sdv_services/core/app_config_file.cpp" +#include "../../sdv_services/core/installation_manifest.cpp" +#include "../../sdv_services/core/installation_composer.cpp" +#include "../../sdv_services/core/toml_parser/parser_toml.cpp" +#include "../../sdv_services/core/toml_parser/lexer_toml.cpp" +#include "../../sdv_services/core/toml_parser/lexer_toml_token.cpp" +#include "../../sdv_services/core/toml_parser/parser_node_toml.cpp" +#include "../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" +#include "../../sdv_services/core/toml_parser/miscellaneous.cpp" +#include "../../sdv_services/core/toml_parser/code_snippet.cpp" + diff --git a/sdv_executables/sdv_packager/core_control.h b/sdv_executables/sdv_packager/core_control.h new file mode 100644 index 0000000..62d211b --- /dev/null +++ b/sdv_executables/sdv_packager/core_control.h @@ -0,0 +1,133 @@ +/******************************************************************************** + * 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_CONTROL_H + +#include +#include +#include +#include + +// Core control service implementation functions targeting sdv_packager. + +// Prevent including app_control.h, module_control.h, repository.h, logger.h +#define APP_CONTROL_H +#define MODULE_CONTROL_H +#define REPOSITORY_H +#define LOGGER_H +#define LOGGER_CONTROL_H + +/** + * @brief CAppControl redefined + */ +class CAppControl : public sdv::IInterfaceAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() + + /** + * @brief Application control is only in configuration state. + * @return Returns the operation state of the application. + */ + virtual sdv::app::EAppOperationState GetOperationState() const + { + return sdv::app::EAppOperationState::configuring; + } +}; + +/** + * @brief Return the application control. + * @return Reference to the application control. + */ +inline CAppControl& GetAppControl() +{ + static CAppControl app_control; + return app_control; +} + +/** + * @brief CModuleControl redefined + */ +class CModuleControl : public sdv::IInterfaceAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() +}; + +/** + * @brief Return the module control. + * @return Reference to the module control. + */ +inline CModuleControl& GetModuleControl() +{ + static CModuleControl module_control; + return module_control; +} + +class CAppConfigFile; + +/** + * @brief CRepository redefined + */ +class CRepository : public sdv::IInterfaceAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() + + /** + * @brief Mock function, starting the components from the configuration. + * @return Returns the result, which is always failed. + */ + sdv::core::EConfigProcessResult StartFromConfig(const CAppConfigFile&, bool) { return sdv::core::EConfigProcessResult::failed; } + + /** + * @brief Mock function, resetting the configuration baseline. + */ + void ResetConfigBaseline() {} +}; + +/** + * @brief Return the repository. + * @return Reference to the repository. + */ +inline CRepository& GetRepository() +{ + static CRepository repository; + return repository; +} + +/** + * @brief CLoggerControl redefined + */ +class CLoggerControl : public sdv::IInterfaceAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() +}; + +/** + * @brief Return the logger control. + * @return Reference to the logger control. + */ +inline CLoggerControl& GetLoggerControl() +{ + static CLoggerControl logger_control; + return logger_control; +} + + +#endif // !defined CORE_CONTROL_H \ No newline at end of file diff --git a/sdv_executables/sdv_packager/environment.cpp b/sdv_executables/sdv_packager/environment.cpp index 197e299..96030ff 100644 --- a/sdv_executables/sdv_packager/environment.cpp +++ b/sdv_executables/sdv_packager/environment.cpp @@ -1,10 +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: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + #include "environment.h" #include #include "../../global/cmdlnparser/cmdlnparser.cpp" -CSdvPackagerEnvironment::CSdvPackagerEnvironment() : m_cmdln(static_cast(CCommandLine::EParseFlags::no_assignment_character)) {} +CSdvPackagerEnvironment::CSdvPackagerEnvironment() : + m_cmdln(static_cast(CCommandLine::EParseFlags::no_assignment_character)) +{} CSdvPackagerEnvironment::CSdvPackagerEnvironment(const std::vector& rvecArgs) : m_cmdln(static_cast(CCommandLine::EParseFlags::no_assignment_character)) @@ -25,7 +40,7 @@ bool CSdvPackagerEnvironment::Help() const void CSdvPackagerEnvironment::ShowHelp() const { - size_t nArgumentGroup = 0; + size_t nArgumentGroup = 0; std::stringstream sstreamText; // Show default information: @@ -34,6 +49,63 @@ void CSdvPackagerEnvironment::ShowHelp() const << std::endl << std::endl; + constexpr std::string_view svCollectModules = "Provide one or more files to add to the installation on the command line. " + "Wildcards (**, *, ?) are supported, allowing multiple files and directories to be added automatically. Regular " + "expressions can also be used when prefixing the paths with \"regex:\". If a specific target location (relative to the " + "installation directory) is required, this can be added by \"=\".\n\n" + "Examples:\n" + " *.sdv - This will pack all SDV files from the input directory.\n" + " **/*.* - This will pack all files from all directories.\n" + " abc.sdv=mysub - This will pack \"abc.sdv\" into target \"mysub\".\n" + " regex:[^/].sdv - This will pack all SDV files from the input directory.\n\n" + "REMARKS: To create updatable components, use the '--set_version' option to set a version.\n"; + constexpr std::string_view svCollectConfigFiles = "Provide one or more configuration files to merge into installed " + "configuration files. Wildcards (**, *, ?) are supported, allowing multiple files and directories to be added " + "automatically. Regular expressions can also be used when prefixing the paths with \"regex:\".\n\n" + "Examples:\n" + " *.toml - This will use all TOML files from the input directory.\n" + " **/*.* - This will use all files from all directories.\n" + " abc.toml - This will use \"abc.toml\".\n" + " regex:[^/].toml - This will use all TOML files from the input directory.\n"; + constexpr std::string_view svConfiguration = "The configuration uses an optional string following the option to define what to " + "configure. The string is separated by the '+' separator followed by a list of components (separated by a comma ',' or a " + "'+') to include in the configuration. If a component should be instantiated using a specific name, this can be achieved " + "by adding the assignment character '='. For example to update a local configuration ('--local' option) use:\n\n" + " --config_fileabc.toml+my_component1,my_component2,my_component2=test21\n\n" + "and for a server configuration:\n\n" + " --user_config+my_component1+my_component2+my_component2=test21\n\n" + "If the configuration file is not existing, it will be created automatically.\n\n" + "To provide component specific parameters, there are two options available. The first option would be to assign parameters " + "directly to a component instance by providing the parameter at the command line. Hereby the instance name of the " + "component is the assigned name of the component (if any was assigned) or the default name if known or the component class " + "name if one instance is available. The value assignment to the parameter is optional. If the value is omitted, a default " + "value is assigned. Here are two examples:\n\n" + " --parametersmy_component1:param1=10,param2=20,groupA.param3=string value\n" + " --parameterstest21:param5=,groupB.param6=60\n\n" + "The second option would be to specify a parameter file containing the parameters for the object instances. The parameter " + "file is a TOML file containing all the parameters for several components. Each component is present as a table containing " + "the instance name of the component (the assigned name, if there is any, or the default name or the class name). " + "Underneath the table header the parameters follow in a \"parameter_name = value\" format. Parameter names must follow the " + "TOML rules for keys. If they contain special characters, they need to be quoted. Furthermore, the names must follow the " + "grouping format of the component, whereas any group is either specified as additional TOML table, or is preceded in the " + "key name (the parameter name) and separated by a dot. An example for the command line to assign a configuration to the " + "one or more component instances is as follow:\n" + " --param_fileMyComponentParams.toml\n" + "And an example file could be like this:\n\n" + " # Parameters for my_component1\n" + " [my_component1]\n" + " param1 = 10\n" + " param2 = 20\n" + " [my_component1.groupA] # Example with additional table for the GroupA parameters\n" + " param3 = \"string value\"\n\n" + " # Parameters for test21 (instance of my_component2)\n" + " [test21]\n" + " param5=50\n" + " groupB.param6=60 # Grouping using inline table for GroupB parameters\n\n" + "REMARKS: Utilities cannot be added to the configuration.\n\n" + "REMARKS: For server based user configuration, the configuration cannot contain devices, basic services and system " + "objects.\n"; + // Show information based on the command. switch (m_eOperatingMode) { @@ -42,124 +114,68 @@ void CSdvPackagerEnvironment::ShowHelp() const sstreamText << "PACK command usage:" << std::endl << " sdv_package PACK [options]" << std::endl << std::endl; - sstreamText - << "Provide one or more files to add to the installation onto the command line. Wildcards (**, *, ?) are " - "supported, allowing multiple files and directories to be added automatically. Instead regular expressions can also " - "be used by prefixing the paths by \"regex:\". If a specific target location (relative to the installation " - "directory) is required, this can be added by \"=\"." - << std::endl - << std::endl - << "Examples:" << std::endl - << " *.sdv - This will pack all SDV files from the input directory." << std::endl - << " **/*.* - This will pack all files from all directories." << std::endl - << " abc.sdv=mysub - This will pack \"abc.sdv\" into target \"mysub\"." << std::endl - << " regex:[^/].sdv - This will pack all SDV files from the input directory." << std::endl - << std::endl - << "REMARKS: To create updatable components, use the '--set_version' option to set a version." << std::endl; + sstreamText << svCollectModules; break; case EOperatingMode::install: nArgumentGroup = 2; - sstreamText << "INSTALL command usage:" << std::endl - << " sdv_package INSTALL [options]" << std::endl - << std::endl; - sstreamText - << "Install an installation package and update the configuration if applicable." << std::endl - << std::endl - << "The configuration uses an optional string following the option to define what to configure. The string is " - "separated " - "by the '+' separator followed by a list of components (separated by a comma ',' or a '+') to include in the " - "configuration. For example to update a local configuration ('--local' option) use:" - << std::endl - << " --config_fileabc.toml+my_component1,my_component2" << std::endl - << "and a server configuration:" << std::endl - << " --user_config+my_component1+my_component2" << std::endl - << "If the configuration file is not existing, it will be created automatically." << std::endl - << std::endl - << "REMARKS: The server based installation can only take place when the system is offline. Use \"sdv_control\" " - "to install on a running server instance." - << std::endl; + sstreamText << "INSTALL command usage:" << std::endl << " sdv_package INSTALL [options]" << std::endl << + std::endl; + sstreamText << "Install an installation package and update the configuration if applicable." << std::endl << std::endl << + svConfiguration << std::endl << "REMARKS: The server based installation use sdv_packager can only take place when the " + "system is offline. Use \"sdv_control\" to install on a running server instance (limited installation possible) or " + "to shutdown and restart the server."<< std::endl; break; case EOperatingMode::direct_install: nArgumentGroup = 3; - sstreamText << "DIRECT_INSTALL command usage:" << std::endl - << " sdv_package DIRECT_INSTALL [options]" << std::endl - << std::endl; - sstreamText - << "Provide one or more files to add to the installation onto the command line. Wildcards (**, *, ?) are " - "supported, allowing multiple files and directories to be added automatically. Instead regular expressions can also " - "used by prefixing the paths by \"regex:\". If a specific target location (relative to the installation " - "directory) is required, this can be added by \"=\"." - << std::endl - << std::endl - << "Examples:" << std::endl - << " *.sdv - This will install all SDV files from the input directory." << std::endl - << " **/*.* - This will install all files from the input directory and sub-directories." << std::endl - << " abc.sdv=mysub - This will install \"abc.sdv\" into target \"mysub\"." << std::endl - << " regex:[^/].sdv - This will install all SDV files from the input directory." << std::endl - << std::endl - << "The configuration uses an optional string following the option to define what to configure. The string is " - "separated " - "by the '+' separator followed by a list of components (separated by a comma ',' or a '+') to include in the " - "configuration. For example to update a local configuration ('--local' option) use:" - << std::endl - << " --config_fileabc.toml+my_component1,my_component2" << std::endl - << "and a server configuration:" << std::endl - << " --user_config+my_component1+my_component2" << std::endl - << "If the configuration file is not existing, it will be created automatically." << std::endl - << std::endl - << "REMARKS: The server based installation can only take place when the system is offline. Use \"sdv_control\" to " - "install " - "on a running server instance." - << std::endl - << std::endl - << "REMARKS: To create updatable components, use the '--set_version' option to set a version." << std::endl; + sstreamText << "DIRECT_INSTALL command usage:" << std::endl << " sdv_package DIRECT_INSTALL [options]" << + std::endl << std::endl; + sstreamText << svCollectModules << std::endl << svConfiguration << std::endl << "REMARKS: The server based installation " + "can only take place when the system is offline. Use \"sdv_control\" to install on a running server instance (limited " + "installation possible) or to shutdown and restart the server." << std::endl; + break; + case EOperatingMode::configure: + nArgumentGroup = 4; + sstreamText << "CONFIGURE command usage:" << std::endl << " sdv_package CONFIGURE [options]" << + std::endl << std::endl; + sstreamText << svCollectConfigFiles << std::endl << "REMARKS: The server based configuration can only take place when the " + "system is offline. Use \"sdv_control\" to shutdown and restart the server." << std::endl; break; case EOperatingMode::uninstall: - nArgumentGroup = 4; - sstreamText << "UNINSTALL command usage:" << std::endl - << " sdv_package UNINSTALL [options]" << std::endl - << std::endl; - sstreamText - << "Uninstall a previously installed package with the supplied name. Update the configuration files where " - "applicable." - << std::endl - << std::endl - << "REMARKS: The server based unistallation can only take place when the system is offline. Use \"sdv_control\" to " - "install on a running server instance." - << std::endl; + nArgumentGroup = 5; + sstreamText << "UNINSTALL command usage:" << std::endl << " sdv_package UNINSTALL [options]" << + std::endl << std::endl; + sstreamText << "Uninstall a previously installed package with the supplied name. Update the configuration files where " + "applicable." << std::endl << std::endl << "REMARKS: The server based unistallation can only take place when the " + "system is offline. Use \"sdv_control\" to install on a running server instance (limited un-installation possible) or " + "to shutdown and restart the server." << std::endl; break; case EOperatingMode::verify: - nArgumentGroup = 5; - sstreamText << "VERIFY command usage:" << std::endl - << " sdv_package VERIFY [options]" << std::endl - << std::endl; + nArgumentGroup = 6; + sstreamText << "VERIFY command usage:" << std::endl << " sdv_package VERIFY [options]" << std::endl << + std::endl; sstreamText << "Verify the package for corruption and/or tampering." << std::endl; break; case EOperatingMode::show: - nArgumentGroup = 6; - sstreamText << "SHOW command usage:" << std::endl - << " sdv_package SHOW [ALL|INFO|MODULES|COMPONENTS] [options]" << std::endl - << std::endl; - sstreamText << "Show package information. The following information can be selected (multiple selections are possible):" - << std::endl - << " ALL Show all information about the package content." << std::endl - << " INFO Show package information." << std::endl - << " MODULES Show the modules contained in the package." << std::endl - << " COMPONENTS Show the components contained in the package." << std::endl; + nArgumentGroup = 7; + sstreamText << "SHOW command usage:" << std::endl << " sdv_package SHOW [ALL|INFO|MODULES|COMPONENTS] " + "[options]" << std::endl << std::endl; + sstreamText << "Show package information. The following information can be selected (multiple selections are " + "possible):" << std::endl << " ALL Show all information about the package content." << std::endl << + " INFO Show package information." << std::endl << + " MODULES Show the modules contained in the package." << std::endl << + " COMPONENTS Show the components contained in the package." << std::endl; break; default: sstreamText << "Usage: sdv_package [options]" << std::endl << std::endl; - sstreamText << "The following commands are available:" << std::endl - << " PACK Create an installation package." << std::endl - << " INSTALL Install an installation package." << std::endl - << " DIRECT_INSTALL Install the components directly without creating an installation package." - << std::endl - << " UNINSTALL Uninstall a previously installed installation package." << std::endl - << " VERIFY Verify the integrity of the installation package." << std::endl - << " SHOW Show information about the installation package." << std::endl - << std::endl - << "For information about the options of each command, provide the '--help' option following the command." - << std::endl; + sstreamText << "The following commands are available:" << std::endl << + " PACK Create an installation package." << std::endl << + " INSTALL Install an installation package." << std::endl << + " DIRECT_INSTALL Install the components directly." << std::endl << + " CONFIGURE Configure the target system." << std::endl << + " UNINSTALL Uninstall a previously installed installation package." << std::endl << + " VERIFY Verify the integrity of the installation package." << std::endl << + " SHOW Show information about the installation package." << std::endl << std::endl << + "For information about the options of each command, provide the '--help' option following the command." << std::endl; break; } @@ -174,7 +190,7 @@ void CSdvPackagerEnvironment::ReportInfo() const // Dependable on the verbose and the silent settings, select the output stream for information. std::stringstream sstreamDummy; std::ostream& rstreamVerbose = !m_bHelp && m_bVerbose ? std::cout : sstreamDummy; - std::ostream& rstreamNormal = m_bHelp || m_bSilent ? sstreamDummy : std::cout; + std::ostream& rstreamNormal = m_bHelp || m_bSilent ? sstreamDummy : std::cout; // Assign additional paths and report information... switch (m_eOperatingMode) @@ -228,44 +244,44 @@ void CSdvPackagerEnvironment::ReportInfo() const else { bool bInitial = true; - for (const auto& rssComponent : m_vecUserConfigComponents) + for (const auto& rprComponent : m_vecUserConfigComponents) { if (bInitial) rstreamVerbose << "User config components: "; else rstreamVerbose << " "; bInitial = false; - rstreamVerbose << rssComponent << std::endl; + rstreamVerbose << rprComponent.first << std::endl; } bInitial = true; - for (const auto& rssComponent : m_vecPlatformConfigComponents) + for (const auto& rprComponent : m_vecPlatformConfigComponents) { if (bInitial) rstreamVerbose << "Platform config components: "; else rstreamVerbose << " "; bInitial = false; - rstreamVerbose << rssComponent << std::endl; + rstreamVerbose << rprComponent.first << std::endl; } bInitial = true; - for (const auto& rssComponent : m_vecVehIfcConfigComponents) + for (const auto& rprComponent : m_vecVehIfcConfigComponents) { if (bInitial) rstreamVerbose << "Vehicle interface config components: "; else rstreamVerbose << " "; bInitial = false; - rstreamVerbose << rssComponent << std::endl; + rstreamVerbose << rprComponent.first << std::endl; } bInitial = true; - for (const auto& rssComponent : m_vecVehAbstrConfigComponents) + for (const auto& rprComponent : m_vecVehAbstrConfigComponents) { if (bInitial) rstreamVerbose << "Vehicle abstraction config components: "; else rstreamVerbose << " "; bInitial = false; - rstreamVerbose << rssComponent << std::endl; + rstreamVerbose << rprComponent.first << std::endl; } } break; @@ -311,44 +327,44 @@ void CSdvPackagerEnvironment::ReportInfo() const else { bool bInitial = true; - for (const auto& rssComponent : m_vecUserConfigComponents) + for (const auto& rprComponent : m_vecUserConfigComponents) { if (bInitial) rstreamVerbose << "User config components: "; else rstreamVerbose << " "; bInitial = false; - rstreamVerbose << rssComponent << std::endl; + rstreamVerbose << rprComponent.first << std::endl; } bInitial = true; - for (const auto& rssComponent : m_vecPlatformConfigComponents) + for (const auto& rprComponent : m_vecPlatformConfigComponents) { if (bInitial) rstreamVerbose << "Platform config components: "; else rstreamVerbose << " "; bInitial = false; - rstreamVerbose << rssComponent << std::endl; + rstreamVerbose << rprComponent.first << std::endl; } bInitial = true; - for (const auto& rssComponent : m_vecVehIfcConfigComponents) + for (const auto& rprComponent : m_vecVehIfcConfigComponents) { if (bInitial) rstreamVerbose << "Vehicle interface config components: "; else rstreamVerbose << " "; bInitial = false; - rstreamVerbose << rssComponent << std::endl; + rstreamVerbose << rprComponent.first << std::endl; } bInitial = true; - for (const auto& rssComponent : m_vecVehAbstrConfigComponents) + for (const auto& rprComponent : m_vecVehAbstrConfigComponents) { if (bInitial) rstreamVerbose << "Vehicle abstraction config components: "; else rstreamVerbose << " "; bInitial = false; - rstreamVerbose << rssComponent << std::endl; + rstreamVerbose << rprComponent.first << std::endl; } } break; @@ -404,7 +420,6 @@ void CSdvPackagerEnvironment::ReportInfo() const } } - bool CSdvPackagerEnvironment::Silent() const { return m_bSilent; @@ -440,6 +455,11 @@ const std::vector& CSdvPackagerEnvironment::Mo return m_vecModules; } +const std::vector& CSdvPackagerEnvironment::ConfigFileList() const +{ + return m_vecConfigFiles; +} + const std::filesystem::path& CSdvPackagerEnvironment::PackagePath() const { return m_pathPackage; @@ -490,7 +510,7 @@ bool CSdvPackagerEnvironment::Overwrite() const return m_bOverwrite; } -const std::filesystem::path& CSdvPackagerEnvironment::LocalConfigFile(std::vector& rvecComponents) const +const std::filesystem::path& CSdvPackagerEnvironment::LocalConfigFile(CComponentVector& rvecComponents) const { rvecComponents = m_vecConfigLocalComponents; return m_pathConfigLocal; @@ -501,35 +521,38 @@ const std::vector& CSdvPackagerEnvironment::LocalConfigLo return m_vecLocalConfigDirs; } -bool CSdvPackagerEnvironment::ActivateLocalConfig() const -{ - return m_bActivateLocalConfig; -} - -bool CSdvPackagerEnvironment::InsertIntoUserConfig(std::vector& rvecComponents) const +bool CSdvPackagerEnvironment::InsertIntoUserConfig(CComponentVector& rvecComponents) const { rvecComponents = m_vecUserConfigComponents; return m_bInsertIntoUserConfig; } -bool CSdvPackagerEnvironment::InsertIntoPlatformConfig(std::vector& rvecComponents) const +bool CSdvPackagerEnvironment::InsertIntoPlatformConfig(CComponentVector& rvecComponents) const { rvecComponents = m_vecPlatformConfigComponents; return m_bInsertIntoPlatformConfig; } -bool CSdvPackagerEnvironment::InsertIntoVehicleInterfaceConfig(std::vector& rvecComponents) const +bool CSdvPackagerEnvironment::InsertIntoVehicleInterfaceConfig(CComponentVector& rvecComponents) const { rvecComponents = m_vecVehIfcConfigComponents; return m_bInsertIntoVehIfcConfig; } -bool CSdvPackagerEnvironment::InsertIntoVehicleAbstractionConfig(std::vector& rvecComponents) const +bool CSdvPackagerEnvironment::InsertIntoVehicleAbstractionConfig(CComponentVector& rvecComponents) const { rvecComponents = m_vecVehAbstrConfigComponents; return m_bInsertIntoVehAbstrConfig; } +const TParameterVector& CSdvPackagerEnvironment::ObjectParameters(const std::string& rssObjectInstance) const +{ + static TParameterVector vecEmpty; + auto itParameters = m_mapParameters.find(rssObjectInstance); + if (itParameters == m_mapParameters.end()) return vecEmpty; + return itParameters->second; +} + const std::string& CSdvPackagerEnvironment::InstallName() const { return m_ssInstallName; @@ -585,18 +608,26 @@ const std::string& CSdvPackagerEnvironment::ArgError() const return m_ssArgError; } -void CSdvPackagerEnvironment::SplitConfigString(const std::string& rssInput, - std::filesystem::path& rpath, - std::vector& rvecComponents) +void CSdvPackagerEnvironment::SplitConfigString(const std::string& rssInput, std::filesystem::path& rpath, + std::vector>& rvecComponents) { size_t nPos = rssInput.find('+'); - rpath = std::filesystem::u8path(rssInput.substr(0, nPos)); + rpath = std::filesystem::u8path(rssInput.substr(0, nPos)); while (nPos != std::string::npos) { size_t nNextPos = rssInput.find_first_of("+,", nPos + 1); - std::string ss = rssInput.substr(nPos + 1, nNextPos); + std::string ss = rssInput.substr(nPos + 1, nNextPos - nPos - 1); if (!ss.empty()) - rvecComponents.push_back(ss); + { + // Check for an assignment. + size_t nAssignment = ss.find_first_of('='); + std::string ssClassName = ss.substr(0, nAssignment); + std::string ssName; + if (nAssignment != std::string::npos) + ssName = ss.substr(nAssignment + 1); + if (!ssClassName.empty()) + rvecComponents.push_back(std::make_pair(std::move(ssClassName), std::move(ssName))); + } nPos = nNextPos; } } @@ -622,36 +653,41 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& else if (iequals(rvecCommands[0], "PACK")) { m_eOperatingMode = EOperatingMode::pack; - nOptionGroup = 1; + nOptionGroup = 1; } else if (iequals(rvecCommands[0], "INSTALL")) { m_eOperatingMode = EOperatingMode::install; - nOptionGroup = 2; + nOptionGroup = 2; } else if (iequals(rvecCommands[0], "DIRECT_INSTALL")) { m_eOperatingMode = EOperatingMode::direct_install; - nOptionGroup = 3; + nOptionGroup = 3; + } + else if (iequals(rvecCommands[0], "CONFIGURE")) + { + m_eOperatingMode = EOperatingMode::configure; + nOptionGroup = 4; } else if (iequals(rvecCommands[0], "UNINSTALL")) { m_eOperatingMode = EOperatingMode::uninstall; - nOptionGroup = 4; + nOptionGroup = 5; } else if (iequals(rvecCommands[0], "VERIFY")) { m_eOperatingMode = EOperatingMode::verify; - nOptionGroup = 5; + nOptionGroup = 6; } else if (iequals(rvecCommands[0], "SHOW")) { m_eOperatingMode = EOperatingMode::show; - nOptionGroup = 6; + nOptionGroup = 7; } else { - m_nError = CMDLN_ARG_ERR; + m_nError = CMDLN_ARG_ERR; m_ssArgError = "Invalid command \"" + rvecCommands[0] + "\" supplied at the command line."; return false; } @@ -664,7 +700,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& // Check for silent and verbose flags. if (m_bSilent && m_bVerbose) { - m_nError = CMDLN_SILENT_VERBOSE; + m_nError = CMDLN_SILENT_VERBOSE; m_ssArgError = CMDLN_SILENT_VERBOSE_MSG; return false; } @@ -675,7 +711,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& // Expecting the installation name if (nCommandPos >= rvecCommands.size()) { - m_nError = CMDLN_INSTALL_NAME_MISSING; + m_nError = CMDLN_INSTALL_NAME_MISSING; m_ssArgError = CMDLN_INSTALL_NAME_MISSING_MSG; return false; } @@ -690,7 +726,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& // Expecting at least one source if (nCommandPos >= rvecCommands.size()) { - m_nError = CMDLN_SOURCE_FILE_MISSING; + m_nError = CMDLN_SOURCE_FILE_MISSING; m_ssArgError = CMDLN_SOURCE_FILE_MISSING_MSG; return false; } @@ -707,12 +743,27 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& return true; }; + // Get the configuration files to merge into an existing configuration. + auto fnGetConfigFiles = [rvecCommands, &nCommandPos, this]() -> bool + { + // Expecting at least one source + if (nCommandPos >= rvecCommands.size()) + { + m_nError = CMDLN_SOURCE_FILE_MISSING; + m_ssArgError = CMDLN_SOURCE_FILE_MISSING_MSG; + return false; + } + for (;nCommandPos < rvecCommands.size(); nCommandPos++) + m_vecConfigFiles.push_back(rvecCommands[nCommandPos]); + return true; + }; + // Cneck for no more commands auto fnCheckForNoMoreCommands = [rvecCommands, &nCommandPos, this]() -> bool { if (nCommandPos != rvecCommands.size()) { - m_nError = CMDLN_TOO_MANY_SOURCE_FILES; + m_nError = CMDLN_TOO_MANY_SOURCE_FILES; m_ssArgError = CMDLN_TOO_MANY_SOURCE_FILES_MSG; return false; } @@ -726,7 +777,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& // Expecting exactly one installation package if (nCommandPos >= rvecCommands.size()) { - m_nError = CMDLN_SOURCE_FILE_MISSING; + m_nError = CMDLN_SOURCE_FILE_MISSING; m_ssArgError = CMDLN_SOURCE_FILE_MISSING_MSG; return false; } @@ -737,7 +788,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& if (m_pathPackage.extension() != ".sdv_package" || !std::filesystem::exists(m_pathPackage) || !std::filesystem::is_regular_file(m_pathPackage)) { - m_nError = CMDLN_SOURCE_FILE_ERROR; + m_nError = CMDLN_SOURCE_FILE_ERROR; m_ssArgError = CMDLN_SOURCE_FILE_ERROR_MSG; return false; } @@ -780,7 +831,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& std::filesystem::create_directories(m_pathOutputLocation); if (!std::filesystem::is_directory(m_pathOutputLocation)) { - m_nError = CMDLN_OUTPUT_LOCATION_ERROR; + m_nError = CMDLN_OUTPUT_LOCATION_ERROR; m_ssArgError = std::string(CMDLN_OUTPUT_LOCATION_ERROR_MSG) + "\n Output location: " + m_pathOutputLocation.generic_u8string(); return false; @@ -803,7 +854,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& try { // If not locally, get the target from the environment variable - if (!m_bLocal) + if (!m_bLocal && m_pathTargetLocation.empty()) { #ifdef _WIN32 const wchar_t* szInstallDir = _wgetenv(L"SDV_COMPONENT_INSTALL"); @@ -816,7 +867,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& #endif } - // Is the directory existing? If not, creat ethe directory + // Is the directory existing? If not, create the directory if (!m_pathTargetLocation.empty() && !std::filesystem::is_directory(m_pathTargetLocation)) std::filesystem::create_directories(m_pathTargetLocation); @@ -828,6 +879,9 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& m_pathTargetLocation.generic_u8string(); return false; } + + // Make the directory absolute... + m_pathTargetLocation = std::filesystem::absolute(m_pathTargetLocation); } catch (const std::filesystem::filesystem_error&) { @@ -846,56 +900,86 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& m_ssProductName = m_ssInstallName; }; + // Check configuration targets + auto fnConfigTargets = [this]() -> bool + { + if (m_bLocal && (m_bInsertIntoPlatformConfig || m_bInsertIntoUserConfig || m_bInsertIntoVehAbstrConfig || + m_bInsertIntoVehIfcConfig)) + { + m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS; + m_ssArgError = CMDLN_INCOMPATIBLE_ARGUMENTS_MSG; + return false; + } + if (m_bLocal && m_pathConfigLocal.empty()) + { + m_nError = CMDLN_MISSING_TARGET; + m_ssArgError = CMDLN_MISSING_TARGET_MSG; + return false; + } + if (!m_bLocal && !m_pathConfigLocal.empty()) + { + m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS; + m_ssArgError = CMDLN_INCOMPATIBLE_ARGUMENTS_MSG; + return false; + } + size_t nCntServerConfigs = 0; + if (m_bInsertIntoPlatformConfig) ++nCntServerConfigs; + if (m_bInsertIntoUserConfig) ++nCntServerConfigs; + if (m_bInsertIntoVehAbstrConfig) ++nCntServerConfigs; + if (m_bInsertIntoVehIfcConfig) ++nCntServerConfigs; + if (!m_bLocal && !nCntServerConfigs) + { + m_nError = CMDLN_MISSING_TARGET; + m_ssArgError = CMDLN_MISSING_TARGET_MSG; + return false; + } + if (!m_bLocal && nCntServerConfigs > 1) + { + m_nError = CMDLN_TOO_MANY_CONFIG_TARGETS; + m_ssArgError = CMDLN_TOO_MANY_CONFIG_TARGETS_MSG; + return false; + } + return true; + }; + // Following the initial command, several additional information must be supplied. switch (m_eOperatingMode) { case EOperatingMode::pack: - if (!fnGetInstallName()) - return false; - if (!fnGetModules()) - return false; - if (!fnCheckSourceLocation()) - return false; - if (!fnCheckOutputLocation()) - return false; + if (!fnGetInstallName()) return false; + if (!fnGetModules()) return false; + if (!fnCheckSourceLocation()) return false; + if (!fnCheckOutputLocation()) return false; fnGetProduct(); if (m_pathOutputLocation.empty()) m_pathPackage = std::filesystem::u8path(m_ssInstallName + ".sdv_package"); else m_pathPackage = m_pathOutputLocation / std::filesystem::u8path(m_ssInstallName + ".sdv_package"); break; + case EOperatingMode::install: + if (!fnGetPackage()) return false; + if (!fnCheckForNoMoreCommands()) return false; + if (!fnCheckTargetLocation()) return false; + break; case EOperatingMode::direct_install: - if (!fnGetInstallName()) - return false; - if (!fnGetModules()) - return false; - if (!fnCheckSourceLocation()) - return false; - if (!fnCheckTargetLocation()) - return false; + if (!fnGetInstallName()) return false; + if (!fnGetModules()) return false; + if (!fnCheckSourceLocation()) return false; + if (!fnCheckTargetLocation()) return false; fnGetProduct(); break; - case EOperatingMode::install: - if (!fnGetPackage()) - return false; - if (!fnCheckForNoMoreCommands()) - return false; - if (!fnCheckTargetLocation()) - return false; + case EOperatingMode::configure: + if (!fnGetConfigFiles()) return false; + if (!fnConfigTargets()) return false; break; case EOperatingMode::uninstall: - if (!fnGetInstallName()) - return false; - if (!fnCheckForNoMoreCommands()) - return false; - if (!fnCheckTargetLocation()) - return false; + if (!fnGetInstallName()) return false; + if (!fnCheckForNoMoreCommands()) return false; + if (!fnCheckTargetLocation()) return false; break; case EOperatingMode::verify: - if (!fnGetPackage()) - return false; - if (!fnCheckForNoMoreCommands()) - return false; + if (!fnGetPackage()) return false; + if (!fnCheckForNoMoreCommands()) return false; break; case EOperatingMode::show: // Expecting one or more show commands followed by one installation package @@ -903,7 +987,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& { if (!m_vecModules.empty()) { - m_nError = CMDLN_TOO_MANY_SOURCE_FILES; + m_nError = CMDLN_TOO_MANY_SOURCE_FILES; m_ssArgError = CMDLN_TOO_MANY_SOURCE_FILES_MSG; return false; } @@ -932,7 +1016,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& // Check whether preceded by at least one command if (!(m_uiShowFlags & 0x00ff)) { - m_nError = CMDLN_MISSING_SHOW_COMMAND; + m_nError = CMDLN_MISSING_SHOW_COMMAND; m_ssArgError = CMDLN_MISSING_SHOW_COMMAND_MSG; return false; } @@ -942,7 +1026,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& } if (m_pathPackage.empty()) { - m_nError = CMDLN_SOURCE_FILE_MISSING; + m_nError = CMDLN_SOURCE_FILE_MISSING; m_ssArgError = CMDLN_SOURCE_FILE_MISSING_MSG; return false; } @@ -955,7 +1039,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& auto vecIncompatibleOptions = m_cmdln.IncompatibleArguments(nOptionGroup, false); if (!vecIncompatibleOptions.empty() && m_eOperatingMode != EOperatingMode::none) { - m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS; + m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS; m_ssArgError = std::string("The option '" + vecIncompatibleOptions[0] + "' cannot be used with the " + fnMakeUpper(rvecCommands[0]) + " command."); return false; diff --git a/sdv_executables/sdv_packager/environment.h b/sdv_executables/sdv_packager/environment.h index 803ee67..2b94b99 100644 --- a/sdv_executables/sdv_packager/environment.h +++ b/sdv_executables/sdv_packager/environment.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 ENVIRONMENT_H #define ENVIRONMENT_H @@ -5,9 +18,14 @@ #include #include #include +#include +#include #include "../../global/cmdlnparser/cmdlnparser.h" #include "../error_msg.h" #include +#include "../../sdv_services/core/app_config_file.h" +#include "../../sdv_services/core/toml_parser/parser_toml.h" +#include /** * @brief Compare whether two characters are identical when both are converted to lower case. @@ -39,6 +57,10 @@ inline bool iequals(const std::string& rssLeft, const std::string& rssRight) class CSdvPackagerEnvironment { public: + /** + * @brief Component vector containing the component class names and optionally the component instantiation names. + */ + using CComponentVector = std::vector>; /** * @brief Environment exception triggered on error during command line processing. @@ -74,8 +96,9 @@ public: { none, ///< No mode selected pack, ///< Packing operation (default) - direct_install, ///< Direct installation. install, ///< Installation + direct_install, ///< Direct installation. + configure, ///< Configure uninstall, ///< Remove an installed package. verify, ///< Verify integrity show, ///< Show content @@ -197,6 +220,13 @@ public: */ const std::vector& ModuleList() const; + /** + * @brief List of files to configure the system. + * @return Returns a reference to the variable containing the list of files. Could contain wildcards and regular expression + * strings. + */ + const std::vector& ConfigFileList() const; + /** * @brief Get the package path. Available for installation, uninstallation, verification and content showing commands. * @return Returns a reference to the variable containing the package path. @@ -264,7 +294,7 @@ public: * are to be added (if a configuration file was supplied). * @return Returns a reference to the variable containing the path to the configuration file or empty when no file was supplied. */ - const std::filesystem::path& LocalConfigFile(std::vector& rvecComponents) const; + const std::filesystem::path& LocalConfigFile(CComponentVector& rvecComponents) const; /** * @brief During package installation, the configuration file locations during local operation. @@ -272,12 +302,6 @@ public: */ const std::vector& LocalConfigLocations() const; - /** - * @brief Activate the local configuration in the settings file of the system. - * @return Returns whether a configuration is requested. - */ - bool ActivateLocalConfig() const; - /** * @brief Add the components to the user configuration. * @remarks Only complex services will be inserted. @@ -285,7 +309,7 @@ public: * are to be added (if a the function returns true). * @return Returns true if a configuration update is requested; false otherwise. */ - bool InsertIntoUserConfig(std::vector& rvecComponents) const; + bool InsertIntoUserConfig(CComponentVector& rvecComponents) const; /** * @brief Add the components to the platform configuration. @@ -293,7 +317,7 @@ public: * are to be added (if a the function returns true). * @return Returns true if a configuration update is requested; false otherwise. */ - bool InsertIntoPlatformConfig(std::vector& rvecComponents) const; + bool InsertIntoPlatformConfig(CComponentVector& rvecComponents) const; /** * @brief Add the components to the vehicle interface configuration. @@ -301,7 +325,7 @@ public: * are to be added (if a the function returns true). * @return Returns true if a configuration update is requested; false otherwise. */ - bool InsertIntoVehicleInterfaceConfig(std::vector& rvecComponents) const; + bool InsertIntoVehicleInterfaceConfig(CComponentVector& rvecComponents) const; /** * @brief Add the components to the vehicle abstraction configuration. @@ -309,7 +333,15 @@ public: * are to be added (if a the function returns true). * @return Returns true if a configuration update is requested; false otherwise. */ - bool InsertIntoVehicleAbstractionConfig(std::vector& rvecComponents) const; + bool InsertIntoVehicleAbstractionConfig(CComponentVector& rvecComponents) const; + + /** + * @brief Get the object parameter map for the provided object instance. + * @param[in] rssObjectInstance Reference to the object instance name to get the parameter map for. + * @return Returns a reference to the parameter vector. It is guaranteed that the parameters are unique. Can be empty when + * there are no parameters for the object instance. + */ + const TParameterVector& ObjectParameters(const std::string& rssObjectInstance) const; /** * @brief The installation name for the package creation. @@ -385,9 +417,10 @@ private: * missing, the string starts with '+'. The components might also be optional. If missing, the '+' is also not needed. * @param[in] rssInput Reference to the configuration string. * @param[out] rpath Reference to the path to be returned. - * @param[out] rvecComponents Reference to the vector containing the component names. + * @param[out] rvecComponents Reference to the vector containing the component class names and optionally component + * instantiation names (or empty when the name should be extracted from the component class). */ - void SplitConfigString(const std::string& rssInput, std::filesystem::path& rpath, std::vector& rvecComponents); + void SplitConfigString(const std::string& rssInput, std::filesystem::path& rpath, CComponentVector& rvecComponents); /** * @brief Process the parsed environment settings. @@ -407,14 +440,17 @@ private: bool m_bUpdate = false; ///< Update an existing installation. bool m_bOverwrite = false; ///< Overwrite an existing installation. EOperatingMode m_eOperatingMode = EOperatingMode::none; ///< Operating mode of the packager utility. - std::vector m_vecModules; ///< List of modules (module search string). + std::vector m_vecModules; ///< List of modules (module search strings). + std::vector m_vecConfigFiles; ///< List of configuration files (file search strings). std::filesystem::path m_pathSourceLocation; ///< Path to the input location. std::filesystem::path m_pathOutputLocation; ///< Path to the output location. std::filesystem::path m_pathTargetLocation; ///< Path to the target location (at installation). - std::filesystem::path m_pathRootLocation; ///< Target root location (includes the instance for server location). - std::filesystem::path m_pathInstallLocation; ///< Target install location (root location with installation name). - std::filesystem::path m_pathPackage; ///< Path to the package during installation, uninstallation, integrity checking and - ///< content showing. + std::filesystem::path m_pathRootLocation; ///< Target root location (includes the instance for server + ///< location). + std::filesystem::path m_pathInstallLocation; ///< Target install location (root location with installation + ///< name). + std::filesystem::path m_pathPackage; ///< Path to the package during installation, uninstallation, + ///< integrity checking andcontent showing. uint32_t m_uiInstanceID = 1000u; ///< Instance number (optional). std::string m_ssInstallName; ///< Installation name. std::string m_ssProductName; ///< Product name (default is package name). @@ -424,17 +460,21 @@ private: std::string m_ssCopyrights; ///< Copyright std::string m_ssPackageVersion; ///< Package version string std::filesystem::path m_pathConfigLocal; ///< Configuration file path (local only). - std::vector m_vecConfigLocalComponents; ///< List of components to add to the configuration file. - bool m_bActivateLocalConfig = false; ///< Activate the local config in the settings file. + CComponentVector m_vecConfigLocalComponents; ///< List of components to add to the configuration file. std::vector m_vecLocalConfigDirs; ///< List of directories to scan for the component to uninstall. - std::vector m_vecUserConfigComponents; ///< User configuration (server only). - std::vector m_vecPlatformConfigComponents; ///< Platform configuration (server only). - std::vector m_vecVehIfcConfigComponents; ///< Vehicle interface configuration (server only). - std::vector m_vecVehAbstrConfigComponents; ///< Vehicle abstraction configuration (server only). - bool m_bInsertIntoUserConfig = false; ///< When set, insert the components into the user config (server only). - bool m_bInsertIntoPlatformConfig = false;///< When set, insert the components into the platform config (server only). - bool m_bInsertIntoVehIfcConfig = false; ///< When set, insert the components into the vehicle interface config (server only). - bool m_bInsertIntoVehAbstrConfig = false;///< When set, insert the components into the vehicle abstraction config (server only). + CComponentVector m_vecUserConfigComponents; ///< User configuration (server only). + CComponentVector m_vecPlatformConfigComponents; ///< Platform configuration (server only). + CComponentVector m_vecVehIfcConfigComponents; ///< Vehicle interface configuration (server only). + CComponentVector m_vecVehAbstrConfigComponents; ///< Vehicle abstraction configuration (server only). + std::map m_mapParameters; ///< Map with parameters associated to their instances. + bool m_bInsertIntoUserConfig = false; ///< When set, insert the components into the user config + ///< (server only). + bool m_bInsertIntoPlatformConfig = false;///< When set, insert the components into the platform config + ///< (server only). + bool m_bInsertIntoVehIfcConfig = false; ///< When set, insert the components into the vehicle interface + ///< config (server only). + bool m_bInsertIntoVehAbstrConfig = false;///< When set, insert the components into the vehicle + ///< abstraction config (server only). uint32_t m_uiShowFlags = 0; ///< Show package information bitmask. int m_nError = NO_ERROR; ///< Error code after processing the command line. std::string m_ssArgError; ///< Error text after processing the command line. @@ -451,18 +491,20 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** { std::vector vecCommands; - // COMMANDS (selective argument groups 1-6): - // PACK Pack modules and files into an installation package + // COMMANDS (selective argument groups 1-7): + // PACK Pack modules and files into an installation package. // Usage: sdv_packager PACK - // INSTALL Install a package into the system + // INSTALL Install a package into the system. // Usage: sdv_packager INSTALL - // DIRECT_INSTALL Directly install modules and files into the system (without the creation of a package) + // DIRECT_INSTALL Directly install modules and files into the system (without the creation of a package). // Usage: sdv_packager DIRECT_INSTALL - // UNINSTALL Remove an installation from the system + // CONFIGURE Install a configuration into the system. + // Usage: sdv_packager CONFIGURE + // UNINSTALL Remove an installation from the system. // Usage: sdv_packager UNINSTALL // VERIFY Verify the consistency and the integrity of an installation package. // Usage: sdv_packager VERIFY - // SHOW Show package information + // SHOW Show package information. // Usage: sdv_packager SHOW m_cmdln.DefineDefaultArgument(vecCommands, "COMMAND <...> "); @@ -471,7 +513,7 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** // -s, --silent Silent mode // -v, --verbose Verbose mode // --version Show version - auto& rArgHelpDef = m_cmdln.DefineOption("?", m_bHelp, "Show help", true, 0, 1, 2, 3, 4, 5, 6); + auto& rArgHelpDef = m_cmdln.DefineOption("?", m_bHelp, "Show help", true, 0, 1, 2, 3, 4, 5, 6, 7); rArgHelpDef.AddSubOptionName("help"); auto& rArgSilentDef = m_cmdln.DefineOption("s", m_bSilent, "Do not show any information on STDOUT. Not compatible with " "'--verbose'."); @@ -481,13 +523,13 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** rArgVerboseDef.AddSubOptionName("verbose"); m_cmdln.DefineSubOption("version", m_bVersion, "Show version information."); - // ARGUMENT SELECTION GROUP #2 & #3 & #4: General options during installation/uninstallation + // ARGUMENT SELECTION GROUP #2 & #3, #4 & #5: General options during installation/uninstallation // -L, --local Local installation // --instance Instance ID to use for installation - auto& rLocalArgDef = m_cmdln.DefineOption("L", m_bLocal, "Target the local system.", true, 2, 3, 4); + auto& rLocalArgDef = m_cmdln.DefineOption("L", m_bLocal, "Target the local system.", true, 2, 3, 4, 5); rLocalArgDef.AddSubOptionName("local"); auto& rInstance = m_cmdln.DefineSubOption("instance", m_uiInstanceID, "The instance ID of the SDV server instance when not " - "targeting the local system (default ID is 1000).", true, 2, 3, 4); + "targeting the local system (default ID is 1000).", true, 2, 3, 4, 5); // ARGUMENT SELECTION GROUP #1 - Packing: // -O Optional destination location @@ -519,14 +561,16 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** m_cmdln.DefineSubOption("set_copyright", m_ssCopyrights, "Set copyright information.", true, 1, 3); m_cmdln.DefineSubOption("set_version", m_ssPackageVersion, "Set package version (needed to allow updates).", true, 1, 3); - // ARGUMENT SELECTION GROUP #2 & #3 & #4- Installation from package and direct installation: + // ARGUMENT SELECTION GROUP #2 & #3 & #5- Installation from package and direct installation: // -T Optional target location // -P, --update Use update if a previous package with older version has been found // -W, --overwrite Use overwrite if a previous package has been found // --verify_signed Verify whether the package is signed and if not return an error (not implemented) m_cmdln.DefineGroup("Installation / uninstallation"); - auto& rTargetLoc = m_cmdln.DefineOption("T", m_pathTargetLocation, "The target location for package installation. Required " - "when targeting a local system ('--local' option).", true, 2, 3, 4); + auto& rTargetLoc = m_cmdln.DefineOption("T", m_pathTargetLocation, "The target location for package installation. For " + "server based installations, this is the root location containing the instance ID sub-directories with the " + "installations. For local installations, this is the real target directory for installing the content.", + true, 2, 3, 5); auto& rArgDefUpdate = m_cmdln.DefineOption("P", m_bUpdate, "Update the installation if an older version is existing.", true, 2, 3); rArgDefUpdate.AddSubOptionName("update"); @@ -535,23 +579,20 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** rArgDefOverwrite.AddSubOptionName("overwrite"); // Configuration - // ARGUMENT SELECTION GROUP #2 & #3 & #4 - Update from package and directly and uninstallation: + // ARGUMENT SELECTION GROUP #2 & #3, #4 & #5 - Update from package and directly and uninstallation: // --config_dir Local instance only; directory to configuration files to update. m_cmdln.DefineGroup("Configuration update"); auto& rConfigDir = m_cmdln.DefineSubOption("config_dir", m_vecLocalConfigDirs, "One or more configuration directories " - "to scan for components being updated. Use with local systems only ('--local' option).", true, 2, 3, 4); + "to scan for components being updated. Use with local systems only ('--local' option).", true, 2, 3, 4, 5); - // --config_file[=[all,-component,-...]|[+component,+....]] Local instance only; configuration file. - // --activate_config Local instance only; select config into settings. - // --user_config[=[all,-component,-...]|[+component,+....]] Server instance only; user configuration. - // --platform_config[=[all,-component,-...]|[+component,+....]] Server instance only; platform config. - // --vehifc_config[=[all,-component,-...]|[+component,+....]] Server instance only; vehicle interface config. - // --vehabstr_config[=[all,-component,-...]|[+component,+....]] Server instance only; vehicle abstraction config. + // --config_file[+component[=name]+....]] Local instance only; configuration file. + // --user_config[+component[=name]+....]] Server instance only; user configuration. + // --platform_config[+component[=name]+....]] Server instance only; platform config. + // --interface_config[+component[=name]+....]] Server instance only; vehicle interface config. + // --abstract_config[+component[=name]+....]] Server instance only; vehicle abstraction config. std::string ssConfigFileLocal; auto& rConfigFile = m_cmdln.DefineSubOption("config_file", ssConfigFileLocal, "Update a user configuration file. For " "usage, see explanatory text above. Use with local systems only ('--local' option).", true, 2, 3); - auto& rActivateConfig = m_cmdln.DefineSubOption("activate_config", m_bActivateLocalConfig, "Update the settings file to " - "automatically activate the provided configuration file. Use with local system only ('--local' option).", true, 2, 3); std::string ssUserConfigServer, ssPlatformConfigServer, ssInterfaceConfigServer, ssAbstrConfigServer; auto& rPlatformConfig = m_cmdln.DefineSubOption("platform_config", ssPlatformConfigServer, "Update the platform " "configuration defining which components of this installation are used to interact with the hardware platform. For " @@ -565,18 +606,41 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** auto& rUserConfig = m_cmdln.DefineSubOption("user_config", ssUserConfigServer, "Update the user configuration defining " "which components of this installation need to be instantiated automatically. User configuration can only contain " "complex services. For usage, see explanatory text above. Use on server only.", true, 2, 3); + m_cmdln.DefineSubOption("config_file", m_pathConfigLocal, "Update a user configuration file. usage, see explanatory text " + "above. Use with local systems only ('--local' option).", true, 4); + m_cmdln.DefineSubOption("platform_config", m_bInsertIntoPlatformConfig, "Update the platform " + "configuration defining which components of this installation are used to interact with the hardware platform. Use " + "on server only.", true, 4); + m_cmdln.DefineSubOption("interface_config", m_bInsertIntoVehIfcConfig, "Update the vehicle " + "interface configuration defining which components of this installation represent the vehicle interface. Use on server " + "only.", true, 4); + m_cmdln.DefineSubOption("abstract_config", m_bInsertIntoVehAbstrConfig, "Update the vehicle abstraction " + "configuration defining which components of this installation represent an abstracted vehicle to the application " + "components. Use on server only.", true, 4); + m_cmdln.DefineSubOption("user_config", m_bInsertIntoUserConfig, "Update the user configuration defining " + "which components of this installation need to be instantiated automatically. User configuration can only contain " + "complex services. Use on server only.", true, 4); - // ARGUMENT SELECTION GROUP #5 - Package verififcation + // --parameters:=[,=...] Parameters for an added component. + // --param_file Parameters from a file for an added component. + std::vector vecParametersRaw; + m_cmdln.DefineSubOption("parameters", vecParametersRaw, "Parameters for a component instance. For usage, see explanatory " + "text above.", true, 2, 3); + std::vector vecParamFiles; + m_cmdln.DefineSubOption("param_file", vecParamFiles, "Parameters for one or more component instances located in a " + "parameter TOML file. For usage, see explanatory text above.", true, 2, 3); + + // ARGUMENT SELECTION GROUP #6 - Package verififcation m_cmdln.DefineGroup("Verification options"); // --verify_signed Verify whether the package is signed and if not return an error (not implemented) - // ARGUMENT SELECTION GROUP #6 - Show package information + // ARGUMENT SELECTION GROUP #7 - Show package information // --show_simple // --xml (not implemented) // --json (not implemented) m_cmdln.DefineGroup("Show options"); bool bShowSimple = false; - m_cmdln.DefineSubOption("show_simple", bShowSimple, "Show package information in simple format.", true, 6); + m_cmdln.DefineSubOption("show_simple", bShowSimple, "Show package information in simple format.", true, 7); m_cmdln.Parse(static_cast(nArgs), rgszArgs); @@ -589,7 +653,7 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** else // TODO: if not XML and JSON, add console m_uiShowFlags |= static_cast(EShowMask::console); - // Split the configuration strings + // Split the configuration strings, unless in CONFIGURE mode if (!ssConfigFileLocal.empty()) SplitConfigString(ssConfigFileLocal, m_pathConfigLocal, m_vecConfigLocalComponents); if (rUserConfig.OptionAvailableOnCommandLine()) @@ -655,8 +719,8 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** rInterfaceConfig.OptionAvailableOnCommandLine() || rAbstrConfig.OptionAvailableOnCommandLine()) { m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS; - m_ssArgError = "The configuration options '--user_config', '--platform_config', '--vehifc_config' and " - "'--vehabstr_config' cannot be used with with local installations ('--local' option)."; + m_ssArgError = "The configuration options '--user_config', '--platform_config', '--interface_config' and " + "'--abstract_config' cannot be used with with local installations ('--local' option)."; return; } if (!rTargetLoc.OptionAvailableOnCommandLine()) @@ -666,13 +730,6 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** "option)."; return; } - if (rActivateConfig.OptionAvailableOnCommandLine() && !rConfigFile.OptionAvailableOnCommandLine()) - { - m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS; - m_ssArgError = "Activating a configuration file using the option '--activate_config' can only be used with when a " - "configuration file is provided using the option '--config_file'."; - return; - } } else { if (rConfigFile.OptionAvailableOnCommandLine()) @@ -682,13 +739,6 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** "('--local' option)."; return; } - if (rActivateConfig.OptionAvailableOnCommandLine()) - { - m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS; - m_ssArgError = "The configuration file option '--activate_config' can only be used with with local " - "installations ('--local' option)."; - return; - } if (rConfigDir.OptionAvailableOnCommandLine()) { m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS; @@ -696,10 +746,159 @@ CSdvPackagerEnvironment::CSdvPackagerEnvironment(size_t nArgs, const TCharType** "('--local' option)."; return; } - if (rTargetLoc.OptionAvailableOnCommandLine()) + } + + // Add a parameter to the current instance. If the parameter is already present, the parameter will not be overwritten. + auto itInstance = m_mapParameters.end(); + auto fnAddParameter = [&](const std::string& rssObjectInstance, const std::string& rssKey, + const sdv::any_t& rany) -> bool + { + if (itInstance == m_mapParameters.end() || itInstance->first != rssObjectInstance) + { + // Find or create the instance + itInstance = m_mapParameters.find(rssObjectInstance); + if (itInstance == m_mapParameters.end()) + { + auto prInsert = m_mapParameters.emplace(rssObjectInstance, std::vector>()); + if (!prInsert.second || prInsert.first == m_mapParameters.end()) + return false; + itInstance = prInsert.first; + } + } + if (itInstance == m_mapParameters.end()) return false; + + // Check for the existence + if (std::find_if(itInstance->second.begin(), itInstance->second.end(), [&](const auto& prParameter) + { return prParameter.first == rssKey; }) != itInstance->second.end()) return true; // Not an error... + + // Add the parameter + itInstance->second.push_back(std::make_pair(rssKey, rany)); + + return true; + }; + + // Parameters are only valid when a configuration is supplied. + if (!vecParametersRaw.empty() || !vecParamFiles.empty()) + { + // Parameters can only be added when a configuration is created. + if (!m_bInsertIntoUserConfig && !m_bInsertIntoPlatformConfig && !m_bInsertIntoVehIfcConfig + && !m_bInsertIntoVehAbstrConfig && ssConfigFileLocal.empty()) { m_nError = CMDLN_INCOMPATIBLE_ARGUMENTS; - m_ssArgError = "The target location option '-T' can only be used with with local installations ('--local' option)."; + m_ssArgError = CMDLN_INCOMPATIBLE_ARGUMENTS_MSG; + return; + } + } + + // Process command line parameters + // The list of raw parameters are started with a component instance, followed by parameters. + std::string ssInstance; + for (const std::string& rssParameterRaw : vecParametersRaw) + { + size_t nInstanceSep = rssParameterRaw.find_first_of(':'); + if (nInstanceSep == std::string::npos) + nInstanceSep = 0; + else + { + ssInstance = rssParameterRaw.substr(0, nInstanceSep); + ++nInstanceSep; + } + + size_t nAssignment = rssParameterRaw.find_first_of('=', nInstanceSep); + if (nAssignment == nInstanceSep || nAssignment == std::string::npos) + { + m_nError = CMDLN_INVALID_PARAM_STRING; + m_ssArgError = CMDLN_INVALID_PARAM_STRING_MSG; + return; + } + std::string ssParamName = rssParameterRaw.substr(nInstanceSep, nAssignment - nInstanceSep); + std::string ssParamValue = rssParameterRaw.substr(nAssignment + 1); + if (ssParamName.empty()) // An empty name is not allowed. An empty value is allowed. + { + m_nError = CMDLN_INVALID_PARAM_STRING; + m_ssArgError = CMDLN_INVALID_PARAM_STRING_MSG; + return; + } + + // Add the parameter to the instance in the map. + sdv::any_t anyValue; + if (!ssParamValue.empty()) anyValue = ssParamValue; + if (!fnAddParameter(ssInstance, ssParamName, anyValue)) + { + m_nError = CMDLN_INVALID_PARAM_STRING; + m_ssArgError = CMDLN_INVALID_PARAM_STRING_MSG; + return; + } + } + + // Process parameter files + for (std::filesystem::path& rpathParamFile : vecParamFiles) + { + try + { + // Load the file + std::ifstream fstream(rpathParamFile); + if (!fstream.is_open()) + { + m_nError = CMDLN_INVALID_PARAM_FILE; + m_ssArgError = CMDLN_INVALID_PARAM_FILE_MSG; + return; + } + std::string ssContent((std::istreambuf_iterator(fstream)), std::istreambuf_iterator()); + fstream.close(); + + // Split the parameter path from the object instance. + auto fnSplit = [](const std::string& rssNodePath) -> std::pair + { + size_t nSeparator = rssNodePath.find_first_of('.'); + if (nSeparator == std::string::npos) + return std::make_pair(rssNodePath, std::string()); + return std::make_pair(rssNodePath.substr(0, nSeparator), rssNodePath.substr(nSeparator + 1)); + }; + + // Parse the file + toml_parser::CParser parser(ssContent); + sdv::toml::CNodeCollection root(&parser.Root()); + + // Lambda function to iterative add parameters + std::function fnProcessObjectInstance = + [&](const sdv::toml::CNodeCollection& rTable) + { + for (size_t nNodeIndex = 0; nNodeIndex < rTable.GetCount(); nNodeIndex++) + { + sdv::toml::CNode node = rTable.Get(nNodeIndex); + switch (node.GetType()) + { + case sdv::toml::ENodeType::node_boolean: + case sdv::toml::ENodeType::node_integer: + case sdv::toml::ENodeType::node_floating_point: + case sdv::toml::ENodeType::node_string: + { + auto prParameter = fnSplit(node.GetQualifiedPath()); + if (!prParameter.first.empty() && !prParameter.second.empty()) + fnAddParameter(prParameter.first, prParameter.second, node.GetValue()); + } + break; + case sdv::toml::ENodeType::node_table: + { + sdv::toml::CNodeCollection group(node); + if (!group.IsValid()) + break; + fnProcessObjectInstance(group); + } + break; + default: + // Ignore... + break; + } + } + }; + fnProcessObjectInstance(root); + } + catch (const sdv::toml::XTOMLParseException& /*rxExcept*/) + { + m_nError = CMDLN_INVALID_PARAM_FILE; + m_ssArgError = CMDLN_INVALID_PARAM_FILE_MSG; return; } } diff --git a/sdv_executables/sdv_packager/main.cpp b/sdv_executables/sdv_packager/main.cpp index f7bcdea..28f40c3 100644 --- a/sdv_executables/sdv_packager/main.cpp +++ b/sdv_executables/sdv_packager/main.cpp @@ -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 "../../global/process_watchdog.h" #include #include @@ -5,7 +18,6 @@ #include #include "../../global/exec_dir_helper.h" #include "../../global/filesystem_helper.h" -#include "../../sdv_services/core/installation_manifest.h" #include #include #include @@ -13,6 +25,8 @@ #include #include #include "packager.h" +#include "../../sdv_services/core/toml_parser/miscellaneous.h" +#include "../../sdv_services/core/app_settings.h" #if defined(_WIN32) && defined(_UNICODE) extern "C" int wmain(int iArgc, const wchar_t* rgszArgv[]) @@ -70,445 +84,46 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) // Anything to do? if (environment.OperatingMode() == CSdvPackagerEnvironment::EOperatingMode::none) return NO_ERROR; + // Create a settings startup configuration + std::stringstream sstreamStartupConfig; + sstreamStartupConfig << "[Application]" << std::endl; + if (environment.Local()) + { + sstreamStartupConfig << "Mode = \"Standalone\"" << std::endl; + } + else + { + sstreamStartupConfig << "Mode = \"Maintenance\"" << std::endl; + sstreamStartupConfig << "Instance = " << environment.InstanceID() << std::endl; + if (!environment.TargetLocation().empty()) + sstreamStartupConfig << "InstallDir = \"" << environment.TargetLocation().generic_u8string() << "\"" << std::endl; + } + sstreamStartupConfig << "[Console]" << std::endl; + if (environment.Silent()) + sstreamStartupConfig << "Report = \"Silent\"" << std::endl; + else if (environment.Verbose()) + sstreamStartupConfig << "Report = \"Verbose\"" << std::endl; + else + sstreamStartupConfig << "Report = \"Normal\"" << std::endl; + + // Process the application startup configuration + if (!GetAppSettings().ProcessAppStartupConfig(sstreamStartupConfig.str())) + { + std::cerr << "ERROR: Failed to process the startup configuration; cannot continue!" << std::endl; + return -1; + } + + // If running as server, load the settings file + if (!environment.Local() && !GetAppSettings().LoadSettingsFile()) + { + std::cerr << "ERROR: Failed to load the application settings file; cannot continue!" << std::endl; + return -1; + } + + // Enable the packager. CPackager packager(environment); packager.Execute(); if (!packager.ArgError().empty() && !environment.Silent()) std::cerr << "ERROR: " << packager.ArgError() << std::endl; return packager.Error(); - -#if 0 - if (environment.CreateManifestOnly() && environment.Verbose()) - std::cout << "No file copy, creating manifest only..." << std::endl; - - if (!environment.Silent()) - std::cout << "Installation name: " << environment.InstallName() << std::endl; - if (!environment.CreateManifestOnly()) - { - if (environment.Verbose()) - std::cout << "Instance ID: " << environment.InstanceID() << std::endl; - } - - std::filesystem::path pathTargetRoot = environment.TargetLocation(); - pathTargetRoot /= std::to_string(environment.InstanceID()); - if (environment.Verbose()) - std::cout << "Target root location: " << pathTargetRoot.generic_u8string() << std::endl; - - if (environment.Verbose()) - { - if (environment.OperatingMode() == CSdvPackagerEnvironment::EOperatingMode::pack) - std::cout << "Installing a package..." << std::endl; - else - { - if (environment.OperatingMode() == CSdvPackagerEnvironment::EOperatingMode::direct_install) - std::cout << "Direct installation of modules..." << std::endl; - else if (environment.CreateManifestOnly()) - std::cout << "Creating an installation manifest..." << std::endl; - else - std::cout << "Creating an installation package..." << std::endl; - } - } - - std::filesystem::path pathInstallLocation = environment.TargetLocation(); - if (!environment.CreateManifestOnly()) - { - pathInstallLocation /= std::to_string(environment.InstanceID()); - pathInstallLocation /= environment.InstallName(); - if (environment.Verbose()) - std::cout << "Install location: " << pathInstallLocation.generic_u8string() << std::endl; - } - - if (!environment.ConfigPath().empty()) - { - if (environment.ConfigPath().is_relative()) environment.ConfigPath() = pathTargetRoot / environment.ConfigPath(); - if (environment.Verbose()) - std::cout << "Config location: " << environment.ConfigPath().parent_path().generic_u8string() << std::endl; - } - - if (environment.CreateManifestOnly()) - environment.InputLocation() = environment.TargetLocation(); - if (environment.Verbose()) - std::cout << "Source location: " - << (environment.InputLocation().empty() ? "not provided" : environment.InputLocation().generic_u8string()) << std::endl; - - - // ================= START OF OPERATION ================= - - - - // Start the application control - sdv::app::CAppControl appcontrol; - std::string ssAppConfigTOML = R"code( -[Application] -Mode="Essential" -)code"; - ssAppConfigTOML += "Instance=" + std::to_string(environment.InstanceID()); - - // Start the appcontrol - if (!appcontrol.Startup(ssAppConfigTOML)) - { - if (!environment.Silent()) - std::cerr << "ERROR: " << APP_CONTROL_STARTUP_ERROR_MSG << std::endl; - return APP_CONTROL_STARTUP_ERROR; - } - - // Create the target directory - try - { - std::filesystem::create_directories(environment.TargetLocation()); - } - catch (const std::filesystem::filesystem_error& /*rexcept*/) - { - if (!environment.Silent()) - std::cerr << "ERROR: " << CREATE_TARGET_DIR_ERROR_MSG << std::endl; - return CREATE_TARGET_DIR_ERROR; - } - - if (!environment.CreateManifestOnly()) - { - // Remove existing installation directory - try - { - if (std::filesystem::exists(pathInstallLocation)) - std::filesystem::remove_all(pathInstallLocation); - } - catch (const std::filesystem::filesystem_error& /*rexcept*/) - { - if (!environment.Silent()) - std::cerr << "ERROR: " << CANNOT_REMOVE_INSTALL_DIR_MSG << std::endl; - return CANNOT_REMOVE_INSTALL_DIR; - } - - // Create the installation directory - try - { - std::filesystem::create_directories(pathInstallLocation); - } - catch (const std::filesystem::filesystem_error& /*rexcept*/) - { - if (!environment.Silent()) - std::cerr << "ERROR: " << CREATE_INSTALL_DIR_ERROR_MSG << std::endl; - return CREATE_INSTALL_DIR_ERROR; - } - } - - // Create the config directory - if (!environment.ConfigPath().empty()) - { - try - { - std::filesystem::create_directories(environment.ConfigPath().parent_path()); - } - catch (const std::filesystem::filesystem_error& /*rexcept*/) - { - if (!environment.Silent()) - std::cerr << "ERROR: " << CREATE_CONFIG_DIR_ERROR_MSG << std::endl; - return CREATE_CONFIG_DIR_ERROR; - } - } - - // Add the installation manifest - CInstallManifest manifest; - manifest.Create(environment.InstallName()); - - // Process all modules - std::set setModules; - for (std::filesystem::path& rpathSearchModule : environment.ModuleList()) - { - if (environment.Verbose()) - std::cout << "Supplied module: " << rpathSearchModule.generic_u8string() << std::endl; - if (rpathSearchModule.is_relative()) rpathSearchModule = environment.InputLocation() / rpathSearchModule; - - // Does the path have wildcards? - bool bHasWildcards = rpathSearchModule.generic_u8string().find_first_of("*?") != std::string::npos; - - // Get a list of modules based on the module search criteria - std::vector vecSearchedModules = FindFilesWithWildcards(rpathSearchModule); - if (vecSearchedModules.empty() && !bHasWildcards) - { - if (!environment.Silent()) - std::cerr << "ERROR: " << CMDLN_SOURCE_FILE_ERROR_MSG << " (" << rpathSearchModule.generic_u8string() << ")" << - std::endl; - return CMDLN_SOURCE_FILE_ERROR; - } - - // Process each module... - for (std::filesystem::path& rpathModule : vecSearchedModules) - { - // Processed already? - if (setModules.find(rpathModule) != setModules.end()) continue; - setModules.insert(rpathModule); - - // Determine the relative path to from source location - std::filesystem::path pathRelSource; - try - { - pathRelSource = environment.InputLocation().empty() ? - rpathModule.filename() : std::filesystem::proximate(rpathModule, environment.InputLocation()); - } catch (std::filesystem::filesystem_error& /*rexcept*/) - {} - if (pathRelSource.empty() || pathRelSource.begin()->string() == "..") - { - if (!environment.Silent()) - std::cerr << "ERROR: The source directory must be a parent of the module! The module will not be installed." << - std::endl; - continue; - } - - if (!environment.Silent()) - std::cout << "Processing module: " << pathRelSource.generic_u8string() << std::endl; - - if (!std::filesystem::exists(rpathModule) || !std::filesystem::is_regular_file(rpathModule)) - { - if (!environment.Silent()) - std::cerr << "ERROR: Module cannot be found or is invalid!" << std::endl; - continue; - } - - if (!environment.CreateManifestOnly()) - { - // Create target module path - std::filesystem::path pathPargetModule = pathInstallLocation / pathRelSource; - - // Create the target directory if not existing - try - { - std::filesystem::create_directories(pathPargetModule.parent_path()); - } - catch (std::filesystem::filesystem_error& /*rexcept*/) - { - if (!environment.Silent()) - std::cerr << "ERROR: Cannot create the module target directory!" << std::endl; - continue; - } - - // Copy the file to there - std::filesystem::copy(rpathModule, pathPargetModule); - - if (environment.Verbose()) - std::cout << "Target module: " << pathPargetModule.generic_u8string() << std::endl; - } - - // Add the installation manifest - if (!manifest.AddModule(rpathModule)) - { - if (!environment.Silent()) - std::cerr << "ERROR: Cannot store the component manifest!" << std::endl; - continue; - } - } - } - - if (!manifest.Save(pathInstallLocation)) - { - if (!environment.Silent()) - std::cerr << "ERROR: " << SAVE_INSTALL_MANIFEST_ERROR_MSG << std::endl; - return SAVE_INSTALL_MANIFEST_ERROR; - } - if (environment.Verbose()) - std::cout << "Saved installation manifest: " << (pathInstallLocation / "install_manifest.toml").generic_u8string() << - std::endl; - - // Write config file - if (!environment.ConfigPath().empty()) - { - std::ofstream fstream(environment.ConfigPath()); - if (!fstream.is_open()) - { - if (!environment.Silent()) - std::cerr << "ERROR: " << SAVE_CONFIG_FILE_ERROR_MSG << std::endl; - return SAVE_CONFIG_FILE_ERROR; - } - fstream << "[Configuration]" << std::endl; - fstream << "Version = " << SDVFrameworkInterfaceVersion << std::endl << std::endl; - - // Add each component that is a system object, device, basic service, complex service or an app... - // Also add the modules that contain one of the other component types (utility, proxy or stub). - std::map mapModules; - for (const auto& rsComponent : manifest.ComponentList()) - { - //// Exclude the class from the configuration? - //if (std::find(environment.ExcludeConfigClassList().begin(), environment.ExcludeConfigClassList().end(), rsComponent.ssClassName) != - // environment.ExcludeConfigClassList().end()) continue; - - // Get or insert the module in the map - auto itModule = mapModules.find(rsComponent.pathRelModule); - if (itModule == mapModules.end()) - { - auto prInsert = mapModules.insert(std::make_pair(rsComponent.pathRelModule, false)); - if (!prInsert.second) continue; - itModule = prInsert.first; - } - - // Add the component if it is a system object, device, basic service, complex service or an app. - switch (rsComponent.eType) - { - case sdv::EObjectType::SystemObject: - case sdv::EObjectType::Device: - case sdv::EObjectType::BasicService: - case sdv::EObjectType::ComplexService: - fstream << "[[Component]]" << std::endl; - fstream << "Path = \"" << rsComponent.pathRelModule.generic_u8string() << "\"" << std::endl; - fstream << "Class = \"" << rsComponent.ssClassName << "\"" << std::endl; - if (rsComponent.ssDefaultObjectName.empty()) - fstream << "Name = \"" << rsComponent.ssClassName << "\"" << std::endl; - else - fstream << "Name = \"" << rsComponent.ssDefaultObjectName << "\"" << std::endl; - fstream << std::endl; - itModule->second = true; // Module was added through component. - break; - default: - break; - } - } - - // Add all modules that were not added through a component already - for (const auto& rvtModule : mapModules) - { - if (rvtModule.second) continue; // Module added already - fstream << "[[Module]]" << std::endl; - fstream << "Path = \"" << rvtModule.first.generic_u8string() << "\"" << std::endl << std::endl; - } - fstream.close(); - - if (environment.Verbose()) - std::cout << "Saved configuration file: " << environment.ConfigPath().generic_u8string() << std::endl; - } - - // Write settings file - if (environment.Settings()) - { - // Read the existing settings - std::filesystem::path pathSettings = pathTargetRoot / "settings.toml"; - std::list lstSystemConfigs; - std::string ssAppConfig; - if (std::filesystem::exists(pathSettings)) - { - std::ifstream fstream(pathSettings); - if (fstream.is_open()) - { - if (environment.Verbose()) - std::cout << "Reading existing settings file..." << std::endl; - std::string ssContent((std::istreambuf_iterator(fstream)), std::istreambuf_iterator()); - sdv::toml::CTOMLParser parser(ssContent); - sdv::toml::CNode nodeVersion = parser.GetDirect("Settings.Version"); - if (nodeVersion.GetValue() != SDVFrameworkInterfaceVersion) - { - if (!environment.Silent()) - std::cout << "ERROR: " << SETTING_FILE_VERSION_INVALID_MSG << " No update taken place. (version " << - static_cast(nodeVersion.GetValue()) << " detected; version " << - SDVFrameworkInterfaceVersion << " needed)." << std::endl; - return SETTING_FILE_VERSION_INVALID; - } - - // Read the system configurations - sdv::toml::CNodeCollection nodeSysConfigs = parser.GetDirect("Settings.SystemConfig"); - for (size_t nIndex = 0; nIndex < nodeSysConfigs.GetCount(); nIndex++) - { - sdv::toml::CNode nodeSysConfig = nodeSysConfigs[nIndex]; - if (nodeSysConfig) - { - lstSystemConfigs.push_back(nodeSysConfig.GetValue()); - if (environment.Verbose()) - std::cout << "Detected existing SystemConfig entry: " << - static_cast(nodeSysConfig.GetValue()) << std::endl; - } - } - - // Read the app config - sdv::toml::CNode nodeAppConfig = parser.GetDirect("Settings.AppConfig"); - if (nodeAppConfig) - { - ssAppConfig = static_cast(nodeAppConfig.GetValue()); - if (environment.Verbose()) - std::cout << "Detected existing AppConfig entry: " << ssAppConfig << std::endl; - } - } - } - - // (Over)write existing settings file - std::ofstream fstream(pathSettings); - if (!fstream.is_open()) - { - std::cerr << "ERROR: " << SAVE_SETTINGS_FILE_ERROR_MSG << std::endl; - return SAVE_SETTINGS_FILE_ERROR; - } - - // TODO... allow partial update of TOML file. Needs parser-updates first to be able to do this. See: - // https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/580309 - - const char* szSettingsTemplatePart1 = 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" ] -# -)code"; - fstream << szSettingsTemplatePart1; - if (!lstSystemConfigs.empty()) - fstream << "SystemConfig = [ "; - for (auto it = lstSystemConfigs.begin(); it != lstSystemConfigs.end(); ++it) - { - if (environment.Verbose()) - std::cout << "Storing system config " << *it << " to the settings file." << std::endl; - fstream << "\"" << *it << "\""; - if (std::next(it) != lstSystemConfigs.end()) - { - fstream << ", "; - } - } - if (!lstSystemConfigs.empty()) - fstream << " ]" << std::endl; - - const char* szSettingsTemplatePart2 = R"code( -# 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"; - fstream << szSettingsTemplatePart2; - if (!environment.ConfigPath().empty()) - { - if (!ssAppConfig.empty() && ssAppConfig != environment.ConfigPath().filename().generic_u8string()) - { - if (!environment.Silent()) - std::cout << "WARNING: The application config file in the settings is being updated from " << - environment.ConfigPath().filename().generic_u8string() << " to " << ssAppConfig << std::endl; - } - ssAppConfig = environment.ConfigPath().filename().generic_u8string(); - } - if (!ssAppConfig.empty()) - { - if (environment.Verbose()) - std::cout << "Storing application config " << ssAppConfig << " to the settings file." << std::endl; - fstream << "AppConfig = \"" << ssAppConfig << "\"" << std::endl; - } - fstream.close(); - - if (environment.Verbose()) - std::cout << "Saved settings file: " << pathSettings.generic_u8string() << std::endl; - } - - appcontrol.Shutdown(); - - if (!environment.Silent()) - std::cout << std::endl << "Done..." << std::endl; - - return NO_ERROR; -#endif - return NOT_IMPLEMENTED; } diff --git a/sdv_executables/sdv_packager/packager.cpp b/sdv_executables/sdv_packager/packager.cpp index 3dfcc53..544f126 100644 --- a/sdv_executables/sdv_packager/packager.cpp +++ b/sdv_executables/sdv_packager/packager.cpp @@ -1,5 +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 + ********************************************************************************/ + #include "packager.h" +#include +#include "../../sdv_services/core/app_settings.h" +#include "../../sdv_services/core/app_config.h" #include "../../sdv_services/core/installation_composer.h" +#include "../global/path_match.h" CPackager::CPackager(CSdvPackagerEnvironment& renv) : m_env(renv) {} @@ -17,6 +34,9 @@ bool CPackager::Execute() case CSdvPackagerEnvironment::EOperatingMode::direct_install: if (!Copy()) return false; break; + case CSdvPackagerEnvironment::EOperatingMode::configure: + if (!Configure()) return false; + break; case CSdvPackagerEnvironment::EOperatingMode::uninstall: if (!Remove()) return false; break; @@ -58,8 +78,8 @@ bool CPackager::Pack() if (rsModule.ssSearchString.substr(0, 6) == "regex:") { ssSearchString = rsModule.ssSearchString.substr(6); - uiFlags = static_cast(CInstallComposer::EAddModuleFlags::regex); - bExpectFile = false; + uiFlags = static_cast(CInstallComposer::EAddModuleFlags::regex); + bExpectFile = false; } else { @@ -80,7 +100,7 @@ bool CPackager::Pack() auto vecFiles = composer.AddModule(m_env.SourceLocation(), ssSearchString, rsModule.pathRelTarget, uiFlags); if (bExpectFile && !vecFiles.size()) { - m_nError = CMDLN_SOURCE_FILE_ERROR; + m_nError = CMDLN_SOURCE_FILE_ERROR; m_ssArgError = CMDLN_SOURCE_FILE_ERROR_MSG; return false; } @@ -96,7 +116,7 @@ bool CPackager::Pack() if (!nCount) { - m_nError = NO_SOURCE_FILES; + m_nError = NO_SOURCE_FILES; m_ssArgError = NO_SOURCE_FILES_MSG; return false; } @@ -171,6 +191,13 @@ bool CPackager::Unpack() std::cout << "Copyright: " << *manifest.Property("Copyrights") << std::endl; std::cout << "Version: " << *manifest.Property("Version") << std::endl; } + + // Report count if requested + if (!m_env.Silent()) + std::cout << "Unpacked " << manifest.ModuleList().size() << " files..." << std::endl; + + // Update the configuration + if (!ConfigureFromManifest(manifest)) return false; } catch (const sdv::XSysExcept& rexception) { @@ -186,6 +213,9 @@ bool CPackager::Copy() CInstallManifest manifest; try { + if (m_env.Verbose()) + std::cout << "Direct installation..." << std::endl; + CInstallComposer composer; size_t nCount = 0; for (const CSdvPackagerEnvironment::SModule& rsModule : m_env.ModuleList()) @@ -197,8 +227,8 @@ bool CPackager::Copy() if (rsModule.ssSearchString.substr(0, 6) == "regex:") { ssSearchString = rsModule.ssSearchString.substr(6); - uiFlags = static_cast(CInstallComposer::EAddModuleFlags::regex); - bExpectFile = false; + uiFlags = static_cast(CInstallComposer::EAddModuleFlags::regex); + bExpectFile = false; } else { @@ -219,7 +249,7 @@ bool CPackager::Copy() auto vecFiles = composer.AddModule(m_env.SourceLocation(), ssSearchString, rsModule.pathRelTarget, uiFlags); if (bExpectFile && !vecFiles.size()) { - m_nError = CMDLN_SOURCE_FILE_ERROR; + m_nError = CMDLN_SOURCE_FILE_ERROR; m_ssArgError = CMDLN_SOURCE_FILE_ERROR_MSG; return false; } @@ -235,7 +265,7 @@ bool CPackager::Copy() if (!nCount) { - m_nError = NO_SOURCE_FILES; + m_nError = NO_SOURCE_FILES; m_ssArgError = NO_SOURCE_FILES_MSG; return false; } @@ -257,7 +287,6 @@ bool CPackager::Copy() // Report count if requested if (!m_env.Silent()) std::cout << "Added " << nCount << " files..." << std::endl; - CInstallComposer::EUpdateRules eUpdateRule = CInstallComposer::EUpdateRules::not_allowed; if (m_env.Overwrite()) eUpdateRule = CInstallComposer::EUpdateRules::overwrite; @@ -268,6 +297,9 @@ bool CPackager::Copy() // Report count if requested if (!m_env.Silent()) std::cout << "Copied " << manifest.ModuleList().size() << " files..." << std::endl; + + // Update the configuration + if (!ConfigureFromManifest(manifest)) return false; } catch (const sdv::XSysExcept& rexception) { @@ -278,6 +310,180 @@ bool CPackager::Copy() return true; } +bool CPackager::Configure() +{ + if (m_env.Verbose()) + std::cout << "Configure installation..." << std::endl; + + std::vector vecConfigFiles; + for (const std::string& rssConfigFileSearchString : m_env.ConfigFileList()) + { + // Check for regular expression or wildcard search string + std::string ssSearchString; + EPathMatchAlgorithm eAlgorithm = EPathMatchAlgorithm::wildcards; + if (rssConfigFileSearchString.substr(0, 6) == "regex:") + { + ssSearchString = rssConfigFileSearchString.substr(6); + eAlgorithm = EPathMatchAlgorithm::regex; + } + else + ssSearchString = rssConfigFileSearchString; + + std::filesystem::path pathBasePath = m_env.SourceLocation(); + if (pathBasePath.empty()) + { + // If no base path is supplied, the file path needs to be absolute. + if (!std::filesystem::path(ssSearchString).is_absolute()) + { + m_nError = CMDLN_SOURCE_LOCATION_ERROR; + m_ssArgError = CMDLN_SOURCE_LOCATION_ERROR_MSG; + return false; + } + } + else + { + // The base path needs to be absolute. + if (!pathBasePath.is_absolute() || !std::filesystem::exists(pathBasePath) || + !std::filesystem::is_directory(pathBasePath)) + { + m_nError = CMDLN_SOURCE_LOCATION_ERROR; + m_ssArgError = CMDLN_SOURCE_LOCATION_ERROR_MSG; + return false; + } + } + + // Check for the config search path + if (ssSearchString.empty()) + { + // Base path must be present + if (pathBasePath.empty()) + { + m_nError = CMDLN_SOURCE_LOCATION_ERROR; + m_ssArgError = CMDLN_SOURCE_LOCATION_ERROR_MSG; + return false; + } + } + + // If the pattern is defined using an absolute path, this path needs to correspond with the base path. + std::filesystem::path pathConfigFile(ssSearchString); + if (pathConfigFile.is_absolute()) + { + auto itBasePart = pathBasePath.begin(); + auto itConfigPart = pathConfigFile.begin(); + while (itBasePart != pathBasePath.end()) + { + if (itConfigPart == pathConfigFile.end() || *itBasePart != *itConfigPart) + { + m_nError = CMDLN_SOURCE_LOCATION_ERROR; + m_ssArgError = CMDLN_SOURCE_LOCATION_ERROR_MSG; + return false; + } + + // Next part + itBasePart++; + itConfigPart++; + } + } + + // Get the list of files + auto vecFiles = CollectWildcardPath(pathBasePath, ssSearchString, eAlgorithm); + + // For each file, check whether the file is somewhere within the base path (if provided) and add the file to the file list. + for (const std::filesystem::path& rpathFile : vecFiles) + { + // Check whether the files have already been added + if (std::find_if(vecConfigFiles.begin(), vecConfigFiles.end(), [&](const std::filesystem::path& rpath) -> bool + { return rpath == rpathFile; }) != vecConfigFiles.end()) + continue; + + // Add the file + vecConfigFiles.push_back(rpathFile); + } + } + + // Check whether there are configuration files to merge + if (vecConfigFiles.empty()) + { + m_nError = NO_SOURCE_FILES; + m_ssArgError = NO_SOURCE_FILES_MSG; + return false; + } + + // Detemine the target to update + std::filesystem::path pathTargetConfig; + CSdvPackagerEnvironment::CComponentVector vecUnused; + if (m_env.Local()) + pathTargetConfig = m_env.LocalConfigFile(vecUnused); + else + { + if (m_env.InsertIntoPlatformConfig(vecUnused)) + { + GetAppSettings().EnableConfig(CAppSettings::EConfigType::platform_config); + pathTargetConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::platform_config); + } + if (m_env.InsertIntoVehicleInterfaceConfig(vecUnused)) + { + GetAppSettings().EnableConfig(CAppSettings::EConfigType::vehicle_interface_config); + pathTargetConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::vehicle_interface_config); + } + if (m_env.InsertIntoVehicleAbstractionConfig(vecUnused)) + { + GetAppSettings().EnableConfig(CAppSettings::EConfigType::vehicle_abstraction_config); + pathTargetConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::vehicle_abstraction_config); + } + if (m_env.InsertIntoUserConfig(vecUnused)) + { + GetAppSettings().EnableConfig(CAppSettings::EConfigType::user_config); + pathTargetConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::user_config); + } + } + if (pathTargetConfig.empty()) + { + m_nError = CMDLN_MISSING_TARGET; + m_ssArgError = CMDLN_MISSING_TARGET_MSG; + return false; + } + if (!GetAppSettings().SaveSettingsFile()) + { + SDV_LOG_ERROR("Failed to save application settings. Cannot configure components!"); + return false; + } + + // Open the target location + if (m_env.Verbose()) + std::cout << "Updating configuration " << pathTargetConfig.generic_u8string() << "..." << std::endl; + CAppConfigFile configTarget(pathTargetConfig); + if (!configTarget.LoadConfigFile()) + { + m_nError = CANNOT_READ_CONFIG; + m_ssArgError = CANNOT_READ_CONFIG_MSG; + return false; + } + + // Merge all files + for (const auto& rpathFile : vecConfigFiles) + { + // Report the files if requested + if (m_env.Verbose()) + std::cout << "Merging " << rpathFile.generic_u8string() << "..." << std::endl; + switch (configTarget.MergeConfigFile(rpathFile)) + { + case CAppConfigFile::EMergeResult::not_successful: + m_nError = FAILED_UPDATING_CONFIG; + m_ssArgError = FAILED_UPDATING_CONFIG_MSG; + return false; + case CAppConfigFile::EMergeResult::partly_successfull: + m_nError = PARTLY_FAILED_UPDATING_CONFIG; + m_ssArgError = PARTLY_FAILED_UPDATING_CONFIG_MSG; + return false; + default: + break; + } + } + + return configTarget.SaveConfigFile(); +} + bool CPackager::Remove() { CInstallManifest manifest; @@ -327,14 +533,381 @@ bool CPackager::CheckIntegrity() } catch (const sdv::XSysExcept& rexception) { - m_nError = PACKAGE_READ_ERROR; + m_nError = PACKAGE_READ_ERROR; m_ssArgError = rexception.what(); return false; } return true; } -void DrawTable(const std::vector>& rvecInfoTable, bool bSimple) +bool CPackager::ShowContent() +{ + CInstallManifest manifest; + try + { + if (m_env.Verbose()) + std::cout << "Show package information..." << std::endl; + + CInstallComposer extractor; + manifest = extractor.ExtractInstallManifest(m_env.PackagePath()); + + bool bConsole = m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::console); + + // Currently no support for XML and JSON. + if (!bConsole) return manifest.IsValid(); + + + bool bSimple = m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::console_simple); + + // Information + if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::info)) + { + // Show information about the package + std::cout << std::endl; + if (!bSimple && !m_env.Silent()) + std::cout << "Information:" << std::endl; + + std::vector> vecTable; + auto vecProperties = manifest.PropertyList(); + if (bSimple) + { + // No header, one column + std::vector vecInitial = {std::string("Installation=") + manifest.InstallName()}; + vecTable.push_back(vecInitial); + for (const auto& prProperty : vecProperties) + { + std::vector vecLine = {prProperty.first + "=" + prProperty.second}; + vecTable.push_back(vecLine); + } + } else + { + // Header and multiple columns + std::vector vecHeader = {"Name", "Value"}; + vecTable.push_back(vecHeader); + std::vector vecInitial = {"Installation", manifest.InstallName()}; + vecTable.push_back(vecInitial); + for (const auto& prProperty : vecProperties) + { + std::vector vecLine = {prProperty.first, prProperty.second}; + vecTable.push_back(vecLine); + } + } + DrawTable(vecTable, bSimple); + } + + // Modules + if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::modules)) + { + // Show module information + std::cout << std::endl; + if (!bSimple && !m_env.Silent()) + std::cout << "Modules:" << std::endl; + + std::vector> vecTable; + if (!bSimple) + { + // Header only when not simple + std::vector vecHeader = {"Filename"}; + vecTable.push_back(vecHeader); + } + auto vecModules = manifest.ModuleList(); + for (const auto& pathModule : vecModules) + { + std::vector vecLine = {pathModule.generic_u8string()}; + vecTable.push_back(vecLine); + } + DrawTable(vecTable, bSimple); + } + + // Components + if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::components)) + { + // Show component information + std::cout << std::endl; + if (!bSimple && !m_env.Silent()) + std::cout << "Components:" << std::endl; + + std::vector> vecTable; + if (!bSimple) + { + // Header only when not simple + std::vector vecHeader = {"Class", "Alias", "Type", "Dependency"}; + vecTable.push_back(vecHeader); + } + auto vecClasses = manifest.ClassList(); + for (const auto& sComponent : vecClasses) + { + if (bSimple) + { + // Simple - no header and onle column containing all classes and associated aliases. + vecTable.push_back(std::vector{sComponent.ssName}); + for (const auto& rssAlias : sComponent.seqClassAliases) + vecTable.push_back(std::vector{rssAlias}); + } + else + { + // Not simple - header and multiple columns allowed + // Components can have a list of aliases and a list of dependencies. Add extra lines for each additional + // alias and dependcy. + size_t nIndex = 0; + std::vector vecLine; + bool bAliasEnd = sComponent.seqClassAliases.empty(), bDependencyEnd = sComponent.seqDependencies.empty(); + do + { + vecLine.push_back(nIndex ? "" : sComponent.ssName); + vecLine.push_back(bAliasEnd ? "" : sComponent.seqClassAliases[nIndex]); + if (!nIndex) + vecLine.push_back(sdv::ObjectType2String(sComponent.eType)); + else + vecLine.push_back(""); + vecLine.push_back(bDependencyEnd ? "" : sComponent.seqDependencies[nIndex]); + vecTable.push_back(vecLine); + + nIndex++; + bAliasEnd = nIndex >= sComponent.seqClassAliases.size(); + bDependencyEnd = nIndex >= sComponent.seqDependencies.size(); + } while (!bAliasEnd && !bDependencyEnd); + } + } + DrawTable(vecTable, bSimple); + } + } + catch (const sdv::XSysExcept& rexception) + { + m_nError = PACKAGE_READ_ERROR; + m_ssArgError = rexception.what(); + return false; + } + return manifest.IsValid(); +} + +bool CPackager::ConfigureFromManifest(const CInstallManifest& rmanifest) +{ + // Get the class list. + std::vector vecClasses = rmanifest.ClassList(); + CSdvPackagerEnvironment::CComponentVector vecManifestComponents, vecAddedToConfig; + for (const sdv::SClassInfo& rsClass : vecClasses) + vecManifestComponents.push_back( + std::make_pair(rsClass.ssName, "")); + + // Add modules, classes and components if requested. + if (m_env.Local()) + { + CSdvPackagerEnvironment::CComponentVector vecComponentsToAdd; + std::filesystem::path pathConfig = m_env.LocalConfigFile(vecComponentsToAdd); + if (vecComponentsToAdd.empty()) + vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components. + if (!pathConfig.empty()) + WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, true, vecAddedToConfig); + } + else + { + // Make certain that the settings contain the configurations if there are components to add. + CSdvPackagerEnvironment::CComponentVector vecComponentsToAdd; + if (m_env.InsertIntoPlatformConfig(vecComponentsToAdd)) + { + if (vecComponentsToAdd.empty()) + vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components. + GetAppSettings().EnableConfig(CAppSettings::EConfigType::platform_config); + auto pathConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::platform_config); + WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, false, vecAddedToConfig); + } + if (m_env.InsertIntoVehicleInterfaceConfig(vecComponentsToAdd)) + { + if (vecComponentsToAdd.empty()) + vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components. + GetAppSettings().EnableConfig(CAppSettings::EConfigType::vehicle_interface_config); + auto pathConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::vehicle_interface_config); + WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, false, vecAddedToConfig); + } + if (m_env.InsertIntoVehicleAbstractionConfig(vecComponentsToAdd)) + { + if (vecComponentsToAdd.empty()) + vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components. + GetAppSettings().EnableConfig(CAppSettings::EConfigType::vehicle_abstraction_config); + auto pathConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::vehicle_abstraction_config); + WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, false, vecAddedToConfig); + } + if (m_env.InsertIntoUserConfig(vecComponentsToAdd)) + { + if (vecComponentsToAdd.empty()) + vecComponentsToAdd = vecManifestComponents; // If non are specified, add all components. + GetAppSettings().EnableConfig(CAppSettings::EConfigType::user_config); + auto pathConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::user_config); + WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, true, vecAddedToConfig); + } + if (!GetAppSettings().SaveSettingsFile()) + { + SDV_LOG_ERROR("Failed to save application settings. Cannot configure components!"); + return false; + } + } + + return true; +} + +void CPackager::WriteConfig(const std::vector& rvecAllClasses, const std::filesystem::path& rpathConfig, + const CSdvPackagerEnvironment::CComponentVector& rvecComponents, bool bUserConfig, + CSdvPackagerEnvironment::CComponentVector& rvecAddedToConfig) +{ + CAppConfigFile file(rpathConfig); + if (std::filesystem::exists(rpathConfig) && !file.IsLoaded()) + { + if (!m_env.Silent()) + std::cerr << "ERROR: Failed to read existing configuration file '" << rpathConfig.generic_u8string() << "'" << + std::endl; + return; + } + + // Find the class with the corresponding class name + auto fnFindClass = [&](const std::string& rssClassName) -> std::optional + { + auto itClass = std::find_if(rvecAllClasses.begin(), rvecAllClasses.end(), [&](const sdv::SClassInfo& rsClass) + { return rsClass.ssName == rssClassName; }); + return itClass != rvecAllClasses.end() ? *itClass : std::optional(); + }; + + // Add the components to the configuration. + // For local installation, add any component. For server installation, only add complex services for user congiguration + // and devices, basic services and system services for the other configurations. For server installation, the component + // must be present in the installation and be added only once. + std::vector vecNotInInstallation, vecNotAllowed, vecDuplicate, vecFailed, vecSucceeded; + CSdvPackagerEnvironment::CComponentVector vecComponentsCopy = rvecComponents; + if (rvecComponents.empty()) return; // Nothing to do... + for (const auto& rprComponent : vecComponentsCopy) + { + // For server components, get the corresponding class and check whether installation is allowed. + if (!m_env.Local()) + { + const auto& optClass = fnFindClass(rprComponent.first); + if (!optClass) + { + vecNotInInstallation.push_back(rprComponent.first); + continue; + } + enum class ECompatibility {compatible, incompatible, silent_incompatible} eIncompatible = ECompatibility::incompatible; + switch (optClass->eType) + { + case sdv::EObjectType::device: + case sdv::EObjectType::platform_abstraction: + case sdv::EObjectType::vehicle_bus: + case sdv::EObjectType::basic_service: + case sdv::EObjectType::sensor: + case sdv::EObjectType::actuator: + case sdv::EObjectType::system_object: + eIncompatible = bUserConfig ? ECompatibility::incompatible : ECompatibility::compatible; + break; + case sdv::EObjectType::complex_service: + case sdv::EObjectType::vehicle_function: + case sdv::EObjectType::utility: + eIncompatible = bUserConfig ? ECompatibility::compatible : ECompatibility::incompatible; + break; + case sdv::EObjectType::proxy: + case sdv::EObjectType::stub: + eIncompatible = ECompatibility::silent_incompatible; + break; + default: + break; + } + if (eIncompatible == ECompatibility::incompatible && !rvecComponents.empty()) + { + // Only add to incompatible list if not extracted from manifest list. + vecNotAllowed.push_back(rprComponent.first); + } + if (eIncompatible != ECompatibility::compatible) + continue; + std::string ssInstanceName = rprComponent.second; + if (ssInstanceName.empty()) + ssInstanceName = optClass->ssDefaultObjectName; + if (ssInstanceName.empty()) + ssInstanceName = rprComponent.first; + auto itInstalledComponent = std::find_if(rvecAddedToConfig.begin(), rvecAddedToConfig.end(), + [&](const auto& rprInstalledComponent) { return rprInstalledComponent.second == ssInstanceName; }); + if (itInstalledComponent != rvecAddedToConfig.end()) + { + // Only add to duplicate list if not extracted from manifest list. + if (!rvecComponents.empty()) + vecDuplicate.push_back(ssInstanceName); + continue; + } + rvecAddedToConfig.push_back(std::make_pair(rprComponent.first, ssInstanceName)); + } + + // Find the object instrance parameters + TParameterVector vecParameters = m_env.ObjectParameters(rprComponent.first); + + // Add the component to the configuration... + if (file.InsertComponent(sdv::toml::npos, std::filesystem::path(), rprComponent.first, rprComponent.second, + vecParameters)) + vecSucceeded.push_back(rprComponent.first); + else + vecFailed.push_back(rprComponent.first); + } + + // Report warnings. + if (!vecNotInInstallation.empty()) + { + if (!m_env.Silent()) + std::cerr << "WARNING: in the configuration file '" << rpathConfig.generic_u8string() << "' the following " + "component classes were not part of the installation and cannot be added: "; + bool bInitial = true; + for (const std::string& rssName : vecNotInInstallation) + { + std::cerr << (bInitial ? "" : ", ") << rssName; + bInitial = false; + } + std::cerr << std::endl; + } + if (!vecNotAllowed.empty()) + { + if (!m_env.Silent()) + std::cerr << "WARNING: in the configuration file '" << rpathConfig.generic_u8string() << "' the following " + "component classes are of invalid type and cannot be added: "; + bool bInitial = true; + for (const std::string& rssName : vecNotAllowed) + { + std::cerr << (bInitial ? "" : ", ") << rssName; + bInitial = false; + } + std::cerr << std::endl; + } + if (!vecDuplicate.empty()) + { + if (!m_env.Silent()) + std::cerr << "WARNING: in the configuration file '" << rpathConfig.generic_u8string() << "' the following " + "component classes are duplicated and cannot be added: "; + bool bInitial = true; + for (const std::string& rssName : vecDuplicate) + { + std::cerr << (bInitial ? "" : ", ") << rssName; + bInitial = false; + } + std::cerr << std::endl; + } + if (!vecFailed.empty()) + { + if (!m_env.Silent()) + std::cerr << "WARNING: in the configuration file '" << rpathConfig.generic_u8string() << "' the following " + "component classes cannot be added due ot a technical issue: "; + bool bInitial = true; + for (const std::string& rssName : vecFailed) + { + std::cerr << (bInitial ? "" : ", ") << rssName; + bInitial = false; + } + std::cerr << std::endl; + } + + // Save the configuration file. + file.SaveConfigFile(); + + // Report count if requested + if (!m_env.Silent()) + std::cout << "Added " << vecSucceeded.size() << " components to '" << rpathConfig.generic_u8string() << "'" << + std::endl; +} + +void CPackager::DrawTable(const std::vector>& rvecInfoTable, bool bSimple) { if (rvecInfoTable.empty()) return; // No columns @@ -435,173 +1008,3 @@ void DrawTable(const std::vector>& rvecInfoTable, bool std::cout << sstream.str(); } -bool CPackager::ShowContent() -{ - CInstallManifest manifest; - try - { - if (m_env.Verbose()) - std::cout << "Show package information..." << std::endl; - - CInstallComposer extractor; - manifest = extractor.ExtractInstallManifest(m_env.PackagePath()); - - bool bConsole = m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::console); - bool bSimple = m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::console_simple); - - // Currently no support for XML and JSON. - if (bConsole) - { - // Information - if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::info)) - { - // Show information about the package - std::cout << std::endl; - if (!bSimple && !m_env.Silent()) - std::cout << "Information:" << std::endl; - - std::vector> vecTable; - auto vecProperties = manifest.PropertyList(); - if (bSimple) - { - // No header, one column - std::vector vecInitial = {std::string("Installation=") + manifest.InstallName()}; - vecTable.push_back(vecInitial); - for (const auto& prProperty : vecProperties) - { - std::vector vecLine = {prProperty.first + "=" + prProperty.second}; - vecTable.push_back(vecLine); - } - } else - { - // Header and multiple columns - std::vector vecHeader = {"Name", "Value"}; - vecTable.push_back(vecHeader); - std::vector vecInitial = {"Installation", manifest.InstallName()}; - vecTable.push_back(vecInitial); - for (const auto& prProperty : vecProperties) - { - std::vector vecLine = {prProperty.first, prProperty.second}; - vecTable.push_back(vecLine); - } - } - DrawTable(vecTable, bSimple); - } - - // Modules - if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::modules)) - { - // Show module information - std::cout << std::endl; - if (!bSimple && !m_env.Silent()) - std::cout << "Modules:" << std::endl; - - std::vector> vecTable; - if (!bSimple) - { - // Header only when not simple - std::vector vecHeader = {"Filename"}; - vecTable.push_back(vecHeader); - } - auto vecModules = manifest.ModuleList(); - for (const auto& pathModule : vecModules) - { - std::vector vecLine = {pathModule.generic_u8string()}; - vecTable.push_back(vecLine); - } - DrawTable(vecTable, bSimple); - } - - // Components - if (m_env.CheckShowFlag(CSdvPackagerEnvironment::EShowMask::components)) - { - // Show component information - std::cout << std::endl; - if (!bSimple && !m_env.Silent()) - std::cout << "Components:" << std::endl; - - std::vector> vecTable; - if (!bSimple) - { - // Header only when not simple - std::vector vecHeader = {"Class", "Alias", "Type", "Dependency"}; - vecTable.push_back(vecHeader); - } - auto vecComponents = manifest.ComponentList(); - for (const auto& sComponent : vecComponents) - { - if (bSimple) - { - // Simple - no header and onle column containing all classes and associated aliases. - vecTable.push_back(std::vector{sComponent.ssClassName}); - for (const auto& rssAlias : sComponent.seqAliases) - vecTable.push_back(std::vector{rssAlias}); - } - else - { - // Not simple - header and multiple columns allowed - // Components can have a list of aliases and a list of dependencies. Add extra lines for each additional - // alias and dependcy. - size_t nIndex = 0; - std::vector vecLine; - bool bAliasEnd = sComponent.seqAliases.empty(), bDependencyEnd = sComponent.seqDependencies.empty(); - do - { - vecLine.push_back(nIndex ? "" : sComponent.ssClassName); - vecLine.push_back(bAliasEnd ? "" : sComponent.seqAliases[nIndex]); - if (!nIndex) - { - switch (sComponent.eType) - { - case sdv::EObjectType::SystemObject: - vecLine.push_back("System object"); - break; - case sdv::EObjectType::Device: - vecLine.push_back("Device"); - break; - case sdv::EObjectType::BasicService: - vecLine.push_back("Basic service"); - break; - case sdv::EObjectType::ComplexService: - vecLine.push_back("Complex service"); - break; - case sdv::EObjectType::Application: - vecLine.push_back("Application"); - break; - case sdv::EObjectType::Proxy: - vecLine.push_back("Proxy object"); - break; - case sdv::EObjectType::Stub: - vecLine.push_back("Stub object"); - break; - case sdv::EObjectType::Utility: - vecLine.push_back("Utility"); - break; - default: - vecLine.push_back("Unknown object"); - break; - } - } - else - vecLine.push_back(""); - vecLine.push_back(bDependencyEnd ? "" : sComponent.seqDependencies[nIndex]); - vecTable.push_back(vecLine); - - nIndex++; - bAliasEnd = nIndex >= sComponent.seqAliases.size(); - bDependencyEnd = nIndex >= sComponent.seqDependencies.size(); - } while (!bAliasEnd && !bDependencyEnd); - } - } - DrawTable(vecTable, bSimple); - } - } - } - catch (const sdv::XSysExcept& rexception) - { - m_nError = PACKAGE_READ_ERROR; - m_ssArgError = rexception.what(); - return false; - } - return manifest.IsValid(); -} diff --git a/sdv_executables/sdv_packager/packager.h b/sdv_executables/sdv_packager/packager.h index 76c8f4a..5747f65 100644 --- a/sdv_executables/sdv_packager/packager.h +++ b/sdv_executables/sdv_packager/packager.h @@ -1,7 +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 PACKAGER_H #define PACKAGER_H +#include "core_control.h" #include "environment.h" +#include "../../sdv_services/core/installation_manifest.h" /** * @brief Packager class. @@ -53,6 +68,13 @@ private: */ bool Copy(); + /** + * @brief Configure the system with configuration files from the source location. Uses the environment settings to retrieve the + * configuration files and the set the target configuration the files should integrate into. + * @return Returns whether the configuration operation was successful. + */ + bool Configure(); + /** * @brief Remove an installation from the target location. Uses the environment settings to remove the files. * @return Returns whether the removal operation was successful. @@ -70,8 +92,37 @@ private: * @return Returns whether the show operation was successful. */ bool ShowContent(); + +private: + /** + * @brief After copying files, it is possible to configure the system using the manifest. + * @param[in] rmanifest Reference to the manifest holding the objects that were installed. + * @return Returns whether the copy operation was successful. + */ + bool ConfigureFromManifest(const CInstallManifest& rmanifest); + + /** + * @brief Create/update a configuration file + * @param[in] rvecAllClasses Reference to the vector containing all component classes installed by the packager. + * @param[in] rpathConfig Reference to the path of the configuration file. + * @param[in] rvecComponents Reference to the components to add to the configuration. + * @param[in] bUserConfig Set when the configuration is an user configuration; otherwise the configuration is a system + * configuration. + * @param[in, out] rvecAddedToConfig Reference to the vector containing and being updated with all the installed components. + * This vector is used to prevent adding the component to more than configuration. + */ + void WriteConfig(const std::vector& rvecAllClasses, const std::filesystem::path& rpathConfig, + const CSdvPackagerEnvironment::CComponentVector& rvecComponents, bool bUserConfig, + CSdvPackagerEnvironment::CComponentVector& rvecAddedToConfig); + + /** + * @brief Draw a table using the two dimensional vector with information. + * @param[in] rvecInfoTable Reference to the two dimensional vector containing the table information. + * @param[in] bSimple When set, draws a one column table only. Otherwise all the table information is included. + */ + static void DrawTable(const std::vector>& rvecInfoTable, bool bSimple); - CSdvPackagerEnvironment m_env; ///< The packager environment + CSdvPackagerEnvironment m_env; ///< The packager environment int m_nError = NO_ERROR; ///< Error code after processing the command line. std::string m_ssArgError; ///< Error text after processing the command line. }; diff --git a/sdv_executables/sdv_trace_mon/CMakeLists.txt b/sdv_executables/sdv_trace_mon/CMakeLists.txt index 2971d17..d234987 100644 --- a/sdv_executables/sdv_trace_mon/CMakeLists.txt +++ b/sdv_executables/sdv_trace_mon/CMakeLists.txt @@ -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_trace_mon VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_executables/sdv_trace_mon/main.cpp b/sdv_executables/sdv_trace_mon/main.cpp index 18acce2..0fac0e3 100644 --- a/sdv_executables/sdv_trace_mon/main.cpp +++ b/sdv_executables/sdv_trace_mon/main.cpp @@ -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 #include #include diff --git a/sdv_executables/sdv_vss_util/CMakeLists.txt b/sdv_executables/sdv_vss_util/CMakeLists.txt index 1338d0d..97c4c5a 100644 --- a/sdv_executables/sdv_vss_util/CMakeLists.txt +++ b/sdv_executables/sdv_vss_util/CMakeLists.txt @@ -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_vss_util VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_executables/sdv_vss_util/codegen_base.cpp b/sdv_executables/sdv_vss_util/codegen_base.cpp index 74710de..c076839 100644 --- a/sdv_executables/sdv_vss_util/codegen_base.cpp +++ b/sdv_executables/sdv_vss_util/codegen_base.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "codegen_base.h" #include diff --git a/sdv_executables/sdv_vss_util/codegen_base.h b/sdv_executables/sdv_vss_util/codegen_base.h index dab1eab..379fe14 100644 --- a/sdv_executables/sdv_vss_util/codegen_base.h +++ b/sdv_executables/sdv_vss_util/codegen_base.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef CODEGEN_BASE_H #define CODEGEN_BASE_H diff --git a/sdv_executables/sdv_vss_util/csv_file_reader.cpp b/sdv_executables/sdv_vss_util/csv_file_reader.cpp index 4b592e6..9d5788f 100644 --- a/sdv_executables/sdv_vss_util/csv_file_reader.cpp +++ b/sdv_executables/sdv_vss_util/csv_file_reader.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "csv_file_reader.h" CCSVFileReader::CCSVFileReader(const std::string& ssFileName, const bool bSilent, const bool bVerbose) diff --git a/sdv_executables/sdv_vss_util/csv_file_reader.h b/sdv_executables/sdv_vss_util/csv_file_reader.h index 74d45fb..7680102 100644 --- a/sdv_executables/sdv_vss_util/csv_file_reader.h +++ b/sdv_executables/sdv_vss_util/csv_file_reader.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef CSV_FILE_READER_H #define CSV_FILE_READER_H diff --git a/sdv_executables/sdv_vss_util/main.cpp b/sdv_executables/sdv_vss_util/main.cpp index 4ee2bae..9785294 100644 --- a/sdv_executables/sdv_vss_util/main.cpp +++ b/sdv_executables/sdv_vss_util/main.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include #include diff --git a/sdv_executables/sdv_vss_util/vss_bs_codingrx.cpp b/sdv_executables/sdv_vss_util/vss_bs_codingrx.cpp index 7f3f975..384649b 100644 --- a/sdv_executables/sdv_vss_util/vss_bs_codingrx.cpp +++ b/sdv_executables/sdv_vss_util/vss_bs_codingrx.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "vss_bs_codingrx.h" void CVSSBSCodingRX::GetKeyWordMap(const SSignalBSDefinition& signal, const SSignalVDDefinition& signalVD, CKeywordMap& mapKeywords) const diff --git a/sdv_executables/sdv_vss_util/vss_bs_codingrx.h b/sdv_executables/sdv_vss_util/vss_bs_codingrx.h index d84c508..60a8373 100644 --- a/sdv_executables/sdv_vss_util/vss_bs_codingrx.h +++ b/sdv_executables/sdv_vss_util/vss_bs_codingrx.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef VSS_BS_CODING_RX_H #define VSS_BS_CODING_RX_H @@ -19,121 +32,121 @@ * Beside the definition of the basic service interface also information about the vehicle device is necessary * These 2 definitions must match, there must be a corresponding 'signalVD' definition * the function container of the basic service signal must match the function container if the 'signalVD' - * Creates the code out of templates + * Creates the code out of templates */ class CVSSBSCodingRX : public CCodeGeneratorBase, CVSSHelper { public: /** - * @brief create service content for the IDL file of a RX signal - * @param[in] vssParts Interface in vss style separated in parts - * @param[in] vecFunctions container of all functions of a single vss definition - * @return content of the Device part of a IDL Interface of a RX signal - */ + * @brief create service content for the IDL file of a RX signal + * @param[in] vssParts Interface in vss style separated in parts + * @param[in] vecFunctions container of all functions of a single vss definition + * @return content of the Device part of a IDL Interface of a RX signal + */ std::string Code_RXIDLServiceList(const std::vector& vssParts, const std::vector & vecFunctions) const; /** - * @brief fill the KeyWordMap with all required strings - * @param[in] signal signal definition structure to fill the KeyWordMap - * @param[in] signalVD signal definition structure from vehicle device - * @param[in] mapKeywords KeyWordMap to be filled - */ + * @brief fill the KeyWordMap with all required strings + * @param[in] signal signal definition structure to fill the KeyWordMap + * @param[in] signalVD signal definition structure from vehicle device + * @param[in] mapKeywords KeyWordMap to be filled + */ void GetKeyWordMap(const SSignalBSDefinition& signal, const SSignalVDDefinition& signalVD, CKeywordMap& mapKeywords) const; protected: /** - * @brief create single or multiple lines containing include files - * @param[in] vssOriginalNoDot vss string without points e.g. VehicleChassisSteeringWheelAngle - * @return content of a single interface - */ + * @brief create single or multiple lines containing include files + * @param[in] vssOriginalNoDot vss string without points e.g. VehicleChassisSteeringWheelAngle + * @return content of a single interface + */ std::string Code_BS_RXIncludes(const std::string& vssOriginalNoDot) const; /** - * @brief create a single line containing a interface - * @param[in] functionName function which is part of the interface - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @param[in] vd_functionName function name (Vehicle Device) - * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) - * @return content of a single interface - */ + * @brief create a single line containing a interface + * @param[in] functionName function which is part of the interface + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @param[in] vd_functionName function name (Vehicle Device) + * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) + * @return content of a single interface + */ std::string Code_BS_RXInterface(const std::string& functionName, const std::string& vssWithColons, const std::string& vd_functionName, const std::string& vd_vssWithColons) const; /** - * @brief create a single line containing a interface entry - * @param[in] functionName function which is part of the interface - * @param[in] vssWithColons vss string (short version) with colons as separator - * @return content of a single interface entry - */ + * @brief create a single line containing a interface entry + * @param[in] functionName function which is part of the interface + * @param[in] vssWithColons vss string (short version) with colons as separator + * @return content of a single interface entry + */ std::string Code_BS_RXInterfaceEntry(const std::string& functionName, const std::string& vssWithColons) const; /** - * @brief Get the register and unregister part for the RX VD header - * @param[in] vssWithColons vss string with colons as separator - * @param[in] function function definition structure (Basic Service) - * @param[in] functionVD function definition structure of vehicle device (Vehicle Device) - * @return content of a single register and unregister part. - */ + * @brief Get the register and unregister part for the RX VD header + * @param[in] vssWithColons vss string with colons as separator + * @param[in] function function definition structure (Basic Service) + * @param[in] functionVD function definition structure of vehicle device (Vehicle Device) + * @return content of a single register and unregister part. + */ std::string Code_BS_RXReAndUnregisterEvent(const std::string& vssWithColons, const SFunctionBSDefinition& function, const SFunctionVDDefinition& functionVD) const; /** - * @brief Get the private header part of the basic service header - * @param[in] function function definition structure - * @param[in] vssWithColons vss string with colons as separator - * @return content of private header part of the basic service header - */ + * @brief Get the private header part of the basic service header + * @param[in] function function definition structure + * @param[in] vssWithColons vss string with colons as separator + * @return content of private header part of the basic service header + */ std::string Code_BS_RXPrivateHeaderPart(const SFunctionBSDefinition& function, const std::string& vssWithColons) const; /** - * @brief create service content for the IDL file of a RX signal - * @param[in] spaces string containing only space characters for aligning - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @param[in] function function definition structure - * @return content of the Device part of a IDL Interface of a RX signal - */ + * @brief create service content for the IDL file of a RX signal + * @param[in] spaces string containing only space characters for aligning + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @param[in] function function definition structure + * @return content of the Device part of a IDL Interface of a RX signal + */ std::string Code_RXIDLServiceInterface(const std::string& spaces, const std::string& vssWithColons, const SFunctionBSDefinition& function) const; /** - * @brief unregister signal Event - * @param[in] signalVD signal definition structure to fill the KeyWordMap (Vehicle Device) - * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) - * @param[in] functionVD function definition structure (Vehicle Device) - * @param[in] function function definition structure (Basic Service) - * @return content of a unregister code - */ + * @brief unregister signal Event + * @param[in] signalVD signal definition structure to fill the KeyWordMap (Vehicle Device) + * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) + * @param[in] functionVD function definition structure (Vehicle Device) + * @param[in] function function definition structure (Basic Service) + * @return content of a unregister code + */ std::string Code_BS_RXConstructor(const SSignalVDDefinition& signalVD, const std::string& vd_vssWithColons, const SFunctionVDDefinition& functionVD, const SFunctionBSDefinition& function) const; /** - * @brief unregister signal Event - * @param[in] signalVD signal definition structure to fill the KeyWordMap (Vehicle Device) - * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) - * @param[in] functionVD function definition structure (Vehicle Device) - * @return content of a unregister code - */ + * @brief unregister signal Event + * @param[in] signalVD signal definition structure to fill the KeyWordMap (Vehicle Device) + * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) + * @param[in] functionVD function definition structure (Vehicle Device) + * @return content of a unregister code + */ std::string Code_BS_RXDestructor(const SSignalVDDefinition& signalVD, const std::string& vd_vssWithColons, const SFunctionVDDefinition& functionVD) const; /** - * @brief set and get functions for basic service - * @param[in] class_name class name which is part of the interface (Basic Service) - * @param[in] function function definition structure (Basic Service) - * @param[in] functionVD function definition structure (Vehicle Device) - * @return content of a single set and get function code - */ + * @brief set and get functions for basic service + * @param[in] class_name class name which is part of the interface (Basic Service) + * @param[in] function function definition structure (Basic Service) + * @param[in] functionVD function definition structure (Vehicle Device) + * @return content of a single set and get function code + */ std::string Code_BS_RXGetAndSetFunctions(const std::string& class_name, const SFunctionBSDefinition& function, const SFunctionVDDefinition& functionVD) const; /** - * @brief create register/unregister code for basic service - * @param[in] class_name class name which is part of the interface - * @param[in] function function definition structure - * @param[in] vssWithColons vss string with colons as separator - * @return content of a single signal register/unregister code - */ + * @brief create register/unregister code for basic service + * @param[in] class_name class name which is part of the interface + * @param[in] function function definition structure + * @param[in] vssWithColons vss string with colons as separator + * @return content of a single signal register/unregister code + */ std::string Code_BS_RXRegister(const std::string& class_name, const SFunctionBSDefinition& function, const std::string& vssWithColons) const; }; diff --git a/sdv_executables/sdv_vss_util/vss_bs_codingtx.cpp b/sdv_executables/sdv_vss_util/vss_bs_codingtx.cpp index 1223bc3..26d5cb0 100644 --- a/sdv_executables/sdv_vss_util/vss_bs_codingtx.cpp +++ b/sdv_executables/sdv_vss_util/vss_bs_codingtx.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "vss_bs_codingtx.h" void CVSSBSCodingTX::GetKeyWordMap(const SSignalBSDefinition& signal, const SSignalVDDefinition& signalVD, CKeywordMap& mapKeywords) const diff --git a/sdv_executables/sdv_vss_util/vss_bs_codingtx.h b/sdv_executables/sdv_vss_util/vss_bs_codingtx.h index d6126cd..53017ca 100644 --- a/sdv_executables/sdv_vss_util/vss_bs_codingtx.h +++ b/sdv_executables/sdv_vss_util/vss_bs_codingtx.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef VSS_BS_CODING_TX_H #define VSS_BS_CODING_TX_H @@ -19,93 +32,93 @@ * These 2 definitions must match, there must be a corresponding 'signalVD' definition * the function container of the basic service signal must match the function container if the 'signalVD' * Creates the code out of templates -*/ + */ class CVSSBSCodingTX : public CCodeGeneratorBase, CVSSHelper { public: /** - * @brief create basic service content for the IDL file of a TX signal - * @param[in] vssParts Interface in vss style separated in parts - * @param[in] vecFunctions container of all functions of a single (basic service) vss definition (basic service) - * @return content of the basic service part of a IDL Interface of a TX signal - */ + * @brief create basic service content for the IDL file of a TX signal + * @param[in] vssParts Interface in vss style separated in parts + * @param[in] vecFunctions container of all functions of a single (basic service) vss definition (basic service) + * @return content of the basic service part of a IDL Interface of a TX signal + */ std::string Code_BS_TXIDLList(const std::vector& vssParts, const std::vector & vecFunctions) const; /** - * @brief fill the KeyWordMap with all required strings - * @param[in] signal signal definition structure to fill the KeyWordMap - * @param[in] signalVD signal definition structure from vehicle device - * @param[in] mapKeywords KeyWordMap to be filled - */ + * @brief fill the KeyWordMap with all required strings + * @param[in] signal signal definition structure to fill the KeyWordMap + * @param[in] signalVD signal definition structure from vehicle device + * @param[in] mapKeywords KeyWordMap to be filled + */ void GetKeyWordMap(const SSignalBSDefinition& signal, const SSignalVDDefinition& signalVD, CKeywordMap& mapKeywords) const; protected: /** - * @brief create single or multiple lines containing include files - * @param[in] vd_vssOriginalNoDot vss string without points of the corresponding vehicle device - * @param[in] vssOriginalNoDot vss string without points e.g. VehicleChassisSteeringWheelAngle - * @return content of a single interface - */ + * @brief create single or multiple lines containing include files + * @param[in] vd_vssOriginalNoDot vss string without points of the corresponding vehicle device + * @param[in] vssOriginalNoDot vss string without points e.g. VehicleChassisSteeringWheelAngle + * @return content of a single interface + */ std::string Code_BS_TXIncludes(const std::string& vd_vssOriginalNoDot, const std::string& vssOriginalNoDot) const; /** - * @brief create a single line containing a interface for basic service - * @param[in] functionName function which is part of the interface - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @return content of a single interface - */ + * @brief create a single line containing a interface for basic service + * @param[in] functionName function which is part of the interface + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @return content of a single interface + */ std::string Code_BS_TXInterface(const std::string& functionName, const std::string& vssWithColons) const; /** - * @brief create a single line containing a interface entry for basic service - * @param[in] functionName function which is part of the interface - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @return content of a single interface entry - */ + * @brief create a single line containing a interface entry for basic service + * @param[in] functionName function which is part of the interface + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @return content of a single interface entry + */ std::string Code_BS_TXInterfaceEntry(const std::string& functionName, const std::string& vssWithColons) const; /** - * @brief create list of interfaces (TX signals) for IDL file (Basic Service) - * @param[in] spaces string containing only space characters for aligning - * @param[in] function function definition structure - * @return content of a single interface entry - */ + * @brief create list of interfaces (TX signals) for IDL file (Basic Service) + * @param[in] spaces string containing only space characters for aligning + * @param[in] function function definition structure + * @return content of a single interface entry + */ std::string Code_BS_TXIDLInterface(const std::string& spaces, const SFunctionBSDefinition& function) const; /** - * @brief create function definition part (TX signals) for basic service - * @param[in] function function definition structure - * @return content of a single function declaration - */ + * @brief create function definition part (TX signals) for basic service + * @param[in] function function definition structure + * @return content of a single function declaration + */ std::string Code_BS_TXFunction(const SFunctionBSDefinition& function) const; /** - * @brief create definition of a interface pointer (TX signals) for basic service header file - * @param[in] functionVD which is part of the interface (Vehicle Device) - * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) - * @return content of a definition of a interface pointer - */ + * @brief create definition of a interface pointer (TX signals) for basic service header file + * @param[in] functionVD which is part of the interface (Vehicle Device) + * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) + * @return content of a definition of a interface pointer + */ std::string Code_BS_TXVariablePointerFromVD(const SFunctionVDDefinition& functionVD, const std::string& vd_vssWithColons) const; /** - * @brief create function pointer implementation part (TX signals) for basic service cpp file - * @param[in] function function definition structure (Basic Service) - * @param[in] functionVD function definition structure from vehicle device (Vehicle Device) - * @param[in] className class name of the signal (Basic Service) - * @return content of a single function pointer implementation - */ + * @brief create function pointer implementation part (TX signals) for basic service cpp file + * @param[in] function function definition structure (Basic Service) + * @param[in] functionVD function definition structure from vehicle device (Vehicle Device) + * @param[in] className class name of the signal (Basic Service) + * @return content of a single function pointer implementation + */ std::string Code_BS_TXFunctionPointerImplementationFromVD(const SFunctionBSDefinition& function, const SFunctionVDDefinition& functionVD, const std::string& className) const; /** - * @brief create check if a interface pointer is valid (TX signals) for IDL file - * @param[in] functionVD which is part of the interface (Vehicle Device) - * @param[in] className class name of the signal (Basic Service) - * @param[in] vssOriginal original vvs string (Basic Service) - * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) - * @return content of a check if a interface pointer is valid - */ + * @brief create check if a interface pointer is valid (TX signals) for IDL file + * @param[in] functionVD which is part of the interface (Vehicle Device) + * @param[in] className class name of the signal (Basic Service) + * @param[in] vssOriginal original vvs string (Basic Service) + * @param[in] vd_vssWithColons vss string (complete version) with colons as separator (Vehicle Device) + * @return content of a check if a interface pointer is valid + */ std::string Code_BS_TXVariablePointerInitializationFromVD(const SFunctionVDDefinition& functionVD, const std::string& className, const std::string& vssOriginal, const std::string& vd_vssWithColons) const; }; diff --git a/sdv_executables/sdv_vss_util/vss_bs_generator.cpp b/sdv_executables/sdv_vss_util/vss_bs_generator.cpp index 5b9dfbe..e89aa7b 100644 --- a/sdv_executables/sdv_vss_util/vss_bs_generator.cpp +++ b/sdv_executables/sdv_vss_util/vss_bs_generator.cpp @@ -1,4 +1,17 @@ -#include "vss_bs_generator.h" +/******************************************************************************** + * 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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + + #include "vss_bs_generator.h" #include "vss_coding.h" #include "vss_bs_codingrx.h" #include "vss_bs_codingtx.h" @@ -39,8 +52,9 @@ void CVSSBSGenerator::CreateFiles(const std::string& ssVersion) CreateTXFiles(ssVersion); } -void CVSSBSGenerator::CreateRXFiles(const std::string& ssVersion) const +void CVSSBSGenerator::CreateRXFiles(const std::string& ssVersion) { + m_createdFiles.clear(); for (const auto& rxSignal : m_RXsignals) { if (m_enableComponentCreation) @@ -51,12 +65,12 @@ void CVSSBSGenerator::CreateRXFiles(const std::string& ssVersion) const } } -void CVSSBSGenerator::CreateBasicServiceFilesForRXSignal(const SSignalBSDefinition& signal, const std::string& ssVersion) const +void CVSSBSGenerator::CreateBasicServiceFilesForRXSignal(const SSignalBSDefinition& signal, const std::string& ssVersion) { std::string folderName = "BS_"; folderName.append(signal.className); std::transform(folderName.begin(), folderName.end(), folderName.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return static_cast(std::tolower(c)); }); if (!CreateFolder(m_pathProject, folderName)) { return; @@ -71,6 +85,9 @@ void CVSSBSGenerator::CreateBasicServiceFilesForRXSignal(const SSignalBSDefiniti fstreamBSHeader.open(pathLowerCaseHeader, std::ios::out | std::ios::trunc); fstreamBSClass.open(pathLowerCaseClass, std::ios::out | std::ios::trunc); + m_createdFiles.push_back(pathLowerCaseHeader); + m_createdFiles.push_back(pathLowerCaseClass); + CKeywordMap mapKeywords; CVSSCodingCommon coding(m_ssPrefix); coding.GetCommonKeyWordMap(signal, mapKeywords, ssVersion); @@ -80,7 +97,7 @@ void CVSSBSGenerator::CreateBasicServiceFilesForRXSignal(const SSignalBSDefiniti mapKeywords["basic_service_cpp"] = pathLowerCaseClass.filename().generic_u8string(); CVSSBSCodingRX codingRX; - auto signalVD = GetVDSignal(signal.vssVDDefinition); + auto signalVD = GetVDSignal(signal.vssVDDefinition, signal.signalDirection); if (!signalVD.vecFunctions.size()) { mapKeywords["vss_from_vd_not_found"] = "// corresponding vehicle device interface not found"; @@ -136,7 +153,7 @@ void CVSSBSGenerator::CreateIDLBasicServiceFileForRXSignal(const SSignalBSDefini [](unsigned char c) { return static_cast(std::tolower(c)); }); mapKeywords["vd_idl_file"] = vdIDLFileLowerCase; - auto signalVD = GetVDSignal(signal.vssVDDefinition); + auto signalVD = GetVDSignal(signal.vssVDDefinition, signal.signalDirection); if (signalVD.vecFunctions.size()) { std::string vssNoDot = ReplaceCharacters(signal.vssVDDefinition, ".", ""); @@ -168,18 +185,27 @@ void CVSSBSGenerator::CreateBasicServiceFilesForTXSignal(const SSignalBSDefiniti std::string folderName = "BS_"; folderName.append(signal.className); std::transform(folderName.begin(), folderName.end(), folderName.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return static_cast(std::tolower(c)); }); if (!CreateFolder(m_pathProject, folderName)) { return; } + std::filesystem::path pathBSHeader = m_pathProject / folderName / AppendExtension("BS_", signal.className, ".h"); std::filesystem::path pathBSClass = m_pathProject / folderName / AppendExtension("BS_", signal.className, ".cpp"); std::filesystem::path pathCMakeLists = m_pathProject / folderName / "CMakeLists.txt"; std::ofstream fstreamBSHeader; std::ofstream fstreamBSClass; auto pathLowerCaseHeader = MakeLowercaseFilename(pathBSHeader); - auto pathLowerCaseClass = MakeLowercaseFilename(pathBSClass); + auto pathLowerCaseClass = MakeLowercaseFilename(pathBSClass); + + auto headerExists = std::find(m_createdFiles.begin(), m_createdFiles.end(), pathLowerCaseHeader) != m_createdFiles.end(); + auto classExists = std::find(m_createdFiles.begin(), m_createdFiles.end(), pathLowerCaseClass) != m_createdFiles.end(); + if (headerExists || classExists) + { + UpdateExistingFiles(m_pathProject, folderName, signal); + return; + } fstreamBSHeader.open(pathLowerCaseHeader, std::ios::out | std::ios::trunc); fstreamBSClass.open(pathLowerCaseClass, std::ios::out | std::ios::trunc); @@ -192,7 +218,7 @@ void CVSSBSGenerator::CreateBasicServiceFilesForTXSignal(const SSignalBSDefiniti mapKeywords["basic_service_cpp"] = pathLowerCaseClass.filename().generic_u8string(); CVSSBSCodingTX codingTX; - auto signalVD = GetVDSignal(signal.vssVDDefinition); + auto signalVD = GetVDSignal(signal.vssVDDefinition, signal.signalDirection); if (!signalVD.vecFunctions.size()) { mapKeywords["vss_from_vd_not_found"] = "// corresponding vehicle device interface not found"; @@ -245,3 +271,178 @@ void CVSSBSGenerator::CreateIDLBasicServiceFileForTXSignal(const SSignalBSDefini fstreamBSTXIDL << ReplaceKeywords(szBSTXIDLTemplate, mapKeywords); fstreamBSTXIDL.close(); } + + +bool CVSSBSGenerator::UpdateExistingFiles(const std::filesystem::path& rootPath, const std::string& subfolder, const SSignalBSDefinition& signal) const +{ + std::filesystem::path path = rootPath / subfolder; + + CKeywordMap mapKeywords; + CVSSBSCodingTX codingTX; + auto signalVD = GetVDSignal(signal.vssVDDefinition, signal.signalDirection); + if (!signalVD.vecFunctions.size()) + { + mapKeywords["vss_from_vd_not_found"] = "// corresponding vehicle device interface not found"; + } + codingTX.GetKeyWordMap(signal, signalVD, mapKeywords); + + auto pointerList = mapKeywords["tx_bs_variable_pointer_init_list"]; + auto funtionImplementation = mapKeywords["vd_tx_pointer_function_implementations"]; + if (UpdateCppFile(path, signal, pointerList, funtionImplementation)) + { + auto includeList = mapKeywords["tx_bs_includes_list"]; + auto interfaceList = mapKeywords["tx_bs_interface_list"]; + auto interfaceEntryList = mapKeywords["tx_bs_interface_entry_list"]; + auto functionList = mapKeywords["tx_bs_function_list"]; + auto variablePointerList = mapKeywords["tx_bs_variable_pointer_list"]; + + if (UpdateHeaderFile(path, signal, includeList, interfaceList, interfaceEntryList, functionList, variablePointerList)) + { + return true; + } + } + + return false; +} + +bool CVSSBSGenerator::UpdateCppFile(const std::filesystem::path& rootFolder, const SSignalBSDefinition& signal, const std::string& initializeList, const std::string& implementation) const +{ + std::filesystem::path pathBSCpp = rootFolder / AppendExtension("BS_", signal.className, ".cpp"); + auto pathLowerCaseCpp = MakeLowercaseFilename(pathBSCpp); + + std::ifstream in(pathLowerCaseCpp); + if (!in) + { + std::cerr << "Failed to read file '" << pathLowerCaseCpp << ")\n"; + return false; + } + + std::string s; + std::vector lines; // write new file into memory + while (std::getline(in, s)) + { + lines.push_back(s); + if (s.find("::CBasicService") != std::string::npos) // we have to find the constructor + { + std::getline(in, s); // add next line, the bracket + lines.push_back(s); + lines.push_back(initializeList); + break; + } + } + + while (std::getline(in, s)) + { + lines.push_back(s); + } + + lines.push_back(implementation); // implementation at end of file + in.close(); + + std::error_code ec; + std::filesystem::remove(pathLowerCaseCpp, ec); // delete file + if (ec) + { + std::cerr << "Failed to delete file '" << pathLowerCaseCpp << "': " << ec.message() << " (error code: " << ec.value() << ")\n"; + return false; + } + + std::ofstream out(pathLowerCaseCpp, std::ios::trunc); + if (!out) + { + std::cerr << "Failed to write file '" << pathLowerCaseCpp << ")\n"; + return false; + } + + for (const auto& line : lines) + out << line << "\n"; + + out.close(); + return true; +} + +bool CVSSBSGenerator::UpdateHeaderFile(const std::filesystem::path& rootFolder, const SSignalBSDefinition& signal, const std::string& includeList, + const std::string& interfaceList, const std::string& interfaceEntryList, const std::string& functionList, const std::string& variablePointerList) const +{ + std::filesystem::path pathBSCHeader = rootFolder / AppendExtension("BS_", signal.className, ".h"); + auto pathLowerCaseHeader = MakeLowercaseFilename(pathBSCHeader); + + std::ifstream in(pathLowerCaseHeader); + if (!in) + { + std::cerr << "Failed to read file '" << pathLowerCaseHeader << ")\n"; + return false; + } + + std::string s; + std::vector lines; // write new file into memory + while (std::getline(in, s)) + { + lines.push_back(s); + if (s.find("") != std::string::npos) + { + lines.push_back(includeList); + break; + } + } + + while (std::getline(in, s)) + { + lines.push_back(s); + if (s.find(", public") != std::string::npos) + { + lines.push_back(interfaceList); + break; + } + } + + while (std::getline(in, s)) + { + lines.push_back(s); + if (s.find("BEGIN_SDV_INTERFACE_MAP()") != std::string::npos) + { + lines.push_back(interfaceEntryList); + break; + } + } + + while (std::getline(in, s)) + { + if (s.find("private:") != std::string::npos) + { + lines.push_back(functionList); // we need to add entries before and after + lines.push_back(s); + lines.push_back(variablePointerList); + break; + } + lines.push_back(s); + } + + while (std::getline(in, s)) + { + lines.push_back(s); + } + in.close(); + + std::error_code ec; + std::filesystem::remove(pathLowerCaseHeader, ec); // delete file + if (ec) + { + std::cerr << "Failed to delete file '" << pathLowerCaseHeader << "': " << ec.message() << " (error code: " << ec.value() << ")\n"; + return false; + } + + std::ofstream out(pathLowerCaseHeader, std::ios::trunc); + if (!out) + { + std::cerr << "Failed to write file '" << pathLowerCaseHeader << ")\n"; + return false; + } + + for (const auto& line : lines) // write new file to disc + out << line << "\n"; + + out.close(); + return true; +} + diff --git a/sdv_executables/sdv_vss_util/vss_bs_generator.h b/sdv_executables/sdv_vss_util/vss_bs_generator.h index 79917b8..36a45a8 100644 --- a/sdv_executables/sdv_vss_util/vss_bs_generator.h +++ b/sdv_executables/sdv_vss_util/vss_bs_generator.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef VSS_BS_GENERATOR_H #define VSS_BS_GENERATOR_H @@ -20,7 +33,6 @@ public: * @param[in] rsVersion optional version tag, will be wriiten in header of the files * @param[in] enableComponentCreation optional version tag, will be wriiten in header of the files */ - CVSSBSGenerator(const std::vector& signals, const std::vector& signalsVD, const std::filesystem::path& rpathOutputDir, const std::string& rsPrefix, const std::string& rsVersion, const bool enableComponentCreation) : m_enableComponentCreation(enableComponentCreation), @@ -41,87 +53,125 @@ public: private: /** - * @brief create a file name including prefix and extension - * @param[in] prefix - * @param[in] filename - * @param[in] extension - * @return combined file name - */ + * @brief create a file name including prefix and extension + * @param[in] prefix + * @param[in] filename + * @param[in] extension + * @return combined file name + */ std::string AppendExtension(const std::string& prefix, const std::string& filename, const std::string& extension) const; /** - * @brief create vehicle device and basic service .h an .cpp files - * create IDL files - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create vehicle device and basic service .h an .cpp files + * create IDL files + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateFiles(const std::string& ssVersion); /** - * @brief create IDL files and vehicle device and basic service files of RX signals - * @param[in] ssVersion optional information will be placed in the header of the files - */ - void CreateRXFiles(const std::string& ssVersion) const; + * @brief create IDL files and vehicle device and basic service files of RX signals + * @param[in] ssVersion optional information will be placed in the header of the files + */ + void CreateRXFiles(const std::string& ssVersion); /** - * @brief create basic service files of a single RX signal - * @param[in] signal single signal definition - * @param[in] ssVersion optional information will be placed in the header of the files - */ - void CreateBasicServiceFilesForRXSignal(const SSignalBSDefinition& signal, const std::string& ssVersion) const; + * @brief create basic service files of a single RX signal + * @param[in] signal single signal definition + * @param[in] ssVersion optional information will be placed in the header of the files + */ + void CreateBasicServiceFilesForRXSignal(const SSignalBSDefinition& signal, const std::string& ssVersion); /** - * @brief create IDL file of a single RX signal (basic service) - * @param[in] signal single signal definition - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create IDL file of a single RX signal (basic service) + * @param[in] signal single signal definition + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateIDLBasicServiceFileForRXSignal(const SSignalBSDefinition& signal, const std::string& ssVersion) const; /** - * @brief create IDL files and vehicle device and basic service files of TX signals - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create IDL files and vehicle device and basic service files of TX signals + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateTXFiles(const std::string& ssVersion) const; /** - * @brief create basic service files of a single TX signal - * @param[in] signal single signal definition - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create basic service files of a single TX signal + * @param[in] signal single signal definition + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateBasicServiceFilesForTXSignal(const SSignalBSDefinition& signal, const std::string& ssVersion) const; /** - * @brief create IDL file for a single TX signal (basic service) - * @param[in] signal single signal definition - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create IDL file for a single TX signal (basic service) + * @param[in] signal single signal definition + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateIDLBasicServiceFileForTXSignal(const SSignalBSDefinition& signal, const std::string& ssVersion) const; /** - * @brief find a vss definition in the signal list of the vehicle device collection and return the signal - * @param[in] vssDefinition vss name - * @return signal if a signal was found, otherwise a empty signal definition - */ - SSignalVDDefinition GetVDSignal(const std::string& vssDefinition) const + * @brief In case a RX signal and TX signal have the same vss name we need to update the existing rx signals files + * @param[in] rootPath complete root path + * @param[in] subfolder subfolder of the rx signal files + * @return true if files could be updated successfully, otherwise false + */ + bool UpdateExistingFiles(const std::filesystem::path& rootPath, const std::string& subfolder, const SSignalBSDefinition& signal) const; + + /** + * @brief Update existing cpp file of a rx signal + * @param[in] rootPath complete root path + * @param[in] signal single signal definition + * @param[in] initializeList list of interfaces for the constructor + * @param[in] implementation function implementation methods + * @return true if file could be updated successfully, otherwise false + */ + + bool UpdateCppFile(const std::filesystem::path& rootFolder, const SSignalBSDefinition& signal, const std::string& initializeList, const std::string& implementation) const; + + /** + * @brief Update existing cpp file of a rx signal + * @param[in] rootPath complete root path + * @param[in] signal single signal definition + * @param[in] includeList list of headers to be included + * @param[in] interfaceList list if interfaces + * @param[in] interfaceEntryList list of event entry interfaces + * @param[in] functionList list of function definitions + * @param[in] variablePointerList list of interface pointers + * @return true if file could be updated successfully, otherwise false + */ + + bool UpdateHeaderFile(const std::filesystem::path& rootFolder, const SSignalBSDefinition& signal, const std::string& includeList, + const std::string& interfaceList, const std::string& interfaceEntryList, const std::string& functionList, const std::string& variablePointerList) const; + /** + * @brief find a vss definition in the signal list of the vehicle device collection and return the signal + * @param[in] vssDefinition vss name + * @param[in] signalDirection direction of the signal (RX or TX) + * @return signal if a signal was found, otherwise a empty signal definition + */ + SSignalVDDefinition GetVDSignal(const std::string& vssDefinition,const sdv::core::ESignalDirection signalDirection) const { SSignalVDDefinition noSignal; for (const auto& signal : m_signalsVD) { if (signal.vssDefinition.compare(vssDefinition) == 0) { - return signal; + if (signal.signalDirection == signalDirection) + { + return signal; + } } } return noSignal; } - bool m_enableComponentCreation; ///< if set, the code for the components are created, otherwise only idl files - std::string m_ssPrefix; ///< prefix, used by cmake library and signal definition in signal_identifier.h file. - std::string m_ssVersion; ///< optional version tag, will be wriiten in header of the files - std::filesystem::path m_pathProject; ///< Project file path - std::vector m_signals; ///< all signals - std::vector m_signalsVD; ///< all signals - std::vector m_TXsignals; ///< vector containing the RX signals - std::vector m_RXsignals; ///< vector containing the TX signals + bool m_enableComponentCreation; ///< if set, the code for the components are created, otherwise only idl files + std::string m_ssPrefix; ///< prefix, used by cmake library and signal definition in signal_identifier.h file. + std::string m_ssVersion; ///< optional version tag, will be written in header of the files + std::filesystem::path m_pathProject; ///< Project file path + std::vector m_signals; ///< all signals + std::vector m_signalsVD; ///< all signals + std::vector m_TXsignals; ///< vector containing the RX signals + std::vector m_RXsignals; ///< vector containing the TX signals + std::vector m_createdFiles; ///< collect all files created when the RX signals are processed }; #endif // !defined VSS_BS_GENERATOR_H diff --git a/sdv_executables/sdv_vss_util/vss_coding.cpp b/sdv_executables/sdv_vss_util/vss_coding.cpp index 6daca03..c4fa937 100644 --- a/sdv_executables/sdv_vss_util/vss_coding.cpp +++ b/sdv_executables/sdv_vss_util/vss_coding.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "vss_coding.h" diff --git a/sdv_executables/sdv_vss_util/vss_coding.h b/sdv_executables/sdv_vss_util/vss_coding.h index 68a4c25..de9d896 100644 --- a/sdv_executables/sdv_vss_util/vss_coding.h +++ b/sdv_executables/sdv_vss_util/vss_coding.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef VSS_CODING_H #define VSS_CODING_H @@ -14,7 +27,7 @@ /** * @brief VSS code generator coding class. * Creates the code out of templates -*/ + */ class CVSSCodingCommon : public CCodeGeneratorBase, CVSSHelper { @@ -26,76 +39,76 @@ public: CVSSCodingCommon(const std::string& rsPrefix) : m_ssPrefix(rsPrefix) {} /** - * @brief fill the KeyWordMap with all required strings - * @param[in] signal signal definition structure to fill the KeyWordMap - * @param[in] mapKeywords KeyWordMap to be filled - * @param[in] rsVersion optional information will be placed in the header of the files - */ + * @brief fill the KeyWordMap with all required strings + * @param[in] signal signal definition structure to fill the KeyWordMap + * @param[in] mapKeywords KeyWordMap to be filled + * @param[in] rsVersion optional information will be placed in the header of the files + */ void GetCommonKeyWordMap(const SSignalVDDefinition& signal, CKeywordMap& mapKeywords, const std::string& rsVersion) const; /** - * @brief fill the KeyWordMap with all required strings - * @param[in] signal signal definition structure to fill the KeyWordMap - * @param[in] mapKeywords KeyWordMap to be filled - * @param[in] rsVersion optional information will be placed in the header of the files - */ + * @brief fill the KeyWordMap with all required strings + * @param[in] signal signal definition structure to fill the KeyWordMap + * @param[in] mapKeywords KeyWordMap to be filled + * @param[in] rsVersion optional information will be placed in the header of the files + */ void GetCommonKeyWordMap(const SSignalBSDefinition& signal, CKeywordMap& mapKeywords, const std::string& rsVersion) const; /** - * @brief create content of the signal_identifier.h file - * @param[in] allSignals container of all signal definitions - * @param[in] ssFileName file name of the signal_identifier.h file - * @param[in] ssVersion optional information will be placed in the header of the files - * @return content of the signal_identifier.h file - */ + * @brief create content of the signal_identifier.h file + * @param[in] allSignals container of all signal definitions + * @param[in] ssFileName file name of the signal_identifier.h file + * @param[in] ssVersion optional information will be placed in the header of the files + * @return content of the signal_identifier.h file + */ std::string Code_SignalIdentifier(const std::vector & allSignals, const std::string& ssFileName, const std::string& ssVersion) const; /** - * @brief get the version line which will be added in the header of the files - * @param[in] ssVersion optional information will be placed in the header of the files - * @return line for the header of the files to know the file is auto generated - */ + * @brief get the version line which will be added in the header of the files + * @param[in] ssVersion optional information will be placed in the header of the files + * @return line for the header of the files to know the file is auto generated + */ std::string Code_VSSFileVersion(const std::string& ssVersion) const; /** - * @brief generates the unique define to avoid duplicate loading of a header file - * @param[in] ssFileName file name which will be included in the unique define - * @return unique define string - */ + * @brief generates the unique define to avoid duplicate loading of a header file + * @param[in] ssFileName file name which will be included in the unique define + * @return unique define string + */ std::string Code_SafeGuard(const std::string& ssFileName) const; /** - * @brief generates the cmakelist.txt file - * @param[in] targetLibName project cmake name - * @param[in] targetComponentName is the name of the header and cpp file - * @return content of the CMakeList.txt file - */ + * @brief generates the cmakelist.txt file + * @param[in] targetLibName project cmake name + * @param[in] targetComponentName is the name of the header and cpp file + * @return content of the CMakeList.txt file + */ std::string Code_CMakeProject(const std::string& targetLibName, const std::string& targetComponentName) const; protected: /** - * @brief create a signal line for the signal_identifier file - * @param[in] allSignals container of all signals for aligning of the string - * @param[in] signal single signal the function belongs to - * @param[in] func the function which should be returned in a line - * @param[in] signalTitle signal title string in file signal_identifier.h - * @param[in] canSignalTitle can signal title string in file signal_identifier.h - * @param[in] cTypeTitle c-type title string in file signal_identifier.h - * @return content of a single function for the signal_identifier file - */ + * @brief create a signal line for the signal_identifier file + * @param[in] allSignals container of all signals for aligning of the string + * @param[in] signal single signal the function belongs to + * @param[in] func the function which should be returned in a line + * @param[in] signalTitle signal title string in file signal_identifier.h + * @param[in] canSignalTitle can signal title string in file signal_identifier.h + * @param[in] cTypeTitle c-type title string in file signal_identifier.h + * @return content of a single function for the signal_identifier file + */ std::string Code_SignalIdentifierList(const std::vector & allSignals, const SSignalVDDefinition& signal, const SFunctionVDDefinition& func, const std::string& signalTitle, const std::string& canSignalTitle, const std::string& cTypeTitle) const; /** - * @brief create a list of all signals for the signal_identifier file - * @param[in] allSignals container of all signals - * @param[in] signalTitle signal title string in file signal_identifier.h - * @param[in] canSignalTitle can signal title string in file signal_identifier.h - * @param[in] cTypeTitle c-type title string in file signal_identifier.h - * @return content of list of all signals for the signal_identifier file - */ + * @brief create a list of all signals for the signal_identifier file + * @param[in] allSignals container of all signals + * @param[in] signalTitle signal title string in file signal_identifier.h + * @param[in] canSignalTitle can signal title string in file signal_identifier.h + * @param[in] cTypeTitle c-type title string in file signal_identifier.h + * @return content of list of all signals for the signal_identifier file + */ std::string Code_AllSignalsIdentifierList(const std::vector & allSignals, const std::string& signalTitle, const std::string& canSignalTitle, const std::string& cTypeTitle) const; diff --git a/sdv_executables/sdv_vss_util/vss_helper.cpp b/sdv_executables/sdv_vss_util/vss_helper.cpp index 21c17d1..1597b6b 100644 --- a/sdv_executables/sdv_vss_util/vss_helper.cpp +++ b/sdv_executables/sdv_vss_util/vss_helper.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "vss_helper.h" std::string CVSSHelper::MapDeclType2CType(const sdv::vss::EDeclType eDeclType) const @@ -533,3 +546,4 @@ bool CVSSHelper::VehicleDeviceVSSDefinitionExists(bool bSilent, const std::vecto } return true; } + diff --git a/sdv_executables/sdv_vss_util/vss_helper.h b/sdv_executables/sdv_vss_util/vss_helper.h index cf89e34..646dca3 100644 --- a/sdv_executables/sdv_vss_util/vss_helper.h +++ b/sdv_executables/sdv_vss_util/vss_helper.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef VSS_HELPER_H #define VSS_HELPER_H @@ -10,7 +23,6 @@ #include #include #include -//#include #include namespace sdv @@ -125,7 +137,7 @@ namespace sdv /** * @brief VSS code generator helper class. -*/ + */ class CVSSHelper { @@ -133,19 +145,19 @@ public: /** * @brief Signal definition structure (vehicle device) with a single function/value combination - */ + */ struct SFunctionVDDefinition { std::string idlType; ///< signal value type (IDL: boolean, float, uint8, ...) std::string functionName; ///< signal value set function name std::string signalName; ///< signal name - std::string canSignalName; ///< can signal name (combination of signak messages and signal name) + std::string canSignalName; ///< can signal name (combination of signa messages and signal name) std::string formula; ///< c++ code if the value has to be converted }; /** * @brief Signal definition structure (basic service) with a single function/value combination - */ + */ struct SFunctionBSDefinition { std::string idlType; ///< signal value type (IDL: boolean, float, uint8, ...) @@ -155,7 +167,7 @@ public: /** * @brief Signal definition structure (vehicle device) with a single function/value combination - */ + */ struct SSignalVDDefinition { std::string vssDefinition; ///< vss interface string @@ -166,7 +178,7 @@ public: /** * @brief Signal definition structure (basic service) with a single function/value combination - */ + */ struct SSignalBSDefinition { std::string vssDefinition; ///< vss interface string @@ -177,257 +189,257 @@ public: }; /** - * @brief Check if all required VD definitions are found required by the BS signals - * @param[in] bSilent if true do not print error to console. - * @param[in] vdSignals containing all signals definitions of the vehicle devices. - * @param[in] bsSignals containing all signals definitions of the basic services. - * @return Returns true on success when all definitions are found, otherwise false. - */ + * @brief Check if all required VD definitions are found required by the BS signals + * @param[in] bSilent if true do not print error to console. + * @param[in] vdSignals containing all signals definitions of the vehicle devices. + * @param[in] bsSignals containing all signals definitions of the basic services. + * @return Returns true on success when all definitions are found, otherwise false. + */ static bool VehicleDeviceVSSDefinitionExists(bool bSilent, const std::vector& vdSignals, const std::vector& bsSignals); protected: /** - * @brief Create the cmake if not exists and if content has changed - * @param[in] filePath full path to the file - * @param[in] cmakeContent content of the cmake file - */ + * @brief Create the cmake if not exists and if content has changed + * @param[in] filePath full path to the file + * @param[in] cmakeContent content of the cmake file + */ void CreateCMakeFile(const std::filesystem::path& filePath, const std::string& cmakeContent) const; /** - * @brief Converts the filename of a full path to lowercase - * @param[in] fullPath full path - * @return Returns full path but the filename is lowercase - */ + * @brief Converts the filename of a full path to lowercase + * @param[in] fullPath full path + * @return Returns full path but the filename is lowercase + */ std::filesystem::path MakeLowercaseFilename(std::filesystem::path& fullPath) const; /** - * @brief Add multiple space characters to increase the input string to a certain size. - * @param[in] ssInput Reference to the input string. - * @param[in] size of the final string. - * @return Returns the string increased by space characters. - */ + * @brief Add multiple space characters to increase the input string to a certain size. + * @param[in] ssInput Reference to the input string. + * @param[in] size of the final string. + * @return Returns the string increased by space characters. + */ std::string Align(const std::string& ssInput, size_t size) const; /** - * @brief trim spaces from begin of the string and end of the string - * @param[in] ssInput Reference to the input string to be checked. - * @return Returns string without the spaces at front and at the end - */ + * @brief trim spaces from begin of the string and end of the string + * @param[in] ssInput Reference to the input string to be checked. + * @return Returns string without the spaces at front and at the end + */ std::string Trim(const std::string& ssInput) const; /** - * @brief check for space characters in the string. - * @param[in] ssInput Reference to the input string to be checked. - * @return Returns true if string contains no space character, otherwise false. - */ + * @brief check for space characters in the string. + * @param[in] ssInput Reference to the input string to be checked. + * @return Returns true if string contains no space character, otherwise false. + */ bool MustNotContainSpaces(const std::string& ssInput) const; /** - * @brief Get the maximal string size of all class names in the vector container. - * @param[in] signals containing all signals definitions which have to be checked. - * @param[in] ssTitle Reference to the title string. Is minimum of the return value. - * @return Returns the string size of the largest class name string. - */ + * @brief Get the maximal string size of all class names in the vector container. + * @param[in] signals containing all signals definitions which have to be checked. + * @param[in] ssTitle Reference to the title string. Is minimum of the return value. + * @return Returns the string size of the largest class name string. + */ size_t GetMaxClassName(const std::vector& signals, const std::string& ssTitle) const; /** - * @brief Get the maximal string size of all IDL type strings in the vector container. - * @param[in] signals containing all signals definitions which have to be checked. - * @param[in] ssTitle Reference to the title string. Is minimum of the return value. - * @return Returns the string size of the largest IDL type string. - */ + * @brief Get the maximal string size of all IDL type strings in the vector container. + * @param[in] signals containing all signals definitions which have to be checked. + * @param[in] ssTitle Reference to the title string. Is minimum of the return value. + * @return Returns the string size of the largest IDL type string. + */ size_t GetMaxIDLType(const std::vector& signals, const std::string& ssTitle) const; /** - * @brief Get the maximal string size of all ctype strings in the vector container. - * @param[in] signals containing all signals definitions which have to be checked. - * @param[in] ssTitle Reference to the title string. Is minimum of the return value. - * @return Returns the string size of the largest ctype string. - */ + * @brief Get the maximal string size of all ctype strings in the vector container. + * @param[in] signals containing all signals definitions which have to be checked. + * @param[in] ssTitle Reference to the title string. Is minimum of the return value. + * @return Returns the string size of the largest ctype string. + */ size_t GetMaxCTypeFromIDLType(const std::vector& signals, const std::string& ssTitle) const; /** - * @brief Get the maximal string size of all signal name strings in the vector container. - * @param[in] signals containing all signals definitions which have to be checked. - * @param[in] ssTitle Reference to the title string. Is minimum of the return value. - * @return Returns the string size of the largest signal names string. - */ + * @brief Get the maximal string size of all signal name strings in the vector container. + * @param[in] signals containing all signals definitions which have to be checked. + * @param[in] ssTitle Reference to the title string. Is minimum of the return value. + * @return Returns the string size of the largest signal names string. + */ size_t GetMaxSignalName(const std::vector& signals, const std::string& ssTitle) const; /** - * @brief Get the maximal string size of all CAN signal name strings in the vector container. - * @param[in] signals containing all signals definitions which have to be checked. - * @param[in] ssTitle Reference to the title string. Is minimum of the return value. - * @return Returns the string size of the largest signal names string. - */ + * @brief Get the maximal string size of all CAN signal name strings in the vector container. + * @param[in] signals containing all signals definitions which have to be checked. + * @param[in] ssTitle Reference to the title string. Is minimum of the return value. + * @return Returns the string size of the largest signal names string. + */ size_t GetMaxCANSignalName(const std::vector& signals, const std::string& ssTitle) const; /** - * @brief Get the maximal string size of all function name strings in the vector container. - * @param[in] signals containing all signals definitions which have to be checked. - * @param[in] ssTitle Reference to the title string. Is minimum of the return value. - * @return Returns the string size of the largest function names string. - */ + * @brief Get the maximal string size of all function name strings in the vector container. + * @param[in] signals containing all signals definitions which have to be checked. + * @param[in] ssTitle Reference to the title string. Is minimum of the return value. + * @return Returns the string size of the largest function names string. + */ size_t GetMaxFunctionName(const std::vector& signals, const std::string& ssTitle) const; /** - * @brief Get the maximal string size of all vss strings in the vector container. - * @param[in] signals containing all signals definitions which have to be checked. - * @param[in] ssTitle Reference to the title string. Is minimum of the return value. - * @return Returns the string size of the largest vss string. - */ + * @brief Get the maximal string size of all vss strings in the vector container. + * @param[in] signals containing all signals definitions which have to be checked. + * @param[in] ssTitle Reference to the title string. Is minimum of the return value. + * @return Returns the string size of the largest vss string. + */ size_t GetMaxVSSDefinition(const std::vector& signals, const std::string& ssTitle) const; /** - * @brief check for '.' characters in the string. - * @param[in] ssInput Reference to the input string to be checked. - * @param[in] mustBeEmpty If true input string must not be empty - * @return Returns true if string contains '.' or is empty depending on mustBeEmpty, otherwise false. - */ + * @brief check for '.' characters in the string. + * @param[in] ssInput Reference to the input string to be checked. + * @param[in] mustBeEmpty If true input string must not be empty + * @return Returns true if string contains '.' or is empty depending on mustBeEmpty, otherwise false. + */ bool MustContainDotOrIsEmpty(const std::string& ssInput, bool mustBeEmpty) const; /** - * @brief check if vss parts are string and no number. If number add '_' - * for vehicle.chassis.axel.01.left return vehicle.chassis.axel._01.left - * @param[in] ssInput Reference to the input string to be checked. - * @return Returns valid vss string. - */ + * @brief check if vss parts are string and no number. If number add '_' + * for vehicle.chassis.axel.01.left return vehicle.chassis.axel._01.left + * @param[in] ssInput Reference to the input string to be checked. + * @return Returns valid vss string. + */ std::string ValidateVSSFormatNumbers(const std::string& ssInput) const; /** - * @brief check if string is a number - * @param[in] ssInput Reference to the input string to be checked. - * @return Returns false if it is a number, otherwise true. - */ + * @brief check if string is a number + * @param[in] ssInput Reference to the input string to be checked. + * @return Returns false if it is a number, otherwise true. + */ bool IsNumber(const std::string& ssInput) const; /** - * @brief Returns input string or if empty, returns default string. - * @param[in] ssInput string - * @param[in] ssDefaultString string used as default if input string is empty - * @param[in] index number will be added to the default string. - * @return Returns input string or if empty returns default string added by the number. - */ + * @brief Returns input string or if empty, returns default string. + * @param[in] ssInput string + * @param[in] ssDefaultString string used as default if input string is empty + * @param[in] index number will be added to the default string. + * @return Returns input string or if empty returns default string added by the number. + */ std::string GenerateDefaultIfEmpty(const std::string& ssInput, std::string ssDefaultString, const uint32_t& index) const; /** - * @brief Returns input string but starts and ends with "'". - * @param[in] ssInput string - * @return Returns the input string with "'" added at the frond and the end. - */ + * @brief Returns input string but starts and ends with "'". + * @param[in] ssInput string + * @return Returns the input string with "'" added at the frond and the end. + */ std::string AddQuotationMarks(const std::string& ssInput) const; /** - * @brief Replaces some characters of the input string. - * @param[in] ssInput string. - * @param[in] from character to be replaced - * @param[in] to replacement character. - * @return Returns the string with replacements. - */ + * @brief Replaces some characters of the input string. + * @param[in] ssInput string. + * @param[in] from character to be replaced + * @param[in] to replacement character. + * @return Returns the string with replacements. + */ std::string ReplaceCharacters(const std::string& ssInput, const std::string& from, const std::string& to) const; /** - * @brief turn the input string to upper case - * @param[in] ssInput string. - * @return Returns the input string but all characters are upper case - */ + * @brief turn the input string to upper case + * @param[in] ssInput string. + * @return Returns the input string but all characters are upper case + */ std::string ToUpper(const std::string& ssInput) const; /** - * @brief remove the last part of the vss string - * @param[in] ssInput vss string - * @return Returns the string but removed the last part after charatcer '.'. - */ + * @brief remove the last part of the vss string + * @param[in] ssInput vss string + * @return Returns the string but removed the last part after charatcer '.'. + */ std::string ShortenVSSString(const std::string& ssInput) const; /** - * @brief add to the type a cast, for example for 'std::string' it adds "const std::string&' - * @param[in] ssSignalType the string representing the type - * @return Returns either original string or 'const' and '&' added. - */ + * @brief add to the type a cast, for example for 'std::string' it adds "const std::string&' + * @param[in] ssSignalType the string representing the type + * @return Returns either original string or 'const' and '&' added. + */ std::string CastValueType(const std::string& ssSignalType) const; /** - * @brief find IDL type and return CType - * @param[in] ssIDLType string representing IDL type - * @return CType string if IDL type found, otherwise empty string - */ + * @brief find IDL type and return CType + * @param[in] ssIDLType string representing IDL type + * @return CType string if IDL type found, otherwise empty string + */ std::string GetCTypeFromIDLType(const std::string& ssIDLType) const; /** - * @brief validate if all IDL type are valid - * @param[in] vecTypes container of the IDL types - * @param[in] silent information to console if not silent - * @return True if IDL types are valid, otherwise false - */ + * @brief validate if all IDL type are valid + * @param[in] vecTypes container of the IDL types + * @param[in] silent information to console if not silent + * @return True if IDL types are valid, otherwise false + */ bool ValidateIDLTypes(const std::vector& vecTypes, const bool silent) const; /** - * @brief Create folder or sub folder - * @param[in] rootPath complete root path - * @param[in] subfolder in case this parameter is empty only root path is created otherwise rootPath + subfolder - * @return true if directory exists or is created successfully, otherwise false - */ + * @brief Create folder or sub folder + * @param[in] rootPath complete root path + * @param[in] subfolder in case this parameter is empty only root path is created otherwise rootPath + subfolder + * @return true if directory exists or is created successfully, otherwise false + */ bool CreateFolder(const std::filesystem::path& rootPath, const std::string& subfolder) const; /** - * @brief deletes a header file - * @param[in] path full path to a folder - * @param[in] fileNameNoExtension a file name without the extension - */ + * @brief deletes a header file + * @param[in] path full path to a folder + * @param[in] fileNameNoExtension a file name without the extension + */ void DeleteHeaderFile(const std::filesystem::path& path, const std::string& fileNameNoExtension) const; /** - * @brief Validates the code style (signal name starts with lower case, function name upper case ... - * @param[in] function function definition structure - * @param[in] verbose print information to console only if true - */ + * @brief Validates the code style (signal name starts with lower case, function name upper case ... + * @param[in] function function definition structure + * @param[in] verbose print information to console only if true + */ void ValidateVDCodeStyle(const SFunctionVDDefinition& function, const bool verbose) const; /** - * @brief Validates the code style (signal name starts with lower case, function name upper case ... - * @param[in] function function definition structure - * @param[in] verbose print information to console only if true - */ + * @brief Validates the code style (signal name starts with lower case, function name upper case ... + * @param[in] function function definition structure + * @param[in] verbose print information to console only if true + */ void ValidateBSCodeStyle(const SFunctionBSDefinition& function, const bool verbose) const; /** - * @brief Validates the code style (signal name starts with lower case, function name upper case ... - * @param[in] signal signal definition structure - * @param[in] verbose print information to console only if true - */ + * @brief Validates the code style (signal name starts with lower case, function name upper case ... + * @param[in] signal signal definition structure + * @param[in] verbose print information to console only if true + */ void ValidateVDCodeStyle(const SSignalVDDefinition& signal, const bool verbose) const; /** - * @brief Validates the code style (signal name starts with lower case, function name upper case ... - * @param[in] signal signal definition structure - * @param[in] verbose print information to console only if true - */ + * @brief Validates the code style (signal name starts with lower case, function name upper case ... + * @param[in] signal signal definition structure + * @param[in] verbose print information to console only if true + */ void ValidateBSCodeStyle(const SSignalBSDefinition& signal, const bool verbose) const; private: /** -* @brief Declaration type name association. -*/ + * @brief Declaration type name association. + */ using TDeclTypeAssoc = std::pair; /** - * @brief change EDeclType to cType string - * @param[in] eDeclType EDeclTyp type - * @return Returns cType stringif available, otherwise empty string - */ + * @brief change EDeclType to cType string + * @param[in] eDeclType EDeclTyp type + * @return Returns cType stringif available, otherwise empty string + */ std::string MapDeclType2CType(const sdv::vss::EDeclType eDeclType) const; /** - * @brief Mapping between declaration type and string name. - * @attention Some types have multiple string representatives (e.g. sdv::vss::EDeclType::decltype_short vs. 'short' and - * 'int16'). - * @attention Based on the provided extensions on the command line, the types might be extended by: char16, char32, u8string, - * u16string, u32string, pointer, interface_id, interface_t and exception_id - */ + * @brief Mapping between declaration type and string name. + * @attention Some types have multiple string representatives (e.g. sdv::vss::EDeclType::decltype_short vs. 'short' and + * 'int16'). + * @attention Based on the provided extensions on the command line, the types might be extended by: char16, char32, u8string, + * u16string, u32string, pointer, interface_id, interface_t and exception_id + */ const std::vector m_vecDeclTypes = { {"short", sdv::vss::EDeclType::decltype_short}, {"unsigned short", sdv::vss::EDeclType::decltype_unsigned_short}, diff --git a/sdv_executables/sdv_vss_util/vss_rx_templates.h b/sdv_executables/sdv_vss_util/vss_rx_templates.h index 133328d..6832f61 100644 --- a/sdv_executables/sdv_vss_util/vss_rx_templates.h +++ b/sdv_executables/sdv_vss_util/vss_rx_templates.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + /** * @brief file template for the RX signals (device header) */ @@ -21,15 +34,13 @@ const char szRXVehicleDeviceHeaderTemplate[] = R"code(/** */ class CVehicleDevice%class_name% : public sdv::CSdvObject - , public sdv::IObjectControl %rx_vd_interface_list%{ public: BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) %rx_vd_interface_entry_list% END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_bus) DECLARE_OBJECT_CLASS_NAME("%vss_original%_Device") /** @@ -37,32 +48,19 @@ public: */ CVehicleDevice%class_name%(); - /** - * @brief initialize device to get the object name or use the default. - * @param[in] objectConfig Configuration containing the object name - */ - void Initialize(const sdv::u8string& objectConfig) override; + /** + * @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 bool OnInitialize() override; - /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object - */ - sdv::EObjectStatus GetStatus() const override; - - /** - * @brief Set the component operation mode. Overlovd of sdv::IObjectControl::SetOperationMode. - * @param[in] eMode The operation mode, the component should run in. - */ - void SetOperationMode(/*in*/ sdv::EOperationMode eMode) override; - - /** - * @brief Shutdown function is to shutdown the execution of request thread - */ - void Shutdown() override; + /** + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. + */ + virtual void OnShutdown() override; %rx_vd_register_events_list% private: %rx_vd_private_vd_header_part% - std::atomic m_status = { sdv::EObjectStatus::initialization_pending }; ///< To update the object status when it changes. }; DEFINE_SDV_OBJECT(CVehicleDevice%class_name%) @@ -88,58 +86,17 @@ CVehicleDevice%class_name%::CVehicleDevice%class_name%() { } -/** -* @brief initialize device to get the object name or use the default. -* @param[in] objectConfig Configuration containing the object name -*/ -void CVehicleDevice%class_name%::Initialize(const sdv::u8string&) +bool CVehicleDevice%class_name%::OnInitialize() { - if (m_status != sdv::EObjectStatus::initialization_pending) - { - return; - } sdv::core::CDispatchService dispatch; %rx_vd_subscribe_signals% %rx_check_subscriptions% + return true; } -/** -* @brief Gets the current status of the object -* @return EObjectStatus The current status of the object -*/ -sdv::EObjectStatus CVehicleDevice%class_name%::GetStatus() const -{ - return m_status; -} - -/** - * @brief Set the component operation mode. Overlovd of sdv::IObjectControl::SetOperationMode. - * @param[in] eMode The operation mode, the component should run in. - */ -void CVehicleDevice%class_name%::SetOperationMode(/*in*/ sdv::EOperationMode eMode) -{ - switch (eMode) - { - case sdv::EOperationMode::configuring: - if (m_status == sdv::EObjectStatus::running || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::configuring; - break; - case sdv::EOperationMode::running: - if (m_status == sdv::EObjectStatus::configuring || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::running; - break; - default: - break; - } -} - -/** -* @brief Shutdown function is to shutdown the execution of request thread -*/ -void CVehicleDevice%class_name%::Shutdown() +void CVehicleDevice%class_name%::OnShutdown() { %rx_reset_signals% - m_status = sdv::EObjectStatus::destruction_pending; } %rx_vd_register% )code"; @@ -171,7 +128,7 @@ public: BEGIN_SDV_INTERFACE_MAP() %rx_bs_interface_entry_list% END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::BasicService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("%vss_original%_Service") /** diff --git a/sdv_executables/sdv_vss_util/vss_tx_templates.h b/sdv_executables/sdv_vss_util/vss_tx_templates.h index 7e9c882..de30c99 100644 --- a/sdv_executables/sdv_vss_util/vss_tx_templates.h +++ b/sdv_executables/sdv_vss_util/vss_tx_templates.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + /** * @brief file template for the TX signals (device header) */ @@ -21,15 +34,13 @@ const char szTXVehicleDeviceHeaderTemplate[] = R"code(/** */ class CVehicleDevice%class_name% : public sdv::CSdvObject - , public sdv::IObjectControl %tx_vd_interface_list%{ public: BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) %tx_vd_interface_entry_list% END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_bus) DECLARE_OBJECT_CLASS_NAME("%vss_original%_Device") /** @@ -37,32 +48,19 @@ public: */ CVehicleDevice%class_name%(); - /** - * @brief initialize device to get the object name or use the default. - * @param[in] objectConfig Configuration containing the object name - */ - void Initialize(const sdv::u8string& objectConfig) override; + /** + * @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 bool OnInitialize() override; - /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object - */ - sdv::EObjectStatus GetStatus() const override; - - /** - * @brief Set the component operation mode. Overlovd of sdv::IObjectControl::SetOperationMode. - * @param[in] eMode The operation mode, the component should run in. - */ - void SetOperationMode(/*in*/ sdv::EOperationMode eMode) override; - - /** - * @brief Shutdown function is to shutdown the execution of request thread - */ - void Shutdown() override; + /** + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. + */ + virtual void OnShutdown() override; %tx_vd_function_list% private: %tx_variable_list% - std::atomic m_status = { sdv::EObjectStatus::initialization_pending }; ///< To update the object status when it changes. }; DEFINE_SDV_OBJECT(CVehicleDevice%class_name%) @@ -81,66 +79,21 @@ const char szTXVehicleDeviceClassTemplate[] = R"code(/** #include #include "vd_%class_name_lowercase%.h" -/** - * @brief Constructor - */ CVehicleDevice%class_name%::CVehicleDevice%class_name%() { } -/** -* @brief initialize device to get the object name or use the default. -* @param[in] objectConfig Configuration containing the object name -*/ -void CVehicleDevice%class_name%::Initialize(const sdv::u8string&) +bool CVehicleDevice%class_name%::OnInitialize() { - if (m_status != sdv::EObjectStatus::initialization_pending) - { - return; - } sdv::core::CDispatchService dispatch; %tx_variable_init_list% %tx_variable_check_list% + return true; } - -/** -* @brief Gets the current status of the object -* @return EObjectStatus The current status of the object -*/ -sdv::EObjectStatus CVehicleDevice%class_name%::GetStatus() const -{ - return m_status; -} - -/** - * @brief Set the component operation mode. Overlovd of sdv::IObjectControl::SetOperationMode. - * @param[in] eMode The operation mode, the component should run in. - */ -void CVehicleDevice%class_name%::SetOperationMode(/*in*/ sdv::EOperationMode eMode) -{ - switch (eMode) - { - case sdv::EOperationMode::configuring: - if (m_status == sdv::EObjectStatus::running || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::configuring; - break; - case sdv::EOperationMode::running: - if (m_status == sdv::EObjectStatus::configuring || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::running; - break; - default: - break; - } -} - -/** -* @brief Shutdown function is to shutdown the execution of request thread -*/ -void CVehicleDevice%class_name%::Shutdown() +void CVehicleDevice%class_name%::OnShutdown() { %tx_reset_signals% - m_status = sdv::EObjectStatus::destruction_pending; } %tx_function_implementations% @@ -169,10 +122,9 @@ class CBasicService%class_name% public: BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::CSdvObject) %tx_bs_interface_entry_list% END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::BasicService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("%vss_original%_Service") /** diff --git a/sdv_executables/sdv_vss_util/vss_vd_codingrx.cpp b/sdv_executables/sdv_vss_util/vss_vd_codingrx.cpp index 0782437..853c830 100644 --- a/sdv_executables/sdv_vss_util/vss_vd_codingrx.cpp +++ b/sdv_executables/sdv_vss_util/vss_vd_codingrx.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "vss_vd_codingrx.h" void CVSSVDCodingRX::GetKeyWordMap(const SSignalVDDefinition& signal, CKeywordMap& mapKeywords) const @@ -48,11 +61,6 @@ void CVSSVDCodingRX::GetKeyWordMap(const SSignalVDDefinition& signal, CKeywordMa } mapKeywords["rx_vd_subscribe_signals"] = std::move(sstreamVDSubscribe.str()); - if (signal.vecFunctions.size() > 0) - { - mapKeywords["rx_check_subscriptions"] = Code_VD_RXCheckSignalSubscribtions(signal.vecFunctions); - } - std::stringstream sstreamVDResetSignal; for (const auto& func : signal.vecFunctions) { @@ -361,34 +369,11 @@ std::string CVSSVDCodingRX::Code_VD_RXSubscribeSignal(const std::string& class_n if (!m_%signal_name%Signal) { SDV_LOG_ERROR("Could not get signal: ", %object_prefix%::ds%start_with_uppercase%, " [CVehicleDevice%class_name%]"); + return false; } )code", mapKeywords); } -std::string CVSSVDCodingRX::Code_VD_RXCheckSignalSubscribtions(const std::vector & vecFunctions) const -{ - uint32_t count{ 0 }; - std::stringstream sstreamFunctions; - sstreamFunctions << " if (!"; - for (const auto& func : vecFunctions) - { - count++; - sstreamFunctions << "m_" << func.signalName << "Signal"; - if (count != vecFunctions.size()) - { - sstreamFunctions << " || !"; - } - } - sstreamFunctions << ")" << std::endl; - sstreamFunctions << " {" << std::endl; - sstreamFunctions << " m_status = sdv::EObjectStatus::initialization_failure;" << std::endl; - sstreamFunctions << " return;" << std::endl; - sstreamFunctions << " }" << std::endl; - sstreamFunctions << " m_status = sdv::EObjectStatus::initialized;"; - - return sstreamFunctions.str(); -} - std::string CVSSVDCodingRX::Code_VD_RXResetSignal(const SFunctionVDDefinition& function) const { CKeywordMap mapKeywords; diff --git a/sdv_executables/sdv_vss_util/vss_vd_codingrx.h b/sdv_executables/sdv_vss_util/vss_vd_codingrx.h index e42e30d..434034d 100644 --- a/sdv_executables/sdv_vss_util/vss_vd_codingrx.h +++ b/sdv_executables/sdv_vss_util/vss_vd_codingrx.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef VSS_VD_CODING_RX_H #define VSS_VD_CODING_RX_H @@ -17,7 +30,7 @@ /** * @brief VSS code generator coding class for vehicle devices. * Creates the code out of templates -*/ + */ class CVSSVDCodingRX : public CCodeGeneratorBase, CVSSHelper { @@ -29,125 +42,118 @@ public: CVSSVDCodingRX(const std::string& rsPrefix) : m_ssPrefix(rsPrefix) {} /** - * @brief create device content for the IDL file of a RX signal - * @param[in] vssParts Interface in vss style separated in parts - * @param[in] vecFunctions container of all functions of a single vss definition - * @return content of the Device part of a IDL Interface of a RX signal - */ + * @brief create device content for the IDL file of a RX signal + * @param[in] vssParts Interface in vss style separated in parts + * @param[in] vecFunctions container of all functions of a single vss definition + * @return content of the Device part of a IDL Interface of a RX signal + */ std::string Code_RXIDLDeviceList(const std::vector& vssParts, const std::vector & vecFunctions) const; /** - * @brief create service content for the IDL file of a RX signal - * @param[in] vssParts Interface in vss style separated in parts - * @param[in] vecFunctions container of all functions of a single vss definition - * @return content of the Device part of a IDL Interface of a RX signal - */ + * @brief create service content for the IDL file of a RX signal + * @param[in] vssParts Interface in vss style separated in parts + * @param[in] vecFunctions container of all functions of a single vss definition + * @return content of the Device part of a IDL Interface of a RX signal + */ std::string Code_RXIDLServiceList(const std::vector& vssParts, const std::vector & vecFunctions) const; /** - * @brief fill the KeyWordMap with all required strings - * @param[in] signal signal definition structure to fill the KeyWordMap - * @param[in] mapKeywords KeyWordMap to be filled - */ + * @brief fill the KeyWordMap with all required strings + * @param[in] signal signal definition structure to fill the KeyWordMap + * @param[in] mapKeywords KeyWordMap to be filled + */ void GetKeyWordMap(const SSignalVDDefinition& signal, CKeywordMap& mapKeywords) const; protected: /** - * @brief create single or multiple lines containing include files - * @param[in] vssOriginalNoDot vss string without points e.g. VehicleChassisSteeringWheelAngle - * @return content of a single interface - */ + * @brief create single or multiple lines containing include files + * @param[in] vssOriginalNoDot vss string without points e.g. VehicleChassisSteeringWheelAngle + * @return content of a single interface + */ std::string Code_VD_RXIncludes(const std::string& vssOriginalNoDot) const; /** - * @brief create a single line containing a interface - * @param[in] functionName function which is part of the interface - * @param[in] vssShortenWithColons vss string (short version) with colons as separator - * @return content of a single interface - */ + * @brief create a single line containing a interface + * @param[in] functionName function which is part of the interface + * @param[in] vssShortenWithColons vss string (short version) with colons as separator + * @return content of a single interface + */ std::string Code_VD_RXInterface(const std::string& functionName, const std::string& vssShortenWithColons) const; /** - * @brief create a single line containing a interface entry - * @param[in] functionName function which is part of the interface - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @return content of a single interface entry - */ + * @brief create a single line containing a interface entry + * @param[in] functionName function which is part of the interface + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @return content of a single interface entry + */ std::string Code_VD_RXInterfaceEntry(const std::string& functionName, const std::string& vssWithColons) const; /** - * @brief Get the register and unregister part for the RX VD header - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @param[in] function function definition structure - * @return content of a single register and unregister part. - */ + * @brief Get the register and unregister part for the RX VD header + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @param[in] function function definition structure + * @return content of a single register and unregister part. + */ std::string Code_VD_RXReAndUnregisterEvent(const std::string& vssWithColons, const SFunctionVDDefinition& function) const; /** - * @brief Get the private header part of the vehicle device header - * @param[in] function function definition structure - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @param[in] class_name class name which is part of the interface - * @return content of private header part of the vehicle device header - */ + * @brief Get the private header part of the vehicle device header + * @param[in] function function definition structure + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @param[in] class_name class name which is part of the interface + * @return content of private header part of the vehicle device header + */ std::string Code_VD_RXPrivateHeaderPart(const SFunctionVDDefinition& function, const std::string& vssWithColons, const std::string& class_name) const; /** - * @brief create device content for the IDL file of a RX signal - * @param[in] spaces string containing only space characters for aligning - * @param[in] function function definition structure - * @return content of the Device part of a IDL Interface of a RX signal - */ + * @brief create device content for the IDL file of a RX signal + * @param[in] spaces string containing only space characters for aligning + * @param[in] function function definition structure + * @return content of the Device part of a IDL Interface of a RX signal + */ std::string Code_RXIDLDeviceInterface(const std::string& spaces, const SFunctionVDDefinition& function) const; /** - * @brief create service content for the IDL file of a RX signal - * @param[in] spaces string containing only space characters for aligning - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @param[in] function function definition structure - * @return content of the Device part of a IDL Interface of a RX signal - */ + * @brief create service content for the IDL file of a RX signal + * @param[in] spaces string containing only space characters for aligning + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @param[in] function function definition structure + * @return content of the Device part of a IDL Interface of a RX signal + */ std::string Code_RXIDLServiceInterface(const std::string& spaces, const std::string& vssWithColons, const SFunctionVDDefinition& function) const; /** - * @brief subscribe signals (RX type) for vehicle device - * @param[in] class_name class name which is part of the interface - * @param[in] function function definition structure - * @return content of a initializing code - */ + * @brief subscribe signals (RX type) for vehicle device + * @param[in] class_name class name which is part of the interface + * @param[in] function function definition structure + * @return content of a initializing code + */ std::string Code_VD_RXSubscribeSignal(const std::string& class_name, const SFunctionVDDefinition& function) const; /** - * @brief subscribe signals (RX type) for vehicle device - * @param[in] vecFunctions list of functions of the vss interface - * @return content of a initializing code - */ - std::string Code_VD_RXCheckSignalSubscribtions(const std::vector & vecFunctions) const; - - /** - * @brief Rest signal (RX signals) for vehicle device cpp file - * @param[in] function function definition structure - * @return content of a single signal reset implementation - */ + * @brief Rest signal (RX signals) for vehicle device cpp file + * @param[in] function function definition structure + * @return content of a single signal reset implementation + */ std::string Code_VD_RXResetSignal(const SFunctionVDDefinition& function) const; /** - * @brief create register/unregister and callback function code for vehicle device - * @param[in] class_name class name which is part of the interface - * @param[in] function function definition structure - * @param[in] vssWithColons vss string with colons as separator - * @return content of a single signal register/unregister and callback code - */ + * @brief create register/unregister and callback function code for vehicle device + * @param[in] class_name class name which is part of the interface + * @param[in] function function definition structure + * @param[in] vssWithColons vss string with colons as separator + * @return content of a single signal register/unregister and callback code + */ std::string Code_VD_RXRegister(const std::string& class_name, const SFunctionVDDefinition& function, const std::string& vssWithColons) const; /** - * @brief create code to get the value or use use user defined c++ code - * @param[in] function function definition structure - * @return default code to get the value - */ + * @brief create code to get the value or use use user defined c++ code + * @param[in] function function definition structure + * @return default code to get the value + */ std::string Code_VD_RXFormular(const SFunctionVDDefinition& function) const; std::string m_ssPrefix; ///< prefix, used by cmake library and signal definition in signal_identifier.h file. diff --git a/sdv_executables/sdv_vss_util/vss_vd_codingtx.cpp b/sdv_executables/sdv_vss_util/vss_vd_codingtx.cpp index b2c6a93..c60dac0 100644 --- a/sdv_executables/sdv_vss_util/vss_vd_codingtx.cpp +++ b/sdv_executables/sdv_vss_util/vss_vd_codingtx.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "vss_vd_codingtx.h" void CVSSVDCodingTX::GetKeyWordMap(const SSignalVDDefinition& signal, CKeywordMap& mapKeywords) const @@ -257,6 +270,7 @@ std::string CVSSVDCodingTX::Code_VD_TXVariableInitialization(const SFunctionVDDe if (!m_%signal_name%) { SDV_LOG_ERROR("Could not get signal: ", %object_prefix%::ds%start_with_uppercase%, " [CVehicleDevice%class_name%]"); + return false; } )code", mapKeywords); } @@ -277,10 +291,8 @@ std::string CVSSVDCodingTX::Code_VD_TXVariableCheckInitialization(const std::vec } sstreamFunctions << ")" << std::endl; sstreamFunctions << " {" << std::endl; - sstreamFunctions << " m_status = sdv::EObjectStatus::initialization_failure;" << std::endl; - sstreamFunctions << " return;" << std::endl; + sstreamFunctions << " return false;" << std::endl; sstreamFunctions << " }" << std::endl; - sstreamFunctions << " m_status = sdv::EObjectStatus::initialized;"; return sstreamFunctions.str(); } diff --git a/sdv_executables/sdv_vss_util/vss_vd_codingtx.h b/sdv_executables/sdv_vss_util/vss_vd_codingtx.h index 8c3026c..eb09509 100644 --- a/sdv_executables/sdv_vss_util/vss_vd_codingtx.h +++ b/sdv_executables/sdv_vss_util/vss_vd_codingtx.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef VSS_CODING_TX_H #define VSS_CODING_TX_H @@ -16,7 +29,7 @@ /** * @brief VSS code generator coding class. * Creates the code out of templates -*/ + */ class CVSSVDCodingTX : public CCodeGeneratorBase, CVSSHelper { @@ -28,94 +41,94 @@ public: CVSSVDCodingTX(const std::string& rsPrefix) : m_ssPrefix(rsPrefix) {} /** - * @brief create vehicle device content for the IDL file of a TX signal - * @param[in] vssParts Interface in vss style separated in parts - * @param[in] vecFunctions container of all functions of a single vss definition - * @return content of the vehicle device part of a IDL Interface of a TX signal - */ + * @brief create vehicle device content for the IDL file of a TX signal + * @param[in] vssParts Interface in vss style separated in parts + * @param[in] vecFunctions container of all functions of a single vss definition + * @return content of the vehicle device part of a IDL Interface of a TX signal + */ std::string Code_VD_TXIDLList(const std::vector& vssParts, const std::vector & vecFunctions) const; /** - * @brief fill the KeyWordMap with all required strings - * @param[in] signal signal definition structure to fill the KeyWordMap - * @param[in] mapKeywords KeyWordMap to be filled - */ + * @brief fill the KeyWordMap with all required strings + * @param[in] signal signal definition structure to fill the KeyWordMap + * @param[in] mapKeywords KeyWordMap to be filled + */ void GetKeyWordMap(const SSignalVDDefinition& signal, CKeywordMap& mapKeywords) const; protected: /** - * @brief create single or multiple lines containing include files - * @param[in] vssOriginalNoDot vss string without points e.g. VehicleChassisSteeringWheelAngle - * @return content of a single interface - */ + * @brief create single or multiple lines containing include files + * @param[in] vssOriginalNoDot vss string without points e.g. VehicleChassisSteeringWheelAngle + * @return content of a single interface + */ std::string Code_VD_TXIncludes(const std::string& vssOriginalNoDot) const; /** - * @brief create a single line containing a interface for vehicle device - * @param[in] functionName function which is part of the interface - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @return content of a single interface - */ + * @brief create a single line containing a interface for vehicle device + * @param[in] functionName function which is part of the interface + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @return content of a single interface + */ std::string Code_VD_TXInterface(const std::string& functionName, const std::string& vssWithColons) const; /** - * @brief create a single line containing a interface entry for vehicle device - * @param[in] functionName function which is part of the interface - * @param[in] vssWithColons vss string (complete version) with colons as separator - * @return content of a single interface entry - */ + * @brief create a single line containing a interface entry for vehicle device + * @param[in] functionName function which is part of the interface + * @param[in] vssWithColons vss string (complete version) with colons as separator + * @return content of a single interface entry + */ std::string Code_VD_TXInterfaceEntry(const std::string& functionName, const std::string& vssWithColons) const; /** - * @brief create list of interfaces (TX signals) for IDL file (Vehicle Device) - * @param[in] spaces string containing only space characters for aligning - * @param[in] function function definition structure - * @return content of a single interface entry - */ + * @brief create list of interfaces (TX signals) for IDL file (Vehicle Device) + * @param[in] spaces string containing only space characters for aligning + * @param[in] function function definition structure + * @return content of a single interface entry + */ std::string Code_VD_TXIDLInterface(const std::string& spaces, const SFunctionVDDefinition& function) const; /** - * @brief create function definition part (TX signals) for vehicle device - * @param[in] function function definition structure - * @return content of a single function declaration - */ + * @brief create function definition part (TX signals) for vehicle device + * @param[in] function function definition structure + * @return content of a single function declaration + */ std::string Code_VD_TXFunction(const SFunctionVDDefinition& function) const; /** - * @brief Rest signal (TX signals) for vehicle device cpp file - * @param[in] function function definition structure - * @return content of a single signal reset implementation - */ + * @brief Rest signal (TX signals) for vehicle device cpp file + * @param[in] function function definition structure + * @return content of a single signal reset implementation + */ std::string Code_VD_TXResetSignal(const SFunctionVDDefinition& function) const; /** - * @brief create function implementation part (TX signals) for vehicle device cpp file - * @param[in] function function definition structure - * @param[in] className class name of the signal - * @return content of a single function implementation - */ + * @brief create function implementation part (TX signals) for vehicle device cpp file + * @param[in] function function definition structure + * @param[in] className class name of the signal + * @return content of a single function implementation + */ std::string Code_VD_TXFunctionImplementation(const SFunctionVDDefinition& function, const std::string& className) const; /** - * @brief create single variable definition (TX signals) for vehicle device header - * @param[in] signalName name of the signal - * @return content of a single variable definition - */ + * @brief create single variable definition (TX signals) for vehicle device header + * @param[in] signalName name of the signal + * @return content of a single variable definition + */ std::string Code_VD_TXVariable(const std::string& signalName) const; /** - * @brief create single variable initialization (TX signals) for vehicle device cpp file - * @param[in] function which is part of the interface - * @return content of a single variable initialization - */ + * @brief create single variable initialization (TX signals) for vehicle device cpp file + * @param[in] function which is part of the interface + * @return content of a single variable initialization + */ std::string Code_VD_TXVariableInitialization(const SFunctionVDDefinition& function) const; /** - * @brief create check if all interfaces are valid (TX signals) for vehicle device cpp file - * @param[in] vecFunctions container of all functions of a single vss definition - * @return content to check if all interfaces are valid - */ + * @brief create check if all interfaces are valid (TX signals) for vehicle device cpp file + * @param[in] vecFunctions container of all functions of a single vss definition + * @return content to check if all interfaces are valid + */ std::string Code_VD_TXVariableCheckInitialization(const std::vector & vecFunctions) const; std::string m_ssPrefix; ///< prefix, used by cmake library and signal definition in signal_identifier.h file. diff --git a/sdv_executables/sdv_vss_util/vss_vd_generator.cpp b/sdv_executables/sdv_vss_util/vss_vd_generator.cpp index 889ed9a..6ffba0d 100644 --- a/sdv_executables/sdv_vss_util/vss_vd_generator.cpp +++ b/sdv_executables/sdv_vss_util/vss_vd_generator.cpp @@ -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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #include "vss_vd_generator.h" #include "vss_coding.h" #include "vss_vd_codingrx.h" @@ -99,7 +112,7 @@ void CVSSVDGenerator::CreateFiles(const std::string& ssVersion) CreateTXFiles(ssVersion); } -void CVSSVDGenerator::CreateRXFiles(const std::string& ssVersion) const +void CVSSVDGenerator::CreateRXFiles(const std::string& ssVersion) { for (const auto& rxSignal : m_RXsignals) { @@ -111,7 +124,7 @@ void CVSSVDGenerator::CreateRXFiles(const std::string& ssVersion) const } } -void CVSSVDGenerator::CreateVehicleDeviceFilesForRXSignal(const SSignalVDDefinition& signal, const std::string& ssVersion) const +void CVSSVDGenerator::CreateVehicleDeviceFilesForRXSignal(const SSignalVDDefinition& signal, const std::string& ssVersion) { std::string folderName = "VD_"; folderName.append(signal.className); @@ -127,7 +140,10 @@ void CVSSVDGenerator::CreateVehicleDeviceFilesForRXSignal(const SSignalVDDefinit std::ofstream fstreamVDHeader; std::ofstream fstreamVDClass; auto pathLowerCaseHeader = MakeLowercaseFilename(pathVDHeader); - auto pathLowerCaseClass = MakeLowercaseFilename(pathVDClass); + auto pathLowerCaseClass = MakeLowercaseFilename(pathVDClass); + m_createdFiles.push_back(pathLowerCaseHeader); + m_createdFiles.push_back(pathLowerCaseClass); + fstreamVDHeader.open(pathLowerCaseHeader, std::ios::out | std::ios::trunc); fstreamVDClass.open(pathLowerCaseClass, std::ios::out | std::ios::trunc); @@ -211,18 +227,27 @@ void CVSSVDGenerator::CreateVehicleDeviceFilesForTXSignal(const SSignalVDDefinit std::string folderName = "VD_"; folderName.append(signal.className); std::transform(folderName.begin(), folderName.end(), folderName.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return static_cast(std::tolower(c)); }); if (!CreateFolder(m_pathProject, folderName)) { return; } + std::filesystem::path pathVDHeader = m_pathProject / folderName / AppendExtension("VD_", signal.className, ".h"); std::filesystem::path pathVDClass = m_pathProject / folderName / AppendExtension("VD_", signal.className, ".cpp"); std::filesystem::path pathCMakeLists = m_pathProject / folderName / "CMakeLists.txt"; std::ofstream fstreamVDHeader; std::ofstream fstreamVDClass; auto pathLowerCaseHeader = MakeLowercaseFilename(pathVDHeader); - auto pathLowerCaseClass = MakeLowercaseFilename(pathVDClass); + auto pathLowerCaseClass = MakeLowercaseFilename(pathVDClass); + + auto headerExists = std::find(m_createdFiles.begin(), m_createdFiles.end(), pathLowerCaseHeader) != m_createdFiles.end(); + auto classExists = std::find(m_createdFiles.begin(), m_createdFiles.end(), pathLowerCaseClass) != m_createdFiles.end(); + if (headerExists || classExists) + { + UpdateExistingFiles(m_pathProject, folderName, signal); + return; + } fstreamVDHeader.open(pathLowerCaseHeader, std::ios::out | std::ios::trunc); fstreamVDClass.open(pathLowerCaseClass, std::ios::out | std::ios::trunc); @@ -282,3 +307,172 @@ void CVSSVDGenerator::CreateIDLVehicleDeviceFileForTXSignal(const SSignalVDDefin fstreamVDTXIDL << ReplaceKeywords(szVDTXIDLTemplate, mapKeywords); fstreamVDTXIDL.close(); } + + +bool CVSSVDGenerator::UpdateExistingFiles(const std::filesystem::path& rootPath, const std::string& subfolder, const SSignalVDDefinition& signal) const +{ + std::filesystem::path path = rootPath / subfolder; + + CKeywordMap mapKeywords; + CVSSVDCodingTX codingTX(m_ssPrefix); + codingTX.GetKeyWordMap(signal, mapKeywords); + + auto initializeList = mapKeywords["tx_variable_init_list"]; + auto functionImplementation = mapKeywords["tx_function_implementations"]; + if (UpdateCppFile(path, signal, initializeList, functionImplementation)) + { + + auto includeList = mapKeywords["tx_vd_includes_list"]; + auto interfaceList = mapKeywords["tx_vd_interface_list"]; + auto interfaceEntryList = mapKeywords["tx_vd_interface_entry_list"]; + auto functionList = mapKeywords["tx_vd_function_list"]; + auto variablePointerList = mapKeywords["tx_variable_list"]; + + if (UpdateHeaderFile(path, signal, includeList, interfaceList, interfaceEntryList, functionList, variablePointerList)) + { + return true; + } + } + + return false; +} + +bool CVSSVDGenerator::UpdateCppFile(const std::filesystem::path& rootFolder, const SSignalVDDefinition& signal, const std::string& initializeList, const std::string& implementation) const +{ + std::filesystem::path pathVDCpp = rootFolder / AppendExtension("VD_", signal.className, ".cpp"); + auto pathLowerCaseCpp = MakeLowercaseFilename(pathVDCpp); + + std::ifstream in(pathLowerCaseCpp); + if (!in) + { + std::cerr << "Failed to read file '" << pathLowerCaseCpp << ")\n"; + return false; + } + + std::string s; + std::vector lines; // write new file into memory + while (std::getline(in, s)) + { + lines.push_back(s); + if (s.find("sdv::core::CDispatchService dispatch;") != std::string::npos) + { + lines.push_back(initializeList); + break; + } + } + + while (std::getline(in, s)) + { + lines.push_back(s); + } + + lines.push_back(implementation); // implementation at end of file + in.close(); + + std::error_code ec; + std::filesystem::remove(pathLowerCaseCpp, ec); // delete file + if (ec) + { + std::cerr << "Failed to delete file '" << pathLowerCaseCpp << "': " << ec.message() << " (error code: " << ec.value() << ")\n"; + return false; + } + + std::ofstream out(pathLowerCaseCpp, std::ios::trunc); + if (!out) + { + std::cerr << "Failed to write file '" << pathLowerCaseCpp << ")\n"; + return false; + } + + for (const auto& line : lines) + out << line << "\n"; + + out.close(); + return true; +} + +bool CVSSVDGenerator::UpdateHeaderFile(const std::filesystem::path& rootFolder, const SSignalVDDefinition& signal, const std::string& includeList, + const std::string& interfaceList, const std::string& interfaceEntryList, const std::string& functionList, const std::string& variablePointerList) const +{ + std::filesystem::path pathVDHeader = rootFolder / AppendExtension("VD_", signal.className, ".h"); + auto pathLowerCaseHeader = MakeLowercaseFilename(pathVDHeader); + + std::ifstream in(pathLowerCaseHeader); + if (!in) + { + std::cerr << "Failed to read file '" << pathLowerCaseHeader << ")\n"; + return false; + } + + std::string s; + std::vector lines; // write new file into memory + while (std::getline(in, s)) + { + lines.push_back(s); + if (s.find("") != std::string::npos) + { + lines.push_back(includeList); + break; + } + } + + while (std::getline(in, s)) + { + lines.push_back(s); + if (s.find(", public") != std::string::npos) + { + lines.push_back(interfaceList); + break; + } + } + + while (std::getline(in, s)) + { + lines.push_back(s); + if (s.find("BEGIN_SDV_INTERFACE_MAP()") != std::string::npos) + { + lines.push_back(interfaceEntryList); + break; + } + } + + while (std::getline(in, s)) + { + if (s.find("private:") != std::string::npos) + { + lines.push_back(functionList); // we need to add entries before and after + lines.push_back(s); + lines.push_back(variablePointerList); + break; + } + lines.push_back(s); + } + + while (std::getline(in, s)) + { + lines.push_back(s); + } + in.close(); + + + std::error_code ec; + std::filesystem::remove(pathLowerCaseHeader, ec); // delete file + if (ec) + { + std::cerr << "Failed to delete file '" << pathLowerCaseHeader << "': " << ec.message() << " (error code: " << ec.value() << ")\n"; + return false; + } + + std::ofstream out(pathLowerCaseHeader, std::ios::trunc); + if (!out) + { + std::cerr << "Failed to write file '" << pathLowerCaseHeader << ")\n"; + return false; + } + + for (const auto& line : lines) // write new file to disc + out << line << "\n"; + out.close(); + return true; +} + diff --git a/sdv_executables/sdv_vss_util/vss_vd_generator.h b/sdv_executables/sdv_vss_util/vss_vd_generator.h index 5038ce6..4c12b08 100644 --- a/sdv_executables/sdv_vss_util/vss_vd_generator.h +++ b/sdv_executables/sdv_vss_util/vss_vd_generator.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: + * Thomas Pfleiderer - initial API and implementation + ********************************************************************************/ + #ifndef VSS_VD_GENERATOR_H #define VSS_VD_GENERATOR_H @@ -36,74 +49,109 @@ public: private: /** - * @brief create content of the summary tect file - * @return string containing the complete content of the summary text file; - */ + * @brief create content of the summary tect file + * @return string containing the complete content of the summary text file; + */ std::string SummaryTextFile() const; /** - * @brief create a file name including prefix and extension - * @param[in] prefix - * @param[in] filename - * @param[in] extension - * @return combined file name - */ + * @brief create a file name including prefix and extension + * @param[in] prefix + * @param[in] filename + * @param[in] extension + * @return combined file name + */ std::string AppendExtension(const std::string& prefix, const std::string& filename, const std::string& extension) const; /** - * @brief create vehicle device and basic service .h an .cpp files - * create IDL files - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create vehicle device and basic service .h an .cpp files + * create IDL files + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateFiles(const std::string& ssVersion); /** - * @brief create IDL files and vehicle device and basic service files of RX signals - * @param[in] ssVersion optional information will be placed in the header of the files - */ - void CreateRXFiles(const std::string& ssVersion) const; + * @brief create IDL files and vehicle device and basic service files of RX signals + * @param[in] ssVersion optional information will be placed in the header of the files + */ + void CreateRXFiles(const std::string& ssVersion); /** - * @brief create vehicle device files of a single RX signal - * @param[in] signal single signal definition - * @param[in] ssVersion optional information will be placed in the header of the files - */ - void CreateVehicleDeviceFilesForRXSignal(const SSignalVDDefinition& signal, const std::string& ssVersion) const; + * @brief create vehicle device files of a single RX signal + * @param[in] signal single signal definition + * @param[in] ssVersion optional information will be placed in the header of the files + */ + void CreateVehicleDeviceFilesForRXSignal(const SSignalVDDefinition& signal, const std::string& ssVersion); /** - * @brief create IDL file of a single RX signal (vehicle device) - * @param[in] signal single signal definition - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create IDL file of a single RX signal (vehicle device) + * @param[in] signal single signal definition + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateIDLVehicleDeviceFileForRXSignal(const SSignalVDDefinition& signal, const std::string& ssVersion) const; /** - * @brief create IDL files and vehicle device and basic service files of TX signals - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create IDL files and vehicle device and basic service files of TX signals + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateTXFiles(const std::string& ssVersion) const; /** - * @brief create vehicle device files of a single TX signal - * @param[in] signal single signal definition - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create vehicle device files of a single TX signal + * @param[in] signal single signal definition + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateVehicleDeviceFilesForTXSignal(const SSignalVDDefinition& signal, const std::string& ssVersion) const; /** - * @brief create IDL file for a single TX signal (vehicle device) - * @param[in] signal single signal definition - * @param[in] ssVersion optional information will be placed in the header of the files - */ + * @brief create IDL file for a single TX signal (vehicle device) + * @param[in] signal single signal definition + * @param[in] ssVersion optional information will be placed in the header of the files + */ void CreateIDLVehicleDeviceFileForTXSignal(const SSignalVDDefinition& signal, const std::string& ssVersion) const; - bool m_enableComponentCreation; ///< if set, the code for the components are created, otherwise only idl files - std::string m_ssPrefix; ///< prefix, used by cmake library and signal definition in signal_identifier.h file. - std::string m_ssVersion; ///< optional version tag, will be wriiten in header of the files - std::filesystem::path m_pathProject; ///< Project file path - std::vector m_signals; ///< all signals - std::vector m_TXsignals; ///< vector containing the RX signals - std::vector m_RXsignals; ///< vector containing the TX signals + /** + * @brief In case a RX signal and TX signal have the same vss name we need to update the existing rx signal files + * @param[in] rootPath complete root path + * @param[in] subfolder subfolder of the rx signal files + * @return true if files could be updated successfully, otherwise false + */ + bool UpdateExistingFiles(const std::filesystem::path& rootPath, const std::string& subfolder, const SSignalVDDefinition& signal) const; + + /** + * @brief Update existing cpp file of a rx signal + * @param[in] rootPath complete root path + * @param[in] signal single signal definition + * @param[in] initializeList list of interfaces for the initialization method + * @param[in] implementation function implementation methods + * @return true if file could be updated successfully, otherwise false + */ + + bool UpdateCppFile(const std::filesystem::path& rootFolder, const SSignalVDDefinition& signal, const std::string& initializeList, const std::string& implementation) const; + + /** + * @brief Update existing cpp file of a rx signal + * @param[in] rootPath complete root path + * @param[in] signal single signal definition + * @param[in] includeList list of headers to be included + * @param[in] interfaceList list if interfaces + * @param[in] interfaceEntryList list of event entry interfaces + * @param[in] functionList list of function definitions + * @param[in] variablePointerList list of interface pointers + * @return true if file could be updated successfully, otherwise false + */ + + bool UpdateHeaderFile(const std::filesystem::path& rootFolder, const SSignalVDDefinition& signal, const std::string& includeList, + const std::string& interfaceList, const std::string& interfaceEntryList, const std::string& functionList, const std::string& variablePointerList) const; + + bool m_enableComponentCreation; ///< if set, the code for the components are created, otherwise only idl files + std::string m_ssPrefix; ///< prefix, used by cmake library and signal definition in signal_identifier.h file. + std::string m_ssVersion; ///< optional version tag, will be written in header of the files + std::filesystem::path m_pathProject; ///< Project file path + std::vector m_signals; ///< all signals + std::vector m_TXsignals; ///< vector containing the RX signals + std::vector m_RXsignals; ///< vector containing the TX signals + std::vector m_createdFiles; ///< collect all files created when the RX signals are processed }; #endif // !defined VSS_VD_GENERATOR_H diff --git a/sdv_services/CMakeLists.txt b/sdv_services/CMakeLists.txt index c62e0a2..db29f90 100644 --- a/sdv_services/CMakeLists.txt +++ b/sdv_services/CMakeLists.txt @@ -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) diff --git a/sdv_services/can_communication_silkit/CMakeLists.txt b/sdv_services/can_communication_silkit/CMakeLists.txt index bbfc538..c022f59 100644 --- a/sdv_services/can_communication_silkit/CMakeLists.txt +++ b/sdv_services/can_communication_silkit/CMakeLists.txt @@ -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) diff --git a/sdv_services/can_communication_silkit/can_com_silkit.cpp b/sdv_services/can_communication_silkit/can_com_silkit.cpp index 9c5f2d4..b873de3 100644 --- a/sdv_services/can_communication_silkit/can_com_silkit.cpp +++ b/sdv_services/can_communication_silkit/can_com_silkit.cpp @@ -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 #include -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(nodeSilKitConfig.GetValue()); - } - - sdv::toml::CNode nodeSilKitParticipant = config.GetDirect("SilKitParticipantName"); - if (nodeSilKitParticipant.GetType() == sdv::toml::ENodeType::node_string) - { - m_SilKitParticipantName = static_cast(nodeSilKitParticipant.GetValue()); - } - - sdv::toml::CNode nodeSilKitNetwork = config.GetDirect("CanSilKitNetwork"); - if (nodeSilKitNetwork.GetType() == sdv::toml::ENodeType::node_string) - { - silKitNetwork = static_cast(nodeSilKitNetwork.GetValue()); - } - - sdv::toml::CNode nodeSilKitRegistryURI = config.GetDirect("RegistryURI"); - if (nodeSilKitRegistryURI.GetType() == sdv::toml::ENodeType::node_string) - { - silKitRegistryUri = static_cast(nodeSilKitRegistryURI.GetValue()); - } - - sdv::toml::CNode nodeSilKitSyncMode = config.GetDirect("SyncMode"); - if (nodeSilKitSyncMode.GetType() == sdv::toml::ENodeType::node_boolean) - { - m_SilKitIsSynchronousMode = static_cast(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("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 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 CCANSilKit::GetInterfaces() const { sdv::sequence 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 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 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) diff --git a/sdv_services/can_communication_silkit/can_com_silkit.h b/sdv_services/can_communication_silkit/can_com_silkit.h index 7352d8a..b3675bf 100644 --- a/sdv_services/can_communication_silkit/can_com_silkit.h +++ b/sdv_services/can_communication_silkit/can_com_silkit.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: + * 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 m_SetReceivers; ///< Set with receiver interfaces. + std::mutex m_ReceiversMtx; ///< Protect the receiver set. + std::set m_SetReceivers; ///< Set with receiver interfaces. - std::queue m_MessageQueue; ///< Map of the messages to be sent on SilKit. - std::mutex m_QueueMutex; ///< Protection for message map. + std::queue 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 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 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 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) diff --git a/sdv_services/can_communication_sim/CMakeLists.txt b/sdv_services/can_communication_sim/CMakeLists.txt index 6d40e0d..713e526 100644 --- a/sdv_services/can_communication_sim/CMakeLists.txt +++ b/sdv_services/can_communication_sim/CMakeLists.txt @@ -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) diff --git a/sdv_services/can_communication_sim/can_com_sim.cpp b/sdv_services/can_communication_sim/can_com_sim.cpp index e291650..8df5299 100644 --- a/sdv_services/can_communication_sim/can_com_sim.cpp +++ b/sdv_services/can_communication_sim/can_com_sim.cpp @@ -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 #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 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 CCANSimulation::GetInterfaces() const { sdv::sequence seqIfcNames; - if (m_eStatus != sdv::EObjectStatus::running) return seqIfcNames; + if (GetObjectState() != sdv::EObjectState::running) return seqIfcNames; std::unique_lock lock(m_mtxInterfaces); for (const auto& rprInterface : m_vecInterfaces) seqIfcNames.push_back(rprInterface.second); @@ -186,7 +151,7 @@ sdv::sequence 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{}; diff --git a/sdv_services/can_communication_sim/can_com_sim.h b/sdv_services/can_communication_sim/can_com_sim.h index 88b985f..2496b49 100644 --- a/sdv_services/can_communication_sim/can_com_sim.h +++ b/sdv_services/can_communication_sim/can_com_sim.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 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 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 m_setReceivers; ///< Set with receiver interfaces. diff --git a/sdv_services/can_communication_socket_can/CMakeLists.txt b/sdv_services/can_communication_socket_can/CMakeLists.txt index 65b33b1..b68befc 100644 --- a/sdv_services/can_communication_socket_can/CMakeLists.txt +++ b/sdv_services/can_communication_socket_can/CMakeLists.txt @@ -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) diff --git a/sdv_services/can_communication_socket_can/can_com_sockets.cpp b/sdv_services/can_communication_socket_can/can_com_sockets.cpp index b97ce6f..ec17be0 100644 --- a/sdv_services/can_communication_socket_can/can_com_sockets.cpp +++ b/sdv_services/can_communication_socket_can/can_com_sockets.cpp @@ -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 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& vecConfigInterfaces) @@ -60,13 +100,16 @@ bool CCANSockets::SetupCANSockets(const std::deque& vecConfigInterf // Retrieve the list of available interfaces std::set 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& 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 CCANSockets::GetInterfaces() const { sdv::sequence seqIfcNames; - if (m_eStatus != sdv::EObjectStatus::running) + if (GetObjectState() != sdv::EObjectState::running) { return seqIfcNames; } @@ -241,7 +258,7 @@ sdv::sequence 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 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(&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(&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: diff --git a/sdv_services/can_communication_socket_can/can_com_sockets.h b/sdv_services/can_communication_socket_can/can_com_sockets.h index cf25e52..18381bd 100644 --- a/sdv_services/can_communication_socket_can/can_com_sockets.h +++ b/sdv_services/can_communication_socket_can/can_com_sockets.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: + * 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& vecConfigInterfaces, const std::set& 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 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 m_setReceivers; ///< Set with receiver interfaces. diff --git a/sdv_services/can_communication_socket_can/config/test_can_communication_sockets.toml b/sdv_services/can_communication_socket_can/config/test_can_communication_sockets.toml index ab53e29..63f9d07 100644 --- a/sdv_services/can_communication_socket_can/config/test_can_communication_sockets.toml +++ b/sdv_services/can_communication_socket_can/config/test_can_communication_sockets.toml @@ -12,6 +12,7 @@ Class = "DataDispatchService" [[Component]] Path = "can_com_sockets.sdv" Class = "CAN_Com_Sockets" +[Component.Parameters] canSockets=["llcecan0"] [[Component]] diff --git a/sdv_services/core/CMakeLists.txt b/sdv_services/core/CMakeLists.txt index 343cde9..37ce962 100644 --- a/sdv_services/core/CMakeLists.txt +++ b/sdv_services/core/CMakeLists.txt @@ -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") diff --git a/sdv_services/core/app_config.cpp b/sdv_services/core/app_config.cpp index 2cc7055..7cb921a 100644 --- a/sdv_services/core/app_config.cpp +++ b/sdv_services/core/app_config.cpp @@ -1,22 +1,110 @@ +/******************************************************************************** + * 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.h" -#include "sdv_core.h" #include "../../global/exec_dir_helper.h" #include #include #include #include +#include "toml_parser/parser_toml.h" +#include +#include "module_control.h" +#include "repository.h" +#include "app_settings.h" #if __unix__ #include #endif +CAppConfig& GetAppConfig() +{ + static CAppConfig app_config; + return app_config; +} + +// GetCoreDirectory might have been redirected for unit tests. +#ifndef GetCoreDirectory +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(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 +} +#endif // !defined GetCoreDirectory + bool CAppConfig::LoadInstallationManifests() { - if (!GetAppControl().IsMainApplication() && !GetAppControl().IsIsolatedApplication()) return false; + // Check for allowance + bool bServerApp = false; + switch (GetAppSettings().GetContextType()) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: + bServerApp = true; + default: + break; + } std::filesystem::path pathCore = GetCoreDirectory(); std::filesystem::path pathExe = GetExecDirectory(); - std::filesystem::path pathInstall = GetAppControl().GetInstallDir(); + std::filesystem::path pathInstall = GetAppSettings().GetInstallDir(); // Load the core manifest (should work in any case). m_manifestCore = CInstallManifest(); @@ -31,309 +119,435 @@ bool CAppConfig::LoadInstallationManifests() else m_manifestExe.Clear(); - // Get the user manifests. If there is no manifest, this is not an error. - for (auto const& dir_entry : std::filesystem::directory_iterator{ pathInstall }) - { - if (!dir_entry.is_directory()) continue; - if (!std::filesystem::exists(dir_entry.path() / "install_manifest.toml")) continue; - CInstallManifest manifestUser; - if (manifestUser.Load(dir_entry.path(), true)) - m_vecUserManifests.push_back(manifestUser); - } + // Done when not server application. + if (!bServerApp) return true; + // Get the user manifests. If there is no manifest, this is not an error. + if (!pathInstall.empty()) + { + try + { + for (auto const& dir_entry : std::filesystem::directory_iterator{pathInstall}) + { + if (!dir_entry.is_directory()) + continue; + if (!std::filesystem::exists(dir_entry.path() / "install_manifest.toml")) + continue; + CInstallManifest manifestUser; + if (manifestUser.Load(dir_entry.path(), false)) + m_vecUserManifests.push_back(manifestUser); + } + } catch (const std::filesystem::filesystem_error& /*rexcept*/) + { + SDV_LOG_ERROR("Failed to iterate installation directory: '", pathInstall.generic_u8string(), "'."); + return false; + } + } return true; } void CAppConfig::UnloadInstallatonManifests() { - if (!GetAppControl().IsMainApplication() && !GetAppControl().IsIsolatedApplication()) return; - m_vecUserManifests.clear(); m_manifestExe.Clear(); m_manifestCore.Clear(); } -sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8string& ssContent) +bool CAppConfig::LoadAppConfigs() { + // Isolated applications do not load configurations + if (GetAppSettings().IsIsolatedApplication()) return false; + + // When running as server application, load the system configurations + if (GetAppSettings().IsMainApplication() || GetAppSettings().IsMaintenanceApplication()) + { + m_vecSysConfigs.clear(); + + auto vecSysConfigs = GetAppSettings().GetSystemConfigPaths(); + for (const std::filesystem::path& rpathConfig : vecSysConfigs) + { + CAppConfigFile config(rpathConfig); + if (!config.IsLoaded()) + { + SDV_LOG_ERROR("Failed to load system configuration '", rpathConfig.generic_u8string(), "'."); + return false; + } + + // Start the configuration by the repository + // cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning. + // cppcheck-suppress knownConditionTrueFalse + if (GetRepository().StartFromConfig(config, false) == sdv::core::EConfigProcessResult::failed) + { + SDV_LOG_ERROR("Failed to start the system configuration '", rpathConfig.generic_u8string(), "'."); + return false; + } + + // Loading was successful. + m_vecSysConfigs.push_back(std::move(config)); + } + } + + // Reset config baseline (all changes from here are logged). + ResetConfigBaseline(); + + // Load the user configuration + std::filesystem::path pathUserConfig = GetAppSettings().GetUserConfigPath(); + if (pathUserConfig.empty()) + m_configUserConfig.Clear(); + else + { + CAppConfigFile config(pathUserConfig); + if (!config.IsLoaded()) + { + SDV_LOG_ERROR("Failed to load user configuration '", pathUserConfig.generic_u8string(), "'."); + return false; + } + + // Load the configuration by the repository + // cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning. + // cppcheck-suppress knownConditionTrueFalse + if (GetRepository().StartFromConfig(config, false) == sdv::core::EConfigProcessResult::failed) + { + SDV_LOG_ERROR("Failed to start the system configuration '", pathUserConfig.generic_u8string(), "'."); + return false; + } + + // Loading was successful. + m_configUserConfig = std::move(config); + } + + return true; +} + +void CAppConfig::SaveAppConfigs() +{ + // User configuration is always saved when changed. + if (m_configUserConfig.IsLoaded() && !m_configUserConfig.SaveConfigFile()) + SDV_LOG_ERROR("Failed to save the user configuration '", m_configUserConfig.ConfigPath(), "'."); + + // System configurations are only saved when running as maintenance application. + if (GetAppSettings().IsMaintenanceApplication()) + { + for (const CAppConfigFile& rconfig : m_vecSysConfigs) + { + if (!rconfig.IsLoaded() && !rconfig.SaveConfigFile()) + SDV_LOG_ERROR("Failed to save system configuration '", rconfig.ConfigPath(), "'."); + } + } +} + +sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8string& /*ssContent*/) +{ + // Check for allowance + switch (GetAppSettings().GetContextType()) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: + return sdv::core::EConfigProcessResult::failed; + default: + break; + } + if (GetAppControl().GetOperationState() != sdv::app::EAppOperationState::configuring) return sdv::core::EConfigProcessResult::failed; - // Reset the current baseline - ResetConfigBaseline(); + //// Reset the current baseline + //ResetConfigBaseline(); - toml_parser::CParser parser(ssContent); + //toml_parser::CParser parser(ssContent); - // Check for config file compatibility - auto ptrConfigVersion = parser.Root().Direct("Configuration.Version"); - if (!ptrConfigVersion) - { - SDV_LOG_ERROR("Configuration version must be present in the configuration file."); - return sdv::core::EConfigProcessResult::failed; - } - if (ptrConfigVersion->GetValue() != SDVFrameworkInterfaceVersion) - { - SDV_LOG_ERROR("Incompatible configuration file version."); - return sdv::core::EConfigProcessResult::failed; - } + //// Check for config file compatibility + //auto ptrConfigVersion = parser.Root().Direct("Configuration.Version"); + //if (!ptrConfigVersion) + //{ + // SDV_LOG_ERROR("Configuration version must be present in the configuration file."); + // return sdv::core::EConfigProcessResult::failed; + //} + //if (ptrConfigVersion->GetValue() != SDVFrameworkInterfaceVersion) + //{ + // SDV_LOG_ERROR("Incompatible configuration file version."); + // return sdv::core::EConfigProcessResult::failed; + //} - // If this is not a main, isolated or maintenance application, load all modules in the component and module sections. - std::map mapModules; - size_t nLoadable = 0; - size_t nNotLoadable = 0; - if (!GetAppControl().IsMainApplication() && !GetAppControl().IsIsolatedApplication() && - !GetAppControl().IsMaintenanceApplication()) - { - // Load all modules in the component section - auto ptrComponents = parser.Root().Direct("Component"); - if (ptrComponents && ptrComponents->Cast()) - { - for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast()->GetCount(); uiIndex++) - { - auto ptrComponent = ptrComponents->Cast()->Get(uiIndex); - if (ptrComponent) - { - // Get the information from the component. - std::filesystem::path pathModule; - auto ptrModule = ptrComponent->Direct("Path"); - if (ptrModule) pathModule = static_cast(ptrModule->GetValue()); + //// If this is not a main, isolated or maintenance application, load all modules in the component and module sections. + //std::map mapModules; + //size_t nLoadable = 0; + //size_t nNotLoadable = 0; + //if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() && + // !GetAppSettings().IsMaintenanceApplication()) + //{ + // // Load all modules in the component section + // auto ptrComponents = parser.Root().Direct("Component"); + // if (ptrComponents && ptrComponents->Cast()) + // { + // for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast()->GetCount(); uiIndex++) + // { + // auto ptrComponent = ptrComponents->Cast()->Get(uiIndex); + // if (ptrComponent) + // { + // // Get the information from the component. + // std::filesystem::path pathModule; + // auto ptrModule = ptrComponent->Direct("Path"); + // if (ptrModule) pathModule = static_cast(ptrModule->GetValue()); - // If there is no path, this is allowed... skip this module - if (pathModule.empty()) continue; + // // If there is no path, this is allowed... skip this module + // if (pathModule.empty()) continue; - // Load the module. - sdv::core::TModuleID tModuleID = GetModuleControl().Load(pathModule.generic_u8string()); - if (tModuleID) - { - mapModules[pathModule] = tModuleID; - nLoadable++; - } - else - { - SDV_LOG_ERROR("Failed to load module: ", pathModule); - nNotLoadable++; - continue; - } - } - } - } + // // Load the module. + // sdv::core::TModuleID tModuleID = GetModuleControl().Load(pathModule.generic_u8string()); + // if (tModuleID) + // { + // mapModules[pathModule] = tModuleID; + // nLoadable++; + // } + // else + // { + // SDV_LOG_ERROR("Failed to load module: ", pathModule); + // nNotLoadable++; + // continue; + // } + // } + // } + // } - // Load all modules from the module section. - auto ptrModules = parser.Root().Direct("Module"); - if (ptrModules && ptrModules->Cast()) - { - for (uint32_t uiIndex = 0; uiIndex < ptrModules->Cast()->GetCount(); uiIndex++) - { - auto ptrModule = ptrModules->Cast()->Get(uiIndex); - if (ptrModule) - { - // Get the information from the component. - std::filesystem::path pathModule; - auto ptrModulePath = ptrModule->Direct("Path"); - if (ptrModulePath) pathModule = static_cast(ptrModulePath->GetValue()); + // // Load all modules from the module section. + // auto ptrModules = parser.Root().Direct("Module"); + // if (ptrModules && ptrModules->Cast()) + // { + // for (uint32_t uiIndex = 0; uiIndex < ptrModules->Cast()->GetCount(); uiIndex++) + // { + // auto ptrModule = ptrModules->Cast()->Get(uiIndex); + // if (ptrModule) + // { + // // Get the information from the component. + // std::filesystem::path pathModule; + // auto ptrModulePath = ptrModule->Direct("Path"); + // if (ptrModulePath) pathModule = static_cast(ptrModulePath->GetValue()); - if (pathModule.empty()) - { - SDV_LOG_ERROR("Missing module path for component configuration."); - nNotLoadable++; - continue; - } + // if (pathModule.empty()) + // { + // SDV_LOG_ERROR("Missing module path for component configuration."); + // nNotLoadable++; + // continue; + // } - // Load the module. - sdv::core::TModuleID tModuleID = GetModuleControl().Load(pathModule.generic_u8string()); - if (tModuleID) - { - mapModules[pathModule] = tModuleID; - nLoadable++; - } - else - { - SDV_LOG_ERROR("Failed to load module: ", pathModule); - nNotLoadable++; - continue; - } - } - } - } - } + // // Load the module. + // sdv::core::TModuleID tModuleID = GetModuleControl().Load(pathModule.generic_u8string()); + // if (tModuleID) + // { + // mapModules[pathModule] = tModuleID; + // nLoadable++; + // } + // else + // { + // SDV_LOG_ERROR("Failed to load module: ", pathModule); + // nNotLoadable++; + // continue; + // } + // } + // } + // } + //} - // Start all components from the component section - std::filesystem::path pathLastModule; - auto ptrComponents = parser.Root().Direct("Component"); - if (ptrComponents && ptrComponents->Cast()) - { - for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast()->GetCount(); uiIndex++) - { - auto ptrComponent = ptrComponents->Cast()->Get(uiIndex); - if (ptrComponent) - { - // Get the information from the component. - std::filesystem::path pathModule; - auto ptrModule = ptrComponent->Direct("Path"); - if (ptrModule) pathModule = static_cast(ptrModule->GetValue()); - std::string ssClass; - auto ptrClass = ptrComponent->Direct("Class"); - if (ptrClass) ssClass = static_cast(ptrClass->GetValue()); - std::string ssName; - auto ptrName = ptrComponent->Direct("Name"); - if (ptrName) ssName = static_cast(ptrName->GetValue()); + //// Start all components from the component section + //std::filesystem::path pathLastModule; + //auto ptrComponents = parser.Root().Direct("Component"); + //if (ptrComponents && ptrComponents->Cast()) + //{ + // for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast()->GetCount(); uiIndex++) + // { + // auto ptrComponent = ptrComponents->Cast()->Get(uiIndex); + // if (ptrComponent) + // { + // // Get the information from the component. + // std::filesystem::path pathModule; + // auto ptrModule = ptrComponent->Direct("Path"); + // if (ptrModule) pathModule = static_cast(ptrModule->GetValue()); + // std::string ssClass; + // auto ptrClass = ptrComponent->Direct("Class"); + // if (ptrClass) ssClass = static_cast(ptrClass->GetValue()); + // std::string ssName; + // auto ptrName = ptrComponent->Direct("Name"); + // if (ptrName) ssName = static_cast(ptrName->GetValue()); - // If there is a path, store it. If there is none, take the last stored - if (!pathModule.empty()) - pathLastModule = pathModule; - else - pathModule = pathLastModule; + // // If there is a path, store it. If there is none, take the last stored + // if (!pathModule.empty()) + // pathLastModule = pathModule; + // else + // pathModule = pathLastModule; - if (pathModule.empty()) - { - SDV_LOG_ERROR("Missing module path for component configuration."); - nNotLoadable++; - continue; - } - if (ssClass.empty()) - { - SDV_LOG_ERROR("Missing component class name in the configuration."); - nNotLoadable++; - continue; - } + // if (pathModule.empty()) + // { + // SDV_LOG_ERROR("Missing module path for component configuration."); + // nNotLoadable++; + // continue; + // } + // if (ssClass.empty()) + // { + // SDV_LOG_ERROR("Missing component class name in the configuration."); + // nNotLoadable++; + // continue; + // } - // In case of a main, isolated or maintenance application, ignore the module. In all other cases, the module was - // loaded; get the module ID. - sdv::core::TObjectID tObjectID = 0; - if (!GetAppControl().IsMainApplication() && !GetAppControl().IsIsolatedApplication() && - !GetAppControl().IsMaintenanceApplication()) - { - auto itModule = mapModules.find(pathModule); - if (itModule == mapModules.end()) continue; // Module was not loaded before... - tObjectID = GetRepository().CreateObjectFromModule(itModule->second, ssClass, ssName, ptrComponent->GenerateTOML()); - } - else - tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->GenerateTOML()); + // // In case of a main, isolated or maintenance application, ignore the module. In all other cases, the module was + // // loaded; get the module ID. + // sdv::core::TObjectID tObjectID = 0; + // if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() && + // !GetAppSettings().IsMaintenanceApplication()) + // { + // auto itModule = mapModules.find(pathModule); + // if (itModule == mapModules.end()) continue; // Module was not loaded before... + // tObjectID = GetRepository().CreateObjectFromModule(itModule->second, ssClass, ssName, ptrComponent->GenerateTOML()); + // } + // else + // tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->GenerateTOML()); - if (!tObjectID) - { - SDV_LOG_ERROR("Failed to load component: ", ssClass); - nNotLoadable++; - continue; - } - else - nLoadable++; - } - } - } - if (!nNotLoadable) - return sdv::core::EConfigProcessResult::successful; - if (!nLoadable) - return sdv::core::EConfigProcessResult::failed; + // if (!tObjectID) + // { + // SDV_LOG_ERROR("Failed to load component: ", ssClass); + // nNotLoadable++; + // continue; + // } + // else + // nLoadable++; + // } + // } + //} + //if (!nNotLoadable) + // return sdv::core::EConfigProcessResult::successful; + //if (!nLoadable) + // return sdv::core::EConfigProcessResult::failed; return sdv::core::EConfigProcessResult::partially_successful; } sdv::core::EConfigProcessResult CAppConfig::LoadConfig(/*in*/ const sdv::u8string& ssConfigPath) { - if (GetAppControl().GetOperationState() != sdv::app::EAppOperationState::configuring) - return sdv::core::EConfigProcessResult::failed; - if (ssConfigPath.empty()) - return sdv::core::EConfigProcessResult::failed; + // Even though a server based application is not allowed to call this function, it is called by the startup function. The + // prevention of access to this function is done through the interface not being available. + bool bServerApp = false; + switch (GetAppSettings().GetContextType()) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: + bServerApp = true; + default: + break; + } std::filesystem::path pathConfig; - if (GetAppControl().IsMainApplication()) - { - pathConfig = GetAppControl().GetInstallDir() / static_cast(ssConfigPath); - if (!std::filesystem::exists(pathConfig) || !std::filesystem::is_regular_file(pathConfig)) - pathConfig.clear(); - } - else + if (!bServerApp) { + // Close the configuration before + CloseConfig(); + + // Add the current path to the directory search path list. AddCurrentPath(); - try + // Determine the absolute path. + pathConfig = ComposeConfigPathStandaloneApp(static_cast(ssConfigPath)); + if (pathConfig.empty()) { - // Get the search paths if the module path is relative - std::list lstSearchPaths; - std::filesystem::path pathSupplied(static_cast(ssConfigPath)); - if (pathSupplied.is_relative()) - lstSearchPaths = m_lstSearchPaths; - - // Add an empty path to allow the OS to search when our own search paths could not find the module. - lstSearchPaths.push_back(std::filesystem::path()); - - // Run through the search paths and try to find the config file. - for (const std::filesystem::path& rpathDirectory : lstSearchPaths) - { - // Compose the path - std::filesystem::path pathConfigTemp; - if (rpathDirectory.is_absolute()) - pathConfigTemp = (rpathDirectory / pathSupplied).lexically_normal(); - else - { - if (rpathDirectory.empty()) - pathConfigTemp = pathSupplied.lexically_normal(); - else - pathConfigTemp = (GetExecDirectory() / rpathDirectory / pathSupplied).lexically_normal(); - } - if (std::filesystem::exists(pathConfigTemp)) - { - pathConfig = pathConfigTemp; - m_pathLastSearchDir = rpathDirectory; - break; - } - } - } - catch (const std::exception& re) - { - SDV_LOG_ERROR("Supplied path to config load is not valid \"", ssConfigPath, "\": ", re.what()); + SDV_LOG_ERROR("Configuration file was not found \"", ssConfigPath, "\""); return sdv::core::EConfigProcessResult::failed; } } - if (pathConfig.empty()) + else + pathConfig = std::filesystem::u8path(static_cast(ssConfigPath)); + + CAppConfigFile config(pathConfig); + if (!config.IsLoaded()) { - SDV_LOG_ERROR("Configuration file was not found \"", ssConfigPath, "\""); + SDV_LOG_ERROR("Failed to load user configuration '", pathConfig.generic_u8string(), "'."); return sdv::core::EConfigProcessResult::failed; } - std::ifstream fstream(pathConfig); - if (!fstream.is_open()) return sdv::core::EConfigProcessResult::failed; - std::string ssContent((std::istreambuf_iterator(fstream)), std::istreambuf_iterator()); - return ProcessConfig(ssContent); + // Load the configuration by the repository + sdv::core::EConfigProcessResult eResult = GetRepository().StartFromConfig(config, true); + // cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning. + // cppcheck-suppress knownConditionTrueFalse + if (eResult == sdv::core::EConfigProcessResult::failed) + { + SDV_LOG_ERROR("Failed to start the system configuration '", pathConfig.generic_u8string(), "'."); + return sdv::core::EConfigProcessResult::failed; + } + + // Loading was successful. + m_configUserConfig = std::move(config); + + return eResult; } bool CAppConfig::SaveConfig(/*in*/ const sdv::u8string& ssConfigPath) const { - std::filesystem::path pathConfig = static_cast(ssConfigPath); - if (pathConfig.is_relative()) + // Check for allowance + switch (GetAppSettings().GetContextType()) { - if (GetAppControl().IsMainApplication()) - pathConfig = GetAppControl().GetInstallDir() / pathConfig; - else - { - if (m_pathLastSearchDir.empty()) - pathConfig = GetExecDirectory() / pathConfig; - else - pathConfig = m_pathLastSearchDir / pathConfig; - } - } - - std::ofstream fstream(pathConfig, std::ios::out | std::ios::trunc); - if (!fstream.is_open()) - { - SDV_LOG_ERROR("Cannot open config file \"", ssConfigPath, "\""); + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: return false; + default: + break; } - // Write header - fstream << "[Configuration]" << std::endl; - fstream << "Version = " << SDVFrameworkInterfaceVersion << " # Configuration file version." << std::endl; + // TODO... synchronize the configuration from the repository to the configuration file. + return m_configUserConfig.SaveConfigFile(static_cast(ssConfigPath)); +} - // Write all objects and modules - fstream << GetRepository().SaveConfig(); +sdv::u8string CAppConfig::GenerateConfigString() const +{ + // Check for allowance + switch (GetAppSettings().GetContextType()) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: + return {}; + default: + break; + } - fstream.close(); + // Get the configuration string + std::string ssConfig; + bool bChanged = false; + if (!m_configUserConfig.UpdateConfigString(ssConfig, bChanged)) + return {}; + return ssConfig; +} - return true; +void CAppConfig::CloseConfig() +{ + // Check for allowance + switch (GetAppSettings().GetContextType()) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: + return; + default: + break; + } + + // TODO... remove all components from baseline } bool CAppConfig::AddConfigSearchDir(/*in*/ const sdv::u8string& ssDir) { - if (GetAppControl().GetOperationState() != sdv::app::EAppOperationState::configuring) return false; + // Check for allowance + switch (GetAppSettings().GetContextType()) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: + return false; + default: + break; + } + + if (GetAppControl().GetOperationState() != sdv::app::EAppOperationState::configuring) + return false; // Add initial paths if not done so already AddCurrentPath(); @@ -364,6 +578,17 @@ bool CAppConfig::AddConfigSearchDir(/*in*/ const sdv::u8string& ssDir) void CAppConfig::ResetConfigBaseline() { + //// Check for allowance + //switch (GetAppSettings().GetContextType()) + //{ + //case sdv::app::EAppContext::main: + //case sdv::app::EAppContext::isolated: + //case sdv::app::EAppContext::maintenance: + // return; + //default: + // break; + //} + GetRepository().ResetConfigBaseline(); } @@ -373,241 +598,241 @@ void CAppConfig::ResetConfigBaseline() #pragma warning(disable : 6001) #endif -bool CAppConfig::Install(/*in*/ const sdv::u8string& ssInstallName, /*in*/ const sdv::sequence& seqFiles) +bool CAppConfig::Install(/*in*/ const sdv::u8string& /*ssInstallName*/, /*in*/ const sdv::sequence& /*seqFiles*/) { - // Installations can only be done in the main application. - if (!GetAppControl().IsMainApplication()) return false; - - std::unique_lock lock(m_mtxInstallations); - - // Check whether the installation already exists. If so, cancel the installation. - if (m_mapInstallations.find(ssInstallName) != m_mapInstallations.end()) - { - throw sdv::installation::XDuplicateInstall(); - } - - // Rollback class - automatically reverses the loading. - class CRollback - { - public: - ~CRollback() - { - bool bSuccess = true; - for (sdv::core::TModuleID tModuleID : m_vecLoadedModules) - { - try - { - bSuccess &= GetModuleControl().ContextUnload(tModuleID, true); - } - catch (const sdv::XSysExcept&) - { - } - catch (const std::exception&) - { - } - } - if (!bSuccess) - { - SDV_LOG_ERROR("Failed to rollback \"", m_pathInstallRoot, "\"."); - return; - } - - if (!m_pathInstallRoot.empty()) - { - try - { - std::filesystem::remove_all(m_pathInstallRoot); - } - catch (const std::filesystem::filesystem_error& rexcept) - { - SDV_LOG_ERROR("Failed to rollback \"", m_pathInstallRoot, "\": ", rexcept.what()); - } - } - } - void Commit() - { - // Do not roll back. - m_pathInstallRoot.clear(); - m_vecLoadedModules.clear(); - } - void SetInstallPath(const std::filesystem::path& rPathInstallDir) { m_pathInstallRoot = rPathInstallDir; } - void AddLoadedModule(sdv::core::TModuleID tModuleID) { m_vecLoadedModules.push_back(tModuleID); } - private: - std::filesystem::path m_pathInstallRoot; ///< Installation root directory - std::vector m_vecLoadedModules; ///< loaded modules - } rollback; - - std::filesystem::path pathInstallation = GetAppControl().GetInstallDir() / static_cast(ssInstallName); - std::vector vecComponents; - try - { - // A sub-directory with the name must not already exist, if so, delete it. - if (std::filesystem::exists(pathInstallation)) - std::filesystem::remove_all(pathInstallation); - - // Create a directory for the installation - std::filesystem::create_directories(pathInstallation); - rollback.SetInstallPath(pathInstallation); - - // Add each file - for (const sdv::installation::SFileDesc& rsFileDesc : seqFiles) - { - // File path - std::filesystem::path pathFileDir = pathInstallation; - //if (!rsFileDesc.ssRelativeDir.empty()) - //{ - // std::filesystem::path pathTemp = static_cast(rsFileDesc.ssRelativeDir); - // if (pathTemp.is_absolute() || !pathTemp.is_relative()) - // throw sdv::installation::XIncorrectPath(); - // pathFileDir /= pathTemp; - // if (!std::filesystem::exists(pathFileDir)) - // std::filesystem::create_directories(pathFileDir); - //} - std::filesystem::path pathFile = pathFileDir; - if (std::filesystem::exists(pathFile)) - throw sdv::installation::XIncorrectPath(); - - // TODO EVE - //// Check the file CRC - //sdv::crcCRC32C crc; - //uint32_t uiCRC = crc.calc_checksum(rsFileDesc.ptrContent.get(), rsFileDesc.ptrContent.size()); - //if (uiCRC != rsFileDesc.uiCRC32c) - // throw sdv::installation::XIncorrectCRC(); - - // Create the file - std::ofstream fstream(pathFile, std::ios_base::out | std::ios_base::binary); - if (!fstream.is_open()) throw sdv::installation::XIncorrectPath(); - if (rsFileDesc.ptrContent.size()) - fstream.write(reinterpret_cast(rsFileDesc.ptrContent.get()), rsFileDesc.ptrContent.size()); - fstream.close(); - - // Set file times - // Note: use dedicated OS functions. C++11 filesystem library doesn't define a proper way to convert between UNIX time - // and the time defined by the C++11 functions. This deficit has been solved in C++20. - -#ifdef _WIN32 - // Windows uses file access for this. - HANDLE hFile = CreateFile(pathFile.native().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == nullptr || hFile == INVALID_HANDLE_VALUE) - { - SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\""); - throw sdv::installation::XIncorrectPath(); - } - - // Set the filetime on the file. - // The filetime starts counting from 1st of January 1601. The resolution is 100ns. - // The UNIX time starts counting from 1st of January 1970. The resolution is 1s. - // Both times ignore leap seconds. - // Thus the UNIX time is running 11644473600 seconds behind the filetime and is a factor 10000000 larger. - ULARGE_INTEGER sCreateTime{}, sChangeTime{}; - sCreateTime.QuadPart = (rsFileDesc.uiCreationDate + 11644473600ull) * 10000000ull; - FILETIME ftCreateTime{ sCreateTime.LowPart, sChangeTime.HighPart }; - sChangeTime.QuadPart = (rsFileDesc.uiChangeDate + 11644473600ull) * 10000000ull; - FILETIME ftChangeTime{ sChangeTime.LowPart, sChangeTime.HighPart }; - BOOL bRes = SetFileTime(hFile, &ftCreateTime, nullptr, &ftChangeTime); - - // Close our handle. - CloseHandle(hFile); - - if (!bRes) - { - SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\""); - throw sdv::installation::XIncorrectPath(); - } -#elif defined __unix__ - // Note: POSIX doesn't define the creation time. - utimbuf sTimes{}; - sTimes.actime = std::time(0); - sTimes.modtime = static_cast(rsFileDesc.uiChangeDate); - if (utime(pathFile.native().c_str(), &sTimes)) - { - SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\""); - throw sdv::installation::XIncorrectPath(); - } -#endif - - // Set attributes - if (rsFileDesc.bAttrExecutable) - std::filesystem::permissions(pathFile, std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | - std::filesystem::perms::others_exec, std::filesystem::perm_options::add); - if (rsFileDesc.bAttrReadonly) - std::filesystem::permissions(pathFile, std::filesystem::perms::owner_write | std::filesystem::perms::group_write | - std::filesystem::perms::others_write, std::filesystem::perm_options::remove); - - // Component paths are stored - //if (rsFileDesc.bAttrComponent) vecComponents.push_back(pathFile); - } - } - catch (const std::filesystem::filesystem_error& rexcept) - { - SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\": ", rexcept.what()); - throw sdv::installation::XIncorrectPath(); - } - - // TODO: Restriction. Only complex services will be started dynamically and will be added to the configuration. Utilities will - // also be installed but will not be started. Devices and basic services will not be available this way. - - // TODO: - // For each component, get the manifest using the manifest helper utility. - // Note: to get the manifest, the component module needs to be loaded and the GetManifest exported function is called. Loading - // a component module causes part of the component to start already and dependencies to be resolved. If this malfunctions, the - // component could crash or do other malicious things. OR if on purpose, an application or complex service component, that - // normally would run in an isolated environment, gets access to the memory and everything else within the process. If this - // module is not isolated from the core, it could access the core and do nasty things. THEREFORE, the manifest helper utility is - // started as a component in its own isolated environment and then loading the component. No component code is executed within - // the core process. - sdv::TObjectPtr ptrManifestUtil = sdv::core::CreateUtility("ManifestHelperUtility"); - sdv::helper::IModuleManifestHelper* pManifestHelper = ptrManifestUtil.GetInterface(); - if (!pManifestHelper) - { - SDV_LOG_ERROR("Manifest helper utility cannot be loaded."); - throw sdv::installation::XComponentNotLoadable(); - } - - // Get the manifest of all components - std::map mapComponentManifests; - for (const std::filesystem::path& rpathComponent : vecComponents) - { - sdv::u8string ssManifest = pManifestHelper->ReadModuleManifest(rpathComponent.generic_u8string()); - if (!ssManifest.empty()) - mapComponentManifests[rpathComponent] = ssManifest; - } - - // Check whether there ary any manifests - if (mapComponentManifests.empty()) - { - SDV_LOG_ERROR("No component in installation", ssInstallName, "."); - throw sdv::installation::XNoManifest(); - } - - // Load the modules - bool bSuccess = true; - for (const auto& rvtModule : mapComponentManifests) - { - sdv::core::TModuleID tModuleID = GetModuleControl().ContextLoad(rvtModule.first, rvtModule.second); - if (!tModuleID) - { - bSuccess = false; - break; - } - - // TODO: What about the dependent modules that were started as well. They are not rolled back... - rollback.AddLoadedModule(tModuleID); - } - if (!bSuccess) - { - SDV_LOG_ERROR("Manifest helper utility cannot be loaded."); - throw sdv::installation::XComponentNotLoadable(); - } - - // Add the installation to the installation map - //SInstallation sInstallation{ seqFiles }; - //for (sdv::core::) - //m_mapInstallations[ssInstallName] = SInstallation{ seqFiles }; - - // Commit the changes - prevent rollback - rollback.Commit(); +// // Installations can only be done in the main application. +// if (!GetAppSettings().IsMainApplication()) return false; +// +// std::unique_lock lock(m_mtxInstallations); +// +// // Check whether the installation already exists. If so, cancel the installation. +// if (m_mapInstallations.find(ssInstallName) != m_mapInstallations.end()) +// { +// throw sdv::installation::XDuplicateInstall(); +// } +// +// // Rollback class - automatically reverses the loading. +// class CRollback +// { +// public: +// ~CRollback() +// { +// bool bSuccess = true; +// for (sdv::core::TModuleID tModuleID : m_vecLoadedModules) +// { +// try +// { +// bSuccess &= GetModuleControl().ContextUnload(tModuleID, true); +// } +// catch (const sdv::XSysExcept&) +// { +// } +// catch (const std::exception&) +// { +// } +// } +// if (!bSuccess) +// { +// SDV_LOG_ERROR("Failed to rollback \"", m_pathInstallRoot, "\"."); +// return; +// } +// +// if (!m_pathInstallRoot.empty()) +// { +// try +// { +// std::filesystem::remove_all(m_pathInstallRoot); +// } +// catch (const std::filesystem::filesystem_error& rexcept) +// { +// SDV_LOG_ERROR("Failed to rollback \"", m_pathInstallRoot, "\": ", rexcept.what()); +// } +// } +// } +// void Commit() +// { +// // Do not roll back. +// m_pathInstallRoot.clear(); +// m_vecLoadedModules.clear(); +// } +// void SetInstallPath(const std::filesystem::path& rPathInstallDir) { m_pathInstallRoot = rPathInstallDir; } +// void AddLoadedModule(sdv::core::TModuleID tModuleID) { m_vecLoadedModules.push_back(tModuleID); } +// private: +// std::filesystem::path m_pathInstallRoot; ///< Installation root directory +// std::vector m_vecLoadedModules; ///< loaded modules +// } rollback; +// +// std::filesystem::path pathInstallation = GetAppSettings().GetInstallDir() / static_cast(ssInstallName); +// std::vector vecComponents; +// try +// { +// // A sub-directory with the name must not already exist, if so, delete it. +// if (std::filesystem::exists(pathInstallation)) +// std::filesystem::remove_all(pathInstallation); +// +// // Create a directory for the installation +// std::filesystem::create_directories(pathInstallation); +// rollback.SetInstallPath(pathInstallation); +// +// // Add each file +// for (const sdv::installation::SFileDesc& rsFileDesc : seqFiles) +// { +// // File path +// std::filesystem::path pathFileDir = pathInstallation; +// //if (!rsFileDesc.ssRelativeDir.empty()) +// //{ +// // std::filesystem::path pathTemp = static_cast(rsFileDesc.ssRelativeDir); +// // if (pathTemp.is_absolute() || !pathTemp.is_relative()) +// // throw sdv::installation::XIncorrectPath(); +// // pathFileDir /= pathTemp; +// // if (!std::filesystem::exists(pathFileDir)) +// // std::filesystem::create_directories(pathFileDir); +// //} +// std::filesystem::path pathFile = pathFileDir; +// if (std::filesystem::exists(pathFile)) +// throw sdv::installation::XIncorrectPath(); +// +// // TODO EVE +// //// Check the file CRC +// //sdv::crcCRC32C crc; +// //uint32_t uiCRC = crc.calc_checksum(rsFileDesc.ptrContent.get(), rsFileDesc.ptrContent.size()); +// //if (uiCRC != rsFileDesc.uiCRC32c) +// // throw sdv::installation::XIncorrectCRC(); +// +// // Create the file +// std::ofstream fstream(pathFile, std::ios_base::out | std::ios_base::binary); +// if (!fstream.is_open()) throw sdv::installation::XIncorrectPath(); +// if (rsFileDesc.ptrContent.size()) +// fstream.write(reinterpret_cast(rsFileDesc.ptrContent.get()), rsFileDesc.ptrContent.size()); +// fstream.close(); +// +// // Set file times +// // Note: use dedicated OS functions. C++11 filesystem library doesn't define a proper way to convert between UNIX time +// // and the time defined by the C++11 functions. This deficit has been solved in C++20. +// +//#ifdef _WIN32 +// // Windows uses file access for this. +// HANDLE hFile = CreateFile(pathFile.native().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, +// NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +// if (hFile == nullptr || hFile == INVALID_HANDLE_VALUE) +// { +// SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\""); +// throw sdv::installation::XIncorrectPath(); +// } +// +// // Set the filetime on the file. +// // The filetime starts counting from 1st of January 1601. The resolution is 100ns. +// // The UNIX time starts counting from 1st of January 1970. The resolution is 1s. +// // Both times ignore leap seconds. +// // Thus the UNIX time is running 11644473600 seconds behind the filetime and is a factor 10000000 larger. +// ULARGE_INTEGER sCreateTime{}, sChangeTime{}; +// sCreateTime.QuadPart = (rsFileDesc.uiCreationDate + 11644473600ull) * 10000000ull; +// FILETIME ftCreateTime{ sCreateTime.LowPart, sChangeTime.HighPart }; +// sChangeTime.QuadPart = (rsFileDesc.uiChangeDate + 11644473600ull) * 10000000ull; +// FILETIME ftChangeTime{ sChangeTime.LowPart, sChangeTime.HighPart }; +// BOOL bRes = SetFileTime(hFile, &ftCreateTime, nullptr, &ftChangeTime); +// +// // Close our handle. +// CloseHandle(hFile); +// +// if (!bRes) +// { +// SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\""); +// throw sdv::installation::XIncorrectPath(); +// } +//#elif defined __unix__ +// // Note: POSIX doesn't define the creation time. +// utimbuf sTimes{}; +// sTimes.actime = std::time(0); +// sTimes.modtime = static_cast(rsFileDesc.uiChangeDate); +// if (utime(pathFile.native().c_str(), &sTimes)) +// { +// SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\""); +// throw sdv::installation::XIncorrectPath(); +// } +//#endif +// +// // Set attributes +// if (rsFileDesc.bAttrExecutable) +// std::filesystem::permissions(pathFile, std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | +// std::filesystem::perms::others_exec, std::filesystem::perm_options::add); +// if (rsFileDesc.bAttrReadonly) +// std::filesystem::permissions(pathFile, std::filesystem::perms::owner_write | std::filesystem::perms::group_write | +// std::filesystem::perms::others_write, std::filesystem::perm_options::remove); +// +// // Component paths are stored +// //if (rsFileDesc.bAttrComponent) vecComponents.push_back(pathFile); +// } +// } +// catch (const std::filesystem::filesystem_error& rexcept) +// { +// SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\": ", rexcept.what()); +// throw sdv::installation::XIncorrectPath(); +// } +// +// // TODO: Restriction. Only complex services will be started dynamically and will be added to the configuration. Utilities will +// // also be installed but will not be started. Devices and basic services will not be available this way. +// +// // TODO: +// // For each component, get the manifest using the manifest helper utility. +// // Note: to get the manifest, the component module needs to be loaded and the GetManifest exported function is called. Loading +// // a component module causes part of the component to start already and dependencies to be resolved. If this malfunctions, the +// // component could crash or do other malicious things. OR if on purpose, an application or complex service component, that +// // normally would run in an isolated environment, gets access to the memory and everything else within the process. If this +// // module is not isolated from the core, it could access the core and do nasty things. THEREFORE, the manifest helper utility is +// // started as a component in its own isolated environment and then loading the component. No component code is executed within +// // the core process. +// sdv::TObjectPtr ptrManifestUtil = sdv::core::CreateUtility("ManifestHelperUtility"); +// sdv::helper::IModuleManifestHelper* pManifestHelper = ptrManifestUtil.GetInterface(); +// if (!pManifestHelper) +// { +// SDV_LOG_ERROR("Manifest helper utility cannot be loaded."); +// throw sdv::installation::XComponentNotLoadable(); +// } +// +// // Get the manifest of all components +// std::map mapComponentManifests; +// for (const std::filesystem::path& rpathComponent : vecComponents) +// { +// sdv::u8string ssManifest = pManifestHelper->ReadModuleManifest(rpathComponent.generic_u8string()); +// if (!ssManifest.empty()) +// mapComponentManifests[rpathComponent] = ssManifest; +// } +// +// // Check whether there ary any manifests +// if (mapComponentManifests.empty()) +// { +// SDV_LOG_ERROR("No component in installation", ssInstallName, "."); +// throw sdv::installation::XNoManifest(); +// } +// +// // Load the modules +// bool bSuccess = true; +// for (const auto& rvtModule : mapComponentManifests) +// { +// sdv::core::TModuleID tModuleID = GetModuleControl().ContextLoad(rvtModule.first, rvtModule.second); +// if (!tModuleID) +// { +// bSuccess = false; +// break; +// } +// +// // TODO: What about the dependent modules that were started as well. They are not rolled back... +// rollback.AddLoadedModule(tModuleID); +// } +// if (!bSuccess) +// { +// SDV_LOG_ERROR("Manifest helper utility cannot be loaded."); +// throw sdv::installation::XComponentNotLoadable(); +// } +// +// // Add the installation to the installation map +// //SInstallation sInstallation{ seqFiles }; +// //for (sdv::core::) +// //m_mapInstallations[ssInstallName] = SInstallation{ seqFiles }; +// +// // Commit the changes - prevent rollback +// rollback.Commit(); return true; } @@ -631,7 +856,7 @@ bool CAppConfig::Update(/*in*/ const sdv::u8string& ssInstallName, /*in*/ const bool CAppConfig::Uninstall(/*in*/ const sdv::u8string& /*ssInstallName*/) { // Installations can only be done in the main application. - if (!GetAppControl().IsMainApplication()) return false; + if (!GetAppSettings().IsMainApplication()) return false; return false; @@ -649,6 +874,18 @@ sdv::sequence CAppConfig::GetInstallationFiles(/*i std::filesystem::path CAppConfig::FindInstalledModule(const std::filesystem::path& rpathRelModule) { + // Check for allowance + bool bServerApp = false; + switch (GetAppSettings().GetContextType()) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: + bServerApp = true; + default: + break; + } + // Search for the module in the following order: // - Core manifest // - Exe manifest @@ -657,16 +894,32 @@ std::filesystem::path CAppConfig::FindInstalledModule(const std::filesystem::pat std::filesystem::path pathModule = m_manifestCore.FindModule(rpathRelModule); if (pathModule.empty()) pathModule = m_manifestExe.FindModule(rpathRelModule); - for (const CInstallManifest& rmanifest : m_vecUserManifests) + if (bServerApp) { - if (!pathModule.empty()) break; - pathModule = rmanifest.FindModule(rpathRelModule); + for (const CInstallManifest& rmanifest : m_vecUserManifests) + { + if (!pathModule.empty()) + break; + pathModule = rmanifest.FindModule(rpathRelModule); + } } return pathModule; } std::string CAppConfig::FindInstalledModuleManifest(const std::filesystem::path& rpathRelModule) { + // Check for allowance + bool bServerApp = false; + switch (GetAppSettings().GetContextType()) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: + bServerApp = true; + default: + break; + } + // Search for the module in the following order: // - Core manifest // - Exe manifest @@ -675,18 +928,31 @@ std::string CAppConfig::FindInstalledModuleManifest(const std::filesystem::path& std::string ssManifest = m_manifestCore.FindModuleManifest(rpathRelModule); if (ssManifest.empty()) ssManifest = m_manifestExe.FindModuleManifest(rpathRelModule); - for (const CInstallManifest& rmanifest : m_vecUserManifests) + if (bServerApp) { - if (!ssManifest.empty()) break; - ssManifest = rmanifest.FindModuleManifest(rpathRelModule); + for (const CInstallManifest& rmanifest : m_vecUserManifests) + { + if (!ssManifest.empty()) + break; + ssManifest = rmanifest.FindModuleManifest(rpathRelModule); + } } return ssManifest; } -std::optional CAppConfig::FindInstalledComponent(const std::string& rssClass) const +std::optional CAppConfig::FindInstalledComponent(const std::string& rssClass) const { - // For main and isolated applications, check whether the module is in one of the installation manifests. - if (!GetAppControl().IsMainApplication() && !GetAppControl().IsIsolatedApplication()) return {}; + // Check for allowance + bool bServerApp = false; + switch (GetAppSettings().GetContextType()) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::maintenance: + bServerApp = true; + default: + break; + } // Search for the component in the following order: // - Core manifest @@ -694,14 +960,81 @@ std::optional CAppConfig::FindInstalledComponent(c // - User manifests auto optManifest = m_manifestCore.FindComponentByClass(rssClass); if (!optManifest) optManifest = m_manifestExe.FindComponentByClass(rssClass); - for (const CInstallManifest& rmanifest : m_vecUserManifests) + if (bServerApp) { - if (optManifest) break; - optManifest = rmanifest.FindComponentByClass(rssClass); + for (const CInstallManifest& rmanifest : m_vecUserManifests) + { + if (optManifest) + break; + optManifest = rmanifest.FindComponentByClass(rssClass); + } } return optManifest; } +bool CAppConfig::RemoveFromConfig(const CInstallManifest& /*rManifest*/) +{ + //// Check for allowance + //bool bServerApp = false; + //switch (GetAppSettings().GetContextType()) + //{ + //case sdv::app::EAppContext::main: + //case sdv::app::EAppContext::isolated: + //case sdv::app::EAppContext::maintenance: + // bServerApp = true; + //default: + // break; + //} + + // auto fnRemoveFromConfig = [&](const std::filesystem::path& /*rpathConfig*/, bool /*bIsSysConfig*/) + //{ + // // Iterate through the manifest and search for the component in the configuration. + //}; + + // Get the list of system configurations. + //auto vecSysConfigs = GetAppSettings().GetSystemConfigPaths(); + return true; +} + +std::filesystem::path CAppConfig::ComposeConfigPathStandaloneApp(const std::filesystem::path& rpathConfigFile) const +{ + try + { + // Get the search paths if the module path is relative + std::list lstSearchPaths; + if (rpathConfigFile.is_relative()) + lstSearchPaths = m_lstSearchPaths; + + // Add an empty path to allow the OS to search when our own search paths could not find the module. + lstSearchPaths.push_back(std::filesystem::path()); + + // Run through the search paths and try to find the config file. + for (const std::filesystem::path& rpathDirectory : lstSearchPaths) + { + // Compose the path + std::filesystem::path pathConfigTemp; + if (rpathDirectory.is_absolute()) + pathConfigTemp = (rpathDirectory / rpathConfigFile).lexically_normal(); + else + { + if (rpathDirectory.empty()) + pathConfigTemp = rpathConfigFile.lexically_normal(); + else + pathConfigTemp = (GetExecDirectory() / rpathDirectory / rpathConfigFile).lexically_normal(); + } + if (std::filesystem::exists(pathConfigTemp)) + { + return pathConfigTemp; + } + } + } + catch (const std::exception& re) + { + SDV_LOG_ERROR("Supplied path to config load is not valid \"", rpathConfigFile, "\": ", re.what()); + } + return {}; +} + void CAppConfig::AddCurrentPath() { std::unique_lock lock(m_mtxSearchPaths); @@ -718,8 +1051,6 @@ void CAppConfig::AddCurrentPath() if (pathExeDir != pathCoreDir) m_lstSearchPaths.push_back(pathExeDir / "config/"); } -#ifndef DO_NOT_INCLUDE_IN_UNIT_TEST - CAppConfig& CAppConfigService::GetAppConfig() { return ::GetAppConfig(); @@ -727,13 +1058,11 @@ CAppConfig& CAppConfigService::GetAppConfig() bool CAppConfigService::EnableAppConfigAccess() { - return GetAppControl().IsStandaloneApplication() || - GetAppControl().IsEssentialApplication(); + return GetAppSettings().IsStandaloneApplication() || + GetAppSettings().IsEssentialApplication(); } bool CAppConfigService::EnableAppInstallAccess() { - return GetAppControl().IsMainApplication(); + return GetAppSettings().IsMainApplication(); } - -#endif // !defined DO_NOT_INCLUDE_IN_UNIT_TEST \ No newline at end of file diff --git a/sdv_services/core/app_config.h b/sdv_services/core/app_config.h index dd332ab..11b3575 100644 --- a/sdv_services/core/app_config.h +++ b/sdv_services/core/app_config.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 APP_CONFIG_H #define APP_CONFIG_H @@ -9,6 +22,7 @@ #include #include #include +#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 FindInstalledComponent(const std::string& rssClass) const; + std::optional 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 seqFiles; ///< Companion files }; - std::mutex m_mtxSearchPaths; ///< Access protection to directory list. - std::list 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 m_vecUserManifests; ///< Install manifests for user components (main and isolated apps). - std::map m_mapInstallations; ///< Installation map. + std::mutex m_mtxSearchPaths; ///< Access protection to directory list. + std::list 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 m_vecUserManifests; ///< Install manifests for user components (main and isolated apps). + std::map m_mapInstallations; ///< Installation map. + std::vector 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 \ No newline at end of file diff --git a/sdv_services/core/app_config_file.cpp b/sdv_services/core/app_config_file.cpp new file mode 100644 index 0000000..48d64bd --- /dev/null +++ b/sdv_services/core/app_config_file.cpp @@ -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 +#include "app_settings.h" +#include "../../global/ipc_named_mutex.h" +#include "toml_parser/parser_toml.h" +#include +#include "installation_manifest.h" +#include + +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(fstream)), std::istreambuf_iterator()); + 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(), 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(); + sComponent.ssClassName = tableComponent.GetDirect("Class").GetValue().get(); + 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(); + // 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 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(fstream)), std::istreambuf_iterator()); + 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(); + 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 ssClassName = tableClass.GetDirect("Class").GetValue().get(); + 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(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(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 ssClassName = tableComponent.GetDirect("Class").GetValue().get(); + std::string ssInstanceName = tableComponent.GetDirect("Name").GetValue().get(); + + 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(static_cast(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()); + } + 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::GetComponentList() const +{ + return m_vecComponentList; +} + +const std::vector& CAppConfigFile::GetClassList() const +{ + return m_vecClassList; +} + +const std::vector& 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 + { + 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()); + itComponent->ssParameterTOML = parserExisting.GenerateTOML(); + } + catch (const toml_parser::XTOMLParseException&) + { + ++nFailed; + continue; + } + } + ++nSucceeded; + } + } + return nFailed ? (nSucceeded ? EMergeResult::partly_successfull : EMergeResult::not_successful) : EMergeResult::successful; +} diff --git a/sdv_services/core/app_config_file.h b/sdv_services/core/app_config_file.h new file mode 100644 index 0000000..02e6329 --- /dev/null +++ b/sdv_services/core/app_config_file.h @@ -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 +#include +#include +#include + +/** + * @brief Parameter vector type holding parameter name and parameter value paires. + */ +using TParameterVector = std::vector>; + +/** + * @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& 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& 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& 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 m_vecComponentList; ///< Component list containing instantiation info for components. + std::vector m_vecClassList; ///< Class list containing default parameters for component classes. + std::vector m_vecModuleList; ///< Module list containing the module paths. +}; + + +#endif // !defined APP_CONFIG_FILE_H diff --git a/sdv_services/core/app_control.cpp b/sdv_services/core/app_control.cpp index 865011b..dfd32fe 100644 --- a/sdv_services/core/app_control.cpp +++ b/sdv_services/core/app_control.cpp @@ -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(); 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({ m_ssLoggerClass }), bForce); + GetRepository().DestroyAllObjects(std::vector({GetAppSettings().GetLoggerClass()}), bForce); // Unload all modules... this should destroy all running services, except the custom logger GetModuleControl().UnloadAll(std::vector({ 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 CAppControl::GetNames() const -{ - sdv::sequence 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::read_only); - if (ssAttribute == "console.info_level") return hlpr::flags(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 ptrLogHandlerPath = parserConfig.Root().Direct("LogHandler.Path"); - std::shared_ptr 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(ptrLogHandlerPath->GetValue()); - m_ssLoggerClass = static_cast(ptrLogHandlerClass->GetValue()); - } else - { - // Default logger - m_ssLoggerClass = "DefaultLoggerService"; - } - - // Get an optional program tag for the logger - std::shared_ptr ptrLogPogramTag = parserConfig.Root().Direct("LogHandler.Tag"); - if (ptrLogPogramTag) m_ssProgramTag = static_cast(ptrLogPogramTag->GetValue()); - - // Get the application-mode - std::string ssApplication = "Standalone"; - std::shared_ptr ptrApplication = parserConfig.Root().Direct("Application.Mode"); - if (ptrApplication) ssApplication = static_cast(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 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 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 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 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(fstream)), std::istreambuf_iterator()); - 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(ptrVersion->GetValue()) << ")" << std::endl; - return false; - } - - // Read the system configurations - auto ptrSystemConfigs = parserSettings.Root().Direct("Settings.SystemConfig"); - if (ptrSystemConfigs && ptrSystemConfigs->Cast()) - { - for (uint32_t uiIndex = 0; uiIndex < ptrSystemConfigs->Cast()->GetCount(); uiIndex++) - { - auto ptrSystemConfig = ptrSystemConfigs->Cast()->Get(uiIndex); - if (!ptrSystemConfig) continue; - m_vecSysConfigs.push_back(static_cast(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(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 diff --git a/sdv_services/core/app_control.h b/sdv_services/core/app_control.h index 94cc5a4..39ed3af 100644 --- a/sdv_services/core/app_control.h +++ b/sdv_services/core/app_control.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 APP_CONTROL_H #define APP_CONTROL_H @@ -6,136 +19,53 @@ #include #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_ -/// 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 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 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 diff --git a/sdv_services/core/app_settings.cpp b/sdv_services/core/app_settings.cpp new file mode 100644 index 0000000..ddf69d2 --- /dev/null +++ b/sdv_services/core/app_settings.cpp @@ -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 + +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() == "Silent"; + m_bVerbose = tableStartupConfig.GetDirect("Console.Report").GetValue().get() == "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(); + m_ssLoggerClass = tableStartupConfig.GetDirect("LogHandler.Class").GetValue().get(); + if (m_ssLoggerClass.empty()) + m_ssLoggerClass = "DefaultLoggerService"; + + // Get an optional program tag for the logger + m_ssProgramTag = tableStartupConfig.GetDirect("LogHandler.Tag").GetValue().get(); + + // 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(fstream)), std::istreambuf_iterator()); + 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 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(fstream)), std::istreambuf_iterator()); + 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 CAppSettings::GetSystemConfigPaths() const +{ + std::vector 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 CAppSettings::GetNames() const +{ + sdv::sequence 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::read_only); + if (ssAttribute == "console.info_level") + return hlpr::flags(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(); +} diff --git a/sdv_services/core/app_settings.h b/sdv_services/core/app_settings.h new file mode 100644 index 0000000..1e53a87 --- /dev/null +++ b/sdv_services/core/app_settings.h @@ -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 +#include + +/** + * @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_ + * 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 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 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 diff --git a/sdv_services/core/installation_composer.cpp b/sdv_services/core/installation_composer.cpp index 1d87151..e8e6d0f 100644 --- a/sdv_services/core/installation_composer.cpp +++ b/sdv_services/core/installation_composer.cpp @@ -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 #include @@ -59,18 +72,18 @@ std::vector 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 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 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; diff --git a/sdv_services/core/installation_composer.h b/sdv_services/core/installation_composer.h index 9385b95..8a3e5b2 100644 --- a/sdv_services/core/installation_composer.h +++ b/sdv_services/core/installation_composer.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 INSTALL_PACKAGING_H #define INSTALL_PACKAGING_H diff --git a/sdv_services/core/installation_manifest.cpp b/sdv_services/core/installation_manifest.cpp index 508e7bd..7255e4b 100644 --- a/sdv_services/core/installation_manifest.cpp +++ b/sdv_services/core/installation_manifest.cpp @@ -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 #include +#include #if defined _WIN32 && defined __GNUC__ #pragma GCC diagnostic push @@ -11,6 +25,74 @@ #include #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(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(), nIndex); +} + +sdv::SClassInfo TOML2ClassInfo(const std::shared_ptr& 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(nodeClass.GetDirect("Singleton").GetValue())) + sClass.uiFlags = static_cast(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(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 ptrPropertyTable; - if (ptrProperties) ptrPropertyTable = ptrProperties->Cast(); - 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(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(); - 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(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(); - 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()) return false; ssComponentsManifest = ptrComponentsNode->GenerateTOML(); @@ -330,39 +394,39 @@ std::string CInstallManifest::FindModuleManifest(const std::filesystem::path& rp return {}; } -std::optional CInstallManifest::FindComponentByClass(const std::string& rssClass) const +std::optional 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::ComponentList() const +std::vector CInstallManifest::ClassList() const { - std::vector vecComponents; + std::vector 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 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(); - 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(); + 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(ptrClassName->GetValue()); - auto ptrAliases = ptrComponent->Direct("Aliases"); - if (ptrAliases) - { - auto ptrAliasesArray = ptrAliases->Cast(); - for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++) - { - auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex); - if (ptrClassAlias) - sComponent.seqAliases.push_back(static_cast(ptrClassAlias->GetValue())); - } - } - auto ptrDefaultName = ptrComponent->Direct("DefaultName"); - if (ptrDefaultName) sComponent.ssDefaultObjectName = static_cast(ptrDefaultName->GetValue()); - else sComponent.ssDefaultObjectName = sComponent.ssClassName; - auto ptrType = ptrComponent->Direct("Type"); - if (!ptrType) continue; - std::string ssType = static_cast(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(ptrSingleton->GetValue())) - sComponent.uiFlags = static_cast(sdv::EObjectFlags::singleton); - auto ptrDependencies = ptrComponent->Direct("Dependencies"); - if (ptrDependencies) - { - auto ptrDependencyArray = ptrDependencies->Cast(); - for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount(); - uiDependencyIndex++) - { - auto ptrDependsOn = ptrDependencyArray->Get(uiDependencyIndex); - if (ptrDependsOn) - sComponent.seqDependencies.push_back(static_cast(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(); + // for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++) + // { + // auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex); + // if (ptrClassAlias) + // sClass.seqClassAliases.push_back(static_cast(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(ptrSingleton->GetValue())) + // sClass.uiFlags = static_cast(sdv::EObjectFlags::singleton); + //auto ptrDependencies = ptrClass->Direct("Dependencies"); + //if (ptrDependencies) + //{ + // auto ptrDependencyArray = ptrDependencies->Cast(); + // for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount(); + // uiDependencyIndex++) + // { + // auto ptrDependsOn = ptrDependencyArray->Get(uiDependencyIndex); + // if (ptrDependsOn) + // sClass.seqDependencies.push_back(static_cast(ptrDependsOn->GetValue())); + // } + //} - vecComponents.push_back(sComponent); + //vecClasses.push_back(sClass); + + auto sClass = TOML2ClassInfo(parser.Root().Cast(), 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); } } diff --git a/sdv_services/core/installation_manifest.h b/sdv_services/core/installation_manifest.h index e6f34c1..0bb5c9c 100644 --- a/sdv_services/core/installation_manifest.h +++ b/sdv_services/core/installation_manifest.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 INSTALL_MANIFEST_H #define INSTALL_MANIFEST_H @@ -7,9 +20,37 @@ #include #include #include +#include "toml_parser/parser_toml.h" #include #include +/** + * @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& 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 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 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 FindComponentByClass(const std::string& rssClass) const; + std::optional 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 ComponentList() const; + std::vector 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 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 vecClasses; ///< Vector with contained component classes. }; std::string m_ssInstallName; ///< Installation name. diff --git a/sdv_services/core/iso_monitor.cpp b/sdv_services/core/iso_monitor.cpp index b4226b0..e356797 100644 --- a/sdv_services/core/iso_monitor.cpp +++ b/sdv_services/core/iso_monitor.cpp @@ -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; } diff --git a/sdv_services/core/iso_monitor.h b/sdv_services/core/iso_monitor.h index 0f49bd0..388318e 100644 --- a/sdv_services/core/iso_monitor.h +++ b/sdv_services/core/iso_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 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 \ No newline at end of file diff --git a/sdv_services/core/local_shutdown_request.h b/sdv_services/core/local_shutdown_request.h index f5d67b4..04323c4 100644 --- a/sdv_services/core/local_shutdown_request.h +++ b/sdv_services/core/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 + ********************************************************************************/ + #ifndef LOCAL_SHUTDOWN_REQUEST_H #define LOCAL_SHUTDOWN_REQUEST_H diff --git a/sdv_services/core/log_csv_writer.cpp b/sdv_services/core/log_csv_writer.cpp index e0de321..6100a07 100644 --- a/sdv_services/core/log_csv_writer.cpp +++ b/sdv_services/core/log_csv_writer.cpp @@ -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 diff --git a/sdv_services/core/log_csv_writer.h b/sdv_services/core/log_csv_writer.h index 860df14..61e670d 100644 --- a/sdv_services/core/log_csv_writer.h +++ b/sdv_services/core/log_csv_writer.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 LOG_CSV_WRITER_H #define LOG_CSV_WRITER_H diff --git a/sdv_services/core/logger.cpp b/sdv_services/core/logger.cpp index ad92d82..f46337f 100644 --- a/sdv_services/core/logger.cpp +++ b/sdv_services/core/logger.cpp @@ -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 -#include "sdv_core.h" #include "../../global/exec_dir_helper.h" #ifdef __unix__ @@ -10,6 +22,12 @@ #include // 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 // Needed for _getpid #endif +CLoggerControl& GetLoggerControl() +{ + static CLoggerControl logger_control; + return logger_control; +} + CLoggerControl::~CLoggerControl() { // Reset the logger. This will finialize an open log. diff --git a/sdv_services/core/logger_control.h b/sdv_services/core/logger_control.h index b3c575f..f8aad2e 100644 --- a/sdv_services/core/logger_control.h +++ b/sdv_services/core/logger_control.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 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 \ No newline at end of file diff --git a/sdv_services/core/memory.cpp b/sdv_services/core/memory.cpp index 80b59fb..cc14893 100644 --- a/sdv_services/core/memory.cpp +++ b/sdv_services/core/memory.cpp @@ -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 CMemoryManager::Allocate(uint32_t uiLength) { return sdv::internal::make_ptr(this, uiLength); diff --git a/sdv_services/core/memory.h b/sdv_services/core/memory.h index 2bfa2cb..872f315 100644 --- a/sdv_services/core/memory.h +++ b/sdv_services/core/memory.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 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 \ No newline at end of file diff --git a/sdv_services/core/module.cpp b/sdv_services/core/module.cpp index 76cf822..2f6cf54 100644 --- a/sdv_services/core/module.cpp +++ b/sdv_services/core/module.cpp @@ -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 #include #include #include +#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 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 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()) + auto ptrClasses = parser.Root().Direct("Class"); + if (!ptrClasses || !ptrClasses->Cast()) { 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()->GetCount(); uiIndex++) + for (std::uint32_t uiIndex = 0; uiIndex < ptrClasses->Cast()->GetCount(); uiIndex++) { // Fill in the class info. - sdv::SClassInfo sInfo{}; - auto ptrComponent = ptrComponents->Cast()->Get(uiIndex); - if (!ptrComponent) continue; - auto ptrClassName = ptrComponent->Direct("Class"); + sdv::SClassInfo sClass{}; + auto ptrClass = ptrClasses->Cast()->Get(uiIndex); + if (!ptrClass) continue; + auto ptrClassName = ptrClass->Direct("Name"); if (!ptrClassName) continue; - sInfo.ssClassName = static_cast(ptrClassName->GetValue()); - auto ptrAliases = ptrComponent->Direct("Aliases"); + sClass.ssName = static_cast(ptrClassName->GetValue()); + auto ptrAliases = ptrClass->Direct("Aliases"); if (ptrAliases) { auto ptrAliasesArray = ptrAliases->Cast(); @@ -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(ptrClassAlias->GetValue())); + sClass.seqClassAliases.push_back(static_cast(ptrClassAlias->GetValue())); } } - auto ptrDefaultName = ptrComponent->Direct("DefaultName"); - if (ptrDefaultName) sInfo.ssDefaultObjectName = static_cast(ptrDefaultName->GetValue()); - else sInfo.ssDefaultObjectName = sInfo.ssClassName; - auto ptrType = ptrComponent->Direct("Type"); + auto ptrDefaultName = ptrClass->Direct("DefaultName"); + if (ptrDefaultName) sClass.ssDefaultObjectName = static_cast(ptrDefaultName->GetValue()); + else sClass.ssDefaultObjectName = sClass.ssName; + auto ptrType = ptrClass->Direct("Type"); if (!ptrType) continue; - std::string ssType = static_cast(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(ptrSingleton->GetValue())) - sInfo.uiFlags = static_cast(sdv::EObjectFlags::singleton); - auto ptrDependencies = ptrComponent->Direct("Dependencies"); + sClass.uiFlags = static_cast(sdv::EObjectFlags::singleton); + auto ptrDependencies = ptrClass->Direct("Dependencies"); if (ptrDependencies) { auto ptrDependencyArray = ptrDependencies->Cast(); @@ -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(ptrDependsOn->GetValue())); + sClass.seqDependencies.push_back(static_cast(ptrDependsOn->GetValue())); } } - m_mapClassInfo[sInfo.ssClassName] = sInfo; + m_mapClassInfo[sClass.ssName] = sClass; } } catch (const sdv::toml::XTOMLParseException&) diff --git a/sdv_services/core/module.h b/sdv_services/core/module.h index 593430b..f899fe6 100644 --- a/sdv_services/core/module.h +++ b/sdv_services/core/module.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 MODULE_H #define MODULE_H diff --git a/sdv_services/core/module_control.cpp b/sdv_services/core/module_control.cpp index 0552c18..72f4ed0 100644 --- a/sdv_services/core/module_control.cpp +++ b/sdv_services/core/module_control.cpp @@ -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 #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(ssModulePath); else { - if (GetAppControl().IsMainApplication() || GetAppControl().IsIsolatedApplication()) + if (GetAppSettings().IsMainApplication() || GetAppSettings().IsIsolatedApplication()) { // Check the installation for the module. pathModule = GetAppConfig().FindInstalledModule(static_cast(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 ptrModule = std::make_shared(static_cast(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 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(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 CModuleControl::GetModule(sdv::core::TModuleID tModuleID) const @@ -281,28 +301,84 @@ void CModuleControl::ResetConfigBaseline() m_setConfigModules.clear(); } -std::string CModuleControl::SaveConfig(const std::set& 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& /*rsetIgnoreModule*/) { std::stringstream sstream; - std::unique_lock lock(m_mtxModules); + //std::unique_lock lock(m_mtxModules); - // Add all the loaded modules - for (const std::shared_ptr& 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& 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 diff --git a/sdv_services/core/module_control.h b/sdv_services/core/module_control.h index e373f18..e23ac31 100644 --- a/sdv_services/core/module_control.h +++ b/sdv_services/core/module_control.h @@ -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 #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__ diff --git a/sdv_services/core/object_lifetime_control.cpp b/sdv_services/core/object_lifetime_control.cpp index cac2b19..97569c2 100644 --- a/sdv_services/core/object_lifetime_control.cpp +++ b/sdv_services/core/object_lifetime_control.cpp @@ -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" diff --git a/sdv_services/core/object_lifetime_control.h b/sdv_services/core/object_lifetime_control.h index 56df482..a8f59de 100644 --- a/sdv_services/core/object_lifetime_control.h +++ b/sdv_services/core/object_lifetime_control.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 diff --git a/sdv_services/core/repository.cpp b/sdv_services/core/repository.cpp index afac423..62df231 100644 --- a/sdv_services/core/repository.cpp +++ b/sdv_services/core/repository.cpp @@ -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 #include #include #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 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(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 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(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(); - 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 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 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(); - 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(); - 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& rvecIgnoreOb void CRepository::ResetConfigBaseline() { + // Reset the configuration set. std::unique_lock 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 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 lock(m_mtxObjects); - std::set setModules; - for (const std::shared_ptr& 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 lock(m_mtxObjects); + //std::set setModules; + //for (const std::shared_ptr& 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 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(); @@ -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 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("ProcessControlService"); @@ -1046,51 +1259,14 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr& 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 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(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 lock(m_mtxObjects); // Reserve the object and return the lock sdv::core::TObjectID tObjectID = CreateObjectID(); auto ptrObjectEntry = std::make_shared(); @@ -1099,31 +1275,30 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptrtObjectID = 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_ptrInitialize(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_ptrDestroyObject(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_ptrptrIsoMon = std::make_shared(ptrObject); ptrObjectEntry->ptrObject = ptrObjectEntry->ptrIsoMon.get(); @@ -1199,32 +1375,23 @@ std::vector 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 \ No newline at end of file diff --git a/sdv_services/core/repository.h b/sdv_services/core/repository.h index 416d2f6..448e099 100644 --- a/sdv_services/core/repository.h +++ b/sdv_services/core/repository.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 + ********************************************************************************/ -#ifndef VAPI_REPOSITORY_H -#define VAPI_REPOSITORY_H +#ifndef REPOSITORY_H +#define REPOSITORY_H #include #include @@ -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 diff --git a/sdv_services/core/sdv_core.cpp b/sdv_services/core/sdv_core.cpp index 1bba33b..5a89201 100644 --- a/sdv_services/core/sdv_core.cpp +++ b/sdv_services/core/sdv_core.cpp @@ -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 @@ -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(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 -} diff --git a/sdv_services/core/sdv_core.h b/sdv_services/core/sdv_core.h index c19c441..eee40ef 100644 --- a/sdv_services/core/sdv_core.h +++ b/sdv_services/core/sdv_core.h @@ -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 #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 \ No newline at end of file diff --git a/sdv_services/core/toml_parser/character_reader_utf_8.cpp b/sdv_services/core/toml_parser/character_reader_utf_8.cpp index 644e160..cd0e132 100644 --- a/sdv_services/core/toml_parser/character_reader_utf_8.cpp +++ b/sdv_services/core/toml_parser/character_reader_utf_8.cpp @@ -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" diff --git a/sdv_services/core/toml_parser/character_reader_utf_8.h b/sdv_services/core/toml_parser/character_reader_utf_8.h index ccd183b..0c10086 100644 --- a/sdv_services/core/toml_parser/character_reader_utf_8.h +++ b/sdv_services/core/toml_parser/character_reader_utf_8.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 diff --git a/sdv_services/core/toml_parser/code_snippet.cpp b/sdv_services/core/toml_parser/code_snippet.cpp new file mode 100644 index 0000000..9bf1654 --- /dev/null +++ b/sdv_services/core/toml_parser/code_snippet.cpp @@ -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& rlstTokens) + { + m_lstTokens = rlstTokens; + m_bUseDefault = false; + } + + void CCodeSnippet::SetTokenList(std::list&& 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 \ No newline at end of file diff --git a/sdv_services/core/toml_parser/code_snippet.h b/sdv_services/core/toml_parser/code_snippet.h new file mode 100644 index 0000000..d7dc9dd --- /dev/null +++ b/sdv_services/core/toml_parser/code_snippet.h @@ -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 +#include +#include +#include +#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 + * + * + * = + * + * @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& 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&& 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 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 \ No newline at end of file diff --git a/sdv_services/core/toml_parser/exception.h b/sdv_services/core/toml_parser/exception.h index aef78dc..b61537e 100644 --- a/sdv_services/core/toml_parser/exception.h +++ b/sdv_services/core/toml_parser/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 CONFIG_EXCEPTION_H #define CONFIG_EXCEPTION_H diff --git a/sdv_services/core/toml_parser/lexer_toml.cpp b/sdv_services/core/toml_parser/lexer_toml.cpp index 22fc0d6..0c66c82 100644 --- a/sdv_services/core/toml_parser/lexer_toml.cpp +++ b/sdv_services/core/toml_parser/lexer_toml.cpp @@ -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 #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; @@ -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 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 ',': diff --git a/sdv_services/core/toml_parser/lexer_toml.h b/sdv_services/core/toml_parser/lexer_toml.h index 49afd39..ac10f20 100644 --- a/sdv_services/core/toml_parser/lexer_toml.h +++ b/sdv_services/core/toml_parser/lexer_toml.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 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 + class enum_stack : public std::stack + { + public: + /** + * @brief Pop the top-most entry from the stack if the stack is not empty. + */ + void pop() + { + if (!std::stack::empty()) + std::stack::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::empty() ? TDefault : std::stack::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 m_stackExpectations; ///< Tracking of key or value expectations in nested structures + enum_stack + m_stackExpectations; ///< Tracking of key or value expectations in nested structures const std::vector m_vecKeyDelimiters{"\n", "\t", "\r", " ", "", ".", "=", "]"}; ///< Characters that delimit a key diff --git a/sdv_services/core/toml_parser/lexer_toml_token.cpp b/sdv_services/core/toml_parser/lexer_toml_token.cpp index 48bdd0d..a558856 100644 --- a/sdv_services/core/toml_parser/lexer_toml_token.cpp +++ b/sdv_services/core/toml_parser/lexer_toml_token.cpp @@ -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 diff --git a/sdv_services/core/toml_parser/lexer_toml_token.h b/sdv_services/core/toml_parser/lexer_toml_token.h index 24a4495..302c6d3 100644 --- a/sdv_services/core/toml_parser/lexer_toml_token.h +++ b/sdv_services/core/toml_parser/lexer_toml_token.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 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 diff --git a/sdv_services/core/toml_parser/miscellaneous.cpp b/sdv_services/core/toml_parser/miscellaneous.cpp index 3b81e96..02208ac 100644 --- a/sdv_services/core/toml_parser/miscellaneous.cpp +++ b/sdv_services/core/toml_parser/miscellaneous.cpp @@ -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 diff --git a/sdv_services/core/toml_parser/miscellaneous.h b/sdv_services/core/toml_parser/miscellaneous.h index 4a77bcc..cfa2885 100644 --- a/sdv_services/core/toml_parser/miscellaneous.h +++ b/sdv_services/core/toml_parser/miscellaneous.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 MISCELLANEOUS_H #define MISCELLANEOUS_H diff --git a/sdv_services/core/toml_parser/parser_node_toml.cpp b/sdv_services/core/toml_parser/parser_node_toml.cpp index 7e7967e..f8be6c7 100644 --- a/sdv_services/core/toml_parser/parser_node_toml.cpp +++ b/sdv_services/core/toml_parser/parser_node_toml.cpp @@ -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: + * Martin Stimpfl - initial API and implementation + * Erik Verhoeven - writing TOML and whitespace preservation + ********************************************************************************/ + #include #include "parser_node_toml.h" #include "exception.h" #include #include "parser_toml.h" +#include /// The TOML parser namespace namespace toml_parser @@ -14,7 +29,16 @@ namespace toml_parser void CGenContext::InitTopMostNode(const std::shared_ptr& rptrNode) { if (!m_ptrTopMostNode) + { m_ptrTopMostNode = rptrNode; + + // When initialized, this is the only node and therefore also the last node... + m_bFinalLastNode = true; + m_bLastNode = true; + + // Extract node specific generation context + ExtractContext(rptrNode); + } } bool CGenContext::PartOfExcludedParents(const std::shared_ptr& rptrNode) const @@ -29,12 +53,21 @@ namespace toml_parser return false; } - CGenContext CGenContext::CopyWithContext(const std::string& rssNewKeyContext) const + CGenContext CGenContext::CopyWithContext(const std::string& rssNewKeyContext, const std::shared_ptr& rptrNode, + bool bLastNode) const { CGenContext context(m_ssPrefixKey, m_uiOptions); + + // Copy the base context context.m_ssKeyContext = rssNewKeyContext; context.m_ptrTopMostNode = m_ptrTopMostNode; context.m_bTopMost = false; + context.m_bLastNode = bLastNode; + context.m_bFinalLastNode = bLastNode && m_bFinalLastNode; + + // Extract node specific generation context + context.ExtractContext(rptrNode); + return context; } @@ -48,6 +81,21 @@ namespace toml_parser return m_ssKeyContext; } + const std::string& CGenContext::KeyPath() const + { + return m_ssKeyPath; + } + + const std::string& CGenContext::FullKeyPath() const + { + return m_ssFullKeyPath; + } + + const std::string& CGenContext::RelKeyPath() const + { + return m_ssRelKeyPath; + } + bool CGenContext::TopMostNode() const { return m_bTopMost; @@ -63,6 +111,124 @@ namespace toml_parser return (m_uiOptions & static_cast(eOption)) ? true : false; } + bool CGenContext::LastNode() const + { + return m_bLastNode; + } + + CGenContext::EPresentation CGenContext::Presentation() const + { + return m_ePresentation; + } + + bool CGenContext::Standard() const + { + return m_ePresentation == EPresentation::standard; + } + + bool CGenContext::Inline() const + { + return m_ePresentation != EPresentation::standard; + } + + bool CGenContext::Embedded() const + { + return m_ePresentation == EPresentation::embedded; + } + + bool CGenContext::Assignment() const + { + return m_bAssignment; + } + + bool CGenContext::CommaNeeded() const + { + return m_bCommaNeeded; + } + + bool CGenContext::CommentAndNewlineAllowed() const + { + return !m_bOneLine && !CheckOption(EGenerateOptions::no_comments); + } + + bool CGenContext::NewlineAllowed() const + { + return !m_bOneLine && !CheckOption(EGenerateOptions::no_comments) && !CheckOption(EGenerateOptions::reduce_whitespace); + } + + bool CGenContext::FinalNewline() const + { + return m_bFinalNewline; + } + + void CGenContext::ExtractContext(const std::shared_ptr& rptrNode) + { + if (!rptrNode) return; + auto ptrParent = rptrNode->GetParentPtr(); + if (ptrParent && ptrParent->Inline() && ptrParent->ExplicitlyDefined()) + m_ePresentation = EPresentation::embedded; + else if (rptrNode->Inline()) + m_ePresentation = EPresentation::standard_inline; + + // Get pointers + std::shared_ptr ptrParentArray, ptrArray; + if (ptrParent) ptrParentArray = ptrParent->Cast(); + ptrArray = rptrNode->Cast(); + std::shared_ptr ptrParentTable, ptrTable; + if (ptrParent) ptrParentTable = ptrParent->Cast(); + ptrTable = rptrNode->Cast(); + std::shared_ptr ptrValueNode = rptrNode->Cast(); + + // Inline or embedded table nodes must be defined on one line, except when multi-line-strings are used. Comments and + // syntax-base newlines are not allowed. + if (ptrParent && (m_ePresentation == EPresentation::embedded)) + m_bOneLine = ptrParent->Cast() ? true : false; + + // Assignements are not allowed within inline arrays + m_bAssignment = !(m_ePresentation == EPresentation::embedded && ptrParentArray); + + // Separation commas are needed for embedded nodes. A final trailing comma behind the last node is allowed for array + // elements only (not for the table elements). + if (m_ePresentation == EPresentation::embedded) + m_bCommaNeeded = !m_bLastNode || (ptrParentArray && ptrParentArray->LastNodeWithSucceedingComma()); + + // Final new line for non-embedded nodes, which are not the last node, unless the last node is a standard table with + // children. + m_bFinalNewline = m_ePresentation != EPresentation::embedded && + !(m_bFinalLastNode && + !(m_ePresentation == EPresentation::standard && (ptrParentTable || ptrParentArray || !m_ssPrefixKey.empty()) && + (ptrTable && ptrTable->GetCount()))); + + // Extract the correct context and the kex paths + if (m_ssKeyContext.empty()) + { + m_ssKeyContext = m_ssPrefixKey; + std::string ssGenContext; + if (m_bTopMost) + { + if (ptrArray || ptrValueNode) // Inline array or value. + { + if (ptrParent) + ssGenContext = static_cast(ptrParent->GetPath(false)); + } + else if (!(CheckOption(EGenerateOptions::full_header) && ptrTable)) // Not table entry and full header enabled. + ssGenContext = static_cast(rptrNode->GetPath(false)); + } + if (!m_ssKeyContext.empty() && !ssGenContext.empty()) + m_ssKeyContext += "."; + m_ssKeyContext += ssGenContext; + } + m_ssKeyPath = m_ssPrefixKey; + m_ssFullKeyPath = m_ssKeyContext.empty() ? m_ssPrefixKey : m_ssKeyContext; + m_ssRelKeyPath = rptrNode->GetCustomPath(m_ssPrefixKey, m_ssKeyContext.empty() ? m_ssPrefixKey : m_ssKeyContext); + if (!m_ssKeyPath.empty() && !m_ssRelKeyPath.empty()) + m_ssKeyPath += "."; + if (!m_ssFullKeyPath.empty() && !m_ssRelKeyPath.empty()) + m_ssFullKeyPath += "."; + m_ssKeyPath += m_ssRelKeyPath; + m_ssFullKeyPath += m_ssRelKeyPath; + } + CNode::CNode(CParser& rparser, const std::string& rssName, const std::string& rssRawName) : m_ssName(rssName), m_ssRawName(rssRawName), m_rParser(rparser) {} @@ -150,10 +316,11 @@ namespace toml_parser uint32_t CNode::GetIndex() const { - std::shared_ptr ptrParent = m_ptrParent.lock(); - if (!ptrParent) return sdv::toml::npos; + std::shared_ptr ptrView = m_ptrView.lock(); + if (!ptrView) ptrView = m_ptrParent.lock(); + if (!ptrView) return sdv::toml::npos; - return ptrParent->FindIndex(std::const_pointer_cast(shared_from_this())); + return ptrView->FindIndex(std::const_pointer_cast(shared_from_this())); } sdv::IInterfaceAccess* CNode::GetParent() const @@ -170,212 +337,49 @@ namespace toml_parser return GenerateTOML(); } - void CNode::SetComment(const sdv::u8string& ssComment, uint32_t uiFlags) + void CNode::SetComment(sdv::toml::INodeInfo::ECommentType eType, const sdv::u8string& ssComment) { - uint32_t uiIndex = uiFlags & static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_index_mask); - - // In case of raw comment code or when the replace flag is enabled, remove the current tokens from the list - if ((uiFlags & static_cast(sdv::toml::INodeInfo::ECommentFlags::raw_comment)) - || (uiFlags & static_cast(sdv::toml::INodeInfo::ECommentFlags::replace_whitespace))) - { - CodeSnippet(uiIndex).List().clear(); - CodeSnippet(uiIndex).Str().clear(); - } - - // Generate code from the string when not raw mode - std::string ssCommentCode; - TTokenListIterator itInsertLocation = CodeSnippet(uiIndex).List().end(); - if (!(uiFlags & static_cast(sdv::toml::INodeInfo::ECommentFlags::raw_comment))) - { - // Find the first comment and then the last comment. Delete everything in between. - TTokenListIterator it = CodeSnippet(uiIndex).List().begin(); - TTokenListIterator itStart = itInsertLocation; - TTokenListIterator itStop = itInsertLocation; - while (it != CodeSnippet(uiIndex).List().end()) - { - if (it->Category() == ETokenCategory::token_comment) - { - if (itStart == itInsertLocation) - itStart = it; - itStop = it; - } - } - - // Move the stop to after the last comment - if (itStop != CodeSnippet(uiIndex).List().end()) - itStop++; - - // Include the newline following the last comment - if (itStop != CodeSnippet(uiIndex).List().end() && itStop->Category() == ETokenCategory::token_syntax_new_line) - itStop++; - - // Determine insert location in text position (when on newline, no insert location, when following the code, determine the insert location based on the other topics. There is a max, then reduce). - // Determine max length in text position - // Build code string - // The whitespace location is dependable on whether this is an assignment and the parent is a normal - - - - - - // Remove the tokens if there are any. - if (itStart != CodeSnippet(uiIndex).List().end()) - CodeSnippet(uiIndex).List().erase(itStart, itStop); - - // The insert location is the stop iterator - itInsertLocation = itStop; - - - } - else - ssCommentCode = ssComment; - - - // Use the lexer to generate the tokens - CLexer lexerComments; - lexerComments.Feed(ssCommentCode); - lexerComments.NavigationMode(CLexer::ENavigationMode::do_not_skip_anything); - std::reference_wrapper refToken = lexerComments.Consume(); - while (refToken.get().Category() != ETokenCategory::token_none) - { - // Add whitespace and comments only... - switch (refToken.get().Category()) - { - case toml_parser::ETokenCategory::token_comment: - case toml_parser::ETokenCategory::token_whitespace: - case toml_parser::ETokenCategory::token_syntax_new_line: - CodeSnippet(uiIndex).List().insert(itInsertLocation, refToken.get()); - break; - default: - break; - } - } - - // Add a newline if the last token was a comment - if (!CodeSnippet(uiIndex).List().empty() && - CodeSnippet(uiIndex).List().back().Category() == ETokenCategory::token_comment) - CodeSnippet(uiIndex).List().push_back(CToken(ETokenCategory::token_syntax_new_line)); + CodeSnippet(eType).SetComment(ssComment); } - sdv::u8string CNode::GetComment(uint32_t uiFlags) + sdv::u8string CNode::GetComment(sdv::toml::INodeInfo::ECommentType eType) { - uint32_t uiIndex = uiFlags & static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_index_mask); - - // Iterate through the code tokens. - std::stringstream sstream; - for (const CToken& rToken : CodeSnippet(uiIndex).List()) - { - // Differentiate between interpreted comment or raw code comment - if (uiIndex & static_cast(sdv::toml::INodeInfo::ECommentFlags::raw_comment)) - { - switch (rToken.Category()) - { - case toml_parser::ETokenCategory::token_whitespace: - case toml_parser::ETokenCategory::token_comment: - case toml_parser::ETokenCategory::token_syntax_new_line: - sstream << rToken.RawString(); - break; - default: - break; - } - } - else - { - 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') - { - // 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 (sstream.str().back() != ' ') - sstream << " "; - } - - // Add the content to the stream - sstream << rToken.RawString().substr(nStartComment); - } - - } - } - - // Return the comment string - return sstream.str(); + return CodeSnippet(eType).GetComment(); } void CNode::AutomaticFormat() { - // TODO EVE - // Read the comment of each code snippet and remove the tokens. This will automatically format the code when recomposing. + // Automatically format the code snippets of the node. for (auto& rmapSnippets : m_vecCodeSnippets) { for (auto& rvtSnippet : rmapSnippets) { - std::cout << rvtSnippet.first; + rvtSnippet.second.RemoveFormat(); } } } + bool CNode::IsInline() const + { + return Inline(); + } + + bool CNode::IsStandard() const + { + return !Inline(); + } + void CNode::UpdateNodeCode(const CNodeTokenRange& rNodeRange) { // Extract the node pre and post comments and whitespace (belonging and not belonging to the node). - CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::out_of_scope_comment_before)).List() = - rNodeRange.LinesBeforeNode().TokenListSLice(); - CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::out_of_scope_comment_behind)).List() = - rNodeRange.LinesBehindNode().TokenListSLice(); - CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_before)).List() = - rNodeRange.NodeCommentsBefore().TokenListSLice(); - CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_behind)).List() = - rNodeRange.NodeCommentsBehind().TokenListSLice(); - } - - bool CNode::DeleteNode() - { - bool bRet = false; - - // Remove the node from the parent. - std::shared_ptr ptrParent = m_ptrParent.lock(); - if (ptrParent) - { - // Remove the node from the parent --> this will put the node in the recycle bin. - bRet = ptrParent->RemoveNode(std::const_pointer_cast(shared_from_this())); - } - - // Remove the parent. - m_ptrParent.reset(); - - // Set the node to be deleted. - m_bDeleted = true; - - return bRet; - } - - bool CNode::IsDeleted() const - { - return m_bDeleted; + CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).SetTokenList( + rNodeRange.LinesBeforeNode().TokenListSLice()); + CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind).SetTokenList( + rNodeRange.LinesBehindNode().TokenListSLice()); + CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_before).SetTokenList( + rNodeRange.NodeCommentsBefore().TokenListSLice()); + CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_behind).SetTokenList( + rNodeRange.NodeCommentsBehind().TokenListSLice()); } bool CNode::ChangeName(const sdv::u8string& ssNewName) @@ -413,14 +417,57 @@ namespace toml_parser return false; } - std::shared_ptr CNode::GetParentPtr() const + bool CNode::DeleteNode() { - return m_ptrParent.lock(); + bool bRet = false; + + // Remove the node from the view (this could also be the parent). + auto ptrView = GetViewPtr(); + if (ptrView) ptrView->RemoveFromView(shared_from_this()); + + // Remove the node from the parent. + auto ptrParent = GetParentPtr(); + if (ptrParent) + { + // Remove the node from the parent --> this will put the node in the recycle bin. + bRet = ptrParent->DeleteNode(std::const_pointer_cast(shared_from_this())); + } + + // Remove the parent. + m_ptrParent.reset(); + + // Set the node to be deleted. + m_bDeleted = true; + + return bRet; + } + + bool CNode::IsDeleted() const + { + return m_bDeleted; } void CNode::SetParentPtr(const std::shared_ptr& rptrParent) { + auto ptrOldParent = m_ptrParent.lock(); + if (rptrParent == ptrOldParent) return; // Nothing to do... + if (ptrOldParent) + { + auto itNode = std::find(ptrOldParent->m_lstNodes.begin(), ptrOldParent->m_lstNodes.end(), shared_from_this()); + if (itNode != ptrOldParent->m_lstNodes.end()) + ptrOldParent->m_lstNodes.erase(itNode); + } + m_ptrParent = rptrParent; + if (!rptrParent) return; + auto itNode = std::find(rptrParent->m_lstNodes.begin(), rptrParent->m_lstNodes.end(), shared_from_this()); + if (itNode == rptrParent->m_lstNodes.end()) + rptrParent->m_lstNodes.push_back(shared_from_this()); + } + + std::shared_ptr CNode::GetParentPtr() const + { + return m_ptrParent.lock(); } std::string CNode::GetParentPath() const @@ -435,6 +482,13 @@ namespace toml_parser m_ptrView = rptrView; } + std::shared_ptr CNode::GetViewPtr() const + { + auto ptrView = m_ptrView.lock(); + if (!ptrView) ptrView = m_ptrParent.lock(); + return ptrView; + } + bool CNode::IsPartOfView(const CGenContext& rContext, const std::shared_ptr& rptrNode) const { std::shared_ptr ptrStoredView = m_ptrView.lock(); @@ -443,17 +497,72 @@ namespace toml_parser return true; } + const CCodeSnippet& CNode::CodeSnippet(size_t nIndex, const std::string& rssKey /*= std::string()*/) const + { + static CCodeSnippet sEmptyCodeSnippet; + sEmptyCodeSnippet.RemoveFormat(); + if (nIndex >= m_vecCodeSnippets.size()) return sEmptyCodeSnippet; + auto itKey = m_vecCodeSnippets[nIndex].find(rssKey); + if (itKey == m_vecCodeSnippets[nIndex].end()) return sEmptyCodeSnippet; + return itKey->second; + } + + CCodeSnippet& CNode::CodeSnippet(size_t nIndex, const std::string& rssKey /*= std::string()*/) + { + if (nIndex >= m_vecCodeSnippets.size()) m_vecCodeSnippets.resize(nIndex + 1); + return m_vecCodeSnippets[nIndex][rssKey]; + } + + const CCodeSnippet& CNode::CodeSnippet(sdv::toml::INodeInfo::ECommentType eType) const + { + static CCodeSnippet sEmptyCodeSnippet; + sEmptyCodeSnippet.RemoveFormat(); + switch (eType) + { + case sdv::toml::INodeInfo::ECommentType::comment_before: + return CodeSnippet(0); + case sdv::toml::INodeInfo::ECommentType::comment_behind: + return CodeSnippet(1); + case sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before: + return CodeSnippet(2); + case sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind: + return CodeSnippet(3); + default: + sEmptyCodeSnippet = CCodeSnippet(); + return sEmptyCodeSnippet; + } + } + + CCodeSnippet& CNode::CodeSnippet(sdv::toml::INodeInfo::ECommentType eType) + { + static CCodeSnippet sEmptyCodeSnippet; + switch (eType) + { + case sdv::toml::INodeInfo::ECommentType::comment_before: + return CodeSnippet(0); + case sdv::toml::INodeInfo::ECommentType::comment_behind: + return CodeSnippet(1); + case sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before: + return CodeSnippet(2); + case sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind: + return CodeSnippet(3); + default: + sEmptyCodeSnippet = CCodeSnippet(); + return sEmptyCodeSnippet; + } + } + std::string CNode::GetCustomPath(const std::string& rssPrefixKey, const std::string& rssContext) const { // Example of path transformation with a table: - // + // // [] (suppressed) "" "" // [fruit] (suppressed) "" "" // [fruit.apple] "" "" // color = "red" "" "fruit.apple" // [fruit.apple.taste] "" "" // sweet = true "" "fruit.apple.taste" - // + // // transfer with all // [transfer] (suppressed) "transfer" "" // [transfer.fruit] (suppressed) "transfer" "transfer" @@ -461,7 +570,7 @@ namespace toml_parser // color = "red" "transfer" "transfer.fruit.apple" // [transfer.fruit.apple.taste] "transfer" "transfer" // sweet = true "transfer" "transfer.fruit.apple.taste" - // + // // transfer with apple // [transfer] (suppressed) "transfer" "fruit" // [transfer.apple] "transfer" "transfer.fruit" @@ -470,9 +579,9 @@ namespace toml_parser // sweet = true "transfer" "transfer.fruit.apple.taste" // Insert the prefix before the key path. - auto lstKeyPath = GetRawPath(false); + auto lstKeyPath = GetRawPath(false); auto prPrefixKey = SplitNodeKey(rssPrefixKey); - auto itPos = lstKeyPath.begin(); + auto itPos = lstKeyPath.begin(); while (!prPrefixKey.first.empty()) { lstKeyPath.insert(itPos, std::make_pair(prPrefixKey.first, prPrefixKey.first)); @@ -500,6 +609,7 @@ namespace toml_parser // Build a custom key path by including all raw keys including their code snippet. Skip the initial pre-key code snippet, // though. std::string ssCustomKeyPath; + bool bLastSpace = false; for (const auto& prKey : lstKeyPath) { // In case of a table array, the table doesn't have a name. Skip the table in the key composition. @@ -511,32 +621,20 @@ namespace toml_parser ssCustomKeyPath += "."; // Add the pre-key cpde snippet. - ssCustomKeyPath += CodeSnippet(m_nPreKeyCode, prKey.first).Compose(CCodeSnippet::EComposeMode::compose_inline); + ssCustomKeyPath += CodeSnippet(m_nPreKeyCode, prKey.first).Compose(CCodeSnippet::EComposeMode::compose_inline, CGenContext()); // Add the raw key string ssCustomKeyPath += prKey.second; // Add the pos-key cpde snippet. - ssCustomKeyPath += CodeSnippet(m_nPostKeyCode, prKey.first).Compose(CCodeSnippet::EComposeMode::compose_inline); + ssCustomKeyPath += CodeSnippet(m_nPostKeyCode, prKey.first).Compose(CCodeSnippet::EComposeMode::compose_inline, CGenContext()); + bLastSpace = CodeSnippet(m_nPostKeyCode, prKey.first).HasCode(); } + if (!bLastSpace && Inline()) + ssCustomKeyPath += " "; return ssCustomKeyPath; } - const CNode::CCodeSnippet& CNode::CodeSnippet(size_t nIndex, const std::string& rssKey /*= std::string()*/) const - { - static CCodeSnippet sEmptyCodeSnippet; - if (nIndex >= m_vecCodeSnippets.size()) return sEmptyCodeSnippet; - auto itKey = m_vecCodeSnippets[nIndex].find(rssKey); - if (itKey == m_vecCodeSnippets[nIndex].end()) return sEmptyCodeSnippet; - return itKey->second; - } - - CNode::CCodeSnippet& CNode::CodeSnippet(size_t nIndex, const std::string& rssKey /*= std::string()*/) - { - if (nIndex >= m_vecCodeSnippets.size()) m_vecCodeSnippets.resize(nIndex + 1); - return m_vecCodeSnippets[nIndex][rssKey]; - } - bool CNode::ExplicitlyDefined() const { // Default implementation is explicitly. @@ -545,106 +643,7 @@ namespace toml_parser void CNode::MakeExplicit() { - // Default implementaiton is explicitly. Therfore, nothing to do. - } - - std::list& CNode::CCodeSnippet::List() - { - return m_lstTokens; - } - - std::string& CNode::CCodeSnippet::Str() - { - return m_ssComment; - } - - std::string CNode::CCodeSnippet::Compose(EComposeMode eMode, size_t nAssignmentOffset /*= 0*/, size_t /*nCommentOffset = 0*/) const - { - // Build the stream until the first comment. - std::stringstream sstream; - TTokenListIterator it = m_lstTokens.begin(); - while (it != m_lstTokens.end() && it->Category() != ETokenCategory::token_comment) - { - if ((eMode != EComposeMode::compose_behind) || (it->Category() != ETokenCategory::token_syntax_comma)) - sstream << it->RawString(); - ++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_ssComment.empty()) - { - for (it = itFirstComment; it != itPostComment; ++it) - { - if ((eMode != EComposeMode::compose_behind) || - (it->Category() != ETokenCategory::token_syntax_comma)) - sstream << it->RawString(); - } - } - else - { - switch (eMode) - { - case EComposeMode::compose_inline: - case EComposeMode::compose_behind: - if (itFirstComment != m_lstTokens.end()) - sstream << " "; - break; - default: - break; - } - // TODO: Align the comment.... - sstream << "# " << m_ssComment << std::endl; - - // TODO: Add spaces for next assignment - switch (eMode) - { - case EComposeMode::compose_inline: - if (itPostComment != m_lstTokens.end() && nAssignmentOffset) - sstream << std::string(nAssignmentOffset, ' '); - break; - default: - break; - } - } - - // Stream the rest of the tokens - for (it = itPostComment; it != m_lstTokens.end(); ++it) - { - if ((eMode != EComposeMode::compose_behind) || (it->Category() != ETokenCategory::token_syntax_comma)) - sstream << it->RawString(); - } - - // In some case a default new-line is needed - if (m_lstTokens.empty() && m_ssComment.empty()) - { - switch (eMode) - { - case EComposeMode::compose_behind: - sstream << std::endl; - break; - default: - break; - } - } - - return sstream.str(); + // Default implementation is explicitly. Therefore, nothing to do. } CValueNode::CValueNode(CParser& rparser, const std::string& rssName, const std::string& rssRawName, @@ -673,44 +672,38 @@ namespace toml_parser { if (IsDeleted()) return {}; + // Create a writable copy of the context and provide this node as potential top node + CGenContext contextCopy = rContext; + contextCopy.InitTopMostNode(shared_from_this()); + std::stringstream sstream; - // Determine whether the statement should be embedded (same line separated by commas) and should have an assignment (not - // having a key name). - auto ptrParent = GetParentPtr(); - if (!ptrParent) return {}; - bool bEmbedded = ptrParent->Inline() && ptrParent->ExplicitlyDefined(); - bool bAssignment = !bEmbedded || !ptrParent->Cast(); // No assignment only with normal array - bool bLastNode = ptrParent->CheckLast(std::const_pointer_cast(shared_from_this())); - std::string ssContext = rContext.KeyContext().empty() ? - (rContext.TopMostNode() ? GetParentPath() : std::string()) : rContext.KeyContext(); - // Add unconnected pre node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::out_of_scope_comment_before)). - Compose(CCodeSnippet::EComposeMode::compose_standalone); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).Compose( + CCodeSnippet::EComposeMode::compose_standalone_before, contextCopy); // Add pre node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_before)). - Compose(CCodeSnippet::EComposeMode::compose_before); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_before).Compose( + CCodeSnippet::EComposeMode::compose_before, contextCopy); // Add assignment - if (bAssignment) // Not an array entry - sstream << GetCustomPath(rContext.PrefixKey(), ssContext) << "="; + if (contextCopy.Assignment()) // Not an array entry + { + sstream << contextCopy.RelKeyPath() << "="; + if (!CodeSnippet(m_nPreValueCode).HasCode())sstream << " "; + } // Stream the value - sstream << CodeSnippet(m_nPreValueCode).Compose(CCodeSnippet::EComposeMode::compose_inline) << RawValueText() << - CodeSnippet(m_nPostValueCode).Compose(CCodeSnippet::EComposeMode::compose_inline); - - // Add a comma if this is not the last node. - if (bEmbedded && !bLastNode) sstream << ","; + sstream << CodeSnippet(m_nPreValueCode).Compose(CCodeSnippet::EComposeMode::compose_inline, contextCopy) << + RawValueText() << CodeSnippet(m_nPostValueCode).Compose(CCodeSnippet::EComposeMode::compose_inline, contextCopy); // Add post node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_behind)). - Compose(bLastNode ? CCodeSnippet::EComposeMode::compose_inline : CCodeSnippet::EComposeMode::compose_behind); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_behind).Compose( + CCodeSnippet::EComposeMode::compose_behind, contextCopy); // Add unconnected post node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::out_of_scope_comment_behind)) - .Compose(CCodeSnippet::EComposeMode::compose_standalone); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind).Compose( + CCodeSnippet::EComposeMode::compose_standalone_behind, contextCopy); return sstream.str(); } @@ -750,7 +743,7 @@ namespace toml_parser return; // Unexpected eState = EState::assignment_or_separator; ssKeyName = refToken.get().StringValue(); - CodeSnippet(m_nPreKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPreKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_boolean: case ETokenCategory::token_integer: @@ -763,19 +756,19 @@ namespace toml_parser if (eState != EState::key_or_value && eState != EState::value) return; // Unexpected eState = EState::post_value; - CodeSnippet(m_nPreValueCode).List() = std::move(lstWhitespace); + CodeSnippet(m_nPreValueCode).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_syntax_assignment: if (eState != EState::assignment_or_separator) return; // Unexpected eState = EState::value; - CodeSnippet(m_nPostKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_syntax_dot: if (eState != EState::assignment_or_separator) return; // Unexpected eState = EState::key; - CodeSnippet(m_nPostKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; default: return; // Unexpected @@ -784,7 +777,7 @@ namespace toml_parser if (eState != EState::post_value) return; // Unexpected - CodeSnippet(m_nPostValueCode).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostValueCode).SetTokenList(std::move(lstWhitespace)); } std::string CValueNode::RawValueText() const @@ -792,6 +785,10 @@ namespace toml_parser return m_ssRawValue.empty() ? ValueText() : m_ssRawValue; } + void CValueNode::ResetRawValueText() + { + m_ssRawValue.clear(); + } CBooleanNode::CBooleanNode(CParser& rparser, const std::string& rssName, const std::string& rssRawName, bool bVal, const std::string& rssRawValue) : @@ -805,12 +802,13 @@ namespace toml_parser sdv::any_t CBooleanNode::GetValue() const { - return sdv::any_t(m_bVal); + return m_bVal; } bool CBooleanNode::ChangeValue(sdv::any_t anyNewValue) { m_bVal = anyNewValue.get(); + ResetRawValueText(); return true; } @@ -831,12 +829,13 @@ namespace toml_parser sdv::any_t CIntegerNode::GetValue() const { - return sdv::any_t(m_iVal); + return m_iVal; } bool CIntegerNode::ChangeValue(sdv::any_t anyNewValue) { m_iVal = anyNewValue.get(); + ResetRawValueText(); return true; } @@ -857,12 +856,13 @@ namespace toml_parser sdv::any_t CFloatingPointNode::GetValue() const { - return sdv::any_t(m_dVal); + return m_dVal; } bool CFloatingPointNode::ChangeValue(sdv::any_t anyNewValue) { m_dVal = anyNewValue.get(); + ResetRawValueText(); return true; } @@ -885,12 +885,13 @@ namespace toml_parser sdv::any_t CStringNode::GetValue() const { - return sdv::any_t(m_ssVal); + return m_ssVal; } bool CStringNode::ChangeValue(sdv::any_t anyNewValue) { m_ssVal = anyNewValue.get(); + ResetRawValueText(); return true; } @@ -918,6 +919,14 @@ namespace toml_parser CNode(rparser, rssName, rssRawName) {} + void CNodeCollection::AutomaticFormat() + { + // Format each child-node + for (std::shared_ptr& rptrNode : m_lstNodes) + rptrNode->AutomaticFormat(); + CNode::AutomaticFormat(); + } + uint32_t CNodeCollection::GetCount() const { return static_cast(m_vecNodeOrder.size()); @@ -965,74 +974,351 @@ namespace toml_parser return static_cast(ptrNode.get()); } - sdv::IInterfaceAccess* CNodeCollection::InsertValue(uint32_t /*uiIndex*/, const sdv::u8string& /*ssName*/, sdv::any_t /*anyValue*/) + sdv::IInterfaceAccess* CNodeCollection::InsertValue(uint32_t uiIndex, const sdv::u8string& ssName, sdv::any_t anyValue) { - //std::shared_ptr ptrNode; - //switch (anyValue.eValType) - //{ - //case sdv::any_t::EValType::val_type_bool: - // ptrNode = std::make_shared(Parser(), ssName, anyValue.get()); - // break; - //case sdv::any_t::EValType::val_type_int8: - //case sdv::any_t::EValType::val_type_uint8: - //case sdv::any_t::EValType::val_type_int16: - //case sdv::any_t::EValType::val_type_uint16: - //case sdv::any_t::EValType::val_type_int32: - //case sdv::any_t::EValType::val_type_uint32: - //case sdv::any_t::EValType::val_type_int64: - //case sdv::any_t::EValType::val_type_uint64: - // ptrNode = std::make_shared(Parser(), ssName, anyValue.get()); - // break; - //case sdv::any_t::EValType::val_type_float: - //case sdv::any_t::EValType::val_type_double: - //case sdv::any_t::EValType::val_type_long_double: - //case sdv::any_t::EValType::val_type_fixed: - // ptrNode = std::make_shared(Parser(), ssName, anyValue.get()); - // break; - //case sdv::any_t::EValType::val_type_string: - //case sdv::any_t::EValType::val_type_u8string: - //case sdv::any_t::EValType::val_type_u16string: - //case sdv::any_t::EValType::val_type_u32string: - //case sdv::any_t::EValType::val_type_wstring: - // ptrNode = std::make_shared(Parser(), ssName, anyValue.get()); - // break; - //case sdv::any_t::EValType::val_type_empty: - //case sdv::any_t::EValType::val_type_char: - //case sdv::any_t::EValType::val_type_char16: - //case sdv::any_t::EValType::val_type_char32: - //case sdv::any_t::EValType::val_type_wchar: - //case sdv::any_t::EValType::val_type_interface: - //case sdv::any_t::EValType::val_type_interface_id: - //case sdv::any_t::EValType::val_type_exception_id: - //default: - // return nullptr; // Not supported - //} + std::stringstream sstreamTOML; + std::string ssAccessName = ssName; + if (GetType() == sdv::toml::ENodeType::node_array) + ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "]"; + else + sstreamTOML << QuoteText(ssName, EQuoteRequest::smart_key) << " = "; + switch (anyValue.eValType) + { + case sdv::any_t::EValType::val_type_bool: + sstreamTOML << (anyValue.get() ? "true" : "false"); + break; + case sdv::any_t::EValType::val_type_int8: + case sdv::any_t::EValType::val_type_uint8: + case sdv::any_t::EValType::val_type_int16: + case sdv::any_t::EValType::val_type_uint16: + case sdv::any_t::EValType::val_type_int32: + case sdv::any_t::EValType::val_type_uint32: + case sdv::any_t::EValType::val_type_int64: + case sdv::any_t::EValType::val_type_uint64: + sstreamTOML << std::to_string(anyValue.get()); + break; + case sdv::any_t::EValType::val_type_float: + case sdv::any_t::EValType::val_type_double: + case sdv::any_t::EValType::val_type_long_double: + case sdv::any_t::EValType::val_type_fixed: + sstreamTOML << std::setprecision(15) << std::defaultfloat << anyValue.get(); + break; + case sdv::any_t::EValType::val_type_string: + case sdv::any_t::EValType::val_type_u8string: + case sdv::any_t::EValType::val_type_u16string: + case sdv::any_t::EValType::val_type_u32string: + case sdv::any_t::EValType::val_type_wstring: + sstreamTOML << QuoteText(anyValue.get()); + break; + case sdv::any_t::EValType::val_type_empty: + case sdv::any_t::EValType::val_type_char: + case sdv::any_t::EValType::val_type_char16: + case sdv::any_t::EValType::val_type_char32: + case sdv::any_t::EValType::val_type_wchar: + case sdv::any_t::EValType::val_type_interface: + case sdv::any_t::EValType::val_type_interface_id: + case sdv::any_t::EValType::val_type_exception_id: + default: + return nullptr; // Not supported + } - - - return {}; + // Insert the node + if (InsertTOML(uiIndex, sstreamTOML.str(), true) != EInsertResult::insert_success) + return nullptr; + + // Return the inserted node. + return GetNodeDirect(ssAccessName); } - sdv::IInterfaceAccess* CNodeCollection::InsertArray(uint32_t /*uiIndex*/, const sdv::u8string& /*ssName*/) + sdv::IInterfaceAccess* CNodeCollection::InsertArray(uint32_t uiIndex, const sdv::u8string& ssName) { - return {}; + // Insert the array node + std::string ssAccessName = ssName; + std::string ssTOML; + if (GetType() == sdv::toml::ENodeType::node_array) + { + ssTOML = "[]"; + ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "]"; + } + else + ssTOML = QuoteText(ssName, EQuoteRequest::smart_key) + " = []"; + if (InsertTOML(uiIndex, ssTOML, true) != EInsertResult::insert_success) + return nullptr; + + // Return the inserted node. + return GetNodeDirect(ssAccessName); } - sdv::IInterfaceAccess* CNodeCollection::InsertTable(uint32_t /*uiIndex*/, const sdv::u8string& /*ssKeyName*/) + sdv::IInterfaceAccess* CNodeCollection::InsertTable(uint32_t uiIndex, const sdv::u8string& ssName, + sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) { - // TODO: Do not insert a table if the parent is a table array. Only allow the use of InsertTableArray to insert table array - // tables. - return {}; + // Insert inline or standard table + std::string ssAccessName = ssName; + std::string ssTOML; + if (Inline() || ePreference == sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline) + { + if (GetType() == sdv::toml::ENodeType::node_array) + { + ssTOML = "{}"; + ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "]"; + } + else + ssTOML = QuoteText(ssName, EQuoteRequest::smart_key) + " = {}"; + } + else if (GetType() == sdv::toml::ENodeType::node_array) // Node is a table array + { + ssTOML = "[[" + QuoteText(GetName(), EQuoteRequest::smart_key) + "]]"; + ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "]"; + } + else + ssTOML = "[" + QuoteText(ssName, EQuoteRequest::smart_key) + "]"; + + // Insert the table node + if (InsertTOML(uiIndex, ssTOML, true) != EInsertResult::insert_success) + return nullptr; + + // Return the inserted node. + return GetNodeDirect(ssAccessName); } - sdv::IInterfaceAccess* CNodeCollection::InsertTableArray(uint32_t /*uiIndex*/, const sdv::u8string& /*ssName*/) + sdv::IInterfaceAccess* CNodeCollection::InsertTableArray(uint32_t uiIndex, const sdv::u8string& ssName, + sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) { - return {}; + // Insert inline or standard table array + std::string ssAccessName = ssName; + std::string ssTOML; + + if (Inline() || ePreference == sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline) + { + if (GetType() == sdv::toml::ENodeType::node_array) + { + ssTOML = "[{}]"; + ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "][0]"; + } + else + { + // If there is a table array with the same name already, the node will be added to the end. + auto ptrExistingNode = Direct(ssName); + auto ptrExistingArray = ptrExistingNode ? ptrExistingNode->Cast() : std::shared_ptr(); + if (ptrExistingArray && !ptrExistingArray->TableArray()) + return nullptr; + size_t nCount = ptrExistingArray ? ptrExistingArray->GetCount() : 0; + ssTOML = QuoteText(ssName, EQuoteRequest::smart_key) + " = [{}]"; + ssAccessName += "[" + std::to_string(nCount) + "]"; + } + } + else + { + // If there are table arrays, the node is added to the table array, but the order is determined by this node. Count the + // amount of nodes before the insertion point. + ssTOML = "[[" + QuoteText(ssName, EQuoteRequest::smart_key) + "]]"; + size_t nTableArrayCnt = std::count_if(m_vecNodeOrder.begin(), + m_vecNodeOrder.begin() + std::min(static_cast(uiIndex), m_vecNodeOrder.size()), + [&](const std::shared_ptr& rptrNode) { return rptrNode->GetName() == ssName; }); + ssAccessName += "[" + std::to_string(nTableArrayCnt) + "]"; + } + + // Insert the table node + if (InsertTOML(uiIndex, ssTOML, true) != EInsertResult::insert_success) + return nullptr; + + // Return the inserted node. + return GetNodeDirect(ssAccessName); } - sdv::toml::INodeCollectionInsert::EInsertResult CNodeCollection::InsertTOML(const sdv::u8string& /*ssTOML*/, bool /*bRollbackOnPartly*/) + sdv::toml::INodeCollectionInsert::EInsertResult CNodeCollection::InsertTOML(uint32_t uiIndex, const sdv::u8string& ssTOML, + bool bRollbackOnPartly) { - return {}; + try + { + // Parser for the new TOML code + CParser parser; + + // In case the target collection is an inline array, add additional code to allow insertion to take place between + // brackets. + std::shared_ptr ptrCollection; + if (Cast() && Inline()) + { + std::string ssTOMLArray = "DummyArray = [" + ssTOML; + size_t nLastCommentPos = ssTOML.rfind('#'); + if (nLastCommentPos != std::string::npos) + { + // Is there a newline following the comment? If not, add a newline. + size_t nLastNewline = ssTOML.find('\n', nLastCommentPos); + if (nLastNewline == std::string::npos) + ssTOMLArray += "\n"; + } + ssTOMLArray += "]"; + + // Parse the specialized TOML + parser.Process(ssTOMLArray); + + auto ptrArray = parser.Root().Direct("DummyArray"); + if (ptrArray) + ptrCollection = ptrArray->Cast(); + if (!ptrCollection) + return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; + } + else + { + // Normal processing + parser.Process(ssTOML); + ptrCollection = parser.Root().Cast(); + } + + // Check for duplicate naming. + std::vector> vecDuplicateNameNodes; + for (auto ptrPotentialNewNode : ptrCollection->m_lstNodes) + { + // When this collection is an inline-collection, all the nodes from the new collection need to be inline as well. + if (Inline()) + { + if (ptrPotentialNewNode->Cast()) + ptrPotentialNewNode->Cast()->MakeInline(); + } + + if (Cast()) continue; // Names in an array have no meaning; skip this phase. + auto ptrNode = Direct(ptrPotentialNewNode->GetName()); + if (!ptrNode) continue; + + // If the node is a standard table array and this node collection is a standard table array with the same name, this + // is allowed. In all other situations, a duplicate name is not allowed. + bool bPotentialTableArray = ptrPotentialNewNode->Cast() && + ptrPotentialNewNode->Cast()->TableArray(); + bool bExistingTableArray = ptrNode->Cast() && + (ptrNode->Cast()->TableArray() || !ptrNode->Cast()->GetCount()); + if (bPotentialTableArray && bExistingTableArray) + { + // Make standard or inline, dependable on the target + if (ptrNode->Inline()) + ptrPotentialNewNode->Cast()->MakeInline(); + else + ptrPotentialNewNode->Cast()->MakeStandard(); + continue; + } + + // Node exists already. Add to duplicate name nodes vector (or fail if no all nodes should fit). + if (bRollbackOnPartly) + return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; + vecDuplicateNameNodes.push_back(ptrPotentialNewNode); + } + + // Delete the nodes that have duplicate names + for (auto& rptrNode : vecDuplicateNameNodes) + rptrNode->DeleteNode(); + + // Any nodes left to insert? + if (!ptrCollection->GetCount()) + return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; + + // Add the child nodes to the collection (use a copy of the nodes list, since SetParentPtr changes the node list). + auto lstCopyNodes = ptrCollection->m_lstNodes; + std::vector> vecSkipNodes; + for (auto ptrNewNode : lstCopyNodes) + { + // If the node is a table array, it is allowed that the tables are added to an existing table array. + auto ptrNewArray = ptrNewNode->Cast(); + auto ptrExistingNode = Direct(ptrNewNode->GetName()); + auto ptrExistingArray = ptrExistingNode ? ptrExistingNode->Cast() : std::shared_ptr(); + if (ptrNewArray && ptrNewArray->TableArray() && ptrExistingArray && + (ptrExistingArray->TableArray() || !ptrExistingArray->GetCount())) + { + // Determine the current index of the array. This is used to determine to insert the nodes before or behind + // the existing nodes within the array. + uint32_t uiCurrentPos = ptrExistingNode->GetIndex(); + + // Insert each table in the already existing table array. Assign the parent pointer and the location in the + // parent table array. + for (uint32_t uiNewArrayIndex = 0; uiNewArrayIndex < ptrNewArray->GetCount(); uiNewArrayIndex++) + { + auto ptrNewTable = ptrNewArray->Get(uiNewArrayIndex); + ptrNewTable->SetParentPtr(ptrExistingArray); + ptrExistingArray->m_vecNodeOrder.insert( + uiCurrentPos > uiIndex ? (ptrExistingArray->m_vecNodeOrder.begin() + uiNewArrayIndex) : + ptrExistingArray->m_vecNodeOrder.end(), + ptrNewTable); + } + + // Do not process the table array when processing the position of all nodes in the parsed TOML code. + vecSkipNodes.push_back(ptrNewNode); + continue; + } + + // Inserting values from an existing array into an array with the same name (which is not a table array) is not + // automatically supported. For this the array content needs to specifically be added to the existing array. + if (ptrExistingArray) + return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; // Should not happen + + // Asign the new parent pointer. + if (ptrNewNode->Cast() && Inline()) + ptrNewNode->Cast()->MakeInline(); + ptrNewNode->SetParentPtr(Cast()); + } + + // Insert the nodes in the node vector at the required index. + size_t nTargetIndex = uiIndex; + for (uint32_t uiNewNodeIndex = 0; uiNewNodeIndex < ptrCollection->GetCount(); uiNewNodeIndex++) + { + auto ptrNewNode = ptrCollection->Get(uiNewNodeIndex); + + // Skip when the node is already inserted previously. + if (std::find(vecSkipNodes.begin(), vecSkipNodes.end(), ptrNewNode) != vecSkipNodes.end()) continue; + + // The target index should not supercede the size of the vector + if (nTargetIndex > GetCount()) + nTargetIndex = GetCount(); + + // If the node collection is + // - inline table or table array + // --> standard nodes will be converted to inline nodes. + // --> table-arrays can be added to additional table arrays + // - standard table or table array + // --> standard nodes will be inserted behind the inline nodes. + // --> inline nodes will be inserted before the standard nodes. + if (Inline()) + { + // Only inline nodes can be inserted in other inline nodes. + if (ptrNewNode->Cast()) + ptrNewNode->Cast()->MakeInline(); + } + else + { + // Determine the begin of standard nodes in the node vector + size_t nBeginStandardIndex = 0; + for (; nBeginStandardIndex < m_vecNodeOrder.size(); nBeginStandardIndex++) + { + if (!m_vecNodeOrder[nBeginStandardIndex]->Inline()) + break; + } + + // Correct the target index if necessary + if (ptrNewNode->Inline()) + { + if (nTargetIndex > nBeginStandardIndex) + nTargetIndex = nBeginStandardIndex; + } + else + { + if (nTargetIndex < nBeginStandardIndex) + nTargetIndex = nBeginStandardIndex; + } + } + + // Insert the node + m_vecNodeOrder.insert(m_vecNodeOrder.begin() + nTargetIndex, ptrNewNode); + + // Increase the target index for the next target + nTargetIndex++; + } + + // Return the result + return vecDuplicateNameNodes.empty() ? sdv::toml::INodeCollectionInsert::EInsertResult::insert_success : + sdv::toml::INodeCollectionInsert::EInsertResult::insert_partly_success; + } + catch (const sdv::toml::XTOMLParseException& /*rexcept*/) + { + return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; + } } bool CNodeCollection::DeleteNode() @@ -1050,41 +1336,219 @@ namespace toml_parser return bRet && CNode::DeleteNode(); } - bool CNodeCollection::RemoveNode(const std::shared_ptr& rptrNode) + bool CNodeCollection::CanMakeInline() const { - // Remove from the view + // To make a node inline is always possible. + return true; + } + + bool CNodeCollection::MakeInline() + { + if (Inline()) return true; + bool bRet = Inline(true); + if (bRet) AutomaticFormat(); + return bRet; + } + + bool CNodeCollection::CanMakeStandard() const + { + // To make a node as standard node, this is only possible when the parent is not inline. + auto ptrParent = GetParentPtr(); + if (!ptrParent) return false; + if (ptrParent->Inline()) return false; + return true; + } + + bool CNodeCollection::MakeStandard() + { + if (!Inline()) return true; + bool bRet = Inline(false); + if (bRet) AutomaticFormat(); + return bRet; + } + + bool CNodeCollection::DeleteNode(const std::shared_ptr& rptrNode) + { + // Remove from the view (just in case). RemoveFromView(rptrNode); + // In case this is an array collection, it could still happen, that the node is in the vector. Explicitly remove the node + // from the vector. + auto itElementVec = std::find(m_vecNodeOrder.begin(), m_vecNodeOrder.end(), rptrNode); + if (itElementVec != m_vecNodeOrder.end()) + m_vecNodeOrder.erase(itElementVec); + // Find the element - auto itElement = std::find(m_lstNodes.begin(), m_lstNodes.end(), rptrNode); - if (itElement == m_lstNodes.end()) + auto itElementLst = std::find(m_lstNodes.begin(), m_lstNodes.end(), rptrNode); + if (itElementLst == m_lstNodes.end()) return false; // Shift the node into the recycle bin. m_lstRecycleBin.push_back(rptrNode); - m_lstNodes.erase(itElement); + m_lstNodes.erase(itElementLst); + return true; + } + + bool CNodeCollection::InsertIntoView(uint32_t uiIndex, const std::shared_ptr& rptrNode) + { + // Check for vaidity + if (!rptrNode) return false; + if (Inline() && !rptrNode->Inline()) return false; // Cannot assign a standard node to an inline node + if (!IsDescendant(rptrNode)) return false; // Must descent directly from this node + + // Get the current view + auto ptrView = rptrNode->GetViewPtr(); + if (!ptrView) ptrView = rptrNode->GetParentPtr(); + if (!ptrView) return false; + + // Determine the start of the standard nodes in the vector. + uint32_t uiStartStandard = 0; + while (m_vecNodeOrder.size() > uiStartStandard && !m_vecNodeOrder[uiStartStandard]->IsStandard()) + ++uiStartStandard; + + // Determine the potential new index location. + uint32_t uiNewIndex = uiIndex; + if (rptrNode->IsStandard() && uiIndex < uiStartStandard) uiNewIndex = uiStartStandard; + else if (rptrNode->IsInline() && uiIndex > uiStartStandard) uiNewIndex = uiStartStandard; + if (static_cast(uiNewIndex) > m_vecNodeOrder.size()) uiNewIndex = static_cast(m_vecNodeOrder.size()); + + // Special case, if the current view is identical and the node will be located at the same position, do not do anything. + // This is necessary to keep the code snippets in place. Otherwise they might be relocated. + // Also, if the current position is before the new position, correct the new index (since we remove the current position). + if (ptrView == Cast()) + { + uint32_t uiCurrentIndex = rptrNode->GetIndex(); + if (uiCurrentIndex == uiNewIndex) return true; // Nothing to do... + if (uiCurrentIndex < uiNewIndex) uiNewIndex--; + if (uiCurrentIndex < uiStartStandard) uiStartStandard--; + } + + // Remove a previous view assignment (if necessary this will shift code snippets as well). + ptrView->RemoveFromView(rptrNode); + + // Check whether the node is already part of the view + if (std::find(m_vecNodeOrder.begin(), m_vecNodeOrder.end(), rptrNode) != m_vecNodeOrder.end()) + return true; // Insert only once. + + // Determine the first position and the beyond-last position + uint32_t uiFirst = rptrNode->IsStandard() ? uiStartStandard : 0; + uint32_t uiLast = rptrNode->IsStandard() ? static_cast(m_vecNodeOrder.size()) : uiStartStandard; + + // If the node is inserted at the first position, shift the out-of-scope code snippet from the first node. + if ((uiNewIndex <= uiFirst) && (uiFirst < m_vecNodeOrder.size())) + { + rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) + .Insert(m_vecNodeOrder[uiFirst]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); + m_vecNodeOrder[uiFirst]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).Clear(); + } + + // If the node is inserted at the last position, shift the out-of-scope code snippet from the last node. + if ((uiNewIndex >= uiLast) && (uiFirst < m_vecNodeOrder.size())) + { + rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) + .Append(m_vecNodeOrder[uiLast - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); + m_vecNodeOrder[uiLast - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).Clear(); + } + + // Insert the node + m_vecNodeOrder.insert(m_vecNodeOrder.begin() + uiNewIndex, rptrNode); + return true; } bool CNodeCollection::RemoveFromView(const std::shared_ptr& rptrNode) { - // Find the element and remove it from the view - auto itElement = std::find(m_vecNodeOrder.begin(), m_vecNodeOrder.end(), rptrNode); - if (itElement != m_vecNodeOrder.end()) + // Check for validity + if (!rptrNode) return false; + if (rptrNode->GetViewPtr() != Cast()) return false; + + // Get the current position + uint32_t uiCurrentIndex = rptrNode->GetIndex(); + if (uiCurrentIndex >= m_vecNodeOrder.size()) return false; // Not present (likely already removed) + + // Determine the start of the standard nodes in the vector. + uint32_t uiStartStandard = 0; + while (m_vecNodeOrder.size() > uiStartStandard && !m_vecNodeOrder[uiStartStandard]->IsStandard()) + ++uiStartStandard; + + // Determine the first position and the beyond-last position + uint32_t uiFirst = rptrNode->IsStandard() ? uiStartStandard : 0; + uint32_t uiLast = rptrNode->IsStandard() ? static_cast(m_vecNodeOrder.size()) : uiStartStandard; + + // Move the out-of-scope code to another node + if ((uiLast - uiFirst) > 1) { - m_vecNodeOrder.erase(itElement); - return true; + // If removing the node and a node with the same classification (standard or inline) is still available, shift the + // out-of-scope code to the left-over nodes with the same classification. + if (uiCurrentIndex <= uiFirst) + { + // The first node will be removed - copy the out-of-scope code to the node behind + m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) + .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); + m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) + .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); + } else if (uiCurrentIndex >= (uiLast - 1)) + { + // The last node will be removed - copy the out-of-scope code to the node before + m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) + .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); + m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) + .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); + } else + { + // Copy the out-of-scope code from the end of the node to the node following + m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) + .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); + m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) + .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); + } } - return false; + else if (m_vecNodeOrder.size() > 1) + { + // If there are no more nodes with the same classification, but there are nodes with another classification, then move + // the out-of-scope code to the node with the other classification. + if (uiCurrentIndex == 0) + { + // The first node will be removed - copy the out-of-scope code to the node behind. + m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) + .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); + m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) + .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); + } else + { + // The last node will be removed - copy the out-of-scope code to the node before. + m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) + .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); + m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) + .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); + } + } + else if (IsStandard()) + { + // If there are no nodes left over, but this node is a standard node, move the out-of-scope code to this node. + CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) + .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); + CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) + .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); + } + + // If this node is not a standard node, then the out-of-scope code cannot be maintained. + + // Delete the out-of-scope code. + rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).Clear(); + rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind).Clear(); + + // Remove the actual pointer. + rptrNode->SetViewPtr(nullptr); + + // And remove from the vector (but only when the collection is not an array). + if (!Cast()) + m_vecNodeOrder.erase(m_vecNodeOrder.begin() + uiCurrentIndex); + + return true; } - bool CNodeCollection::CheckLast(const std::shared_ptr& rptrNode) - { - // NOTE: Check for an empty vector. This is actually an error and should not occur. - return m_vecNodeOrder.empty() || m_vecNodeOrder.back() == rptrNode; - } - - uint32_t CNodeCollection::FindIndex(const std::shared_ptr& rptrNode) + uint32_t CNodeCollection::FindIndex(const std::shared_ptr& rptrNode) const { // Find the element for (uint32_t uiIndex = 0; uiIndex < m_vecNodeOrder.size(); uiIndex++) @@ -1095,6 +1559,19 @@ namespace toml_parser return sdv::toml::npos; } + bool CNodeCollection::IsDescendant(const std::shared_ptr& rptrNode) const + { + // Check wihin the list of nodes, whether the provided node is a child node and if not, ask all child collection nodes. + for (auto ptrChild : m_lstNodes) + { + if (ptrChild == rptrNode) return true; + auto ptrCollection = ptrChild->Cast(); + if (ptrCollection && ptrCollection->IsDescendant(rptrNode)) + return true; + } + return false; + } + CTable::CTable(CParser& rparser, const std::string& rssName, const std::string& rssRawName, bool bDefaultInline, bool bExplicit /*= true*/) : CNodeCollection(rparser, rssName, rssRawName), m_bDefinedExplicitly(bExplicit), m_bInline(bDefaultInline) @@ -1110,44 +1587,24 @@ namespace toml_parser if (IsDeleted()) return {}; // Create a writable copy of the context and provide this node as potential top node - CGenContext rContextCopy = rContext; - rContextCopy.InitTopMostNode(shared_from_this()); + CGenContext contextCopy = rContext; + contextCopy.InitTopMostNode(shared_from_this()); std::stringstream sstream; - // Determine whether the statement should be embedded (same line separated by commas) and should have an assignment (not - // having a key name). The collection node is inline when it is defined as inline or is embedded into a parent node. auto ptrParent = GetParentPtr(); - bool bEmbedded = ptrParent ? ptrParent->Inline() : false; - bool bAssignment = !bEmbedded || (ptrParent ? !ptrParent->Cast() : false); // No assignment only with normal array - bool bInline = bEmbedded || Inline(); - bool bLastNode = ptrParent ? ptrParent->CheckLast(std::const_pointer_cast(shared_from_this())) : false; bool bRoot = dynamic_cast(this) ? true : false; - std::string ssContext; - if (rContextCopy.KeyContext().empty()) - { - ssContext = rContextCopy.PrefixKey(); - std::string ssGenContext = rContextCopy.TopMostNode() ? - static_cast(GetPath(false)) : std::string(); - if (!ssContext.empty() && !ssGenContext.empty()) - ssContext += "."; - ssContext += ssGenContext; - } else - ssContext = rContextCopy.KeyContext(); - std::string ssKeyPath = rContextCopy.PrefixKey(); - std::string ssFullContext = ssContext.empty() ? rContextCopy.PrefixKey() : ssContext; - std::string ssRelKeyPath = - GetCustomPath(rContextCopy.PrefixKey(), ssContext.empty() ? rContextCopy.PrefixKey() : ssContext); - if (!ssKeyPath.empty() && !ssRelKeyPath.empty()) - ssKeyPath += "."; - if (!ssFullContext.empty() && !ssRelKeyPath.empty()) - ssFullContext += "."; - ssKeyPath += ssRelKeyPath; - ssFullContext += ssRelKeyPath; - // Impossible option: suppressing the table name (due to root flag) and being embedded. - if (bEmbedded && bRoot) return {}; + if (contextCopy.Embedded() && bRoot) + return {}; + + // Special case, table as part of table array - but only if the parent is included in the generation. This can be identified + // by the top most flag of the context. + bool bTableArray = false; + if (!contextCopy.TopMostNode() || contextCopy.CheckOption(toml_parser::EGenerateOptions::full_header)) + bTableArray = + !contextCopy.Embedded() && ptrParent && ptrParent->Cast() && ptrParent->Cast()->TableArray(); // Do not print explicit tables with only tables inside (except if they have no children at all or if they have one or more // inline tables). @@ -1157,42 +1614,38 @@ namespace toml_parser { std::shared_ptr ptrNode = Get(uiIndex); if (!ptrNode) continue; - if (!ptrNode->Cast() || ptrNode->Inline()) + if (bTableArray || !ptrNode->Cast() || ptrNode->Inline()) bDoNotPrint = false; } - // Special case, table as part of table array - but only if the parent is included in the generation. This can be identified - // by the top most flag of the context. - bool bTableArray = false; - if (rContextCopy.TopMostNode() == 0) - bTableArray = !bEmbedded && ptrParent && ptrParent->Cast() && ptrParent->Cast()->TableArray(); - // Add unconnected pre node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::out_of_scope_comment_before)) - .Compose(CCodeSnippet::EComposeMode::compose_standalone); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).Compose(CCodeSnippet::EComposeMode::compose_standalone_before, contextCopy); // Add pre node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_before)) - .Compose(CCodeSnippet::EComposeMode::compose_before); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_before).Compose(CCodeSnippet::EComposeMode::compose_before, contextCopy); // Do we need to start a table? - if (!bDoNotPrint && !bInline && !ssKeyPath.empty()) + if (!bDoNotPrint && !contextCopy.Inline() && !contextCopy.KeyPath().empty()) { if (bTableArray) - sstream << "[[" << ssKeyPath << "]]"; + sstream << "[[" << contextCopy.KeyPath() << "]]"; else - sstream << "[" << ssKeyPath << "]"; + sstream << "[" << contextCopy.KeyPath() << "]"; // Add post node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_behind)). - Compose(bLastNode ? CCodeSnippet::EComposeMode::compose_inline : CCodeSnippet::EComposeMode::compose_behind); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_behind) + .Compose(CCodeSnippet::EComposeMode::compose_behind, contextCopy); } - // Stream the table - if (bAssignment && bInline && !bDoNotPrint) // Not an array entry - sstream << ssRelKeyPath << "="; - if ((bEmbedded || bInline) && (!bDoNotPrint || ptrParent->Cast())) // Embedded table in an array - sstream << CodeSnippet(m_nPreValueCode).Compose(CCodeSnippet::EComposeMode::compose_inline) << "{"; + // Stream the table - for a standard table first stream the inline nodes, then the standard nodes + if (contextCopy.Assignment() && contextCopy.Inline() && !bDoNotPrint) // Not an array entry + { + //if (!CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_before).HasCode()) sstream << " "; + sstream << contextCopy.RelKeyPath() << "="; + if (!CodeSnippet(m_nPreValueCode).HasCode())sstream << " "; + } + if ((contextCopy.Inline()) && (!bDoNotPrint || ptrParent->Cast())) // Embedded table in an array + sstream << CodeSnippet(m_nPreValueCode).Compose(CCodeSnippet::EComposeMode::compose_inline, contextCopy) << "{"; for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++) { std::shared_ptr ptrNode = Get(uiIndex); @@ -1200,40 +1653,33 @@ namespace toml_parser // If the node has a view pointer, which is not identical to this pointer, do not print the node... it will be printed // by a different node. - if (!ptrNode->IsPartOfView(rContextCopy, Cast())) continue; + if (!ptrNode->IsPartOfView(contextCopy, Cast())) continue; - if (bInline || ptrNode->Inline()) + if (contextCopy.Inline() || ptrNode->Inline()) { // Inline nodes are only presented with a relative path (in most cases this is no path) for each node. // If the parent node is a table array, the do-not-print-flag is active (because this node is a table as part of // the array, which is used for data management and not for printing the node key) - use the full context for // printing. - sstream << - ptrNode->GenerateTOML(rContextCopy.CopyWithContext((bDoNotPrint && !bTableArray) ? ssContext : ssFullContext)); + sstream << ptrNode->GenerateTOML(contextCopy.CopyWithContext( + (bDoNotPrint && !bTableArray) ? contextCopy.KeyContext() : contextCopy.FullKeyPath(), ptrNode, uiIndex == GetCount() - 1)); } else { // Explicit collection nodes (tables and table-arrays) are presented with the complete path for each node. - sstream << ptrNode->GenerateTOML(rContextCopy.CopyWithContext(ssContext)); + sstream << ptrNode->GenerateTOML(contextCopy.CopyWithContext(contextCopy.KeyContext(), ptrNode, uiIndex == GetCount() - 1)); } } - if ((!bDoNotPrint || (ptrParent && ptrParent->Cast())) && (bEmbedded || bInline)) // Embedded table in an array + if ((!bDoNotPrint || (ptrParent && ptrParent->Cast())) && (contextCopy.Embedded() || contextCopy.Inline())) // Embedded table in an array { - sstream << CodeSnippet(m_nPostValuesArray).Compose(CCodeSnippet::EComposeMode::compose_inline) << "}"; - - // Add a comma if this is not the last node. - if (bEmbedded && !bLastNode) - sstream << ","; + sstream << CodeSnippet(m_nPostValuesArray).Compose(CCodeSnippet::EComposeMode::compose_inline, contextCopy) << "}"; // Add post node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_behind)). - Compose(bLastNode ? CCodeSnippet::EComposeMode::compose_inline : CCodeSnippet::EComposeMode::compose_behind); - + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_behind).Compose(CCodeSnippet::EComposeMode::compose_behind, contextCopy); } // Add unconnected post node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::out_of_scope_comment_behind)) - .Compose(CCodeSnippet::EComposeMode::compose_standalone); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind).Compose(CCodeSnippet::EComposeMode::compose_standalone_behind, contextCopy); return sstream.str(); } @@ -1277,7 +1723,7 @@ namespace toml_parser return; // Unexpected eState = bExplicit ? EState::table_def_close_or_separator : EState::assignment_or_separator; ssKeyName = refToken.get().StringValue(); - CodeSnippet(m_nPreKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPreKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_syntax_table_open: if (eState != EState::key_or_value_or_table_def_open) @@ -1289,25 +1735,25 @@ namespace toml_parser if (eState != EState::table_def_close_or_separator) return; // Unexpected eState = EState::done; - CodeSnippet(m_nPostKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_syntax_inline_table_open: if (eState != EState::key_or_value_or_table_def_open && eState != EState::table_open) return; // Unexpected eState = EState::table_content_and_close; - CodeSnippet(m_nPreValueCode).List() = std::move(lstWhitespace); + CodeSnippet(m_nPreValueCode).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_syntax_assignment: if (eState != EState::assignment_or_separator) return; // Unexpected eState = EState::table_open; - CodeSnippet(m_nPostKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_syntax_dot: if (eState != EState::assignment_or_separator && eState != EState::table_def_close_or_separator) return; // Unexpected eState = EState::key; - CodeSnippet(m_nPostKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; default: return; // Unexpected @@ -1350,7 +1796,7 @@ namespace toml_parser if (eState != EState::done) return; - CodeSnippet(m_nPostValueCode).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostValueCode).SetTokenList(std::move(lstWhitespace)); } bool CTable::Inline() const @@ -1368,6 +1814,7 @@ namespace toml_parser m_bInline = true; else m_bInline = bInline; + return m_bInline == bInline; } @@ -1381,6 +1828,126 @@ namespace toml_parser m_bDefinedExplicitly = true; } + bool CTable::Combine(const std::shared_ptr& rptrCollection) + { + if (!rptrCollection) return false; // No collection supplied. + if (rptrCollection == Cast()) return true; // Collections are identical, nothing to combine. + if (rptrCollection->IsDescendant(shared_from_this())) return false; // Circular reference. + + // Run through the provided collection list + // If nodes don't exist in the current collection, add the nodes + // If nodes exist in the current collection, but are different, update the nodes + + bool bResult = true; + CGenContext contextGeneration; + contextGeneration.SetOption(EGenerateOptions::reduce_whitespace); + contextGeneration.SetOption(EGenerateOptions::full_header); + for (uint32_t uiIndex = 0; uiIndex < rptrCollection->GetCount(); uiIndex++) + { + // Get the new and potentially existing nodes. + auto ptrNewNode = rptrCollection->Get(uiIndex); + if (!ptrNewNode) continue; + auto ptrExistNode = Direct(ptrNewNode->GetName()); + + // Check whether the node is really new; if it is, add the node + // Exception to the rule... when the existing node is a table array and the node to add is a table, do not do replace the node. + if (!ptrExistNode || (ptrNewNode->GetType() == sdv::toml::ENodeType::node_table && + ptrExistNode->Cast() && ptrExistNode->Cast()->TableArray())) + { + bResult &= InsertTOML(sdv::toml::npos, ptrNewNode->GenerateTOML(contextGeneration), true) == + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success; + continue; + } + + // The node exists. Are the node types identical? If not, replace the existing node with the new node + if (ptrNewNode->GetType() != ptrExistNode->GetType()) + { + uint32_t uiExistIndex = ptrExistNode->GetIndex(); + bool bLocalResult = ptrExistNode->DeleteNode(); + bResult &= bLocalResult; + if (bLocalResult) + bResult &= InsertTOML(uiExistIndex, ptrNewNode->GenerateTOML(contextGeneration), true) == + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success; + continue; + } + + // The nodes have identical type. Are they a collection themselves, then use the combine function of the collection. + if (ptrExistNode->Cast()) + { + bResult &= ptrExistNode->Cast()->Combine(ptrNewNode->Cast()); + continue; + } + + // Both nodes are value nodes of the same type. Check the value to be identical. If not, update the value. + if (ptrExistNode->GetValue() != ptrNewNode->GetValue()) + { + bResult &= ptrExistNode->ChangeValue(ptrNewNode->GetValue()); + continue; + } + + // Nodes are identical; there's nothing to do. + } + + return bResult; + } + + bool CTable::Reduce(const std::shared_ptr& rptrCollection) + { + if (!rptrCollection) return false; // No collection supplied. + if (rptrCollection == Cast()) return false; // Collections are identical, this would empty the collection. + if (rptrCollection->IsDescendant(shared_from_this())) return false; // Circular reference. + + // Run through the provided collection list. + // If nodes don't exist in the current collection, leave the nodes in the collection. + // If nodes exist in the current collection, but are different, leave the nodes in the collection. + // If the nodes exist and are identical, delete the nodes from the collection. + + bool bResult = true; + for (uint32_t uiIndex = 0; uiIndex < rptrCollection->GetCount(); uiIndex++) + { + // Get the new and potentially existing nodes. + auto ptrReductorNode = rptrCollection->Get(uiIndex); + if (!ptrReductorNode) continue; + auto ptrExistNode = Direct(ptrReductorNode->GetName()); + + // Check whether the node is exists; if not, leave the node in the collection. + if (!ptrExistNode) continue; + + // Deal with table arrays... + if (ptrReductorNode->GetType() == sdv::toml::ENodeType::node_table && ptrReductorNode->GetParentPtr() && + ptrReductorNode->GetParentPtr()->Cast() && ptrReductorNode->GetParentPtr()->Cast()->TableArray() && + ptrExistNode->Cast() && ptrExistNode->Cast()->TableArray()) + { + bResult &= ptrExistNode->Cast()->Reduce(ptrReductorNode->GetParentPtr()->Cast()); + continue; + } + + // The node exists. Are the node types identical? If not, leave the node in the collection + if (ptrReductorNode->GetType() != ptrExistNode->GetType()) continue; + + // The nodes have identical type. Are they a collection themselves, then use the reduce function of the collection. + if (ptrExistNode->Cast()) + { + bool bLocalResult = ptrExistNode->Cast()->Reduce(ptrReductorNode->Cast()); + bResult &= bLocalResult; + + // if the collection is empty after the reduction; remove the collection completely. + if (bLocalResult && !ptrExistNode->Cast()->GetCount()) + bResult &= ptrExistNode->DeleteNode(); + continue; + } + + // Both nodes are value nodes of the same type. Check the value to be identical. If not, leave the node in the + // collection. + if (ptrExistNode->GetValue() != ptrReductorNode->GetValue()) continue; + + // Nodes are identical and therefore should be removed from the collection. + bResult &= ptrExistNode->DeleteNode(); + } + + return bResult; + } + CArray::CArray(CParser& rparser, const std::string& rssName, const std::string& rssRawName, bool bExplicitTableArray /*= false*/) : CNodeCollection(rparser, rssName, rssRawName), m_bDefinedExplicitly(bExplicitTableArray), m_bInline(!bExplicitTableArray) @@ -1403,10 +1970,21 @@ namespace toml_parser // which are automatically indexed by occurance. if (!GetCount()) return {}; // Unexpected ssSecond = rssPath; - } else - uiIndex = std::stoi(prKey.first); + } + else + { + try + { + uiIndex = static_cast(std::stoul(prKey.first)); + } catch (const std::exception&) + { + return {}; + } + } // Get the node + if (uiIndex == sdv::toml::npos && GetCount()) + uiIndex = GetCount() - 1; if (uiIndex >= GetCount()) return {}; // Not found std::shared_ptr ptrNode = Get(uiIndex); if (!ptrNode) return {}; @@ -1422,8 +2000,8 @@ namespace toml_parser if (IsDeleted()) return {}; // Create a writable copy of the context and provide this node as potential top node - CGenContext rContextCopy = rContext; - rContextCopy.InitTopMostNode(shared_from_this()); + CGenContext contextCopy = rContext; + contextCopy.InitTopMostNode(shared_from_this()); std::stringstream sstream; @@ -1431,82 +2009,55 @@ namespace toml_parser // having a key name). auto ptrParent = GetParentPtr(); if (!ptrParent) return {}; - bool bEmbedded = ptrParent->Inline(); - bool bAssignment = !bEmbedded || !ptrParent->Cast(); // No assignment only with normal array - bool bInline = bEmbedded || Inline(); - bool bLastNode = ptrParent->CheckLast(std::const_pointer_cast(shared_from_this())); - std::string ssContext; - if (rContextCopy.KeyContext().empty()) - { - ssContext = rContextCopy.PrefixKey(); - std::string ssGenContext = rContextCopy.TopMostNode() ? - (TableArray() ? static_cast(ptrParent->GetPath(false)) : static_cast(GetPath(false))) : - std::string(); - if (!ssContext.empty() && !ssGenContext.empty()) - ssContext += "."; - ssContext += ssGenContext; - } - else - ssContext = rContextCopy.KeyContext(); - std::string ssKeyPath = rContextCopy.PrefixKey(); - std::string ssFullContext = ssContext.empty() ? rContextCopy.PrefixKey() : ssContext; - std::string ssRelKeyPath = - GetCustomPath(rContextCopy.PrefixKey(), ssContext.empty() ? rContextCopy.PrefixKey() : ssContext); - if (!ssKeyPath.empty() && !ssRelKeyPath.empty()) - ssKeyPath += "."; - if (!ssFullContext.empty() && !ssRelKeyPath.empty()) - ssFullContext += "."; - ssKeyPath += ssRelKeyPath; - ssFullContext += ssRelKeyPath; - // Stream only for inline - if (bInline) + if (contextCopy.Inline()) { // Add unconnected pre node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::out_of_scope_comment_before)) - .Compose(CCodeSnippet::EComposeMode::compose_standalone); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).Compose(CCodeSnippet::EComposeMode::compose_standalone_before, contextCopy); // Add pre node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_before)) - .Compose(CCodeSnippet::EComposeMode::compose_before); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_before).Compose(CCodeSnippet::EComposeMode::compose_before, contextCopy); // Add assignment - if (bAssignment) // Not an array entry - sstream << ssRelKeyPath << "="; + if (contextCopy.Assignment()) // Not an array entry + { + //if (!CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_before).HasCode()) sstream << " "; + sstream << contextCopy.RelKeyPath() << "="; + if (!CodeSnippet(m_nPreValueCode).HasCode())sstream << " "; + } - sstream << CodeSnippet(m_nPreValueCode).Compose(CCodeSnippet::EComposeMode::compose_inline) << "["; + sstream << CodeSnippet(m_nPreValueCode).Compose(CCodeSnippet::EComposeMode::compose_inline, contextCopy) << "["; } // Stream the array content - for (uint32_t ui = 0; ui < GetCount(); ui++) + for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++) { - std::shared_ptr ptrNode = Get(ui); + std::shared_ptr ptrNode = Get(uiIndex); if (!ptrNode) continue; - // If the node has a view pointer, which is not identical to this pointer, do not print the node... it will be printed - // by a different node. - if (!ptrNode->IsPartOfView(rContextCopy, Cast())) + // If the node has a view pointer, which is not identical to the this pointer, do not print the node... it will be + // printed by a different node. + if (!ptrNode->IsPartOfView(contextCopy, Cast())) continue; - sstream << ptrNode->GenerateTOML(rContextCopy.CopyWithContext(bInline ? ssFullContext : ssContext)); + // Generate the TOML for the array node. Copy the context with the full key path (when inline) or the key context of + // this array (when table array). + sstream << ptrNode->GenerateTOML( + contextCopy.CopyWithContext(contextCopy.Inline() ? contextCopy.FullKeyPath() : contextCopy.KeyContext(), + ptrNode, uiIndex == GetCount() - 1)); } // Stream only for inline - if (bInline) + if (contextCopy.Inline()) { - sstream << CodeSnippet(m_nPostValuesArray).Compose(CCodeSnippet::EComposeMode::compose_inline) << "]"; - - // Add a comma if this is not the last node. - if (bEmbedded && !bLastNode) sstream << ","; + sstream << CodeSnippet(m_nPostValuesArray).Compose(CCodeSnippet::EComposeMode::compose_inline, contextCopy) << "]"; // Add post node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::comment_behind)). - Compose(bLastNode ? CCodeSnippet::EComposeMode::compose_inline : CCodeSnippet::EComposeMode::compose_behind); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::comment_behind).Compose(CCodeSnippet::EComposeMode::compose_behind, contextCopy); // Add unconnected post node comments - sstream << CodeSnippet(static_cast(sdv::toml::INodeInfo::ECommentFlags::out_of_scope_comment_behind)) - .Compose(CCodeSnippet::EComposeMode::compose_standalone); + sstream << CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind).Compose(CCodeSnippet::EComposeMode::compose_standalone_behind, contextCopy); } return sstream.str(); @@ -1547,25 +2098,25 @@ namespace toml_parser return; // Unexpected eState = EState::assignment_or_separator; ssKeyName = refToken.get().StringValue(); - CodeSnippet(m_nPreKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPreKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_syntax_array_open: if (eState != EState::key_or_value && eState != EState::array_open) return; // Unexpected eState = EState::array_content_and_close; - CodeSnippet(m_nPreValueCode).List() = std::move(lstWhitespace); + CodeSnippet(m_nPreValueCode).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_syntax_assignment: if (eState != EState::assignment_or_separator) return; // Unexpected eState = EState::array_open; - CodeSnippet(m_nPostKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; case ETokenCategory::token_syntax_dot: if (eState != EState::assignment_or_separator) return; // Unexpected eState = EState::key; - CodeSnippet(m_nPostKeyCode, ssKeyName).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostKeyCode, ssKeyName).SetTokenList(std::move(lstWhitespace)); break; default: return; // Unexpected @@ -1576,6 +2127,13 @@ namespace toml_parser if (eState != EState::array_content_and_close) return; // Unexpected + // Check the last node of the array for a potential comma in the code snippet following the node assignment. + if (GetCount()) + { + auto ptrNode = Get(GetCount() - 1); + m_bLastChildNodeWithComma = ptrNode->CodeSnippet(m_nPostNodeCode).HasComma(); + } + // Process the main node tokens from array close lstWhitespace.clear(); for (std::reference_wrapper refToken = rNodeRange.NodeMainFinish().Begin(); @@ -1598,7 +2156,7 @@ namespace toml_parser if (eState != EState::array_content_and_close) return; // Unexpected eState = EState::post_value; - CodeSnippet(m_nPostValuesArray).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostValuesArray).SetTokenList(std::move(lstWhitespace)); break; default: return; // Unexpected @@ -1609,7 +2167,7 @@ namespace toml_parser if (eState != EState::post_value) return; - CodeSnippet(m_nPostValueCode).List() = std::move(lstWhitespace); + CodeSnippet(m_nPostValueCode).SetTokenList(std::move(lstWhitespace)); } bool CArray::TableArray() const @@ -1617,7 +2175,7 @@ namespace toml_parser // At least one table is needed if (!GetCount()) return false; - // Iterate throught he child-nodes and if a non-table is found, return false. + // Iterate through the child-nodes and if a non-table is found, return false. for (uint32_t ui = 0; ui < GetCount(); ui++) { std::shared_ptr ptrNode = Get(ui); @@ -1630,6 +2188,16 @@ namespace toml_parser return true; } + bool CArray::CanMakeStandard() const + { + // Check with the collection + if (!CNodeCollection::CanMakeStandard()) + return false; + + // To make an array as standard node, this is only possible when the array is a table array. + return TableArray(); + } + bool CArray::Inline() const { return m_bInline; @@ -1637,13 +2205,233 @@ namespace toml_parser bool CArray::Inline(bool bInline) { + // Does anything change? + if (bInline == m_bInline) return true; + + // When making standard, the parent should not be inline. + auto ptrParent = GetParentPtr(); + if (!ptrParent) return false; + if (ptrParent->Inline() && !bInline) return false; + // The array is inline per default. Only in the case of a table array (an array with only tables, and at least one table) - // the array ould be explicit. - if (bInline && !TableArray()) return false; + // the array could be standard. + if (!TableArray()) return false; + + // Check the order in the vector of the view or of the parent. When becoming inline, must be located before the standard + // nodes. When becoming standard, must be located behind the inline nodes. + // Special case for a table array: when becoming inline, remove all the tables from the view and add the array to the + // correct location. When becoming standard, remove the array from the location and add all the tables into the view. m_bInline = bInline; + if (bInline) + { + // If becoming inline, remove the view and make all children inline as well. + uint32_t uiCurrentPos = sdv::toml::npos; + for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++) + { + auto ptrNode = Get(uiIndex); + if (!ptrNode) continue; + if (uiCurrentPos == sdv::toml::npos) + uiCurrentPos = ptrNode->GetIndex(); + if (ptrNode->Cast()) + ptrNode->Cast()->MakeInline(); + InsertIntoView(uiIndex, ptrNode); + } + + // Move the array into the view of the parent. + ptrParent->InsertIntoView(uiCurrentPos, shared_from_this()); + } else + { + // Get the current position + uint32_t uiCurrentPos = GetIndex(); + + // Make all the children standard as well and move the into the view of the parent. + for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++) + { + auto ptrNode = Get(uiIndex); + if (!ptrNode) continue; + if (uiCurrentPos == sdv::toml::npos) + uiCurrentPos = ptrNode->GetIndex(); + if (ptrNode->Inline() && ptrNode->Cast()) + ptrNode->Cast()->MakeStandard(); + ptrParent->InsertIntoView(uiCurrentPos, ptrNode); + if (uiCurrentPos != sdv::toml::npos) + ++uiCurrentPos; + } + + // Remove the array from the view of the parent + ptrParent->RemoveFromView(shared_from_this()); + } + return true; } + bool CArray::LastNodeWithSucceedingComma() const + { + return m_bLastChildNodeWithComma; + } + + bool CArray::Combine(const std::shared_ptr& rptrCollection) + { + if (!rptrCollection) return false; // No collection supplied. + if (rptrCollection == Cast()) return true; // Collections are identical, nothing to combine. + if (rptrCollection->IsDescendant(shared_from_this())) return false; // Circular reference. + + // Differentiate between a table array and a normal array: + // - A normal array should be identical. If not, take over the complete array. + // - A table array might contain tables that are identical, extend with tables that are not. + + // Run through the provided collection list + // If nodes are a different type, replace the nodes of the existing array by the nodes of the new array. + // In all other cases, update the content if necessary. + + bool bResult = true; + toml_parser::CGenContext contextGeneration; + contextGeneration.SetOption(EGenerateOptions::reduce_whitespace); + contextGeneration.SetOption(EGenerateOptions::full_header); + bool bTableArray = TableArray() && rptrCollection->Cast()->TableArray(); + for (uint32_t uiIndex = 0; uiIndex < rptrCollection->GetCount(); uiIndex++) + { + // Get the new and potentially existing nodes. + auto ptrNewNode = rptrCollection->Get(uiIndex); + if (!ptrNewNode) continue; + std::shared_ptr ptrExistNode; + uint32_t uiTargetIndex = uiIndex; + if (bTableArray) + { + // Go through the current array and compare the generate TOML string for identical values. + toml_parser::CGenContext contextComparison; + contextComparison.SetOption(EGenerateOptions::no_comments); + std::string ssNewNodeTOML = ptrNewNode->GenerateTOML(contextComparison); + bool bIdentical = false; + for (uint32_t uiExistIndex = 0; !bIdentical && (uiExistIndex < GetCount()); uiExistIndex++) + { + ptrExistNode = Get(uiExistIndex); + if (!ptrExistNode) continue; + std::string ssExistTOML = ptrExistNode->GenerateTOML(contextComparison); + bIdentical = ssNewNodeTOML == ssExistTOML; + } + if (!bIdentical) + { + uiTargetIndex = sdv::toml::npos; // New node will be added at the end of the array. + ptrExistNode.reset(); + } + } else + ptrExistNode = Get(uiIndex); + + // Check whether there is an existing node. If not, add the node. + if (!ptrExistNode) + { + bResult &= InsertTOML(uiTargetIndex, ptrNewNode->GenerateTOML(contextGeneration), true) == + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success; + continue; + } + + // The node exists. Are the node types identical? If not, replace the existing node with the new node + if (ptrNewNode->GetType() != ptrExistNode->GetType()) + { + uint32_t uiExistIndex = ptrExistNode->GetIndex(); + bool bLocalResult = ptrExistNode->DeleteNode(); + bResult &= bLocalResult; + if (bLocalResult) + bResult &= InsertTOML(uiExistIndex, ptrNewNode->GenerateTOML(contextGeneration), true) == + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success; + continue; + } + + // The nodes have identical type. Are they a collection themselves, then use the combine function of the collection. + if (ptrExistNode->Cast()) + { + bResult &= ptrExistNode->Cast()->Combine(ptrNewNode->Cast()); + continue; + } + + // Both nodes are value nodes of the same type. Check the value to be identical. If not, update the value. + if (ptrExistNode->GetValue() != ptrNewNode->GetValue()) + { + bResult &= ptrExistNode->ChangeValue(ptrNewNode->GetValue()); + continue; + } + + // Nodes are identical; there's nothing to do. + } + + // Check whether the current array is larger than the provided array. If so, remove the entries at the end to make it the + // same size. + while (!bTableArray && (GetCount() > rptrCollection->GetCount())) + { + auto ptrExistNode = Get(GetCount() - 1); + if (!ptrExistNode) break; + bool bLocalResult = ptrExistNode->DeleteNode(); + bResult &= bLocalResult; + if (!bLocalResult) break; // Prevent endless loop. + } + + return bResult; + } + + bool CArray::Reduce(const std::shared_ptr& rptrCollection) + { + if (!rptrCollection) return false; // No collection supplied. + if (rptrCollection == Cast()) return false; // Collections are identical, this would empty the collection. + if (rptrCollection->IsDescendant(shared_from_this())) return false; // Circular reference. + + // DIfferentiate between a table array and a normal array: + // - A normal array should be identical. If so, remove the complete array. + // - A table array might contain tables that are identical, those are removed. All others stay. + + // For normal array, simply compare the generate TOML strings. If identical, remove the array content. + bool bResult = true; + toml_parser::CGenContext context; + context.SetOption(EGenerateOptions::no_comments); + if (!TableArray() || !rptrCollection->Cast()->TableArray()) + { + // At least one of the arrays is a normal arrays, compare content as strings + std::string ssReductorNodeTOML = rptrCollection->GenerateTOML(context); + std::string ssExistNodeTOML = GenerateTOML(context); + if (ssReductorNodeTOML == ssExistNodeTOML) + { + // Remove the content of the array + while (GetCount()) + { + auto ptrExistNode = Get(GetCount() - 1); + if (!ptrExistNode) break; + bool bLocalResult = ptrExistNode->DeleteNode(); + bResult &= bLocalResult; + if (!bLocalResult) break; // Prevent endless loop. + } + } + return bResult; + } + + // For table arrays, run through the provided collection list. + // If nodes don't exist in the current collection, leave the nodes in the collection. + // If nodes exist in the current collection, but are different, leave the nodes in the collection. + // If the nodes exist and are identical, delete the nodes from the collection. + for (uint32_t uiIndex = 0; uiIndex < rptrCollection->GetCount(); uiIndex++) + { + // Get the new and potentially existing nodes. + auto ptrReductorNode = rptrCollection->Get(uiIndex); + if (!ptrReductorNode) continue; + + // Go through the current array and compare the generate TOML string for identical values. + std::string ssReductorNodeTOML = ptrReductorNode->GenerateTOML(context); + for (uint32_t uiExistIndex = 0; uiExistIndex < GetCount(); uiExistIndex++) + { + std::shared_ptr ptrExistNode = Get(uiExistIndex); + if (!ptrExistNode) continue; + std::string ssExistTOML = ptrExistNode->GenerateTOML(context); + if (ssReductorNodeTOML == ssExistTOML) + { + // Found an identical table. Remove the table from the array. + bResult &= ptrExistNode->DeleteNode(); + break; + } + } + } + + return bResult; + } + bool CRootTable::DeleteNode() { // Cannot delete the root node. diff --git a/sdv_services/core/toml_parser/parser_node_toml.h b/sdv_services/core/toml_parser/parser_node_toml.h index 06d5b57..1d99701 100644 --- a/sdv_services/core/toml_parser/parser_node_toml.h +++ b/sdv_services/core/toml_parser/parser_node_toml.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 PARSER_NODE_TOML_H #define PARSER_NODE_TOML_H @@ -9,10 +23,9 @@ #include #include -#include #include -#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& 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& 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 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& rptrNode); + + std::shared_ptr 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, 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 std::shared_ptr 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 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& 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 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& 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 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 - * - * - * = - * - * @endcode - */ - class CCodeSnippet - { - public: - /** - * @brief Access the token list. - * @return Reference to the list with tokens. - */ - std::list& 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 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 m_ptrParent; ///< Weak pointer to the parent node (if existing). std::weak_ptr 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& rptrNode); + bool DeleteNode(const std::shared_ptr& 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& rptrNode); /** * @brief Remove a node from a view. @@ -985,20 +1136,19 @@ namespace toml_parser */ bool RemoveFromView(const std::shared_ptr& 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& 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& rptrNode); + uint32_t FindIndex(const std::shared_ptr& 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& rptrNode) const; /** * @brief Generic inserting function for nodes. @@ -1023,6 +1173,24 @@ namespace toml_parser template std::shared_ptr 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& 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& 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& 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& 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& 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& 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(Parser(), prKey.first.get().StringValue(), prKey.first.get().RawString()); - ptrTableArray->SetParentPtr(Cast()); - // Add to the list - m_lstNodes.push_back(ptrTableArray); + // Set the parent pointer; this will add the node to the list. + ptrTableArray->SetParentPtr(Cast()); } else ptrTableArray = (*itNode)->template Cast(); @@ -1401,6 +1615,7 @@ namespace toml_parser // Create the table. ptrNode = ptrTableArray->Insert(sdv::toml::npos, CTokenRange(prKey.first, prKey.first.get().Next()), false, true); + ptrNode->SetViewPtr(Cast()); } else if (!Cast() && 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(Parser(), prKey.first.get().StringValue(), prKey.first.get().RawString(), rtArgs...); - ptrNode->SetParentPtr(Cast()); - // Add to the list - m_lstNodes.push_back(ptrNode); + // Set the parent pointer; this will add the node to the list. + ptrNode->SetParentPtr(Cast()); // 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(Parser(), prKey.first.get().StringValue(), prKey.first.get().RawString(), false, false); ptrNode->SetParentPtr(Cast()); - m_lstNodes.push_back(ptrNode); } // Insert the node in the child node @@ -1490,15 +1703,14 @@ namespace toml_parser ptrNode = ptrNodeCollection->Insert(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()); } // Insert the node at the requested location if this node is inline or this is the view of the node. auto itPos = (static_cast(uiIndex) >= m_vecNodeOrder.size()) ? m_vecNodeOrder.end() : m_vecNodeOrder.begin() + static_cast(uiIndex); m_vecNodeOrder.insert(itPos, ptrNode); + if (ptrNode->GetParentPtr() != Cast()) + ptrNode->SetViewPtr(Cast()); // Return the result return ptrNode; diff --git a/sdv_services/core/toml_parser/parser_toml.cpp b/sdv_services/core/toml_parser/parser_toml.cpp index 36e84c4..226825d 100644 --- a/sdv_services/core/toml_parser/parser_toml.cpp +++ b/sdv_services/core/toml_parser/parser_toml.cpp @@ -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 #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(*this); + m_ptrCurrentCollection = m_ptrRoot; + } + auto ptrCollection = m_ptrRoot->Cast(); return *ptrCollection.get(); } @@ -246,7 +267,7 @@ namespace toml_parser auto ptrCurrentCollectionStored = m_ptrCurrentCollection; ptrNode = m_ptrCurrentCollection->Insert(sdv::toml::npos, rrangeKeyPath); m_ptrCurrentCollection = ptrNode->Cast(); - 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(sdv::toml::npos, rrangeKeyPath, true); m_ptrCurrentCollection = ptrNode->Cast(); - 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 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."); diff --git a/sdv_services/core/toml_parser/parser_toml.h b/sdv_services/core/toml_parser/parser_toml.h index 34eeb9a..a390d66 100644 --- a/sdv_services/core/toml_parser/parser_toml.h +++ b/sdv_services/core/toml_parser/parser_toml.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 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 m_stackEnvironment; ///< Tracking of environments in nested structures. - std::shared_ptr m_ptrRoot; ///< The one root node. - std::shared_ptr m_ptrCurrentCollection; ///< The current collection node. - CLexer m_lexer; ///< Lexer. + enum_stack m_stackEnvironment; ///< Tracking of environments in nested structures. + std::shared_ptr m_ptrRoot; ///< The one root node. + std::shared_ptr m_ptrCurrentCollection; ///< The current collection node. + CLexer m_lexer; ///< Lexer. }; } // namespace toml_parser diff --git a/sdv_services/core/toml_parser_util.h b/sdv_services/core/toml_parser_util.h index 1434a6f..188ee2f 100644 --- a/sdv_services/core/toml_parser_util.h +++ b/sdv_services/core/toml_parser_util.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 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 \ No newline at end of file diff --git a/sdv_services/data_dispatch_service/CMakeLists.txt b/sdv_services/data_dispatch_service/CMakeLists.txt index c8e8ee0..538bc5f 100644 --- a/sdv_services/data_dispatch_service/CMakeLists.txt +++ b/sdv_services/data_dispatch_service/CMakeLists.txt @@ -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) diff --git a/sdv_services/data_dispatch_service/dispatchservice.cpp b/sdv_services/data_dispatch_service/dispatchservice.cpp index d9299e9..b68aa03 100644 --- a/sdv_services/data_dispatch_service/dispatchservice.cpp +++ b/sdv_services/data_dispatch_service/dispatchservice.cpp @@ -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(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 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 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 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 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) diff --git a/sdv_services/data_dispatch_service/dispatchservice.h b/sdv_services/data_dispatch_service/dispatchservice.h index 5e6bce6..9fdc816 100644 --- a/sdv_services/data_dispatch_service/dispatchservice.h +++ b/sdv_services/data_dispatch_service/dispatchservice.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 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 m_mapRxSignals; ///< Signal object map. std::map 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 m_lstTransactions; ///< List with transactions. diff --git a/sdv_services/data_dispatch_service/signal.cpp b/sdv_services/data_dispatch_service/signal.cpp index 0fa064c..c08fb77 100644 --- a/sdv_services/data_dispatch_service/signal.cpp +++ b/sdv_services/data_dispatch_service/signal.cpp @@ -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& 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 lock(m_mtxSignalObjects); for (auto& rvtConsumer : m_mapConsumers) diff --git a/sdv_services/data_dispatch_service/signal.h b/sdv_services/data_dispatch_service/signal.h index ccc0fbf..501bfd4 100644 --- a/sdv_services/data_dispatch_service/signal.h +++ b/sdv_services/data_dispatch_service/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 + ********************************************************************************/ + #ifndef SIGNAL_H #define SIGNAL_H diff --git a/sdv_services/data_dispatch_service/transaction.cpp b/sdv_services/data_dispatch_service/transaction.cpp index e1b9e31..e7c0a20 100644 --- a/sdv_services/data_dispatch_service/transaction.cpp +++ b/sdv_services/data_dispatch_service/transaction.cpp @@ -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" diff --git a/sdv_services/data_dispatch_service/transaction.h b/sdv_services/data_dispatch_service/transaction.h index e724861..612abd8 100644 --- a/sdv_services/data_dispatch_service/transaction.h +++ b/sdv_services/data_dispatch_service/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 + ********************************************************************************/ + #ifndef TRANSACTION_H #define TRANSACTION_H diff --git a/sdv_services/data_dispatch_service/trigger.cpp b/sdv_services/data_dispatch_service/trigger.cpp index 641f16e..9f9e3bc 100644 --- a/sdv_services/data_dispatch_service/trigger.cpp +++ b/sdv_services/data_dispatch_service/trigger.cpp @@ -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 && diff --git a/sdv_services/data_dispatch_service/trigger.h b/sdv_services/data_dispatch_service/trigger.h index 0a6354a..0748715 100644 --- a/sdv_services/data_dispatch_service/trigger.h +++ b/sdv_services/data_dispatch_service/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 TRIGGER_H #define TRIGGER_H diff --git a/sdv_services/hardware_ident/CMakeLists.txt b/sdv_services/hardware_ident/CMakeLists.txt index f97fa83..d321d61 100644 --- a/sdv_services/hardware_ident/CMakeLists.txt +++ b/sdv_services/hardware_ident/CMakeLists.txt @@ -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) diff --git a/sdv_services/hardware_ident/hardware_ident.cpp b/sdv_services/hardware_ident/hardware_ident.cpp index fa28792..4d7418c 100644 --- a/sdv_services/hardware_ident/hardware_ident.cpp +++ b/sdv_services/hardware_ident/hardware_ident.cpp @@ -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 diff --git a/sdv_services/hardware_ident/hardware_ident.h b/sdv_services/hardware_ident/hardware_ident.h index a360e96..44669f0 100644 --- a/sdv_services/hardware_ident/hardware_ident.h +++ b/sdv_services/hardware_ident/hardware_ident.h @@ -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() diff --git a/sdv_services/ipc_com/CMakeLists.txt b/sdv_services/ipc_com/CMakeLists.txt index ac51c32..ef48e26 100644 --- a/sdv_services/ipc_com/CMakeLists.txt +++ b/sdv_services/ipc_com/CMakeLists.txt @@ -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) diff --git a/sdv_services/ipc_com/com_channel.cpp b/sdv_services/ipc_com/com_channel.cpp index ca03a5a..31656a6 100644 --- a/sdv_services/ipc_com/com_channel.cpp +++ b/sdv_services/ipc_com/com_channel.cpp @@ -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" diff --git a/sdv_services/ipc_com/com_channel.h b/sdv_services/ipc_com/com_channel.h index e963805..175b157 100644 --- a/sdv_services/ipc_com/com_channel.h +++ b/sdv_services/ipc_com/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 + ********************************************************************************/ + #ifndef COM_CHANNEL_H #define COM_CHANNEL_H diff --git a/sdv_services/ipc_com/com_ctrl.cpp b/sdv_services/ipc_com/com_ctrl.cpp index 0da59e1..0999351 100644 --- a/sdv_services/ipc_com/com_ctrl.cpp +++ b/sdv_services/ipc_com/com_ctrl.cpp @@ -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 #include @@ -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, diff --git a/sdv_services/ipc_com/com_ctrl.h b/sdv_services/ipc_com/com_ctrl.h index 39e88c2..7a10887 100644 --- a/sdv_services/ipc_com/com_ctrl.h +++ b/sdv_services/ipc_com/com_ctrl.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_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> CallStub(sdv::ps::TMarshallID tStubID, sdv::sequence>& seqInputData); private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status. std::mutex m_mtxChannels; ///< Protect the channel map. std::vector> m_vecChannels; ///< Channel vector. std::vector m_vecInitialConnectMon; ///< Initial connection monitor. diff --git a/sdv_services/ipc_com/marshall_object.cpp b/sdv_services/ipc_com/marshall_object.cpp index 21e4a8d..c922783 100644 --- a/sdv_services/ipc_com/marshall_object.cpp +++ b/sdv_services/ipc_com/marshall_object.cpp @@ -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 diff --git a/sdv_services/ipc_com/marshall_object.h b/sdv_services/ipc_com/marshall_object.h index 95d725a..5195693 100644 --- a/sdv_services/ipc_com/marshall_object.h +++ b/sdv_services/ipc_com/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 MARSHALL_OBJECT_H #define MARSHALL_OBJECT_H diff --git a/sdv_services/ipc_connect/CMakeLists.txt b/sdv_services/ipc_connect/CMakeLists.txt index b580e9b..ead16bc 100644 --- a/sdv_services/ipc_connect/CMakeLists.txt +++ b/sdv_services/ipc_connect/CMakeLists.txt @@ -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) diff --git a/sdv_services/ipc_connect/client.cpp b/sdv_services/ipc_connect/client.cpp index 38144a9..366f321 100644 --- a/sdv_services/ipc_connect/client.cpp +++ b/sdv_services/ipc_connect/client.cpp @@ -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 #include @@ -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("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; } diff --git a/sdv_services/ipc_connect/client.h b/sdv_services/ipc_connect/client.h index fde706d..7b50edc 100644 --- a/sdv_services/ipc_connect/client.h +++ b/sdv_services/ipc_connect/client.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 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 m_mapRepositoryProxies; ///< map of remote repositories. }; diff --git a/sdv_services/ipc_connect/listener.cpp b/sdv_services/ipc_connect/listener.cpp index f0f38de..ba3f582 100644 --- a/sdv_services/ipc_connect/listener.cpp +++ b/sdv_services/ipc_connect/listener.cpp @@ -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 #include @@ -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(); 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("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("LocalChannelControl"); + if (!pEndpoint) { - uint32_t uiInstanceID = config.GetDirect("Listener.Instance").GetValue(); - m_bLocalListener = true; - pEndpoint = sdv::core::GetObject("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("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("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 diff --git a/sdv_services/ipc_connect/listener.h b/sdv_services/ipc_connect/listener.h index c27a990..1e36b7a 100644 --- a/sdv_services/ipc_connect/listener.h +++ b/sdv_services/ipc_connect/listener.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 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. diff --git a/sdv_services/ipc_shared_mem/CMakeLists.txt b/sdv_services/ipc_shared_mem/CMakeLists.txt index 4fc2936..6247564 100644 --- a/sdv_services/ipc_shared_mem/CMakeLists.txt +++ b/sdv_services/ipc_shared_mem/CMakeLists.txt @@ -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) diff --git a/sdv_services/ipc_shared_mem/channel_mgnt.cpp b/sdv_services/ipc_shared_mem/channel_mgnt.cpp index a69fdbc..9506e10 100644 --- a/sdv_services/ipc_shared_mem/channel_mgnt.cpp +++ b/sdv_services/ipc_shared_mem/channel_mgnt.cpp @@ -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 -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) diff --git a/sdv_services/ipc_shared_mem/channel_mgnt.h b/sdv_services/ipc_shared_mem/channel_mgnt.h index 5ac9a24..6784152 100644 --- a/sdv_services/ipc_shared_mem/channel_mgnt.h +++ b/sdv_services/ipc_shared_mem/channel_mgnt.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 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 GetClassAliasesStaticMyTest() \ +#define TEST_DECLARE_OBJECT_CLASS_ALIAS(...) \ + static sdv::sequence GetClassAliasesStaticMyTest() \ { \ return sdv::sequence({__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> m_mapChannels; ///< Map with channels. CWatchDog m_watchdog; ///< Process monitor for connections. }; diff --git a/sdv_services/ipc_shared_mem/connection.cpp b/sdv_services/ipc_shared_mem/connection.cpp index 7bd9a2a..7b7fd5e 100644 --- a/sdv_services/ipc_shared_mem/connection.cpp +++ b/sdv_services/ipc_shared_mem/connection.cpp @@ -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 diff --git a/sdv_services/ipc_shared_mem/connection.h b/sdv_services/ipc_shared_mem/connection.h index 913625c..3a627e4 100644 --- a/sdv_services/ipc_shared_mem/connection.h +++ b/sdv_services/ipc_shared_mem/connection.h @@ -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 diff --git a/sdv_services/ipc_shared_mem/in_process_mem_buffer.h b/sdv_services/ipc_shared_mem/in_process_mem_buffer.h index 3aabbc5..17f1a0b 100644 --- a/sdv_services/ipc_shared_mem/in_process_mem_buffer.h +++ b/sdv_services/ipc_shared_mem/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 + ********************************************************************************/ + #ifndef IN_PROCESS_MEM_BUFFER_H #define IN_PROCESS_MEM_BUFFER_H diff --git a/sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp b/sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp index 2fb11d0..85c9f9b 100644 --- a/sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp +++ b/sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp @@ -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 diff --git a/sdv_services/ipc_shared_mem/mem_buffer_accessor.h b/sdv_services/ipc_shared_mem/mem_buffer_accessor.h index eead15c..45872bb 100644 --- a/sdv_services/ipc_shared_mem/mem_buffer_accessor.h +++ b/sdv_services/ipc_shared_mem/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 + ********************************************************************************/ + #ifndef MEM_BUFFER_ACCESSOR_H #define MEM_BUFFER_ACCESSOR_H diff --git a/sdv_services/ipc_shared_mem/shared_mem_buffer_posix.h b/sdv_services/ipc_shared_mem/shared_mem_buffer_posix.h index 4013865..d7dfc8f 100644 --- a/sdv_services/ipc_shared_mem/shared_mem_buffer_posix.h +++ b/sdv_services/ipc_shared_mem/shared_mem_buffer_posix.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 diff --git a/sdv_services/ipc_shared_mem/shared_mem_buffer_windows.h b/sdv_services/ipc_shared_mem/shared_mem_buffer_windows.h index 41507d4..1e2aba7 100644 --- a/sdv_services/ipc_shared_mem/shared_mem_buffer_windows.h +++ b/sdv_services/ipc_shared_mem/shared_mem_buffer_windows.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 diff --git a/sdv_services/ipc_shared_mem/watchdog.cpp b/sdv_services/ipc_shared_mem/watchdog.cpp index 543699d..9cbb30d 100644 --- a/sdv_services/ipc_shared_mem/watchdog.cpp +++ b/sdv_services/ipc_shared_mem/watchdog.cpp @@ -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 diff --git a/sdv_services/ipc_shared_mem/watchdog.h b/sdv_services/ipc_shared_mem/watchdog.h index 4eefdd0..73f1af0 100644 --- a/sdv_services/ipc_shared_mem/watchdog.h +++ b/sdv_services/ipc_shared_mem/watchdog.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 WATCH_DOG_H #define WATCH_DOG_H diff --git a/sdv_services/manifest_util/CMakeLists.txt b/sdv_services/manifest_util/CMakeLists.txt index e934c30..59a4e91 100644 --- a/sdv_services/manifest_util/CMakeLists.txt +++ b/sdv_services/manifest_util/CMakeLists.txt @@ -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) diff --git a/sdv_services/manifest_util/manifest_util.cpp b/sdv_services/manifest_util/manifest_util.cpp index 7c378ce..c7f4a6b 100644 --- a/sdv_services/manifest_util/manifest_util.cpp +++ b/sdv_services/manifest_util/manifest_util.cpp @@ -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 #include diff --git a/sdv_services/manifest_util/manifest_util.h b/sdv_services/manifest_util/manifest_util.h index 2b69816..8ff61a1 100644 --- a/sdv_services/manifest_util/manifest_util.h +++ b/sdv_services/manifest_util/manifest_util.h @@ -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") /** diff --git a/sdv_services/process_control/CMakeLists.txt b/sdv_services/process_control/CMakeLists.txt index a546f52..777847b 100644 --- a/sdv_services/process_control/CMakeLists.txt +++ b/sdv_services/process_control/CMakeLists.txt @@ -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(process_control VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_services/process_control/process_control.cpp b/sdv_services/process_control/process_control.cpp index 015748c..5ba14f0 100644 --- a/sdv_services/process_control/process_control.cpp +++ b/sdv_services/process_control/process_control.cpp @@ -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 "process_control.h" #include "../../global/base64.h" #include "../../global/exec_dir_helper.h" @@ -43,50 +56,22 @@ CProcessControl::~CProcessControl() Shutdown(); } -void CProcessControl::Initialize(const sdv::u8string& /*ssObjectConfig*/) +bool CProcessControl::OnInitialize() { - if (m_eObjectStatus != sdv::EObjectStatus::initialization_pending) return; - // Without monitor no trigger... m_threadMonitor = std::thread(&CProcessControl::MonitorThread, this); - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } -sdv::EObjectStatus CProcessControl::GetStatus() const -{ - return m_eObjectStatus; -} - -void CProcessControl::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 CProcessControl::Shutdown() +void CProcessControl::OnShutdown() { // TODO: Close process handles - if (m_eObjectStatus == sdv::EObjectStatus::destruction_pending) return; - m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress; // Shutdown the monitor m_bShutdown = true; if (m_threadMonitor.joinable()) m_threadMonitor.join(); - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; } bool CProcessControl::AllowProcessControl() const diff --git a/sdv_services/process_control/process_control.h b/sdv_services/process_control/process_control.h index 1a1cf21..1d1334a 100644 --- a/sdv_services/process_control/process_control.h +++ b/sdv_services/process_control/process_control.h @@ -1,13 +1,15 @@ -/** - * @file process_control.h - * @author Erik Verhoeven - * @brief - * @version 1.0 - * @date 2024-08-16 +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG * - * @copyright Copyright ZF Friedrichshafen AG (c) 2024 + * 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 PROCESS_CONTROL_H #define PROCESS_CONTROL_H @@ -22,8 +24,8 @@ /** * @brief Process control service class */ -class CProcessControl : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::process::IProcessInfo, - public sdv::process::IProcessLifetime, public sdv::process::IProcessControl +class CProcessControl : public sdv::CSdvObject, public sdv::process::IProcessInfo, public sdv::process::IProcessLifetime, + public sdv::process::IProcessControl { public: /** @@ -38,7 +40,6 @@ public: // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(sdv::process::IProcessInfo) SDV_INTERFACE_ENTRY(sdv::process::IProcessLifetime) SDV_INTERFACE_CHECK_CONDITION(AllowProcessControl()) @@ -46,34 +47,22 @@ 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("ProcessControlService") DECLARE_OBJECT_SINGLETON() /** - * @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; - - /** - * @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; + virtual bool OnInitialize() override; // Ignore cppcheck warning for not using dynamic binding when being called through the destructor. // cppcheck-suppress virtualCallInConstructor /** - * @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown. + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - void Shutdown() override; + virtual void OnShutdown() override; /** * @brief Check for the application context mode being main or standalone. @@ -135,41 +124,39 @@ public: */ void MonitorThread(); - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status. - std::mutex m_mtxProcessThreadShutdown; ///< Synchronize access -#ifdef _WIN32 - std::map> m_mapProcessThreadShutdown; ///< Map with process IDs and event handles -#elif __unix__ - std::set m_setProcessThreadShutdown; ///< Set with process IDs -#else -#error OS is not supported! -#endif - /** * @brief Process helper structure */ struct SProcessHelper { - sdv::process::TProcessID tProcessID = 0; ///< Process ID + sdv::process::TProcessID tProcessID = 0; ///< Process ID #ifdef _WIN32 - HANDLE hProcess = 0; ///< process handle + HANDLE hProcess = 0; ///< process handle #elif defined __unix__ - bool bNotAChild = false; ///< When set, the process is not a child of the monitor process. + bool bNotAChild = false; ///< When set, the process is not a child of the monitor process. #else #error OS is not supported! #endif - std::atomic_bool bRunning = true; ///< Set when the process is running and not terminated yet. - int64_t iRetVal = 0; ///< Process return value. - std::map mapAssociatedMonitors; ///< Map with associated monitors. - std::mutex mtxProcess; ///< Mutex for process access. - std::condition_variable cvWaitForProcess; ///< Condition variable to wait for process termination. + std::atomic_bool bRunning = true; ///< Set when the process is running and not terminated yet. + int64_t iRetVal = 0; ///< Process return value. + std::map mapAssociatedMonitors; ///< Map with associated monitors. + std::mutex mtxProcess; ///< Mutex for process access. + std::condition_variable cvWaitForProcess; ///< Condition variable to wait for process termination. }; - mutable std::mutex m_mtxProcesses; ///< Access control for monitor map. + std::mutex m_mtxProcessThreadShutdown; ///< Synchronize access +#ifdef _WIN32 + std::map> m_mapProcessThreadShutdown; ///< Map with process IDs and handles +#elif __unix__ + std::set m_setProcessThreadShutdown; ///< Set with process IDs +#else + #error OS is not supported! +#endif + mutable std::mutex m_mtxProcesses; ///< Access control for monitor map. std::map> m_mapProcesses; ///< Monitor map - uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie - std::map> m_mapMonitors; ///< Map with monitors. - std::atomic_bool m_bShutdown = false; ///< Set to shutdown the monitor thread. - std::thread m_threadMonitor; ///< Monitor thread. + uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie + std::map> m_mapMonitors; ///< Map with monitors. + std::atomic_bool m_bShutdown = false; ///< Set to shutdown the monitor thread. + std::thread m_threadMonitor; ///< Monitor thread. }; DEFINE_SDV_OBJECT(CProcessControl) diff --git a/sdv_services/proxy_stub/CMakeLists.txt b/sdv_services/proxy_stub/CMakeLists.txt index ef86c65..bffd3ba 100644 --- a/sdv_services/proxy_stub/CMakeLists.txt +++ b/sdv_services/proxy_stub/CMakeLists.txt @@ -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) diff --git a/sdv_services/task_timer/CMakeLists.txt b/sdv_services/task_timer/CMakeLists.txt index da05b1c..22b0327 100644 --- a/sdv_services/task_timer/CMakeLists.txt +++ b/sdv_services/task_timer/CMakeLists.txt @@ -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(task_timer VERSION 1.0 LANGUAGES CXX) diff --git a/sdv_services/task_timer/simulationtasktimer.cpp b/sdv_services/task_timer/simulationtasktimer.cpp index 3a61730..9b7ac02 100644 --- a/sdv_services/task_timer/simulationtasktimer.cpp +++ b/sdv_services/task_timer/simulationtasktimer.cpp @@ -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 "simulationtasktimer.h" #include #include @@ -44,7 +57,7 @@ void CSimulationTimer::SimulationStep(uint64_t uiSimulationStep) //#ifdef _WIN32 //void CSimulationTimer::ExecuteCallback() //{ -// if (m_rtimersvc.GetStatus() != sdv::EObjectStatus::running) return; +// if (m_rtimersvc.GetObjectState() != sdv::EObjectState::running) return; // if (!m_pExecute) return; // if (!m_bPrioritySet) // SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); @@ -62,52 +75,27 @@ CSimulationTaskTimerService::~CSimulationTaskTimerService() { } -void CSimulationTaskTimerService::Initialize(/*in*/ const sdv::u8string& /*ssObjectConfig*/) +bool CSimulationTaskTimerService::OnInitialize() { #ifdef _WIN32 - m_eObjectStatus = sdv::EObjectStatus::initializing; SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); // set up time resolution and maybe some other things timeBeginPeriod(1); #endif - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } -sdv::EObjectStatus CSimulationTaskTimerService::GetStatus() const -{ - return m_eObjectStatus; -} - -void CSimulationTaskTimerService::SetOperationMode(/*in*/ 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 CSimulationTaskTimerService::Shutdown() +void CSimulationTaskTimerService::OnShutdown() { #ifdef _WIN32 - m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress; timeEndPeriod(1); #endif - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; } sdv::IInterfaceAccess* CSimulationTaskTimerService::CreateTimer(uint32_t uiPeriod, sdv::IInterfaceAccess* pTask) { - if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr; + if (GetObjectState() != sdv::EObjectState::configuring) return nullptr; if (!uiPeriod) return nullptr; if (!pTask) return nullptr; sdv::core::ITaskExecute* pExecute = pTask->GetInterface(); diff --git a/sdv_services/task_timer/simulationtasktimer.h b/sdv_services/task_timer/simulationtasktimer.h index 6c1852d..7197c44 100644 --- a/sdv_services/task_timer/simulationtasktimer.h +++ b/sdv_services/task_timer/simulationtasktimer.h @@ -1,13 +1,15 @@ -/** -* @file simulationtasktimer.h -* @author Sudipta Babu Durjoy FRD DISS21 (mailto:sudipta.durjoy@zf.com) -* @brief -* @version 1.0 -* @date 2023-05-24 -* -* @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 SIMULATION_TASK_TIMER_H #define SIMULATION_TASK_TIMER_H @@ -108,8 +110,7 @@ private: /** * @brief Task timer class to execute task periodically */ -class CSimulationTaskTimerService : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::core::ITaskTimer, - public sdv::core::ITimerSimulationStep +class CSimulationTaskTimerService : public sdv::CSdvObject, public sdv::core::ITaskTimer, public sdv::core::ITimerSimulationStep { public: /** @@ -124,42 +125,25 @@ public: // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(sdv::core::ITaskTimer) SDV_INTERFACE_ENTRY(sdv::core::ITimerSimulationStep) 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("SimulationTaskTimerService") DECLARE_OBJECT_SINGLETON() /** - * @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(/*in*/ 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. - */ - virtual void SetOperationMode(/*in*/ sdv::EOperationMode eMode) 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 - */ - virtual void Shutdown() override; + virtual void OnShutdown() override; /** * @brief Method to execute the user-defined task periodically until ShutdownTask is called. @@ -170,7 +154,6 @@ public: */ virtual sdv::IInterfaceAccess* CreateTimer(uint32_t uiPeriod, sdv::IInterfaceAccess* pTask) override; - /** * @brief Method to set the time which has past from the last simulation step. * @param[in] uiSimulationStep the time in microseconds which has past from the last simulation step. @@ -184,9 +167,8 @@ public: void RemoveTimer(CSimulationTimer* pTimer); private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object operation status - std::mutex m_mtxTasks; ///< Mutex for tasks - std::map> m_mapTasks; ///< Set to get the active tasks + std::mutex m_mtxTasks; ///< Mutex for tasks + std::map> m_mapTasks; ///< Set to get the active tasks }; DEFINE_SDV_OBJECT(CSimulationTaskTimerService) diff --git a/sdv_services/task_timer/tasktimer.cpp b/sdv_services/task_timer/tasktimer.cpp index b1e84ff..8f1bf41 100644 --- a/sdv_services/task_timer/tasktimer.cpp +++ b/sdv_services/task_timer/tasktimer.cpp @@ -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 "tasktimer.h" #include #include @@ -96,7 +109,7 @@ CTimer::operator bool() const #ifdef _WIN32 void CTimer::ExecuteCallback() { - if (m_rtimersvc.GetStatus() != sdv::EObjectStatus::running) return; + if (m_rtimersvc.GetObjectState() != sdv::EObjectState::running) return; if (!m_pExecute) return; if (!m_bPrioritySet) SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); @@ -113,53 +126,28 @@ CTaskTimerService::~CTaskTimerService() { } -void CTaskTimerService::Initialize(/*in*/ const sdv::u8string& /*ssObjectConfig*/) +bool CTaskTimerService::OnInitialize() { #ifdef _WIN32 - m_eObjectStatus = sdv::EObjectStatus::initializing; SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); // set up time resolution and maybe some other things timeBeginPeriod(1); #endif - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } -sdv::EObjectStatus CTaskTimerService::GetStatus() const -{ - return m_eObjectStatus; -} - -void CTaskTimerService::SetOperationMode(/*in*/ 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 CTaskTimerService::Shutdown() +void CTaskTimerService::OnShutdown() { #ifdef _WIN32 - m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress; timeEndPeriod(1); #endif - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; } sdv::IInterfaceAccess* CTaskTimerService::CreateTimer(uint32_t uiPeriod, sdv::IInterfaceAccess* pTask) { - if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr; + if (GetObjectState() != sdv::EObjectState::configuring) return nullptr; if (!uiPeriod) return nullptr; if (!pTask) return nullptr; diff --git a/sdv_services/task_timer/tasktimer.h b/sdv_services/task_timer/tasktimer.h index 89a9fa5..f6c4619 100644 --- a/sdv_services/task_timer/tasktimer.h +++ b/sdv_services/task_timer/tasktimer.h @@ -1,13 +1,15 @@ -/** -* @file tasktimer.h -* @author Sudipta Babu Durjoy FRD DISS21 (mailto:sudipta.durjoy@zf.com) -* @brief -* @version 1.0 -* @date 2023-05-24 -* -* @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 TASK_TIMER_H #define TASK_TIMER_H @@ -19,6 +21,7 @@ #include #include #include +#include #ifdef _WIN32 // Resolve conflict @@ -93,10 +96,10 @@ private: sdv::core::ITaskExecute* m_pExecute = nullptr; ///< Pointer to the execution callback interface. #ifdef _WIN32 UINT m_uiTimerID = 0ul; ///< Timer ID - bool m_bPrioritySet = false; ///< When set, the priority of the task was increased. + std::atomic_bool m_bPrioritySet = false; ///< When set, the priority of the task was increased. #elif defined __unix__ timer_t m_timerid = 0; ///< Timer ID. - bool m_bRunning = false; ///< When set, the timer is running. + std::atomic_bool m_bRunning = false; ///< When set, the timer is running. std::mutex m_mtxExecution; ///< Prevent killing the timer when in execution. #endif }; @@ -104,7 +107,7 @@ private: /** * @brief Task timer class to execute task periodically */ -class CTaskTimerService : public sdv::CSdvObject, public sdv::IObjectControl, public sdv::core::ITaskTimer +class CTaskTimerService : public sdv::CSdvObject, public sdv::core::ITaskTimer { public: /** @@ -119,41 +122,24 @@ public: // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(sdv::core::ITaskTimer) 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("TaskTimerService") DECLARE_OBJECT_SINGLETON() /** - * @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(/*in*/ 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. - */ - virtual void SetOperationMode(/*in*/ sdv::EOperationMode eMode) 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 - */ - virtual void Shutdown() override; + virtual void OnShutdown() override; /** * @brief Method to execute the user-defined task periodically until ShutdownTask is called. @@ -171,9 +157,8 @@ public: void RemoveTimer(CTimer* pTimer); private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object operation status - std::mutex m_mtxTasks; ///< Mutex for tasks - std::map> m_mapTasks; ///< Set to get the active tasks + std::mutex m_mtxTasks; ///< Mutex for tasks + std::map> m_mapTasks; ///< Set to get the active tasks }; DEFINE_SDV_OBJECT(CTaskTimerService) diff --git a/sdv_services/uds_unix_sockets/CMakeLists.txt b/sdv_services/uds_unix_sockets/CMakeLists.txt new file mode 100644 index 0000000..361990c --- /dev/null +++ b/sdv_services/uds_unix_sockets/CMakeLists.txt @@ -0,0 +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 +#******************************************************************************* + +if(UNIX) +# Define project +project(uds_unix_sockets VERSION 1.0 LANGUAGES CXX) + +# Define target +add_library(uds_unix_sockets SHARED + "channel_mgnt.h" + "channel_mgnt.cpp" + "connection.h" + "connection.cpp" + ) + +target_link_libraries(uds_unix_sockets rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) + +target_link_options(uds_unix_sockets PRIVATE) +target_include_directories(uds_unix_sockets PRIVATE ./include/) +set_target_properties(uds_unix_sockets PROPERTIES PREFIX "") +set_target_properties(uds_unix_sockets PROPERTIES SUFFIX ".sdv") + +# Build dependencies +add_dependencies(uds_unix_sockets CompileCoreIDL) + +# Appending the service in the service list +set(SDV_Service_List ${SDV_Service_List} uds_unix_sockets PARENT_SCOPE) + +endif() \ No newline at end of file diff --git a/sdv_services/uds_unix_sockets/channel_mgnt.cpp b/sdv_services/uds_unix_sockets/channel_mgnt.cpp new file mode 100644 index 0000000..a8ba4b2 --- /dev/null +++ b/sdv_services/uds_unix_sockets/channel_mgnt.cpp @@ -0,0 +1,161 @@ +/******************************************************************************** +* 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: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#if defined(__unix__) + +#include "channel_mgnt.h" +#include "connection.h" + +#include + +#include +#include +#include +#include +#include +#include + +// Anonymous namespace for internal helpers +namespace +{ + /** + * @brief Parse a semicolon-separated list of key=value pairs + * + * Expected input format: + *    "key1=value1;key2=value2;key3=value3;" + * + * Whitespace is not trimmed and empty entries are ignored + * Keys without '=' are skipped + * + * Example: + *    Input:  "proto=uds;path=/tmp/test.sock;" + *    Output: { {"proto","uds"}, {"path","/tmp/test.sock"} } + * + * @param[in] s   Raw string containing "key=value;" pairs + * + * @return A map of parsed key/value pairs (order not preserved) + */ + static std::map ParseKV(const std::string& s) + { + std::map kv; + std::stringstream ss(s); + std::string item; + + while (std::getline(ss, item, ';')) + { + auto pos = item.find('='); + if (pos != std::string::npos) + kv[item.substr(0, pos)] = item.substr(pos + 1); + } + return kv; + } + + /** + * @brief Clamp an AF_UNIX pathname to Linux `sockaddr_un::sun_path` size + * + * Linux allows paths up to ~108 bytes inside `sun_path` + * If the input string exceeds the allowed size, it is truncated so that: + *    resulting_length <= sizeof(sockaddr_un::sun_path) - 1 + * + * @param[in] p  Original path string + * + * @return A safe, clamped path that fits inside `sun_path` + */ + static std::string ClampSunPath(const std::string& p) + { + constexpr size_t MaxLen = sizeof(sockaddr_un::sun_path); + return (p.size() < MaxLen) ? p : p.substr(0, MaxLen - 1); + } +} + +// Directory selection (/run/user//sdv or /tmp/sdv) +std::string CUnixDomainSocketsChannelMgnt::MakeUserRuntimeDir() +{ + std::ostringstream oss; + oss << "/run/user/" << ::getuid(); + + struct stat st{}; + if (::stat(oss.str().c_str(), &st) == 0) + { + std::string path = oss.str() + "/sdv"; + ::mkdir(path.c_str(), 0770); + return path; + } + + ::mkdir("/tmp/sdv", 0770); + return "/tmp/sdv"; +} + +bool CUnixDomainSocketsChannelMgnt::OnInitialize() +{ + return true; +} + +void CUnixDomainSocketsChannelMgnt::OnShutdown() +{ +} + +// Endpoint creation (server) +sdv::ipc::SChannelEndpoint CUnixDomainSocketsChannelMgnt::CreateEndpoint(const sdv::u8string& ssEndpointConfig) +{ + const std::string baseDir = MakeUserRuntimeDir(); + std::string name = "UDS_" + std::to_string(::getpid()); + std::string path = baseDir + "/" + name + ".sock"; + + if (!ssEndpointConfig.empty()) + { + sdv::toml::CTOMLParser cfg(ssEndpointConfig.c_str()); + + auto nameNode = cfg.GetDirect("IpcChannel.Name"); + if (nameNode.GetType() == sdv::toml::ENodeType::node_string) + name = static_cast(nameNode.GetValue()); + + auto pathNode = cfg.GetDirect("IpcChannel.Path"); + if (pathNode.GetType() == sdv::toml::ENodeType::node_string) + path = static_cast(pathNode.GetValue()); + else + path = baseDir + "/" + name + ".sock"; + } + + path = ClampSunPath(path); + + // Use a shared_ptr and store it to keep the server connection alive + auto server = std::make_shared(-1, true, path); + m_ServerConnections.push_back(server); + + sdv::ipc::SChannelEndpoint ep{}; + ep.pConnection = static_cast(server.get()); + ep.ssConnectString = server->GetConnectionString(); + return ep; +} + +// Access existing endpoint (server or client) +sdv::IInterfaceAccess* CUnixDomainSocketsChannelMgnt::Access(const sdv::u8string& ssConnectString) +{ + const auto kv = ParseKV(static_cast(ssConnectString)); + const bool isServer = (kv.count("role") && kv.at("role") == "server"); + const std::string path = kv.count("path") ? kv.at("path") : (MakeUserRuntimeDir() + "/UDS_auto.sock"); + + if (isServer) + { + auto server = std::make_shared(-1, true, path); + m_ServerConnections.push_back(server); + return static_cast(server.get()); + } + + // Client: allocated raw pointer (expected to be managed by SDV framework) + auto* client = new CUnixSocketConnection(-1, false, path); + return static_cast(client); +} + +#endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_unix_sockets/channel_mgnt.h b/sdv_services/uds_unix_sockets/channel_mgnt.h new file mode 100644 index 0000000..258966e --- /dev/null +++ b/sdv_services/uds_unix_sockets/channel_mgnt.h @@ -0,0 +1,90 @@ +/******************************************************************************** +* 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: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#if defined(__unix__) +#ifndef UNIX_SOCKET_CHANNEL_MGNT_H +#define UNIX_SOCKET_CHANNEL_MGNT_H + +#include +#include + +class CUnixSocketConnection; + +/** + * @brief IPC channel management class for Unix Domain Sockets communication. + */ +class CUnixDomainSocketsChannelMgnt : + public sdv::CSdvObject, + public sdv::ipc::ICreateEndpoint, + public sdv::ipc::IChannelAccess +{ +public: + // Interface map + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IChannelAccess) + SDV_INTERFACE_ENTRY(sdv::ipc::ICreateEndpoint) + END_SDV_INTERFACE_MAP() + + // Object declarations + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) + DECLARE_OBJECT_CLASS_NAME("UnixDomainSocketsChannelControl") + DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl") + DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl") + DECLARE_OBJECT_SINGLETON() + + /** + * @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 bool OnInitialize() override; + + /** + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. + */ + virtual void OnShutdown() override; + + /** + * @brief Create IPC connection object and return the endpoint information. Overload of + * sdv::ipc::ICreateEndpoint::CreateEndpoint. + * @details The endpoints are generated using either a size and a name based on the provided channel configuration or if no + * configuration is supplied a default size of 10k and a randomly generated name. The following configuration + * can be supplied: + * @code + * [IpcChannel] + * Name = "CHANNEL_1234" + * Size = 10240 + * @endcode + * @param[in] ssChannelConfig Optional channel type specific endpoint configuration. + * @return IPC connection object + */ + sdv::ipc::SChannelEndpoint CreateEndpoint(/*in*/ const sdv::u8string& ssChannelConfig) override; + + /** + * @brief Create a connection object from the channel connection parameters string + * @param[in] ssConnectString Reference to the string containing the channel connection parameters. + * @return Pointer to IInterfaceAccess interface of the connection object or NULL when the object cannot be created. + */ + sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override; + +private: + + // Helper: choose runtime dir (/run/user//sdv) or fallback (/tmp/sdv) + static std::string MakeUserRuntimeDir(); + + std::vector> m_ServerConnections; + +}; +DEFINE_SDV_OBJECT(CUnixDomainSocketsChannelMgnt) + +#endif // UNIX_SOCKET_CHANNEL_MGNT_H +#endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_unix_sockets/connection.cpp b/sdv_services/uds_unix_sockets/connection.cpp new file mode 100644 index 0000000..d7d621d --- /dev/null +++ b/sdv_services/uds_unix_sockets/connection.cpp @@ -0,0 +1,1166 @@ +/******************************************************************************** +* 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: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#if defined(__unix__) + +#include "connection.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// Anonymous namespace: local helpers (not exported) +namespace +{ + /** + * @brief Return the directory component of a UNIX path. + * + * Behavior matches POSIX dirname-like semantics: + *   - If there is no '/', returns "." + *   - If the only slash is at index 0, returns "/" + *   - Otherwise returns the substring up to the final '/' + * + * Examples: + *    "/tmp/a/b.sock" → "/tmp/a" + *    "b.sock"        → "." + *    "/a"            → "/" + * + * @param[in] p  Full path string. + * + * @return Directory component of the path. + */ + static std::string DirNameOf(const std::string& p) + { + const auto pos = p.find_last_of('/'); + if (pos == std::string::npos) return "."; + if (pos == 0) return "/"; + return p.substr(0, pos); + } + + /** + * @brief Ensure that a directory exists on the filesystem + * + * Equivalent to `mkdir -p` for a single-level directory: + *   - If the directory already exists, returns true + *   - If it does not exist, attempts to create it using mode 0770 + *   - Returns true on success, false on failure + * + * NOTE: + *   - Only creates one directory level (not recursive) + *   - errno is checked for EEXIST to handle race conditions + * + * @param[in] dir  Directory path to check or create + * + * @return true if the directory exists or was successfully created; + *         false otherwise + */ + static bool EnsureDir(const std::string& dir) + { + struct stat st{}; + if (::stat(dir.c_str(), &st) == 0) return S_ISDIR(st.st_mode); + if (::mkdir(dir.c_str(), 0770) == 0) return true; + return errno == EEXIST; + } + + /** + * @brief Return the current UNIX process ID as a 64-bit value + * + * Used in SDV AF_UNIX connect headers to identify processes across + * handshake messages (connect_request / connect_answer) + * + * @return Current process ID as uint64_t + */ + uint64_t GetCurrentProcessID_Local() + { + return static_cast(::getpid()); + } +} // namespace + +// Construction / Destruction +CUnixSocketConnection::CUnixSocketConnection(int preconfiguredFd, + bool acceptConnectionRequired, + const std::string& udsPath) + : m_Fd(preconfiguredFd) + , m_ListenFd(-1) + , m_AcceptConnectionRequired(acceptConnectionRequired) + , m_UdsPath(udsPath) + , m_StopReceiveThread(false) + , m_StopConnectThread(false) + , m_eStatus(sdv::ipc::EConnectStatus::uninitialized) + , m_pReceiver(nullptr) + , m_pEvent(nullptr) +{ + // clean constructor +} + +CUnixSocketConnection::~CUnixSocketConnection() +{ + try + { + StopThreadsAndCloseSockets(/*unlinkPath*/ false); + } + catch (...) + { + // never throw from destructor + } +} + +// Public API +std::string CUnixSocketConnection::GetConnectionString() +{ + std::ostringstream oss; + oss << "proto=uds;" + << "role=" << (m_AcceptConnectionRequired ? "server" : "client") << ";" + << "path=" << m_UdsPath << ";" + << "timeout_ms=" << 5000; + return oss.str(); +} + +bool CUnixSocketConnection::SendSizedPacket(const void* pData, uint32_t uiDataLength) +{ + if (m_Fd < 0) return false; + + const uint32_t len = uiDataLength; + + std::lock_guard lock(m_SendMtx); + + // Transport header: packet size + if (::send(m_Fd, &len, sizeof(len), 0) != static_cast(sizeof(len))) + return false; + + // SDV payload + const char* ptr = reinterpret_cast(pData); + uint32_t remain = uiDataLength; + + while (remain > 0) + { + const ssize_t sent = ::send(m_Fd, ptr, remain, 0); + if (sent <= 0) return false; + ptr += sent; + remain -= static_cast(sent); + } + return true; +} + +bool CUnixSocketConnection::SendData(sdv::sequence>& seqData) +{ +#if ENABLE_REPORTING >= 1 + std::stringstream sizes; + for (const auto& rptr : seqData) { if (!sizes.str().empty()) sizes << ", "; sizes << rptr.size(); } + TRACE("[UDS] Send ", seqData.size(), " chunk(s) [", sizes.str(), "] bytes"); +#endif + + // Only send when connected and FD valid + if (m_eStatus != sdv::ipc::EConnectStatus::connected || m_Fd < 0) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][TX] Send requested while not connected or FD invalid (status=", static_cast(m_eStatus.load()), ")"); + return false; + } + + // Build the length table (little-endian) and compute total payload size + sdv::sequence*> seqTemp; + sdv::pointer table; + + const uint32_t nChunks = static_cast(seqData.size()); + const uint32_t tableBytes = (nChunks + 1u) * sizeof(uint32_t); + table.resize(tableBytes); + + // Write the chunk count (little endian) + std::array raw{}; + std::memcpy(raw.data(), &nChunks, sizeof(uint32_t)); + size_t idx = 0; + for (uint8_t b : raw) table[idx++] = b; + + uint64_t required = sizeof(uint32_t); // count field + + // Write each chunk size + for (const sdv::pointer& buf : seqData) + { + const uint32_t len = static_cast(buf.size()); + required += sizeof(uint32_t); + required += static_cast(len); + + std::memcpy(raw.data(), &len, sizeof(uint32_t)); + for (uint8_t b : raw) table[idx++] = b; + + seqTemp.push_back(&buf); + } + + // Prepend table as the first "chunk" + seqTemp.insert(seqTemp.begin(), &table); + + // Per-frame capacity (leave header room) + const uint32_t maxPayloadData = + (kMaxUdsPacketSize > sizeof(SMsgHdr)) ? (kMaxUdsPacketSize - static_cast(sizeof(SMsgHdr))) : 0; + + const uint32_t maxPayloadFrag = + (kMaxUdsPacketSize > sizeof(SFragmentedMsgHdr)) ? (kMaxUdsPacketSize - static_cast(sizeof(SFragmentedMsgHdr))) : 0; + + // Single-frame data? + const bool fitsSingle = (required <= static_cast(maxPayloadData)); + + auto itChunk = seqTemp.cbegin(); + size_t pos = 0; + uint32_t offset = 0; + + if (fitsSingle) + { + const uint32_t payloadBytes = static_cast(required); + const uint32_t totalBytes = payloadBytes + static_cast(sizeof(SMsgHdr)); + + std::vector frame(totalBytes); + uint32_t msgOff = 0; + + // Header + { + auto* hdr = reinterpret_cast(frame.data()); + hdr->uiVersion = SDVFrameworkInterfaceVersion; + hdr->eType = EMsgType::data; + msgOff = static_cast(sizeof(SMsgHdr)); + } + + // Table + chunks, contiguously + while (itChunk != seqTemp.cend() && msgOff < totalBytes) + { + const auto& ref = *itChunk; + const uint32_t len = static_cast(ref->size()); + const uint8_t* src = reinterpret_cast(ref->get()); + + const uint32_t canCopy = std::min(len - static_cast(pos), + totalBytes - msgOff); + if (canCopy) + std::memcpy(frame.data() + msgOff, src + pos, canCopy); + + pos += canCopy; + msgOff += canCopy; + + if (pos >= len) { ++itChunk; pos = 0; } + } + + if (!SendSizedPacket(frame.data(), totalBytes)) + { + SDV_LOG_ERROR("[UDS][TX] SendSizedPacket failed for single-frame data (", totalBytes, " bytes)"); + return false; + } + return true; + } + + // Fragmented sending + if (maxPayloadFrag == 0) + { + SDV_LOG_ERROR("[UDS][TX] Fragmentation impossible: header too large (maxPayloadFrag=0)"); + return false; + } + + while (itChunk != seqTemp.cend() && offset < required) + { + const uint32_t remaining = static_cast(required - offset); + const uint32_t dataBytes = std::min(remaining, maxPayloadFrag); + const uint32_t allocBytes = dataBytes + static_cast(sizeof(SFragmentedMsgHdr)); + if (dataBytes == 0) { SDV_LOG_ERROR("[UDS][TX] Internal error: dataBytes==0 during fragmentation"); return false; } + + std::vector frame(allocBytes); + uint32_t msgOff = 0; + + // Fragment header + { + auto* hdr = reinterpret_cast(frame.data()); + hdr->uiVersion = SDVFrameworkInterfaceVersion; + hdr->eType = EMsgType::data_fragment; + hdr->uiTotalLength= static_cast(required); + hdr->uiOffset = offset; + msgOff = static_cast(sizeof(SFragmentedMsgHdr)); + } + + // Copy table + payload slice for this fragment + uint32_t copied = 0; + while (itChunk != seqTemp.cend() && copied < dataBytes) + { + const auto& ref = *itChunk; + const uint32_t len = static_cast(ref->size()); + const uint8_t* src = reinterpret_cast(ref->get()); + + const uint32_t canCopy = std::min(len - static_cast(pos), + dataBytes - copied); + if (canCopy) + std::memcpy(frame.data() + msgOff, src + pos, canCopy); + + pos += canCopy; + msgOff += canCopy; + copied += canCopy; + + if (pos >= len) { ++itChunk; pos = 0; } + } + + if (!SendSizedPacket(frame.data(), allocBytes)) + { + SDV_LOG_ERROR("[UDS][TX] SendSizedPacket failed for fragment (offset=", offset, ", size=", allocBytes, ")"); + return false; + } + offset += copied; + } + + if (offset < required) + { + SDV_LOG_ERROR("[UDS][TX] Incomplete fragmented send: required=", required, ", sent=", offset); + return false; + } + return true; +} + +uint64_t CUnixSocketConnection::RegisterStatusEventCallback(sdv::IInterfaceAccess* pEventCallback) +{ + if (!pEventCallback) return 0; + + auto* pCb = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface(); + if (!pCb) return 0; + + // Generate a non-zero cookie + uint64_t cookie = 0; + do + { + cookie = static_cast(::rand()); + } while (cookie == 0); + + // Write-lock: add entry + { + std::unique_lock lock(m_mtxEventCallbacks); + m_lstEventCallbacks.emplace_front(SEventCallback{ cookie, pCb }); + } + + return cookie; +} + +void CUnixSocketConnection::UnregisterStatusEventCallback(uint64_t uiCookie) +{ + if (!uiCookie) return; + + // Read-lock first (fast path): find entry + { + std::shared_lock rlock(m_mtxEventCallbacks); + auto it = std::find_if(m_lstEventCallbacks.begin(), m_lstEventCallbacks.end(), + [&](const auto& e){ return e.uiCookie == uiCookie; }); + if (it == m_lstEventCallbacks.end()) + return; + // mark as removed under read lock? Prefer upgrading to write-lock to mutate. + } + + // Write-lock to mutate safely + { + std::unique_lock wlock(m_mtxEventCallbacks); + auto it = std::find_if(m_lstEventCallbacks.begin(), m_lstEventCallbacks.end(), + [&](const auto& e){ return e.uiCookie == uiCookie; }); + if (it != m_lstEventCallbacks.end()) + { + it->pCallback = nullptr; // logically removed + // Optional: compact immediately to keep list clean + m_lstEventCallbacks.remove_if([](const SEventCallback& e){ return e.pCallback == nullptr; }); + } + } +} + +bool CUnixSocketConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) +{ + // Capture callbacks under lock + { + std::lock_guard lk(m_StateMtx); + m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); + m_pEvent = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); + m_eStatus = sdv::ipc::EConnectStatus::initializing; + + // Reset stop flags + m_StopReceiveThread.store(false); + m_StopConnectThread.store(false); + } + m_StateCv.notify_all(); + + // If an old worker exists, join it to avoid duplicates + if (m_ConnectThread.joinable()) m_ConnectThread.join(); + + // Start the unique connect worker (server/client) + m_ConnectThread = std::thread(&CUnixSocketConnection::ConnectWorker, this); + return true; +} + +int CUnixSocketConnection::AcceptConnection() // deprecated +{ + const std::string dir = DirNameOf(m_UdsPath); + if (!EnsureDir(dir)) + { + SDV_LOG_ERROR("[UDS][Server] ensure_dir('", dir, "') failed: ", std::strerror(errno)); + return -1; + } + + const int listenFd = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (listenFd < 0) + { + SDV_LOG_ERROR("[UDS][Server] socket() failed: ", std::strerror(errno)); + return -1; + } + + ::unlink(m_UdsPath.c_str()); + sockaddr_un addr{}; addr.sun_family = AF_UNIX; + ::snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", m_UdsPath.c_str()); + + if (::bind(listenFd, reinterpret_cast(&addr), sizeof(addr)) < 0) + { + SDV_LOG_ERROR("[UDS][Server] bind('", m_UdsPath, "') failed: ", std::strerror(errno)); + ::close(listenFd); + return -1; + } + ::chmod(m_UdsPath.c_str(), 0770); + + if (::listen(listenFd, 1) < 0) + { + SDV_LOG_ERROR("[UDS][Server] listen() failed: ", std::strerror(errno)); + ::close(listenFd); + return -1; + } + + const int clientFd = ::accept(listenFd, nullptr, nullptr); + if (clientFd < 0) + { + SDV_LOG_ERROR("[UDS][Server] accept() failed: ", std::strerror(errno)); + ::close(listenFd); + return -1; + } + ::close(listenFd); + return clientFd; +} + +bool CUnixSocketConnection::WaitForConnection(uint32_t uiWaitMs) +{ + if (m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected) return true; + + std::unique_lock lk(m_MtxConnect); + + if (uiWaitMs == 0xFFFFFFFFu) + { + m_CvConnect.wait(lk, [this]{ + return m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected; + }); + return true; + } + if (uiWaitMs == 0u) + return (m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected); + + return m_CvConnect.wait_for(lk, std::chrono::milliseconds(uiWaitMs), + [this]{ return m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected; }); +} + +void CUnixSocketConnection::CancelWait() +{ + // optional: m_CvConnect.notify_all(); +} + +void CUnixSocketConnection::Disconnect() +{ + StopThreadsAndCloseSockets(/*unlinkPath*/ false); + SetStatus(sdv::ipc::EConnectStatus::disconnected); +} + +sdv::ipc::EConnectStatus CUnixSocketConnection::GetStatus() const +{ + return m_eStatus; +} + +void CUnixSocketConnection::DestroyObject() +{ + m_StopReceiveThread.store(true); + m_eStatus = sdv::ipc::EConnectStatus::disconnected; +} + +void CUnixSocketConnection::SetStatus(sdv::ipc::EConnectStatus eStatus) +{ + // Update internal state atomically and wake up waiters. + { + std::lock_guard lk(m_MtxConnect); + m_eStatus.store(eStatus, std::memory_order_release); + } + m_CvConnect.notify_all(); + + // Notify the legacy single-listener (kept for backward compatibility). + try + { + m_pEvent->SetStatus(eStatus); + } + catch (...) { /* swallow: user callback must not crash transport */ } + + // Notify all registered listeners from the registry (read-mostly path). + bool needCompact = false; + { + std::shared_lock rlock(m_mtxEventCallbacks); + for (const auto& entry : m_lstEventCallbacks) + { + if (!entry.pCallback) { needCompact = true; continue; } + try + { + entry.pCallback->SetStatus(eStatus); + } + catch (...) { /* swallow per-listener */ } + } + } + + // Compact registry if we saw null entries (write path). + if (needCompact) + { + std::unique_lock wlock(m_mtxEventCallbacks); + m_lstEventCallbacks.remove_if([](const SEventCallback& e){ return e.pCallback == nullptr; }); + } +} + +bool CUnixSocketConnection::IsServer() const +{ + return m_AcceptConnectionRequired; +} + +// Transport helpers +bool CUnixSocketConnection::ReadNumberOfBytes(char* buffer, uint32_t bufferLength) +{ + uint32_t remaining = bufferLength; + char* ptr = buffer; + + while (remaining > 0 && !m_StopReceiveThread.load()) + { + const int fd = m_Fd; // snapshot to reduce races + if (fd < 0) return false; + + const ssize_t ret = ::recv(fd, ptr, remaining, 0); + if (ret == 0) return false; // EOF + if (ret < 0) + { + if (errno == EINTR) continue; + if (errno == EAGAIN || errno == EWOULDBLOCK) continue; + SDV_LOG_WARNING("[UDS][RX] recv() error: ", std::strerror(errno)); + return false; + } + + ptr += ret; + remaining -= static_cast(ret); + } + return (remaining == 0); +} + +bool CUnixSocketConnection::ReadTransportHeader(uint32_t& packetSize) +{ + if (!ReadNumberOfBytes(reinterpret_cast(&packetSize), sizeof(packetSize))) + return false; + + if (packetSize == 0 || packetSize > kMaxUdsPacketSize) + { + SDV_LOG_WARNING("[UDS][RX] Invalid UDS packet size: ", packetSize); + return false; + } + + return true; +} + + +// Connect worker (server/client) +void CUnixSocketConnection::ConnectWorker() +{ + try + { + if (m_AcceptConnectionRequired) + { + // --- SERVER --- + const std::string dir = DirNameOf(m_UdsPath); + if (!EnsureDir(dir)) + { + SDV_LOG_ERROR("[UDS][Server] ensure_dir('", dir, "') failed: ", std::strerror(errno)); + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return; + } + + m_ListenFd = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (m_ListenFd < 0) + { + SDV_LOG_ERROR("[UDS][Server] socket() failed: ", std::strerror(errno)); + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return; + } + + ::unlink(m_UdsPath.c_str()); + sockaddr_un addr{}; addr.sun_family = AF_UNIX; + ::snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", m_UdsPath.c_str()); + + if (::bind(m_ListenFd, reinterpret_cast(&addr), sizeof(addr)) < 0) + { + SDV_LOG_ERROR("[UDS][Server] bind('", m_UdsPath, "') failed: ", std::strerror(errno)); + ::close(m_ListenFd); m_ListenFd = -1; + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return; + } + + ::chmod(m_UdsPath.c_str(), 0770); + + if (::listen(m_ListenFd, 1) < 0) + { + SDV_LOG_ERROR("[UDS][Server] listen() failed: ", std::strerror(errno)); + ::close(m_ListenFd); m_ListenFd = -1; + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return; + } + +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][Server] Listening on path ", m_UdsPath); +#endif + + // Cancellable accept loop (poll) + int clientFd = -1; + while (!m_StopConnectThread.load()) + { + struct pollfd pfd{ m_ListenFd, POLLIN, 0 }; + const int pr = ::poll(&pfd, 1, /*timeout_ms*/ 100); + if (pr < 0) { if (errno == EINTR) continue; SDV_LOG_WARNING("[UDS][Server] poll() error: ", std::strerror(errno)); break; } + if (pr == 0) continue; + + if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) + { +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][Server] Listening socket closed/invalidated"); +#endif + break; + } + + if (pfd.revents & POLLIN) + { + clientFd = ::accept(m_ListenFd, nullptr, nullptr); + if (clientFd < 0) + { + if (errno == EINTR) continue; + if (m_StopConnectThread.load()) break; + SDV_LOG_ERROR("[UDS][Server] accept() failed: ", std::strerror(errno)); + break; + } + break; // accepted + } + } + + // Close listen fd (safe if already closed elsewhere) + const int lfd = m_ListenFd; m_ListenFd = -1; + if (lfd >= 0) ::close(lfd); + + if (m_StopConnectThread.load()) + { +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][Server] ConnectWorker stopped intentionally"); +#endif + return; // intentional stop + } + if (clientFd < 0) + { + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return; + } + + m_Fd = clientFd; + SetStatus(sdv::ipc::EConnectStatus::connected); + StartReceiveThread_Unsafe(); + return; + } + else + { + // --- CLIENT --- + m_Fd = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (m_Fd < 0) + { + SDV_LOG_ERROR("[UDS][Client] socket() failed: ", std::strerror(errno)); + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return; + } + + sockaddr_un addr{}; addr.sun_family = AF_UNIX; + ::snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", m_UdsPath.c_str()); + +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][Client] Connecting to ", m_UdsPath); +#endif + + int attempts = 10; + while (attempts-- > 0 && !m_StopConnectThread.load()) + { + if (::connect(m_Fd, reinterpret_cast(&addr), sizeof(addr)) == 0) + { + SetStatus(sdv::ipc::EConnectStatus::connected); +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][Client] Connected"); +#endif + StartReceiveThread_Unsafe(); + return; + } + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + + if (!m_StopConnectThread.load()) + { + SDV_LOG_WARNING("[UDS][Client] connect() timeout to '", m_UdsPath, + "', last errno=", errno, " (", std::strerror(errno), ")"); + } + + ::close(m_Fd); m_Fd = -1; + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return; + } + } + catch (const std::exception& ex) + { + SDV_LOG_ERROR("[UDS][ConnectWorker] exception: ", ex.what()); + SetStatus(sdv::ipc::EConnectStatus::connection_error); + } + catch (...) + { + SDV_LOG_ERROR("[UDS][ConnectWorker] unknown exception"); + SetStatus(sdv::ipc::EConnectStatus::connection_error); + } +} + +// Receive thread and SDV state machine +void CUnixSocketConnection::ReceiveSyncAnswer(const CMessage& message) +{ + const auto hdr = message.GetMsgHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] sync_answer with invalid version"); + return; + } + + // Client -> send connect_request + SConnectMsg msg{}; + msg.uiVersion = SDVFrameworkInterfaceVersion; + msg.eType = EMsgType::connect_request; + msg.tProcessID = GetCurrentProcessID_Local(); + + if (!SendSizedPacket(&msg, sizeof(msg))) + SDV_LOG_ERROR("[UDS][RX] Failed to send connect_request in response to sync_answer"); +} + +void CUnixSocketConnection::ReceiveMessages() +{ + try + { + // Inform potential waiters that RX started + m_StateCv.notify_all(); + + SDataContext dataCtx; + auto tpStart = std::chrono::high_resolution_clock::time_point{}; + +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][RX] Start receive loop"); +#endif + + while (!m_StopReceiveThread.load()) + { + if (m_eStatus == sdv::ipc::EConnectStatus::terminating) break; + + // Snapshot FD + const int fd = m_Fd; + if (fd < 0) + { + SetStatus(sdv::ipc::EConnectStatus::disconnected); + SDV_LOG_WARNING("[UDS][RX] FD invalidated -> disconnected"); + break; + } + + // SHM-like pacing via poll() + struct pollfd pfd{ fd, POLLIN, 0 }; + const int pr = ::poll(&pfd, 1, /*timeout_ms*/ 100); + + if (pr == 0) + { + if (!m_AcceptConnectionRequired && (m_eStatus == sdv::ipc::EConnectStatus::initialized)) + { + auto now = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration(now - tpStart).count() > 0.5) + { + tpStart = now; + SMsgHdr sync{ SDVFrameworkInterfaceVersion, EMsgType::sync_request }; + if (!SendSizedPacket(&sync, static_cast(sizeof(sync)))) + SDV_LOG_WARNING("[UDS][RX] Failed to send periodic sync_request (client pacing)"); + } + } + continue; + } + + if (pr < 0 || (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) + { + SetStatus(sdv::ipc::EConnectStatus::disconnected); + SDV_LOG_WARNING("[UDS][RX] poll() hangup/error -> disconnected"); + break; + } + if ((pfd.revents & POLLIN) == 0) continue; + + // Transport header + uint32_t packetSize = 0; + if (!ReadTransportHeader(packetSize)) + { + SetStatus(sdv::ipc::EConnectStatus::disconnected); + SDV_LOG_WARNING("[UDS][RX] Invalid/missing transport header -> disconnected"); + break; + } + + // Payload + std::vector payload(packetSize); + if (!ReadNumberOfBytes(reinterpret_cast(payload.data()), packetSize)) + { + SetStatus(sdv::ipc::EConnectStatus::disconnected); + SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected"); + break; + } + + CMessage msg(std::move(payload)); + if (!msg.IsValid()) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] Invalid SDV message (envelope)"); + continue; + } + + if (m_eStatus == sdv::ipc::EConnectStatus::terminating) break; + +#if ENABLE_REPORTING >= 1 + switch (msg.GetMsgHdr().eType) + { + case EMsgType::data: + case EMsgType::data_fragment: break; + default: + TRACE("[UDS][RX] Receive raw ", static_cast(msg.GetMsgHdr().eType), + " (", msg.GetSize(), " bytes)"); + } +#endif + + // SDV state machine + switch (msg.GetMsgHdr().eType) + { + case EMsgType::sync_request: ReceiveSyncRequest(msg); break; + case EMsgType::connect_request: ReceiveConnectRequest(msg); break; + case EMsgType::sync_answer: ReceiveSyncAnswer(msg); break; + case EMsgType::connect_answer: ReceiveConnectAnswer(msg); break; + case EMsgType::connect_term: + ReceiveConnectTerm(msg); + if (m_AcceptConnectionRequired) tpStart = std::chrono::high_resolution_clock::now(); + break; + case EMsgType::data: ReceiveDataMessage(msg, dataCtx); break; + case EMsgType::data_fragment: ReceiveDataFragmentMessage(msg, dataCtx); break; + default: /* ignore */ break; + } + } + +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][RX] Stop receive loop"); +#endif + } + catch (const std::exception& ex) + { + SDV_LOG_ERROR("[UDS][RX] exception: ", ex.what()); + SetStatus(sdv::ipc::EConnectStatus::disconnected); + } + catch (...) + { + SDV_LOG_ERROR("[UDS][RX] unknown exception"); + SetStatus(sdv::ipc::EConnectStatus::disconnected); + } +} + +void CUnixSocketConnection::ReceiveSyncRequest(const CMessage& message) +{ + const auto hdr = message.GetMsgHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] sync_request with invalid version"); + return; + } + + // Reply with sync_answer + SMsgHdr reply{}; + reply.uiVersion = SDVFrameworkInterfaceVersion; + reply.eType = EMsgType::sync_answer; + + if (!SendSizedPacket(&reply, sizeof(reply))) + SDV_LOG_ERROR("[UDS][RX] Failed to send sync_answer to sync_request"); +} + +void CUnixSocketConnection::ReceiveConnectRequest(const CMessage& message) +{ + const auto hdr = message.GetConnectHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] connect_request with invalid version"); + return; + } + + // Reply with connect_answer + SConnectMsg reply{}; + reply.uiVersion = SDVFrameworkInterfaceVersion; + reply.eType = EMsgType::connect_answer; + reply.tProcessID = GetCurrentProcessID_Local(); + + if (!SendSizedPacket(&reply, sizeof(reply))) + SDV_LOG_ERROR("[UDS][RX] Failed to send connect_answer"); +} + +void CUnixSocketConnection::ReceiveConnectAnswer(const CMessage& message) +{ + const auto hdr = message.GetConnectHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] connect_answer with invalid version"); + return; + } + + // Fully established + SetStatus(sdv::ipc::EConnectStatus::connected); +} + +void CUnixSocketConnection::ReceiveConnectTerm(const CMessage& /*message*/) +{ + // Peer requested termination + SetStatus(sdv::ipc::EConnectStatus::disconnected); + m_StopReceiveThread.store(true); +} + +void CUnixSocketConnection::StartReceiveThread_Unsafe() +{ + if (m_ReceiveThread.joinable()) + { + if (m_ReceiveThread.get_id() == std::this_thread::get_id()) + m_ReceiveThread.detach(); + else + m_ReceiveThread.join(); + } + + m_StopReceiveThread.store(false); + m_ReceiveThread = std::thread(&CUnixSocketConnection::ReceiveMessages, this); +} + +void CUnixSocketConnection::StopThreadsAndCloseSockets(bool unlinkPath) +{ + // Signal stop + m_StopReceiveThread.store(true); + m_StopConnectThread.store(true); + + // Close listen FD to break accept() + const int lfd = m_ListenFd; m_ListenFd = -1; + if (lfd >= 0) ::close(lfd); + + // Close active FD to break recv()/send() + const int fd = m_Fd; m_Fd = -1; + if (fd >= 0) { ::shutdown(fd, SHUT_RDWR); ::close(fd); } + + // Join/detach threads (avoid self-join) + const auto self = std::this_thread::get_id(); + + if (m_ReceiveThread.joinable()) + { + if (m_ReceiveThread.get_id() == self) m_ReceiveThread.detach(); + else m_ReceiveThread.join(); + } + + if (m_ConnectThread.joinable()) + { + if (m_ConnectThread.get_id() == self) m_ConnectThread.detach(); + else m_ConnectThread.join(); + } + + if (unlinkPath && !m_UdsPath.empty()) + ::unlink(m_UdsPath.c_str()); +} + +// Data receive helpers +void CUnixSocketConnection::ReceiveDataMessage(const CMessage& rMessage, SDataContext& rsDataCtxt) +{ +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][RX] Start receive data message"); +#endif + uint32_t uiOffset = ReadDataTable(rMessage, rsDataCtxt); + if (!uiOffset) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] Invalid data table"); + return; + } + +#if ENABLE_REPORTING >= 1 + std::stringstream ss; + for (const auto& p : rsDataCtxt.seqDataChunks) { if (!ss.str().empty()) ss << ", "; ss << p.size(); } + TRACE("[UDS][RX] Data message has ", rsDataCtxt.seqDataChunks.size(), " chunk(s) [", ss.str(), "] bytes"); +#endif + + if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] Failed to read data chunk"); + return; + } + +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][RX] Finished reception of data message"); +#endif +} + +void CUnixSocketConnection::ReceiveDataFragmentMessage(const CMessage& rMessage, SDataContext& rsDataCtxt) +{ + uint32_t uiOffset = sizeof(SFragmentedMsgHdr); + + if (rMessage.GetFragmentedHdr().uiOffset == 0) + { +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][RX] Start receive fragmented data message"); +#endif + uiOffset = ReadDataTable(rMessage, rsDataCtxt); + if (!uiOffset) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] Invalid fragmented data table"); + return; + } + +#if ENABLE_REPORTING >= 1 + std::stringstream ss; + for (const auto& p : rsDataCtxt.seqDataChunks) { if (!ss.str().empty()) ss << ", "; ss << p.size(); } + TRACE("[UDS][RX] Fragmented message has ", rsDataCtxt.seqDataChunks.size(), " chunk(s) [", ss.str(), "] bytes"); +#endif + } + + if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] Failed to read fragmented chunk"); + return; + } +} + +uint32_t CUnixSocketConnection::ReadDataTable(const CMessage& rMessage, SDataContext& rsDataCtxt) +{ + uint32_t uiOffset = 0; + + switch (rMessage.GetMsgHdr().eType) + { + case EMsgType::data: + uiOffset = static_cast(sizeof(SMsgHdr)); + rsDataCtxt.uiTotalSize = rMessage.GetSize() - static_cast(sizeof(SMsgHdr)); + break; + case EMsgType::data_fragment: + uiOffset = static_cast(sizeof(SFragmentedMsgHdr)); + if (rMessage.GetFragmentedHdr().uiOffset) return 0; // only in first fragment + rsDataCtxt.uiTotalSize = rMessage.GetFragmentedHdr().uiTotalLength; + break; + default: + return 0; + } + + rsDataCtxt.uiCurrentOffset = 0; + + // Buffer count + if (rMessage.GetSize() < (uiOffset + static_cast(sizeof(uint32_t)))) return 0; + const uint32_t uiAmount = *reinterpret_cast(rMessage.GetData() + uiOffset); + uiOffset += sizeof(uint32_t); + rsDataCtxt.uiCurrentOffset += sizeof(uint32_t); + + // Chunk sizes + if (rMessage.GetSize() < (uiOffset + uiAmount * static_cast(sizeof(uint32_t)))) return 0; + + std::vector sizes; + sizes.reserve(uiAmount); + + for (uint32_t i = 0; i < uiAmount; ++i) + { + const uint32_t sz = *reinterpret_cast(rMessage.GetData() + uiOffset); + sizes.push_back(static_cast(sz)); + uiOffset += sizeof(uint32_t); + rsDataCtxt.uiCurrentOffset += sizeof(uint32_t); + } + + const uint32_t computed = rsDataCtxt.uiCurrentOffset + + static_cast(std::accumulate(sizes.begin(), sizes.end(), static_cast(0))); + if (computed != rsDataCtxt.uiTotalSize) return 0; + + // Allocate chunks + for (size_t n : sizes) + { + rsDataCtxt.seqDataChunks.push_back(sdv::pointer()); + rsDataCtxt.seqDataChunks.back().resize(n); + } + + rsDataCtxt.nChunkIndex = 0; + rsDataCtxt.uiChunkOffset = 0; + + return uiOffset; +} + +bool CUnixSocketConnection::ReadDataChunk(const CMessage& rMessage, uint32_t uiOffset, SDataContext& rsDataCtxt) +{ + if (uiOffset < sizeof(SMsgHdr)) return false; // header not skipped + if (rMessage.GetMsgHdr().eType == EMsgType::data_fragment && uiOffset < sizeof(SFragmentedMsgHdr)) return false; + + while (uiOffset < rMessage.GetSize() && rsDataCtxt.nChunkIndex < rsDataCtxt.seqDataChunks.size()) + { + const uint32_t msgAvail = rMessage.GetSize() - uiOffset; + sdv::pointer& chunk = rsDataCtxt.seqDataChunks[rsDataCtxt.nChunkIndex]; + + if (rsDataCtxt.uiChunkOffset > static_cast(chunk.size())) return false; + + const uint32_t need = static_cast(chunk.size()) - rsDataCtxt.uiChunkOffset; + const uint32_t toCopy = std::min(msgAvail, need); + + std::copy(rMessage.GetData() + uiOffset, + rMessage.GetData() + uiOffset + toCopy, + chunk.get() + rsDataCtxt.uiChunkOffset); + + uiOffset += toCopy; + rsDataCtxt.uiChunkOffset += toCopy; + + // Done with this chunk? + if (rsDataCtxt.uiChunkOffset >= static_cast(chunk.size())) + { + rsDataCtxt.uiChunkOffset = 0; + ++rsDataCtxt.nChunkIndex; + + if (rsDataCtxt.nChunkIndex == rsDataCtxt.seqDataChunks.size()) + { +#if ENABLE_REPORTING >= 1 + std::stringstream ss; + for (const auto& p : rsDataCtxt.seqDataChunks) { if (!ss.str().empty()) ss << ", "; ss << p.size(); } + TRACE("Received complete data package with ", rsDataCtxt.seqDataChunks.size(), + " chunk(s) of {", ss.str(), "} bytes"); +#endif + +#if ENABLE_DECOUPLING > 0 + // Optional queueing path (omitted here-feature behind macro) +#else + if (m_pReceiver) m_pReceiver->ReceiveData(rsDataCtxt.seqDataChunks); + rsDataCtxt = SDataContext(); // reset context +#endif + break; + } + } + } + + return true; +} + +#endif // defined(__unix__) diff --git a/sdv_services/uds_unix_sockets/connection.h b/sdv_services/uds_unix_sockets/connection.h new file mode 100644 index 0000000..8962df9 --- /dev/null +++ b/sdv_services/uds_unix_sockets/connection.h @@ -0,0 +1,392 @@ +/******************************************************************************** +* 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: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#if defined(__unix__) +#ifndef CONNECTION_H +#define CONNECTION_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Local IPC connection over Unix Domain Sockets (UDS). + * + * Frames on the wire are sent as size-prefixed buffers: + * [ uint32_t packetSize ][ SDV message bytes ... ] + * + * SDV protocol decoding (SMsgHdr / EMsgType / fragmentation) + */ +class CUnixSocketConnection + : public std::enable_shared_from_this + , public sdv::IInterfaceAccess + , public sdv::IObjectDestroy + , public sdv::ipc::IDataSend + , public sdv::ipc::IConnect +{ +public: + /** + * @brief Construct a UDS connection. + * @param preconfiguredFd Already-open FD (>=0) or -1 if none. + * @param acceptConnectionRequired true for server (must accept()); false for client. + * @param udsPath Filesystem path of the UDS socket. + */ + CUnixSocketConnection(int preconfiguredFd, bool acceptConnectionRequired, const std::string& udsPath); + + /** @brief Virtual destructor. */ + virtual ~CUnixSocketConnection(); + + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnect) + SDV_INTERFACE_ENTRY(sdv::IObjectDestroy) + END_SDV_INTERFACE_MAP() + + /** @brief Returns the connection string (proto/role/path/timeout). */ + std::string GetConnectionString(); + + // ---------- IDataSend ---------- + /** @brief Send a sequence of data buffers (may be fragmented). */ + bool SendData(/*inout*/ sdv::sequence>& seqData) override; + + // ---------- IConnect ---------- + /** @brief Start asynchronous connect/accept worker. */ + bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override; + + /** + * @brief Wait for the connection to become 'connected'. + * @param uiWaitMs Timeout in milliseconds (0 = immediate, 0xFFFFFFFFu = infinite). + * @return true if connected within the timeout; false otherwise. + */ + bool WaitForConnection(/*in*/ uint32_t uiWaitMs) override; + + /** @brief Optionally cancel WaitForConnection (no-op in current implementation). */ + void CancelWait() override; + + /** @brief Disconnect and teardown threads/FDs; sets status to 'disconnected'. */ + void Disconnect() override; + + // ---------- IConnect: event callbacks ---------- + /** @brief Register a status event callback (no-op storage in UDS). */ + uint64_t RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; + + /** @brief Unregister a previously registered callback (no-op storage in UDS). */ + void UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) override; + + /** @brief Get current connection status. */ + sdv::ipc::EConnectStatus GetStatus() const override; + + /** @brief Destroy object (IObjectDestroy). */ + void DestroyObject() override; + + /** @brief Set status and notify listeners (callback-safe). */ + void SetStatus(sdv::ipc::EConnectStatus eStatus); + + /** @brief @return true if this side is server (needs accept()), false otherwise. */ + bool IsServer() const; + + // ---------- Protocol types ---------- + /** @brief SDV message type. */ + enum class EMsgType : uint32_t + { + sync_request = 0, ///< Sync request (version check; no payload). + sync_answer = 1, ///< Sync answer (version check; no payload). + connect_request = 10, ///< Connection initiation (SConnectMsg). + connect_answer = 11, ///< Connection answer (SConnectMsg). + connect_term = 90, ///< Connection terminated. + data = 0x10000000,///< Data message. + data_fragment = 0x10000001 ///< Data fragment if payload exceeds frame size. + }; + + /** @brief SDV base message header. */ + struct SMsgHdr + { + uint32_t uiVersion; ///< Protocol version. + EMsgType eType; ///< Message type. + }; + + /** @brief Connection initiation message (extends SMsgHdr). */ + struct SConnectMsg : SMsgHdr + { + sdv::process::TProcessID tProcessID; ///< Process ID for lifetime monitoring. + }; + + /** @brief Fragmented data message header. */ + struct SFragmentedMsgHdr : SMsgHdr + { + uint32_t uiTotalLength; ///< Total payload length across all fragments. + uint32_t uiOffset; ///< Current byte offset into the total payload. + }; + + // ---------- Minimal SDV message wrapper ---------- + class CMessage + { + public: + explicit CMessage(std::vector&& data) : m_Data(std::move(data)) {} + + /** @return Pointer to message bytes (may be null if empty). */ + const uint8_t* GetData() const { return m_Data.empty() ? nullptr : m_Data.data(); } + + /** @return Message size in bytes. */ + uint32_t GetSize() const { return static_cast(m_Data.size()); } + + /** @return Interpreted SDV base header (or a fallback). */ + SMsgHdr GetMsgHdr() const + { + if (GetSize() < sizeof(SMsgHdr)) return SMsgHdr{0, EMsgType::connect_term}; + return *reinterpret_cast(GetData()); + } + + /** @return SDV connect header (or default if undersized). */ + SConnectMsg GetConnectHdr() const + { + if (GetSize() < sizeof(SConnectMsg)) return SConnectMsg{}; + return *reinterpret_cast(GetData()); + } + + /** @return SDV fragmented header (or default if undersized). */ + SFragmentedMsgHdr GetFragmentedHdr() const + { + if (GetSize() < sizeof(SFragmentedMsgHdr)) return SFragmentedMsgHdr{}; + return *reinterpret_cast(GetData()); + } + + /** @return true if the SDV envelope is well-formed. */ + bool IsValid() const + { + if (!GetData() || GetSize() < sizeof(SMsgHdr)) return false; + SMsgHdr hdr = GetMsgHdr(); + switch (hdr.eType) + { + case EMsgType::sync_request: + case EMsgType::sync_answer: + case EMsgType::connect_term: + case EMsgType::data: + return true; + case EMsgType::connect_request: + case EMsgType::connect_answer: + return GetSize() >= sizeof(SConnectMsg); + case EMsgType::data_fragment: + return GetSize() >= sizeof(SFragmentedMsgHdr); + default: + return false; + } + } + + private: + std::vector m_Data; + }; + + /** @brief Receive-time data reassembly context. */ + struct SDataContext + { + uint32_t uiTotalSize = 0; ///< Total payload size (without SDV header). + uint32_t uiCurrentOffset = 0; ///< Current filled byte count. + size_t nChunkIndex = 0; ///< Current chunk index being filled. + uint32_t uiChunkOffset = 0; ///< Offset within the current chunk. + sdv::sequence> seqDataChunks; ///< Output buffers. + }; + + /** + * @brief Event callback structure. + */ + struct SEventCallback + { + uint64_t uiCookie = 0; ///< Registration cookie + sdv::ipc::IConnectEventCallback* pCallback = nullptr; ///< Pointer to the callback. Could be NULL when the callback + ///< was deleted. + }; + + /** @brief UDS transport maximum frame size (safety cap). */ + static constexpr uint32_t kMaxUdsPacketSize = 64u * 1024u * 1024u; // 64 MiB + + // ---------- Transport helpers ---------- + /** + * @brief Accept incoming client (server side). + * @return Returns a connected socket FD or -1 on error. + * @deprecated The poll-based accept loop in ConnectWorker() is used instead. + */ + int AcceptConnection(); + + /** + * @brief Read exactly 'bufferLength' bytes from m_Fd into 'buffer' (blocking). + * @return true on success; false on EOF/error. + */ + bool ReadNumberOfBytes(char* buffer, uint32_t bufferLength); + + /** + * @brief Read UDS transport header (size prefix) into packetSize. + * @param[out] packetSize SDV message size to follow. + * @return true if the header was read and valid; false otherwise. + */ + bool ReadTransportHeader(uint32_t& packetSize); + + /** + * @brief Send an SDV message as a UDS frame: [packetSize][payload]. + * @param pData Pointer to serialized SDV payload. + * @param uiDataLength Payload size. + * @return true if fully sent; false otherwise. + */ + bool SendSizedPacket(const void* pData, uint32_t uiDataLength); + + /** + * @brief Receive loop: read UDS frames and dispatch to the SDV state machine. + */ + void ReceiveMessages(); + + /** + * @brief Handle an incoming sync_request message. + * + * Sent by the peer during the initial handshake. + * The server replies with sync_answer. + * + * @param message SDV envelope containing the sync_request header. + */ + void ReceiveSyncRequest(const CMessage& message); + + /** + * @brief Handle an incoming sync_answer message. + * + * Received by the client after sending sync_request. + * Triggers the next handshake step: the client sends connect_request. + * + * @param message SDV envelope containing the sync_answer header. + */ + void ReceiveSyncAnswer(const CMessage& message); + + /** + * @brief Handle an incoming connect_request message. + * + * Sent by the peer to request establishment of a logical SDV connection. + * The server replies with connect_answer. + * + * @param message SDV envelope containing the connect_request header. + */ + void ReceiveConnectRequest(const CMessage& message); + + /** + * @brief Handle an incoming connect_answer message. + * + * Sent by the peer after receiving our connect_request. + * Marks completion of the SDV handshake on the client side. + * + * @param message SDV envelope containing the connect_answer header. + */ + void ReceiveConnectAnswer(const CMessage& message); + + /** + * @brief Handle an incoming connect_term message. + * + * Indicates that the peer requests immediate termination of the connection. + * Sets status to disconnected and stops the RX loop. + * + * @param message SDV envelope containing the connect_term header. + */ + void ReceiveConnectTerm(const CMessage& message); + + /** + * @brief Handle a non-fragmented SDV data message. + * + * Format: + * [SMsgHdr][LengthTable][DataChunks...] + * The function decodes the table, allocates chunk buffers, + * copies the full payload, and dispatches it via IDataReceiveCallback. + * + * @param rMessage SDV envelope containing the full data frame. + * @param rsDataCtxt Reassembly context used to extract chunks. + */ + void ReceiveDataMessage(const CMessage& rMessage, SDataContext& rsDataCtxt); + + /** + * @brief Handle a fragmented SDV data message. + * + * Format: + * [SFragmentedMsgHdr][(LengthTable only in first fragment)][PayloadSlice] + * + * This function appends each slice into the appropriate chunk buffer. + * When the final fragment is received and all chunks are complete, + * the assembled data is dispatched via IDataReceiveCallback. + * + * @param rMessage SDV envelope containing the current fragment. + * @param rsDataCtxt Reassembly context shared across fragments. + */ + void ReceiveDataFragmentMessage(const CMessage& rMessage, SDataContext& rsDataCtxt); + + /** + * @brief Read the data size table (buffer count + each buffer size). + * @param rMessage SDV message containing the table. + * @param rsDataCtxt Output reassembly context; buffers are allocated. + * @return Offset within message data where the payload begins; 0 on error. + */ + uint32_t ReadDataTable(const CMessage& rMessage, SDataContext& rsDataCtxt); + + /** + * @brief Copy payload bytes from 'rMessage' into the buffers allocated by ReadDataTable. + * Multiple calls may be required for fragmented messages. + * @param rMessage SDV message holding the current data/fragment. + * @param uiOffset Offset within 'rMessage' where payload starts in this frame. + * @param rsDataCtxt Reassembly context to be filled; callback fires when complete. + * @return true if progress was made / completion achieved, false if invalid. + */ + bool ReadDataChunk(const CMessage& rMessage, uint32_t uiOffset, SDataContext& rsDataCtxt); + + // ---------- Internal threading ---------- + /** @brief Connect worker (server accept loop or client connect retry). */ + void ConnectWorker(); + /** @brief Start RX thread (precondition: status=connected, FD valid). */ + void StartReceiveThread_Unsafe(); + /** + * @brief Stop workers and close sockets, then optionally unlink path. + * @param unlinkPath If true and server, unlink the socket path on exit. + */ + void StopThreadsAndCloseSockets(bool unlinkPath); + +private: + //Transport state + int m_Fd { -1 }; ///< Active connection FD. + int m_ListenFd { -1 }; ///< Server-side listening FD. + bool m_AcceptConnectionRequired { false }; + std::string m_UdsPath; + + //Threads & control + std::atomic m_StopReceiveThread { false }; + std::atomic m_StopConnectThread { false }; + std::thread m_ReceiveThread; + std::thread m_ConnectThread; + + //Status & synchronization + std::condition_variable m_StateCv; + std::atomic m_eStatus { sdv::ipc::EConnectStatus::uninitialized }; + sdv::ipc::IDataReceiveCallback* m_pReceiver { nullptr }; + sdv::ipc::IConnectEventCallback* m_pEvent { nullptr }; + std::mutex m_MtxConnect; + std::condition_variable m_CvConnect; + std::mutex m_StateMtx; ///< Protects receiver/event assignment. + std::list m_lstEventCallbacks; ///< List containing event callbacks + std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list + + //TX synchronization + std::mutex m_SendMtx; +}; + +#endif // CONNECTION_H +#endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_win_sockets/CMakeLists.txt b/sdv_services/uds_win_sockets/CMakeLists.txt new file mode 100644 index 0000000..290ec77 --- /dev/null +++ b/sdv_services/uds_win_sockets/CMakeLists.txt @@ -0,0 +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 +#******************************************************************************* + +if(WIN32) +# Define project +project(uds_win_sockets VERSION 1.0 LANGUAGES CXX) + +# Define target +add_library(uds_win_sockets SHARED + "channel_mgnt.h" + "channel_mgnt.cpp" + "connection.h" + "connection.cpp") + +target_link_libraries(uds_win_sockets ${CMAKE_THREAD_LIBS_INIT} Ws2_32.lib) + +target_link_options(uds_win_sockets PRIVATE) +target_include_directories(uds_win_sockets PRIVATE ./include/) + +set_target_properties(uds_win_sockets PROPERTIES PREFIX "") +set_target_properties(uds_win_sockets PROPERTIES SUFFIX ".sdv") + +# Build dependencies +add_dependencies(uds_win_sockets CompileCoreIDL) + +# Appending the service in the service list +set(SDV_Service_List ${SDV_Service_List} uds_win_sockets PARENT_SCOPE) + +endif() diff --git a/sdv_services/uds_win_sockets/channel_mgnt.cpp b/sdv_services/uds_win_sockets/channel_mgnt.cpp new file mode 100644 index 0000000..840e111 --- /dev/null +++ b/sdv_services/uds_win_sockets/channel_mgnt.cpp @@ -0,0 +1,485 @@ +/******************************************************************************** +* 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: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#include "channel_mgnt.h" +#include "connection.h" + +#include "../../global/base64.h" +#include +#include + +#include + +#pragma push_macro("interface") +#undef interface + +#pragma push_macro("GetObject") +#undef GetObject + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include +#include +#include +#include + +#include + +#pragma pop_macro("GetObject") +#pragma pop_macro("interface") + +namespace +{ + +/** + * @brief Parse a UDS connect/config string and extract the path + * + * Expected format (substring-based, not strict): + * "proto=uds;path=;" + * + * Behavior: + * - If "proto=uds" is missing -> returns false (not a UDS config) + * - If "path=" is missing -> returns true and outPath is cleared + * - If "path=" is present -> extracts the substring until ';' or end + * + * @param cs Input configuration / connect string + * @param outPath Output: extracted path (possibly empty) + * @return true if this looks like a UDS string, false otherwise + */ +static bool ParseUdsPath(const std::string& cs, std::string& outPath) +{ + constexpr const char* protoKey = "proto=uds"; + constexpr const char* pathKey = "path="; + + // Must contain "proto=uds" to be considered UDS + if (cs.find(protoKey) == std::string::npos) + { + return false; + } + + const auto p = cs.find(pathKey); + if (p == std::string::npos) + { + // No path given, but proto=uds is present -> use default later + outPath.clear(); + return true; + } + + const auto start = p + std::strlen(pathKey); + const auto end = cs.find(';', start); + + if (end == std::string::npos) + { + outPath = cs.substr(start); + } + else + { + outPath = cs.substr(start, end - start); + } + + return true; +} + +/** + * @brief Expand Windows environment variables in the form %VAR%. + * + * Example: + * "%TEMP%\\sdv\\vapi.sock" → "C:\\Users\\...\\AppData\\Local\\Temp\\sdv\\vapi.sock" + * + * If environment expansion fails, the original string is returned unchanged + * + * @param[in] in Input string that may contain %VAR% tokens + * + * @return Expanded string, or the original input on failure + */ +static std::string ExpandEnvVars(const std::string& in) +{ + if (in.find('%') == std::string::npos) + { + return in; + } + + char buf[4096] = {}; + DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast(sizeof(buf))); + + if (n > 0 && n < sizeof(buf)) + { + return std::string(buf); + } + + return in; +} + +/** + * @brief Clamp a Unix Domain Socket pathname to the maximum allowed size + * + * Windows AF_UNIX pathname sockets require that `sun_path` fits in + * `sizeof(sockaddr_un.sun_path) - 1` bytes (including terminating NUL) + * + * If the input exceeds this limit, it is truncated + * + * @param[in] p The original pathname + * + * @return A safe pathname guaranteed to fit into sun_path + */ +static std::string ClampUdsPath(const std::string& p) +{ + SOCKADDR_UN tmp{}; + constexpr auto kMax = sizeof(tmp.sun_path) - 1; + + if (p.size() <= kMax) + { + return p; + } + + return p.substr(0, kMax); +} + +/** + * @brief Normalize a UDS path for display/logging purposes + * + * Extracts the basename from an input path and clamps it to the + * AF_UNIX pathname size limit. This is *not* the final path used for + * the socket bind/connection — it is intended only for user-visible logs. + * + * Example: + * Input: "C:/Users/.../very/long/path/vapi.sock" + * Output: "vapi.sock" + * + * @param[in] raw Raw input path (may contain directories or %VAR%) + * + * @return Normalized/clamped basename suitable for logging + */ +static std::string NormalizeUdsPathForWindows(const std::string& raw) +{ + std::string p = ExpandEnvVars(raw); + + const size_t pos = p.find_last_of("/\\"); + std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1); + + if (base.empty()) + { + base = "sdv.sock"; + } + + SDV_LOG_INFO("[AF_UNIX] Normalize raw='", raw, "' -> base='", base, "'"); + + return ClampUdsPath(base); +} + +/** + * @brief Build a short absolute Win32 path suitable for AF_UNIX `sun_path` + * + * AF_UNIX pathname sockets on Windows require short, absolute paths + * under the OS temporary directory. + * + * Algorithm: + * 1. Expand environment variables + * 2. Extract the basename + * 3. Place it under "%TEMP%\\sdv\\" + * 4. Ensure the directory exists + * 5. Clamp to AF_UNIX size limits + * + * Example: + * raw: "%TEMP%\\sdv\\vapi.sock" + * -> "\\sdv\\vapi.sock" + * + * @param[in] raw Raw input path (may contain %VAR%) + * + * @return Fully expanded, clamped, absolute path suitable for bind()/connect() + */ +static std::string MakeShortWinUdsPath(const std::string& raw) +{ + // Expand raw first (may already contain environment variables) + std::string p = ExpandEnvVars(raw); + + const size_t pos = p.find_last_of("/\\"); + std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1); + + if (base.empty()) + { + base = "sdv.sock"; + } + + // Use %TEMP%\sdv\ as a base directory + std::string dir = ExpandEnvVars("%TEMP%\\sdv\\"); + CreateDirectoryA(dir.c_str(), nullptr); // OK if already exists + + const std::string full = dir + base; + + // Ensure it fits into sun_path + return ClampUdsPath(full); +} + +/** + * @brief Create a listening AF_UNIX socket on Windows + * + * This creates a WinSock AF_UNIX stream socket, constructs a pathname + * using MakeShortWinUdsPath(), removes any stale socket file, binds, + * and marks it for listening. + * + * It logs all success/error cases for diagnostic purposes + * + * @param[in] rawPath Raw path string from configuration/connect-string + * + * @return A valid SOCKET on success, or INVALID_SOCKET on failure + */ +static SOCKET CreateUnixListenSocket(const std::string& rawPath) +{ + SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) + { + SDV_LOG_ERROR("[AF_UNIX] socket() FAIL (listen), WSA=", WSAGetLastError()); + return INVALID_SOCKET; + } + + // Short absolute path, common for server and clients + std::string udsPath = MakeShortWinUdsPath(rawPath); + + SOCKADDR_UN addr{}; + addr.sun_family = AF_UNIX; + strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); + + // Effective length: offsetof + strlen + 1 for "pathname" AF_UNIX + const int addrlen = static_cast( + offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1 + ); + + // Remove any leftover file for that path + ::remove(udsPath.c_str()); + + if (bind(s, reinterpret_cast(&addr), addrlen) == SOCKET_ERROR) + { + SDV_LOG_ERROR( + "[AF_UNIX] bind FAIL (pathname), WSA=", + WSAGetLastError(), ", path='", udsPath, "'" + ); + closesocket(s); + return INVALID_SOCKET; + } + + if (listen(s, SOMAXCONN) == SOCKET_ERROR) + { + SDV_LOG_ERROR( + "[AF_UNIX] listen FAIL, WSA=", + WSAGetLastError(), ", path='", udsPath, "'" + ); + closesocket(s); + return INVALID_SOCKET; + } + + SDV_LOG_INFO("[AF_UNIX] bind OK (pathname), listen OK, path='", udsPath, "'"); + return s; +} + +/** + * @brief Connect to a Windows AF_UNIX server socket with retry logic + * + * Repeatedly attempts to connect to the server's UDS path until either: + * - connection succeeds, or + * - total timeout is exceeded + * + * On each attempt: + * - a new socket() is created + * - connect() is attempted + * - on failure the socket is closed and retried + * + * This mirrors Linux AF_UNIX behavior where the client waits for the + * server's socket file to appear/become ready + * + * @param[in] rawPath Raw UDS path from configuration + * @param[in] totalTimeoutMs Maximum total wait time in milliseconds + * @param[in] retryDelayMs Delay between retries in milliseconds + * + * @return Connected SOCKET on success, INVALID_SOCKET on timeout or error + */ +static SOCKET ConnectUnixSocket( + const std::string& rawPath, + uint32_t totalTimeoutMs, + uint32_t retryDelayMs) +{ + const std::string udsPath = MakeShortWinUdsPath(rawPath); + + SOCKADDR_UN addr{}; + addr.sun_family = AF_UNIX; + strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); + + const int addrlen = static_cast( + offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1 + ); + + const auto deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(totalTimeoutMs); + + int lastError = 0; + + while (true) + { + SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) + { + lastError = WSAGetLastError(); + SDV_LOG_ERROR("[AF_UNIX] socket() FAIL (client), WSA=", lastError); + return INVALID_SOCKET; + } + + if (connect(s, reinterpret_cast(&addr), addrlen) == 0) + { + SDV_LOG_INFO("[AF_UNIX] connect OK (pathname), path='", udsPath, "'"); + return s; + } + + lastError = WSAGetLastError(); + closesocket(s); + + if (std::chrono::steady_clock::now() >= deadline) + { + SDV_LOG_ERROR( + "[AF_UNIX] connect TIMEOUT (pathname), last WSA=", + lastError, ", path='", udsPath, "'" + ); + return INVALID_SOCKET; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs)); + } +} + +} // anonymous namespace + +bool CSocketsChannelMgnt::OnInitialize() +{ + return true; +} + +void CSocketsChannelMgnt::OnServerClosed(const std::string& udsPath, CConnection* ptr) +{ + std::lock_guard lock(m_udsMtx); + + auto it = m_udsServers.find(udsPath); + if (it != m_udsServers.end() && it->second.get() == ptr) + { + // Remove the server entry only if it matches the pointer we know + m_udsServers.erase(it); + } + + // Mark this UDS path as no longer claimed + m_udsServerClaimed.erase(udsPath); +} + +void CSocketsChannelMgnt::OnShutdown() +{} + +sdv::ipc::SChannelEndpoint CSocketsChannelMgnt::CreateEndpoint(const sdv::u8string& cfgStr) +{ + // Ensure WinSock is initialized on Windows + if (StartUpWinSock() != 0) + { + // If WinSock cannot be initialized, we cannot create an endpoint + return {}; + } + + // Parse UDS path from config. If proto!=uds, we still default to UDS + std::string udsRaw; + bool udsRequested = ParseUdsPath(cfgStr, udsRaw); + + if (!udsRequested || udsRaw.empty()) + { + // Default path if not provided or not UDS-specific + udsRaw = "%LOCALAPPDATA%/sdv/vapi.sock"; + } + + std::string udsPath = NormalizeUdsPathForWindows(udsRaw); + SDV_LOG_INFO("[AF_UNIX] endpoint udsPath=", udsPath); + + SOCKET listenSocket = CreateUnixListenSocket(udsPath); + if (listenSocket == INVALID_SOCKET) + { + // Endpoint creation failed + return {}; + } + + // Server-side CConnection, it will accept() a client on first use + auto server = std::make_shared(listenSocket, /*acceptRequired*/ true); + + { + std::lock_guard lock(m_udsMtx); + m_udsServers[udsPath] = server; + m_udsServerClaimed.erase(udsPath); + } + + sdv::ipc::SChannelEndpoint ep{}; + ep.pConnection = static_cast(server.get()); + ep.ssConnectString = "proto=uds;path=" + udsPath + ";"; + + return ep; +} + +sdv::IInterfaceAccess* CSocketsChannelMgnt::Access(const sdv::u8string& cs) +{ + // Ensure WinSock is initialized + if (StartUpWinSock() != 0) + { + return nullptr; + } + + std::string udsRaw; + if (!ParseUdsPath(cs, udsRaw)) + { + // Not a UDS connect string + return nullptr; + } + + if (udsRaw.empty()) + { + udsRaw = "%LOCALAPPDATA%/sdv/vapi.sock"; + } + + std::string udsPath = NormalizeUdsPathForWindows(udsRaw); + SDV_LOG_INFO("[AF_UNIX] Access udsPath=", udsPath); + + { + std::lock_guard lock(m_udsMtx); + + auto it = m_udsServers.find(udsPath); + if (it != m_udsServers.end() && it->second != nullptr) + { + // Return the server-side object only once for this UDS path + if (!m_udsServerClaimed.count(udsPath)) + { + m_udsServerClaimed.insert(udsPath); + SDV_LOG_INFO("[AF_UNIX] Access -> RETURN SERVER for ", udsPath); + return it->second.get(); // server object (acceptRequired=true) + } + // Otherwise, later calls will create a client socket below + } + } + + // CLIENT: create a socket connected to the same udsPath + SOCKET s = ConnectUnixSocket(udsPath, + /*totalTimeoutMs*/ 5000, + /*retryDelayMs*/ 50); + + if (s == INVALID_SOCKET) + { + return nullptr; + } + + SDV_LOG_INFO("[AF_UNIX] Access -> CREATE CLIENT for ", udsPath); + + // Client-side connection object (acceptRequired=false) + // Ownership is transferred to the caller (VAPI runtime) + return new CConnection(s, /*acceptRequired*/ false); +} \ No newline at end of file diff --git a/sdv_services/uds_win_sockets/channel_mgnt.h b/sdv_services/uds_win_sockets/channel_mgnt.h new file mode 100644 index 0000000..1934285 --- /dev/null +++ b/sdv_services/uds_win_sockets/channel_mgnt.h @@ -0,0 +1,237 @@ +/******************************************************************************** +* 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: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#ifndef CHANNEL_MGNT_H +#define CHANNEL_MGNT_H + +#include +#include +#include "connection.h" + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +// Winsock headers are required for SOCKET / AF_UNIX / WSAStartup +// NOTE: The actual initialization is done via StartUpWinSock() +# include +#endif + +/** + * @brief RAII wrapper for addrinfo returned by getaddrinfo(). + * + * This structure owns the addrinfo list and frees it automatically in the + * destructor. Copy and move operations are disabled to avoid double-free. + */ +struct CAddrInfo +{ + /** + * @brief Constructor + */ + CAddrInfo() = default; + /** + * @brief Constructor + */ + CAddrInfo(const CAddrInfo&) = delete; + /** + * @brief Copy constructor + */ + CAddrInfo& operator=(const CAddrInfo&) = delete; + /** + * @brief Move constructor + * @param[in] other Reference to the structure to move + */ + CAddrInfo(CAddrInfo&& other) = delete; + + /** + * @brief Move operator. + * @param[in] other Reference to the structure to move + * @return Returns reference to CAddrInfo structure + */ + CAddrInfo& operator=(CAddrInfo&& other) = delete; + + ~CAddrInfo() + { + if (AddressInfo != nullptr) + { + freeaddrinfo(AddressInfo); + AddressInfo = nullptr; + } + } + + /// @brief Pointer to the addrinfo list returned by getaddrinfo() + addrinfo* AddressInfo { nullptr }; +}; + +/** + * @brief Initialize WinSock on Windows (idempotent) + * + * This helper ensures WSAStartup() is called only once in the process + * + * @return 0 on success, otherwise a WinSock error code + */ +inline int StartUpWinSock() +{ +#ifdef _WIN32 + static bool isInitialized = false; + if (isInitialized) + { + return 0; + } + + WSADATA wsaData {}; + const int error = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (error != 0) + { + SDV_LOG_ERROR("WSAStartup failed with error: ", std::to_string(error)); + } + else + { + SDV_LOG_INFO("WSAStartup initialized"); + isInitialized = true; + } + return error; +#else + // Non-Windows: nothing to do. Return success for symmetry + return 0; +#endif +} + +/** + * @brief Simple pair of sockets used to connect two child processes + * + * This is reserved for potential future use (e.g. TCP/IP or in-proc socket pairs) + */ +struct SocketConnection +{ + SOCKET From { INVALID_SOCKET }; ///< Socket used by the "from" side (child process) + SOCKET To { INVALID_SOCKET }; ///< Socket used by the "to" side (child process) +}; + +/** + * @brief IPC channel management class for socket-based communication on Windows + * + * This implementation uses AF_UNIX (Unix domain sockets) provided by WinSock + * on Windows to establish a local IPC channel between VAPI processes + * + * The class: + * - implements IObjectControl (lifecycle, operation mode) + * - implements ipc::ICreateEndpoint (endpoint creation) + * - implements ipc::IChannelAccess (client access via connect string) + */ +class CSocketsChannelMgnt + : public sdv::CSdvObject + , public sdv::ipc::ICreateEndpoint + , public sdv::ipc::IChannelAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IChannelAccess) + SDV_INTERFACE_ENTRY(sdv::ipc::ICreateEndpoint) + END_SDV_INTERFACE_MAP() + + // Object declarations + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) + DECLARE_OBJECT_CLASS_NAME("WinSocketsChannelControl") + DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl") + DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl") + DECLARE_OBJECT_SINGLETON() + + /** + * @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 bool OnInitialize() override; + + /** + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. + */ + virtual void OnShutdown() override; + + /** + * @brief Create an IPC endpoint and return its connection information + * + * Overload of sdv::ipc::ICreateEndpoint::CreateEndpoint + * + * The endpoint is implemented as a local AF_UNIX server socket + * (listen socket) on Windows. The connect string has the format: + * + * proto=uds;path=<udsPath> + * + * If no configuration is provided or no path is specified, a default + * path is used (under %LOCALAPPDATA%/sdv) + * + * Example configuration: + * @code + * [IpcChannel] + * Interface = "127.0.0.1" + * Port = 2000 + * @endcode + * + * @param[in] ssChannelConfig Optional channel-specific configuration string. + * @return SChannelEndpoint structure containing the connection object and + * the connect string + */ + sdv::ipc::SChannelEndpoint CreateEndpoint(/*in*/ const sdv::u8string& ssChannelConfig) override; + + /** + * @brief Create or access a connection object from a connection string + * + * Overload of sdv::ipc::IChannelAccess::Access + * + * The connect string is expected to contain: + * proto=uds;path=<udsPath> + * + * For the first Access() call with a given path, the server-side + * connection object created by CreateEndpoint() can be returned. + * Subsequent calls will create client-side connections + * + * @param[in] ssConnectString String containing the channel connection parameters + * @return Pointer to IInterfaceAccess interface of the connection object or + * nullptr when the object cannot be created + */ + sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override; + + /** + * @brief Called by a CConnection instance when the server side is closed + * + * Used to clean up internal registries for a given UDS path + * + * @param udsPath Path to the UDS endpoint + * @param ptr Pointer to the CConnection instance that was closed + */ + void OnServerClosed(const std::string& udsPath, CConnection* ptr); + +private: + /// @brief Registry of AF_UNIX server connections keyed by normalized UDS path + std::map> m_udsServers; + + /** + * @brief Set of UDS paths that already returned their server-side + * connection once via Access() + * + * This prevents returning the same server object multiple times + */ + std::unordered_set m_udsServerClaimed; + + /// @brief Mutex protecting m_udsServers and m_udsServerClaimed + std::mutex m_udsMtx; +}; + +// SDV object factory macro +DEFINE_SDV_OBJECT(CSocketsChannelMgnt) + +#endif // ! defined CHANNEL_MGNT_H \ No newline at end of file diff --git a/sdv_services/uds_win_sockets/connection.cpp b/sdv_services/uds_win_sockets/connection.cpp new file mode 100644 index 0000000..05c9377 --- /dev/null +++ b/sdv_services/uds_win_sockets/connection.cpp @@ -0,0 +1,952 @@ +/******************************************************************************** +* 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: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#include "connection.h" + +#include +#include +#include +#include + +CConnection::CConnection(SOCKET preconfiguredSocket, bool acceptConnectionRequired) + : m_ConnectionStatus(sdv::ipc::EConnectStatus::uninitialized) + , m_AcceptConnectionRequired(acceptConnectionRequired) + , m_CancelWait(false) +{ + // Initialize legacy buffers with zero (kept for potential compatibility) + std::fill(std::begin(m_SendBuffer), std::end(m_SendBuffer), '\0'); + std::fill(std::begin(m_ReceiveBuffer), std::end(m_ReceiveBuffer), '\0'); + + if (m_AcceptConnectionRequired) + { + // Server side: we own a listening socket, active socket is not yet accepted + m_ListenSocket = preconfiguredSocket; + m_ConnectionSocket = INVALID_SOCKET; + } + else + { + // Client side: we already have a connected socket + m_ListenSocket = INVALID_SOCKET; + m_ConnectionSocket = preconfiguredSocket; + } +} + +CConnection::~CConnection() +{ + try + { + StopThreadsAndCloseSockets(); + m_ConnectionStatus = sdv::ipc::EConnectStatus::disconnected; + } + catch (...) + { + // Destructors must not throw + } +} + +void CConnection::SetStatus(sdv::ipc::EConnectStatus status) +{ + { + std::lock_guard lk(m_MtxConnect); + m_ConnectionStatus.store(status, std::memory_order_release); + } + + // Wake up any waiter + m_CvConnect.notify_all(); + + // Notify event callback if registered + if (m_pEvent) + { + try + { + m_pEvent->SetStatus(status); + } + catch (...) + { + // Ignore callbacks throwing exceptions + } + } +} + +int32_t CConnection::Send(const char* data, int32_t dataLength) +{ + int32_t total = 0; + + while (total < dataLength) + { + const int32_t n = ::send(m_ConnectionSocket, data + total, dataLength - total, 0); + if (n == SOCKET_ERROR) + { + SDV_LOG_ERROR("send failed with error: ", std::to_string(WSAGetLastError())); + m_ConnectionStatus = sdv::ipc::EConnectStatus::communication_error; + m_ConnectionSocket = INVALID_SOCKET; + return (total > 0) ? total : SOCKET_ERROR; + } + total += n; + } + + return total; +} + +int CConnection::SendExact(const char* data, int len) +{ + int total = 0; + + while (total < len) + { + const int n = ::send(m_ConnectionSocket, data + total, len - total, 0); + if (n <= 0) + { + return -1; + } + total += n; + } + + return total; +} + +bool CConnection::SendData(/*inout*/ sdv::sequence>& seqData) +{ + // Must be connected and have a valid socket + if (m_ConnectionStatus != sdv::ipc::EConnectStatus::connected || + m_ConnectionSocket == INVALID_SOCKET) + { + m_ConnectionStatus = sdv::ipc::EConnectStatus::communication_error; + return false; + } + + std::lock_guard lk(m_SendMutex); + + // Build SDV length table + sdv::sequence*> seqTemp; + sdv::pointer table; + + const uint32_t nChunks = static_cast(seqData.size()); + const uint32_t tableBytes = (nChunks + 1u) * sizeof(uint32_t); + + table.resize(tableBytes); + + // First uint32_t = chunk count + std::memcpy(table.get(), &nChunks, sizeof(uint32_t)); + + uint32_t offset = sizeof(uint32_t); + uint64_t required = sizeof(uint32_t); + + // Next are n chunk sizes + for (auto& buf : seqData) + { + const uint32_t len = static_cast(buf.size()); + std::memcpy(table.get() + offset, &len, sizeof(uint32_t)); + offset += sizeof(uint32_t); + required += sizeof(uint32_t); + required += len; + seqTemp.push_back(&buf); + } + + // Prepend table as the first "chunk" + seqTemp.insert(seqTemp.begin(), &table); + + const uint32_t maxPayloadData = + (kMaxUdsPacketSize > sizeof(SMsgHdr)) ? + (kMaxUdsPacketSize - static_cast(sizeof(SMsgHdr))) : + 0; + + const uint32_t maxPayloadFrag = + (kMaxUdsPacketSize > sizeof(SFragmentedMsgHdr)) ? + (kMaxUdsPacketSize - static_cast(sizeof(SFragmentedMsgHdr))) : + 0; + + if (maxPayloadFrag == 0) + { + return false; + } + + // Single-frame vs. fragmented + const bool fitsSingle = (required <= static_cast(maxPayloadData)); + + auto itChunk = seqTemp.cbegin(); + size_t pos = 0; + + if (fitsSingle) + { + const uint32_t payloadBytes = static_cast(required); + const uint32_t totalBytes = payloadBytes + static_cast(sizeof(SMsgHdr)); + + std::vector frame(totalBytes); + uint32_t msgOff = 0; + + // SDV header + { + auto* hdr = reinterpret_cast(frame.data()); + hdr->uiVersion = SDVFrameworkInterfaceVersion; + hdr->eType = EMsgType::data; + msgOff = static_cast(sizeof(SMsgHdr)); + } + + // Copy table + chunks + while (itChunk != seqTemp.cend() && msgOff < totalBytes) + { + const auto& ref = *itChunk; + const uint32_t len = static_cast(ref->size()); + const uint8_t* src = ref->get(); + const uint32_t canCopy = + std::min(len - static_cast(pos), totalBytes - msgOff); + + if (canCopy) + { + std::memcpy(frame.data() + msgOff, src + pos, canCopy); + } + + pos += canCopy; + msgOff += canCopy; + + if (pos >= len) + { + pos = 0; + ++itChunk; + } + } + + uint32_t packetSize = totalBytes; + if (SendExact(reinterpret_cast(&packetSize), sizeof(packetSize)) < 0) + return false; + if (SendExact(reinterpret_cast(frame.data()), totalBytes) < 0) + return false; + + return true; + } + + // Fragmented sending + uint32_t sentOffset = 0; + + while (itChunk != seqTemp.cend() && sentOffset < required) + { + const uint32_t remaining = static_cast(required - sentOffset); + const uint32_t dataBytes = std::min(remaining, maxPayloadFrag); + const uint32_t allocBytes = dataBytes + static_cast(sizeof(SFragmentedMsgHdr)); + + std::vector frame(allocBytes); + uint32_t msgOff = 0; + + // Fragment header + { + auto* hdr = reinterpret_cast(frame.data()); + hdr->uiVersion = SDVFrameworkInterfaceVersion; + hdr->eType = EMsgType::data_fragment; + hdr->uiTotalLength = static_cast(required); + hdr->uiOffset = sentOffset; + msgOff = static_cast(sizeof(SFragmentedMsgHdr)); + } + + // Copy slice across the sequence + uint32_t copied = 0; + + while (itChunk != seqTemp.cend() && copied < dataBytes) + { + const auto& ref = *itChunk; + const uint8_t* src = ref->get(); + const uint32_t len = static_cast(ref->size()); + + const uint32_t canCopy = + std::min(len - static_cast(pos), dataBytes - copied); + + if (canCopy) + { + std::memcpy(frame.data() + msgOff, src + pos, canCopy); + } + + pos += canCopy; + msgOff += canCopy; + copied += canCopy; + + if (pos >= len) + { + pos = 0; + ++itChunk; + } + } + + uint32_t packetSize = allocBytes; + if (SendExact(reinterpret_cast(&packetSize), sizeof(packetSize)) < 0) + return false; + if (SendExact(reinterpret_cast(frame.data()), allocBytes) < 0) + return false; + + sentOffset += copied; + } + + return (sentOffset == required); +} + +SOCKET CConnection::AcceptConnection() +{ + if (m_ListenSocket == INVALID_SOCKET) + { + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return INVALID_SOCKET; + } + + while (!m_StopConnectThread.load()) + { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(m_ListenSocket, &rfds); + + TIMEVAL tv{}; + tv.tv_sec = 0; + tv.tv_usec = 50 * 1000; // 50 ms + + const int sr = ::select(0, &rfds, nullptr, nullptr, &tv); + if (sr == SOCKET_ERROR) + { + SDV_LOG_ERROR("[AF_UNIX] select(listen) FAIL, WSA=", WSAGetLastError()); + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return INVALID_SOCKET; + } + + if (sr == 0) + { + continue; // timeout, re-check stop flag + } + + SOCKET c = ::accept(m_ListenSocket, nullptr, nullptr); + if (c == INVALID_SOCKET) + { + const int err = WSAGetLastError(); + if (err == WSAEINTR || err == WSAEWOULDBLOCK) + { + continue; + } + + SDV_LOG_ERROR("[AF_UNIX] accept FAIL, WSA=", err); + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return INVALID_SOCKET; + } + + SDV_LOG_INFO("[AF_UNIX] accept OK, socket=", static_cast(c)); + return c; + } + + SDV_LOG_ERROR("[AF_UNIX] accept canceled (stop flag)"); + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return INVALID_SOCKET; +} + +bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) +{ + // Store callbacks + m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); + m_pEvent = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); + + // Reset stop flags + m_StopReceiveThread.store(false); + m_StopConnectThread.store(false); + m_CancelWait.store(false); + + // Join old threads if any + if (m_ReceiveThread.joinable()) + m_ReceiveThread.join(); + if (m_ConnectThread.joinable()) + m_ConnectThread.join(); + + // Start the connect worker + m_ConnectThread = std::thread(&CConnection::ConnectWorker, this); + return true; +} + +bool CConnection::WaitForConnection(uint32_t uiWaitMs) +{ + if (m_ConnectionStatus.load(std::memory_order_acquire) == + sdv::ipc::EConnectStatus::connected) + { + return true; + } + + std::unique_lock lk(m_MtxConnect); + + if (uiWaitMs == 0xFFFFFFFFu) // INFINITE + { + m_CvConnect.wait( + lk, + [this] + { + return m_ConnectionStatus.load(std::memory_order_acquire) == + sdv::ipc::EConnectStatus::connected; + }); + return true; + } + + if (uiWaitMs == 0u) // zero wait + { + return (m_ConnectionStatus.load(std::memory_order_acquire) == + sdv::ipc::EConnectStatus::connected); + } + + // finite wait + return m_CvConnect.wait_for( + lk, + std::chrono::milliseconds(uiWaitMs), + [this] + { + return m_ConnectionStatus.load(std::memory_order_acquire) == + sdv::ipc::EConnectStatus::connected; + }); +} + +void CConnection::CancelWait() +{ + m_CancelWait.store(true); +} + +void CConnection::Disconnect() +{ + m_CancelWait.store(true); + StopThreadsAndCloseSockets(); + SetStatus(sdv::ipc::EConnectStatus::disconnected); +} + +uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) +{ + // Extract IConnectEventCallback interface + m_pEvent = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface(); + + // Only one callback is supported; cookie 1 = valid + return (m_pEvent != nullptr) ? 1ULL : 0ULL; +} + +void CConnection::UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) +{ + // Only one callback supported -> cookie value is 1 + if (uiCookie == 1ULL) + { + m_pEvent = nullptr; + } +} + +sdv::ipc::EConnectStatus CConnection::GetStatus() const +{ + return m_ConnectionStatus; +} + +void CConnection::DestroyObject() +{ + m_StopReceiveThread = true; + m_StopConnectThread = true; + + StopThreadsAndCloseSockets(); + m_ConnectionStatus = sdv::ipc::EConnectStatus::disconnected; +} + +void CConnection::ConnectWorker() +{ + try + { + if (m_AcceptConnectionRequired) + { + // SERVER SIDE + if (m_ListenSocket == INVALID_SOCKET) + { + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return; + } + + SetStatus(sdv::ipc::EConnectStatus::initializing); + SDV_LOG_INFO("[AF_UNIX] Srv ConnectWorker start: listen=%llu", + static_cast(m_ListenSocket)); + + SOCKET c = AcceptConnection(); + SDV_LOG_INFO("[AF_UNIX] Srv AcceptConnection returned: sock=%llu status=%d", + static_cast(c), + static_cast(m_ConnectionStatus.load())); + + if (c == INVALID_SOCKET) + { + if (m_pEvent) + { + try + { + m_pEvent->SetStatus(m_ConnectionStatus); + } + catch (...) + { + } + } + return; + } + + m_ConnectionSocket = c; + SetStatus(sdv::ipc::EConnectStatus::connected); + StartReceiveThread_Unsafe(); + return; + } + else + { + // CLIENT SIDE + if (m_ConnectionSocket == INVALID_SOCKET) + { + SetStatus(sdv::ipc::EConnectStatus::connection_error); + return; + } + } + + // Client side: socket is already connected + SetStatus(sdv::ipc::EConnectStatus::connected); + StartReceiveThread_Unsafe(); + } + catch (...) + { + SetStatus(sdv::ipc::EConnectStatus::connection_error); + } +} + +void CConnection::StartReceiveThread_Unsafe() +{ + if (m_ReceiveThread.joinable()) + { + m_ReceiveThread.join(); + } + + m_StopReceiveThread.store(false); + m_ReceiveThread = std::thread(&CConnection::ReceiveMessages, this); +} + +void CConnection::StopThreadsAndCloseSockets() +{ + // Signal stop + m_StopReceiveThread.store(true); + m_StopConnectThread.store(true); + + // Close listen socket to break select()/accept + SOCKET l = m_ListenSocket; + m_ListenSocket = INVALID_SOCKET; + if (l != INVALID_SOCKET) + { + ::closesocket(l); + } + + // Close active connection socket + SOCKET s = m_ConnectionSocket; + m_ConnectionSocket = INVALID_SOCKET; + if (s != INVALID_SOCKET) + { + ::shutdown(s, SD_BOTH); + ::closesocket(s); + } + + const auto self = std::this_thread::get_id(); + + if (m_ReceiveThread.joinable()) + { + if (m_ReceiveThread.get_id() == self) + { + m_ReceiveThread.detach(); + } + else + { + m_ReceiveThread.join(); + } + } + + if (m_ConnectThread.joinable()) + { + if (m_ConnectThread.get_id() == self) + { + m_ConnectThread.detach(); + } + else + { + m_ConnectThread.join(); + } + } + + SDV_LOG_INFO("[AF_UNIX] StopThreadsAndCloseSockets: closing listen=%llu conn=%llu", + static_cast(l), + static_cast(s)); +} + +bool CConnection::ReadNumberOfBytes(char* buffer, uint32_t length) +{ + uint32_t received = 0; + + while (!m_StopReceiveThread.load() && received < length) + { + const int n = ::recv(m_ConnectionSocket, buffer + received, length - received, 0); + if (n == 0) + { + // Graceful close + return false; + } + if (n < 0) + { + const int err = ::WSAGetLastError(); + if (err == WSAEINTR) + continue; // retry + if (err == WSAEWOULDBLOCK) + { + ::Sleep(1); + continue; + } + + SDV_LOG_WARNING("[UDS][RX] recv() error: ", std::strerror(err)); + return false; + } + + received += static_cast(n); + } + + return (received == length); +} + +bool CConnection::ValidateHeader(const SMsgHeader& msgHeader) +{ + // Kept only for compatibility with any legacy users (not used in SDV path) + if ((msgHeader.msgStart == m_MsgStart) && (msgHeader.msgEnd == m_MsgEnd)) + { + return (msgHeader.msgSize != 0); + } + return false; +} + + +uint32_t CConnection::ReadDataTable(const CMessage& message, SDataContext& dataCtx) +{ + uint32_t offset = 0; + + switch (message.GetMsgHdr().eType) + { + case EMsgType::data: + offset = static_cast(sizeof(SMsgHdr)); + dataCtx.uiTotalSize = message.GetSize() - static_cast(sizeof(SMsgHdr)); + break; + + case EMsgType::data_fragment: + offset = static_cast(sizeof(SFragmentedMsgHdr)); + if (message.GetFragmentedHdr().uiOffset != 0) + return 0; // table only in first fragment + dataCtx.uiTotalSize = message.GetFragmentedHdr().uiTotalLength; + break; + + default: + return 0; + } + + dataCtx.uiCurrentOffset = 0; + + if (message.GetSize() < (offset + static_cast(sizeof(uint32_t)))) + return 0; + + const uint32_t count = *reinterpret_cast(message.GetData() + offset); + + offset += static_cast(sizeof(uint32_t)); + dataCtx.uiCurrentOffset += static_cast(sizeof(uint32_t)); + + if (message.GetSize() < + (offset + count * static_cast(sizeof(uint32_t)))) + { + return 0; + } + + std::vector sizes; + sizes.reserve(count); + + for (uint32_t i = 0; i < count; ++i) + { + const uint32_t sz = *reinterpret_cast(message.GetData() + offset); + sizes.push_back(static_cast(sz)); + offset += static_cast(sizeof(uint32_t)); + dataCtx.uiCurrentOffset += static_cast(sizeof(uint32_t)); + } + + const uint32_t computed = + dataCtx.uiCurrentOffset + + static_cast( + std::accumulate( + sizes.begin(), + sizes.end(), + static_cast(0))); + + if (computed != dataCtx.uiTotalSize) + return 0; + + dataCtx.seqDataChunks.clear(); + for (size_t n : sizes) + { + dataCtx.seqDataChunks.push_back(sdv::pointer()); + dataCtx.seqDataChunks.back().resize(n); + } + + dataCtx.nChunkIndex = 0; + dataCtx.uiChunkOffset = 0; + + return offset; +} + +bool CConnection::ReadDataChunk(const CMessage& message, uint32_t offset, SDataContext& dataCtx) +{ + if (offset < sizeof(SMsgHdr)) + return false; + + if (message.GetMsgHdr().eType == EMsgType::data_fragment && + offset < sizeof(SFragmentedMsgHdr)) + return false; + + while (offset < message.GetSize() && + dataCtx.nChunkIndex < dataCtx.seqDataChunks.size()) + { + const uint32_t avail = message.GetSize() - offset; + sdv::pointer& chunk = dataCtx.seqDataChunks[dataCtx.nChunkIndex]; + + if (dataCtx.uiChunkOffset > static_cast(chunk.size())) + return false; + + const uint32_t need = + static_cast(chunk.size()) - dataCtx.uiChunkOffset; + const uint32_t toCopy = std::min(avail, need); + + std::copy( + message.GetData() + offset, + message.GetData() + offset + toCopy, + chunk.get() + dataCtx.uiChunkOffset); + + offset += toCopy; + dataCtx.uiChunkOffset += toCopy; + + if (dataCtx.uiChunkOffset >= static_cast(chunk.size())) + { + dataCtx.uiChunkOffset = 0; + ++dataCtx.nChunkIndex; + + if (dataCtx.nChunkIndex == dataCtx.seqDataChunks.size()) + { +#if ENABLE_DECOUPLING > 0 + // optional queueing path... +#else + if (m_pReceiver) + m_pReceiver->ReceiveData(dataCtx.seqDataChunks); + dataCtx = SDataContext(); // reset context +#endif + break; + } + } + } + + return true; +} + +void CConnection::ReceiveSyncRequest(const CMessage& message) +{ + const auto hdr = message.GetMsgHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + return; + } + + SMsgHdr reply{}; + reply.uiVersion = SDVFrameworkInterfaceVersion; + reply.eType = EMsgType::sync_answer; + + uint32_t packetSize = static_cast(sizeof(reply)); + + if (SendExact(reinterpret_cast(&packetSize), sizeof(packetSize)) < 0) + return; + if (SendExact(reinterpret_cast(&reply), sizeof(reply)) < 0) + return; +} + +void CConnection::ReceiveSyncAnswer(const CMessage& message) +{ + const auto hdr = message.GetMsgHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + return; + } + + SConnectMsg req{}; + req.uiVersion = SDVFrameworkInterfaceVersion; + req.eType = EMsgType::connect_request; + req.tProcessID = static_cast(::GetCurrentProcessId()); + + const uint32_t packetSize = static_cast(sizeof(req)); + + if (SendExact(reinterpret_cast(&packetSize), sizeof(packetSize)) < 0) + return; + if (SendExact(reinterpret_cast(&req), sizeof(req)) < 0) + return; +} + +void CConnection::ReceiveConnectRequest(const CMessage& message) +{ + const auto hdr = message.GetConnectHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + return; + } + + SConnectMsg reply{}; + reply.uiVersion = SDVFrameworkInterfaceVersion; + reply.eType = EMsgType::connect_answer; + reply.tProcessID = static_cast(::GetCurrentProcessId()); + + const uint32_t packetSize = static_cast(sizeof(reply)); + + if (SendExact(reinterpret_cast(&packetSize), sizeof(packetSize)) < 0) + return; + if (SendExact(reinterpret_cast(&reply), sizeof(reply)) < 0) + return; +} + +void CConnection::ReceiveConnectAnswer(const CMessage& message) +{ + const auto hdr = message.GetConnectHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + return; + } + + // Handshake complete (client side) + SetStatus(sdv::ipc::EConnectStatus::connected); +} + +void CConnection::ReceiveConnectTerm(const CMessage& /*message*/) +{ + SetStatus(sdv::ipc::EConnectStatus::disconnected); + m_StopReceiveThread.store(true); +} + +void CConnection::ReceiveDataMessage(const CMessage& message, SDataContext& dataCtx) +{ + const uint32_t payloadOffset = ReadDataTable(message, dataCtx); + if (payloadOffset == 0) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + return; + } + + if (!ReadDataChunk(message, payloadOffset, dataCtx)) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + return; + } +} + +void CConnection::ReceiveDataFragmentMessage(const CMessage& message, SDataContext& dataCtx) +{ + uint32_t offset = static_cast(sizeof(SFragmentedMsgHdr)); + + if (message.GetFragmentedHdr().uiOffset == 0) + { + offset = ReadDataTable(message, dataCtx); + if (offset == 0) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + return; + } + } + + if (!ReadDataChunk(message, offset, dataCtx)) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + return; + } +} + +void CConnection::ReceiveMessages() +{ + try + { + SDataContext dataCtx; + + while (!m_StopReceiveThread.load() && + m_ConnectionSocket != INVALID_SOCKET) + { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(m_ConnectionSocket, &rfds); + + TIMEVAL tv{}; + tv.tv_sec = 0; + tv.tv_usec = 50 * 1000; // 50 ms + + int sr = ::select(0, &rfds, nullptr, nullptr, &tv); + if (sr == SOCKET_ERROR) + { + ::Sleep(1); + continue; + } + + if (sr == 0) + { + continue; // timeout + } + + // 1. Transport header: packet size (LE, 4 bytes) + uint32_t packetSize = 0; + if (!ReadNumberOfBytes(reinterpret_cast(&packetSize), + sizeof(packetSize))) + { + SetStatus(sdv::ipc::EConnectStatus::disconnected); + SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected"); + break; + } + + if (packetSize == 0 || packetSize > kMaxUdsPacketSize) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + break; + } + + // 2. Payload + std::vector payload(packetSize); + if (!ReadNumberOfBytes(reinterpret_cast(payload.data()), + packetSize)) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + SDV_LOG_WARNING("[UDS][RX] Invalid SDV message (envelope)"); + break; + } + + // 3. Parse & dispatch SDV message + CMessage msg(std::move(payload)); + if (!msg.IsValid()) + { + SetStatus(sdv::ipc::EConnectStatus::communication_error); + continue; + } + + switch (msg.GetMsgHdr().eType) + { + case EMsgType::sync_request: ReceiveSyncRequest(msg); break; + case EMsgType::sync_answer: ReceiveSyncAnswer(msg); break; + case EMsgType::connect_request: ReceiveConnectRequest(msg); break; + case EMsgType::connect_answer: ReceiveConnectAnswer(msg); break; + case EMsgType::connect_term: ReceiveConnectTerm(msg); break; + case EMsgType::data: ReceiveDataMessage(msg, dataCtx); break; + case EMsgType::data_fragment: ReceiveDataFragmentMessage(msg, dataCtx); break; + default: + // Ignore unknown types + break; + } + + if (m_ConnectionStatus == sdv::ipc::EConnectStatus::terminating) + break; + } + } + catch (...) + { + SetStatus(sdv::ipc::EConnectStatus::disconnected); + } +} \ No newline at end of file diff --git a/sdv_services/uds_win_sockets/connection.h b/sdv_services/uds_win_sockets/connection.h new file mode 100644 index 0000000..2235ace --- /dev/null +++ b/sdv_services/uds_win_sockets/connection.h @@ -0,0 +1,510 @@ +/******************************************************************************** +* 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: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#ifndef CONNECTION_H +#define CONNECTION_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# ifdef _MSC_VER +# pragma comment(lib, "Ws2_32.lib") +# endif +#endif + +/// @brief Legacy framing markers for the old message header (not used by SDV envelope) +constexpr uint32_t m_MsgStart = 0x01020304; ///< Value to mark the start of the legacy message header +constexpr uint32_t m_MsgEnd = 0x05060708; ///< Value to mark the end of the legacy message header + +/** + * @brief Legacy message header used before the SDV envelope was introduced + * + * Today, the active transport framing is: + * - 4 bytes: packet size (LE) + * - N bytes: SDV protocol envelope (SMsgHdr + payload) + * + * This header is kept for compatibility / potential reuse, but is not used in + * the current SDV-based protocol + */ +struct SMsgHeader +{ + uint32_t msgStart = 0; ///< Marker for the start of the header + uint32_t msgId = 0; ///< Message ID, must match for all message fragments + uint32_t msgSize = 0; ///< Size of the message without the header + uint32_t packetNumber = 0; ///< Index of this packet (starting at 1) + uint32_t totalPacketCount = 0; ///< Total number of packets required for the message + uint32_t msgEnd = 0; ///< Marker for the end of the header +}; + +/** + * @brief Remote IPC connection used by the AF_UNIX-over-WinSock transport. + * + * Instances of this class are typically created and owned by the + * CSocketsChannelMgnt implementation (see channel_mgnt.*). + * + * Exposes: + * - sdv::ipc::IDataSend : sending SDV-framed messages + * - sdv::ipc::IConnect : async connect / wait / status / events + * - sdv::IObjectDestroy : explicit destruction hook for SDV runtime + */ +class CConnection + : public sdv::IInterfaceAccess + , public sdv::ipc::IDataSend + , public sdv::ipc::IConnect + , public sdv::IObjectDestroy +{ +public: + /** + * @brief default constructor used by create endpoint - allocates new buffers m_Sender and m_Receiver + */ + CConnection(); + + /** + * @brief access existing connection + * @param[in] preconfiguredSocket Prepared socket for the connection + * @param[in] acceptConnectionRequired If true connection has to be accepted before receive thread can be started + */ + CConnection(SOCKET preconfiguredSocket, bool acceptConnectionRequired); + + /** + * @brief Virtual destructor needed for "delete this;" + */ + virtual ~CConnection(); + + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnect) + SDV_INTERFACE_ENTRY(sdv::IObjectDestroy) + END_SDV_INTERFACE_MAP() + + /** + * @brief Sends data consisting of multiple data chunks via the IPC connection + * + * Overload of sdv::ipc::IDataSend::SendData + * + * The sequence may be rearranged internally to avoid copying payload + * where possible. The transport uses an SDV envelope compatible with + * the Linux implementation (length table + chunks, with fragmentation) + * + * @param[in,out] seqData Sequence of data buffers to be sent + * @return true if all data could be sent; false otherwise + */ + bool SendData(/*inout*/ sdv::sequence>& seqData) override; + + /** + * @brief Establish a connection and start sending/receiving messages + * + * Overload of sdv::ipc::IConnect::AsyncConnect + * + * @param[in] pReceiver Object that exposes IDataReceiveCallback and + * optionally IConnectEventCallback + * @return true when the connect worker was started successfully + * Use GetStatus / WaitForConnection / callbacks for status + */ + bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override; + + /** + * @brief Wait for a connection to be established + * + * Overload of sdv::ipc::IConnect::WaitForConnection + * + * @param[in] uiWaitMs + * - 0 : do not wait, just check current status + * - 0xFFFFFFFF: wait indefinitely + * - otherwise : wait up to uiWaitMs milliseconds + * @return true if the connection is in connected state when returning + */ + bool WaitForConnection(/*in*/ uint32_t uiWaitMs) override; + + /** + * @brief Cancel a pending WaitForConnection + * + * Overload of sdv::ipc::IConnect::CancelWait + */ + void CancelWait() override; + + /** + * @brief Disconnect from the peer and stop I/O threads + * + * This sets the status to disconnected and releases the event interface + */ + void Disconnect() override; + + /** + * @brief Register event callback interface for connection status updates + * + * Overload of sdv::ipc::IConnect::RegisterStatusEventCallback + * + * @param[in] pEventCallback Pointer to an object exposing + * sdv::ipc::IConnectEventCallback. + * @return A registration cookie (1 = valid, 0 = failure) + */ + uint64_t RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; + + /** + * @brief Unregister the status event callback using its cookie + * + * Overload of sdv::ipc::IConnect::UnregisterStatusEventCallback + * + * @param[in] uiCookie Cookie returned by RegisterStatusEventCallback + */ + void UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) override; + + /** + * @brief Get current status of the connection + * + * @return Current sdv::ipc::EConnectStatus + */ + sdv::ipc::EConnectStatus GetStatus() const override; + + /** + * @brief Destroy the object + * + * Overload of sdv::IObjectDestroy::DestroyObject + * After calling this, all exposed interfaces become invalid + */ + void DestroyObject() override; + +private: + + std::mutex m_MtxConnect; + std::condition_variable m_CvConnect; + std::thread m_ReceiveThread; ///< Thread which receives data from the socket + std::thread m_ConnectThread; + std::atomic m_StopReceiveThread{false}; ///< bool variable to stop thread + std::atomic m_StopConnectThread{false}; + std::atomic m_ConnectionStatus; ///< the status of the connection + + sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages if available + sdv::ipc::IConnectEventCallback* m_pEvent = nullptr; ///< Event receiver + bool m_AcceptConnectionRequired; ///< if true connection has to be accepted before receive thread can be started + mutable std::recursive_mutex m_SendMutex; ///< Synchronize all packages to be send + + SOCKET m_ListenSocket{INVALID_SOCKET}; ///< Server-side listening socket + SOCKET m_ConnectionSocket{INVALID_SOCKET}; ///< Active connected socket (client <-> server) + + static constexpr uint32_t m_SendMessageSize{ 1024 }; ///< size for the message to be send + static constexpr uint32_t m_SendBufferSize = sizeof(SMsgHeader) + m_SendMessageSize; ///< Initial size of the send buffer + char m_SendBuffer[m_SendBufferSize]; ///< send buffer length + char m_ReceiveBuffer[sizeof(SMsgHeader)]; ///< receive buffer, just for reading the message header + uint32_t m_ReceiveBufferLength = sizeof(SMsgHeader); ///< receive buffer length + + std::atomic m_CancelWait{false}; + + /// @brief Server accept loop / client connect confirmation + void ConnectWorker(); + + /// @brief Start the RX thread (pre: status=connected, socket valid) + void StartReceiveThread_Unsafe(); + + /// @brief Stop worker threads and close all sockets cleanly + void StopThreadsAndCloseSockets(); + + /** + * @brief Accept a connection from a client (server mode) + * + * Uses select() with a short timeout to remain responsive to stop flags + * + * @return A valid client socket, or INVALID_SOCKET on error + */ + SOCKET AcceptConnection(); + + /** + * @brief Send exactly \p dataLength bytes over the connection socket + * + * @param[in] data Pointer to bytes to be sent + * @param[in] dataLength Number of bytes to send + * @return Number of bytes actually sent, or SOCKET_ERROR on failure + */ + int32_t Send(const char* data, int32_t dataLength); + + /** + * @brief Low-level helper: send exactly len bytes, retrying until done + * + * @param[in] data Pointer to buffer + * @param[in] len Number of bytes to send + * @return Total bytes sent, or -1 on failure + */ + int SendExact(const char* data, int len); + + /// @brief Main receive loop (runs in m_ReceiveThread) + void ReceiveMessages(); + + /** + * @brief Legacy header validation (not used in current SDV path) + * + * @param[in] msgHeader Header to validate + * @return true if header markers are valid and msgSize != 0 + */ + bool ValidateHeader(const SMsgHeader& msgHeader); + + /** + * @brief Read exactly @p length bytes into @p buffer from the socket + * + * @param[out] buffer Target buffer + * @param[in] length Number of bytes expected + * @return true if all bytes were read, false on error/EOF + */ + bool ReadNumberOfBytes(char* buffer, uint32_t length); + + // Protocol types + /** @brief SDV message type */ + enum class EMsgType : uint32_t + { + sync_request = 0, ///< Sync request (version check; no payload) + sync_answer = 1, ///< Sync answer (version check; no payload) + connect_request = 10, ///< Connection initiation (SConnectMsg) + connect_answer = 11, ///< Connection answer (SConnectMsg) + connect_term = 90, ///< Connection terminated + data = 0x10000000, ///< Data message + data_fragment = 0x10000001 ///< Data fragment if payload exceeds frame size + }; + + /** @brief SDV base message header */ + struct SMsgHdr + { + uint32_t uiVersion; ///< Protocol version + EMsgType eType; ///< Message type + }; + + /** @brief Connection initiation message (extends SMsgHdr) */ + struct SConnectMsg : SMsgHdr + { + sdv::process::TProcessID tProcessID; ///< Process ID for lifetime monitoring + }; + + /** @brief Fragmented data message header */ + struct SFragmentedMsgHdr : SMsgHdr + { + uint32_t uiTotalLength; ///< Total payload length across all fragments + uint32_t uiOffset; ///< Current byte offset into the total payload + }; + + // Minimal SDV message wrapper + class CMessage + { + public: + explicit CMessage(std::vector&& data) : m_Data(std::move(data)) {} + + /** @return Pointer to message bytes (may be null if empty) */ + const uint8_t* GetData() const + { + return m_Data.empty() ? nullptr : m_Data.data(); + } + + /** @return Message size in bytes */ + uint32_t GetSize() const + { + return static_cast(m_Data.size()); + } + + /** @return Interpreted SDV base header (or a fallback) */ + SMsgHdr GetMsgHdr() const + { + if (GetSize() < sizeof(SMsgHdr)) + { + return SMsgHdr{0, EMsgType::connect_term}; + } + return *reinterpret_cast(GetData()); + } + + /** @return SDV connect header (or default if undersized) */ + SConnectMsg GetConnectHdr() const + { + if (GetSize() < sizeof(SConnectMsg)) + { + return SConnectMsg{}; + } + return *reinterpret_cast(GetData()); + } + + /** @return SDV fragmented header (or default if undersized) */ + SFragmentedMsgHdr GetFragmentedHdr() const + { + if (GetSize() < sizeof(SFragmentedMsgHdr)) + { + return SFragmentedMsgHdr{}; + } + return *reinterpret_cast(GetData()); + } + + /** @return true if the SDV envelope is well-formed */ + bool IsValid() const + { + if (!GetData() || GetSize() < sizeof(SMsgHdr)) + { + return false; + } + + const SMsgHdr hdr = GetMsgHdr(); + switch (hdr.eType) + { + case EMsgType::sync_request: + case EMsgType::sync_answer: + case EMsgType::connect_term: + case EMsgType::data: + return true; + + case EMsgType::connect_request: + case EMsgType::connect_answer: + return GetSize() >= sizeof(SConnectMsg); + + case EMsgType::data_fragment: + return GetSize() >= sizeof(SFragmentedMsgHdr); + + default: + return false; + } + } + + private: + std::vector m_Data; + }; + + /** @brief Receive-time data reassembly context */ + struct SDataContext + { + uint32_t uiTotalSize = 0; ///< Total payload size (without SDV header) + uint32_t uiCurrentOffset = 0; ///< Current filled byte count + size_t nChunkIndex = 0; ///< Current chunk index being filled + uint32_t uiChunkOffset = 0; ///< Offset within the current chunk + sdv::sequence> seqDataChunks; ///< Output buffers + }; + + /** + * @brief Event callback structure. + */ + struct SEventCallback + { + uint64_t uiCookie = 0; ///< Registration cookie + sdv::ipc::IConnectEventCallback* pCallback = nullptr; ///< Pointer to the callback; Could be NULL when the callback + ///< was deleted + }; + + /** @brief UDS transport maximum frame size (safety cap) */ + static constexpr uint32_t kMaxUdsPacketSize = 64u * 1024u * 1024u; // 64 MiB + + /** + * @brief Handle an incoming sync_request message + * + * Sent by the peer during the initial handshake + * The server replies with sync_answer + * + * @param message SDV envelope containing the sync_request header + */ + void ReceiveSyncRequest(const CMessage& message); + + /** + * @brief Handle an incoming sync_answer message + * + * Received by the client after sending sync_request + * Triggers the next handshake step: the client sends connect_request + * + * @param message SDV envelope containing the sync_answer header + */ + void ReceiveSyncAnswer(const CMessage& message); + + /** + * @brief Handle an incoming connect_request message + * + * Sent by the peer to request establishment of a logical SDV connection + * The server replies with connect_answer + * + * @param message SDV envelope containing the connect_request header + */ + void ReceiveConnectRequest(const CMessage& message); + + /** + * @brief Handle an incoming connect_answer message + * + * Sent by the peer after receiving our connect_request + * Marks completion of the SDV handshake on the client side + * + * @param message SDV envelope containing the connect_answer header + */ + void ReceiveConnectAnswer(const CMessage& message); + + /** + * @brief Handle an incoming connect_term message + * + * Indicates that the peer requests immediate termination of the connection + * Sets status to disconnected and stops the RX loop + * + * @param message SDV envelope containing the connect_term header + */ + void ReceiveConnectTerm(const CMessage& message); + + /** + * @brief Handle a non-fragmented SDV data message + * + * Format: + * [SMsgHdr][LengthTable][DataChunks...] + * The function decodes the table, allocates chunk buffers, + * copies the full payload, and dispatches it via IDataReceiveCallback. + * + * @param rMessage SDV envelope containing the full data frame + * @param rsDataCtxt Reassembly context used to extract chunks + */ + void ReceiveDataMessage(const CMessage& rMessage, SDataContext& rsDataCtxt); + + /** + * @brief Handle a fragmented SDV data message + * + * Format: + * [SFragmentedMsgHdr][(LengthTable only in first fragment)][PayloadSlice] + * + * This function appends each slice into the appropriate chunk buffer + * When the final fragment is received and all chunks are complete, + * the assembled data is dispatched via IDataReceiveCallback. + * + * @param rMessage SDV envelope containing the current fragment + * @param rsDataCtxt Reassembly context shared across fragments + */ + void ReceiveDataFragmentMessage(const CMessage& rMessage, SDataContext& rsDataCtxt); + + /** + * @brief Read the data size table (buffer count + each buffer size) + * @param rMessage SDV message containing the table + * @param rsDataCtxt Output reassembly context; buffers are allocated + * @return Offset within message data where the payload begins; 0 on error + */ + uint32_t ReadDataTable(const CMessage& rMessage, SDataContext& rsDataCtxt); + + /** + * @brief Copy payload bytes from 'rMessage' into the buffers allocated by ReadDataTable. + * Multiple calls may be required for fragmented messages. + * @param rMessage SDV message holding the current data/fragment. + * @param uiOffset Offset within 'rMessage' where payload starts in this frame. + * @param rsDataCtxt Reassembly context to be filled; callback fires when complete. + * @return true if progress was made / completion achieved, false if invalid. + */ + bool ReadDataChunk(const CMessage& rMessage, uint32_t uiOffset, SDataContext& rsDataCtxt); + + /// @brief Centralized status update (notifies waiters and callbacks) + void SetStatus(sdv::ipc::EConnectStatus status); +}; + +#endif // CONNECTION_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d471853..b3da6e0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Define project project(Tests VERSION 1.0 LANGUAGES CXX) @@ -41,8 +51,10 @@ file (COPY ${PROJECT_SOURCE_DIR}/sdv_core_reloc.toml DESTINATION ${CMAKE_BINARY_ # Copy shell script to the build directory if(WIN32) file (COPY ${PROJECT_SOURCE_DIR}/run_tests_on_windows.ps1 DESTINATION ${CMAKE_BINARY_DIR}/tests/) + file (COPY ${PROJECT_SOURCE_DIR}/loop_test_runner_windows.ps1 DESTINATION ${CMAKE_BINARY_DIR}/tests/) else() file (COPY ${PROJECT_SOURCE_DIR}/run_tests_on_linux.sh DESTINATION ${CMAKE_BINARY_DIR}/tests/) + file (COPY ${PROJECT_SOURCE_DIR}/loop_test_runner_linux.sh DESTINATION ${CMAKE_BINARY_DIR}/tests/) endif() # Add test projects @@ -56,6 +68,7 @@ add_subdirectory(unit_tests/asc_format) add_subdirectory(unit_tests/basic_types) add_subdirectory(unit_tests/smart_ifc) add_subdirectory(unit_tests/toml_parser) +add_subdirectory(unit_tests/parameters) add_subdirectory(unit_tests/module_control) add_subdirectory(unit_tests/repository) add_subdirectory(unit_tests/shared_mem) @@ -72,6 +85,13 @@ add_subdirectory(unit_tests/sdv_control) add_subdirectory(unit_tests/sdv_macro_test) add_subdirectory(unit_tests/install_package_composer) add_subdirectory(unit_tests/path_match) +if(UNIX) +add_subdirectory(unit_tests/unix_sockets) +endif() +if(WIN32) +add_subdirectory(unit_tests/win_sockets) +endif() +add_subdirectory(component_tests/config) add_subdirectory(component_tests/logger) add_subdirectory(component_tests/data_dispatch_service) add_subdirectory(component_tests/repository) @@ -79,6 +99,6 @@ add_subdirectory(component_tests/config_install) add_subdirectory(component_tests/dbc_util) add_subdirectory(component_tests/vss_util) add_subdirectory(component_tests/task_timer) -add_subdirectory(component_tests/core_library) -add_subdirectory(component_tests/config) +add_subdirectory(component_tests/app_control) +add_subdirectory(component_tests/toml_parser) add_subdirectory(manual_tests/silkit_can_com_tests) diff --git a/tests/component_tests/app_control/CMakeLists.txt b/tests/component_tests/app_control/CMakeLists.txt new file mode 100644 index 0000000..2467551 --- /dev/null +++ b/tests/component_tests/app_control/CMakeLists.txt @@ -0,0 +1,41 @@ +#******************************************************************************* +# 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 (ComponentTest_AppControl VERSION 1.0 LANGUAGES CXX) + +# Data maneger executable +add_executable(ComponentTest_AppControl + "main.cpp" + "app_control_test.cpp" + "app_control_test_no_event_handler.cpp" + "app_control_test_event_handler.cpp" + "app_control_test_mgnt_class.cpp" + ) + +target_link_libraries(ComponentTest_AppControl ${CMAKE_DL_LIBS} GTest::GTest) + +# Add the Data Dispatch Service unittest +add_test(NAME ComponentTest_AppControl COMMAND ComponentTest_AppControl WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + +# Execute the test +# Currently disabled due to issues with MINGW causing the application to sporadically crash. A bug ticket has been filed: +# https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608132 +add_custom_command(TARGET ComponentTest_AppControl POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ComponentTest_AppControl.xml + VERBATIM +) + +# Build dependencies +add_dependencies(ComponentTest_AppControl dependency_sdv_components) +add_dependencies(ComponentTest_AppControl ComponentTest_Repository) # Use the repository components for this test diff --git a/tests/component_tests/app_control/app_control_test.cpp b/tests/component_tests/app_control/app_control_test.cpp new file mode 100644 index 0000000..f4695f5 --- /dev/null +++ b/tests/component_tests/app_control/app_control_test.cpp @@ -0,0 +1,46 @@ +/******************************************************************************** + * 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 +#include +#include +#include +#include +#include "../../../global/exec_dir_helper.h" + +TEST(CoreLibrary_AppControl, SetModuleSearchPath) +{ + sdv::core::IModuleControlConfig* pModuleConfig = sdv::core::GetCore(); + ASSERT_NE(pModuleConfig, nullptr); + bool bResult = pModuleConfig->AddModuleSearchDir("../../bin"); + EXPECT_TRUE(bResult); + sdv::sequence seqSearchDirs = pModuleConfig->GetModuleSearchDirs(); + std::filesystem::path pathModuleDir = (GetExecDirectory() / "../../bin").lexically_normal(); + auto itDir = std::find_if(seqSearchDirs.begin(), seqSearchDirs.end(), + [&](const sdv::u8string& rssDir) + { + std::filesystem::path pathDir = static_cast(rssDir); + if (pathDir.is_relative()) + pathDir = (GetExecDirectory() / pathDir).lexically_normal(); + return pathDir == pathModuleDir; + }); + EXPECT_NE(itDir, seqSearchDirs.end()); +} + +TEST(CoreLibrary_AppControl, SetModuleSearchPathMgntClass) +{ + sdv::app::CAppControl appcontrol; + bool bResult = appcontrol.AddModuleSearchDir("../../bin"); + EXPECT_TRUE(bResult); +} + diff --git a/tests/component_tests/app_control/app_control_test_event_handler.cpp b/tests/component_tests/app_control/app_control_test_event_handler.cpp new file mode 100644 index 0000000..ee571e8 --- /dev/null +++ b/tests/component_tests/app_control/app_control_test_event_handler.cpp @@ -0,0 +1,1289 @@ +/******************************************************************************** + * 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 +#include +#include +#include +#include +#include +#include +#include "../../../sdv_services/core/local_shutdown_request.h" +#include "../../../global/base64.h" + +/** + * @brief Helper class for receiving application events. + */ +class CAppEventHandler : public sdv::IInterfaceAccess, public sdv::app::IAppEvent +{ +public: + // Interface map + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::app::IAppEvent) + END_SDV_INTERFACE_MAP() + + /** + * @brief Process the event. Overload of IAppEvent::ProcessEvent. + * @param[inout] sEvent Event structure containing the information to process. + */ + virtual void ProcessEvent(/*inout*/ sdv::app::SAppEvent& sEvent) override + { + std::unique_lock lock(m_mtxEventList); + switch (sEvent.uiEventID) + { + case sdv::app::EVENT_OPERATION_STATE_CHANGED: + m_lstEventList.push_back(sEvent); + break; + case sdv::app::EVENT_RUNNING_LOOP: + m_nLoopCount++; + break; + default: + break; + } + } + + /** + * @brief Get the event list. + * @return A copy of the event list. + */ + std::list GetEventList() const + { + std::unique_lock lock(m_mtxEventList); + return m_lstEventList; + } + + /** + * @brief Reset the event list. + */ + void ResetEventList() + { + std::unique_lock lock(m_mtxEventList); + m_lstEventList.clear(); + } + + /** + * @brief Get the loop counter value. + * @return The loop count. + */ + size_t GetLoopCount() { return m_nLoopCount; } + +private: + mutable std::mutex m_mtxEventList; ///< Event list protection + std::list m_lstEventList; ///< Event list + size_t m_nLoopCount = 0; ///< Loop counter +}; + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Invalid_ApplicationMode) +{ + // Prevent error reporting on std::cerr - they will influence test outcome. + auto* pCErr = std::cerr.rdbuf(); + std::ostringstream sstreamCErr; + std::cerr.rdbuf(sstreamCErr.rdbuf()); + + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Invalid\"", &handler); + EXPECT_FALSE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + std::cerr.rdbuf(pCErr); +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Default_NoConfig) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Standalone_NoConfig) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_External_NoConfig) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"External\"", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::external); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Isolated_NoConfig) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup(R"code( +[Application] +Mode = "Isolated" + +[Console] +Report = "Silent" +)code", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Main_NoConfig) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Main\"", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::main); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Essential_NoConfig) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::essential); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Maintenance_NoConfig) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::maintenance); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Default_DefineInstance) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nInstance=2005", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Standalone_DefineInstance) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"\nInstance=2005", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_External_DefineInstance) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"External\"\nInstance=2005", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::external); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Isolated_DefineInstance) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + std::string ssConfig = R"code([Application] +Mode = "Isolated" +Instance = 2005 +Connection = ")code"; + ssConfig += Base64EncodePlainText("test") + "\""; + bool bResult = pControl->Startup(ssConfig, &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Main_DefineInstance) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Main\"\nInstance=2005", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::main); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Essential_DefineInstance) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"\nInstance=2005", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::essential); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, Startup_Maintenance_DefineInstance) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"\nInstance=2005", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::maintenance); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + sdv::app::EAppOperationState rgeStartupStates[] = { + sdv::app::EAppOperationState::initializing, + sdv::app::EAppOperationState::initialized, + sdv::app::EAppOperationState::configuring, + sdv::app::EAppOperationState::running + }; + auto lstEventList = handler.GetEventList(); + size_t nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeStartupStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } + handler.ResetEventList(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + sdv::app::EAppOperationState rgeShutdownStates[] = { + sdv::app::EAppOperationState::shutting_down, + sdv::app::EAppOperationState::not_started + }; + lstEventList = handler.GetEventList(); + nIndex = 0; + for (const auto& rsEvent : lstEventList) + { + if (rsEvent.uiEventID == sdv::app::EVENT_OPERATION_STATE_CHANGED) + { + if (nIndex < std::extent_v) + EXPECT_EQ(rgeShutdownStates[nIndex++], static_cast(rsEvent.uiInfo)); + else + EXPECT_LT(nIndex, std::extent_v); + } + } +} + +TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Default) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nInstance=2007", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + EXPECT_GT(handler.GetLoopCount(), 5u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Standalone) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"\nInstance=2007", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + EXPECT_GT(handler.GetLoopCount(), 5u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_EventHandler, RunLoop_External) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"External\"\nInstance=2007", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::external); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + EXPECT_GT(handler.GetLoopCount(), 5u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Isolated) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + std::string ssConfig = R"code([Application] +Mode = "Isolated" +Instance = 2007 +Connection = ")code"; + ssConfig += Base64EncodePlainText("test") + "\""; + bool bResult = pControl->Startup(ssConfig, &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); + ASSERT_NE(pShutdownRequest, nullptr); + pShutdownRequest->RequestShutdown(); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + EXPECT_GT(handler.GetLoopCount(), 5u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Main) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Main\"\nInstance=2007", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::main); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); + ASSERT_NE(pShutdownRequest, nullptr); + pShutdownRequest->RequestShutdown(); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + EXPECT_GT(handler.GetLoopCount(), 5u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Essential) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"\nInstance=2007", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::essential); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + EXPECT_GT(handler.GetLoopCount(), 5u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Maintenance) +{ + CAppEventHandler handler; + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"\nInstance=2007", &handler); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::maintenance); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + // Loop not allowed... + EXPECT_THROW(pControl->RunLoop(), sdv::XAccessDenied); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + diff --git a/tests/component_tests/app_control/app_control_test_mgnt_class.cpp b/tests/component_tests/app_control/app_control_test_mgnt_class.cpp new file mode 100644 index 0000000..32a9110 --- /dev/null +++ b/tests/component_tests/app_control/app_control_test_mgnt_class.cpp @@ -0,0 +1,529 @@ +/******************************************************************************** + * 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 +#include +#include +#include +#include +#include "../../../sdv_services/core/local_shutdown_request.h" +#include "../../../global/base64.h" + +TEST(AppControl, Startup_Invalid_ApplicationMode) +{ + // Prevent error reporting on std::cerr - they will influence test outcome. + auto* pCErr = std::cerr.rdbuf(); + std::ostringstream sstreamCErr; + std::cerr.rdbuf(sstreamCErr.rdbuf()); + + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Invalid\""); + EXPECT_FALSE(bResult); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + std::cerr.rdbuf(pCErr); +} + +TEST(AppControl, Startup_Default_NoConfig) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup(""); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::standalone); + EXPECT_EQ(control.GetInstanceID(), 1000u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Standalone_NoConfig) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Standalone\""); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::standalone); + EXPECT_EQ(control.GetInstanceID(), 1000u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +// Testing the external application requires a process running that was initialized as main with the same instance ID. This process +// needs to be started before and shutdown after the execution of this test. +TEST(AppControl, DISABLED_Startup_External_NoConfig) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"External\""); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::external); + EXPECT_EQ(control.GetInstanceID(), 1000u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Isolated_NoConfig) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup(R"code( +[Application] +Mode = "Isolated" + +[Console] +Report = "Silent" +)code"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::isolated); + EXPECT_EQ(control.GetInstanceID(), 1000u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Main_NoConfig) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Main\""); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); + EXPECT_EQ(control.GetInstanceID(), 1000u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Essential_NoConfig) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Essential\""); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::essential); + EXPECT_EQ(control.GetInstanceID(), 1000u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Maintenance_NoConfig) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Maintenance\""); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::maintenance); + EXPECT_EQ(control.GetInstanceID(), 1000u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Default_DefineInstance) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nInstance=2005"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::standalone); + EXPECT_EQ(control.GetInstanceID(), 2005u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Standalone_DefineInstance) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Standalone\"\nInstance=2005"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::standalone); + EXPECT_EQ(control.GetInstanceID(), 2005u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +// Testing the external application requires a process running that was initialized as main with the same instance ID. This process +// needs to be started before and shutdown after the execution of this test. +TEST(AppControl, DISABLED_Startup_External_DefineInstance) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"External\"\nInstance=2005"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::external); + EXPECT_EQ(control.GetInstanceID(), 2005u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Isolated_DefineInstance) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + std::string ssConfig = R"code([Application] +Mode = "Isolated" +Instance = 2005 +Connection = ")code"; + ssConfig += Base64EncodePlainText("test") + "\""; + bool bResult = control.Startup(ssConfig); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::isolated); + EXPECT_EQ(control.GetInstanceID(), 2005u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Main_DefineInstance) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2005"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); + EXPECT_EQ(control.GetInstanceID(), 2005u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Essential_DefineInstance) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Essential\"\nInstance=2005"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::essential); + EXPECT_EQ(control.GetInstanceID(), 2005u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, Startup_Maintenance_DefineInstance) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Maintenance\"\nInstance=2005"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::maintenance); + EXPECT_EQ(control.GetInstanceID(), 2005u); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, RunLoop_Default) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nInstance=2007"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::standalone); + EXPECT_EQ(control.GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + EXPECT_TRUE(control.RunLoop()); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, RunLoop_Standalone) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Standalone\"\nInstance=2007"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::standalone); + EXPECT_EQ(control.GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + EXPECT_TRUE(control.RunLoop()); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +// Testing the external application requires a process running that was initialized as main with the same instance ID. This process +// needs to be started before and shutdown after the execution of this test. +TEST(AppControl, DISABLED_RunLoop_External) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"External\"\nInstance=2007"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::external); + EXPECT_EQ(control.GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + EXPECT_TRUE(control.RunLoop()); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, RunLoop_Isolated) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + std::string ssConfig = R"code([Application] +Mode = "Isolated" +Instance = 2007 +Connection = ")code"; + ssConfig += Base64EncodePlainText("test") + "\""; + bool bResult = control.Startup(ssConfig); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::isolated); + EXPECT_EQ(control.GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); + ASSERT_NE(pShutdownRequest, nullptr); + pShutdownRequest->RequestShutdown(); + }); + EXPECT_TRUE(control.RunLoop()); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, RunLoop_Main) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2007"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); + EXPECT_EQ(control.GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); + ASSERT_NE(pShutdownRequest, nullptr); + pShutdownRequest->RequestShutdown(); + }); + EXPECT_TRUE(control.RunLoop()); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, RunLoop_Essential) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Essential\"\nInstance=2007"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::essential); + EXPECT_EQ(control.GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + EXPECT_TRUE(control.RunLoop()); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppControl, RunLoop_Maintenance) +{ + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup("[Application]\nMode=\"Maintenance\"\nInstance=2007"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::maintenance); + EXPECT_EQ(control.GetInstanceID(), 2007u); + + // Loop not allowed... + EXPECT_FALSE(control.RunLoop()); + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + diff --git a/tests/component_tests/app_control/app_control_test_no_event_handler.cpp b/tests/component_tests/app_control/app_control_test_no_event_handler.cpp new file mode 100644 index 0000000..01a87a4 --- /dev/null +++ b/tests/component_tests/app_control/app_control_test_no_event_handler.cpp @@ -0,0 +1,656 @@ +/******************************************************************************** + * 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 +#include +#include +#include +#include +#include "../../../sdv_services/core/local_shutdown_request.h" +#include "../../../global/base64.h" + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Invalid_ApplicationMode) +{ + // Prevent error reporting on std::cerr - they will influence test outcome. + auto* pCErr = std::cerr.rdbuf(); + std::ostringstream sstreamCErr; + std::cerr.rdbuf(sstreamCErr.rdbuf()); + + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Invalid\"", nullptr); + EXPECT_FALSE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + std::cerr.rdbuf(pCErr); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Default_NoConfig) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Standalone_NoConfig) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_External_NoConfig) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"External\"", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::external); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Isolated_NoConfig) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup(R"code( +[Application] +Mode = "Isolated" + +[Console] +Report = "Silent" +)code", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Main_NoConfig) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Main\"", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::main); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Essential_NoConfig) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::essential); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Maintenance_NoConfig) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::maintenance); + EXPECT_EQ(pContext->GetInstanceID(), 1000u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Default_DefineInstance) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nInstance=2005", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Standalone_DefineInstance) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"\nInstance=2005", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_External_DefineInstance) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"External\"\nInstance=2005", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::external); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Isolated_DefineInstance) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + std::string ssConfig = R"code([Application] +Mode = "Isolated" +Instance = 2005 +Connection = ")code"; + ssConfig += Base64EncodePlainText("test") + "\""; + bool bResult = pControl->Startup(ssConfig, nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Main_DefineInstance) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Main\"\nInstance=2005", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::main); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Essential_DefineInstance) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"\nInstance=2005", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::essential); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Maintenance_DefineInstance) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"\nInstance=2005", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::maintenance); + EXPECT_EQ(pContext->GetInstanceID(), 2005u); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Default) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nInstance=2007", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Standalone) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"\nInstance=2007", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_External) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"External\"\nInstance=2007", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::external); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Isolated) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + std::string ssConfig = R"code([Application] +Mode = "Isolated" +Instance = 2007 +Connection = ")code"; + ssConfig += Base64EncodePlainText("test") + "\""; + bool bResult = pControl->Startup(ssConfig, nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); + ASSERT_NE(pShutdownRequest, nullptr); + pShutdownRequest->RequestShutdown(); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Main) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Main\"\nInstance=2007", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::main); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); + ASSERT_NE(pShutdownRequest, nullptr); + pShutdownRequest->RequestShutdown(); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Essential) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"\nInstance=2007", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::essential); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + auto tpStart = std::chrono::high_resolution_clock::now(); + std::thread threadShutdownRequest([]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + RequestShutdown(2007u); + }); + pControl->RunLoop(); + EXPECT_GT(std::chrono::duration(std::chrono::high_resolution_clock::now() - tpStart).count(), 0.100); + threadShutdownRequest.join(); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + +TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Maintenance) +{ + sdv::app::IAppControl* pControl = sdv::core::GetCore(); + ASSERT_NE(pControl, nullptr); + sdv::app::IAppContext* pContext = sdv::core::GetCore(); + ASSERT_NE(pContext, nullptr); + sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); + ASSERT_NE(pOperation, nullptr); + + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); + + bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"\nInstance=2007", nullptr); + EXPECT_TRUE(bResult); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::maintenance); + EXPECT_EQ(pContext->GetInstanceID(), 2007u); + + // Loop not allowed... + EXPECT_THROW(pControl->RunLoop(), sdv::XAccessDenied); + + pControl->Shutdown(true); + EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); + EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_EQ(pContext->GetInstanceID(), 0u); +} + diff --git a/tests/component_tests/app_control/main.cpp b/tests/component_tests/app_control/main.cpp new file mode 100644 index 0000000..3da742d --- /dev/null +++ b/tests/component_tests/app_control/main.cpp @@ -0,0 +1,27 @@ +/******************************************************************************** + * 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 +#include "../../../global/process_watchdog.h" + +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/component_tests/config/CMakeLists.txt b/tests/component_tests/config/CMakeLists.txt index 75d9607..e58e477 100644 --- a/tests/component_tests/config/CMakeLists.txt +++ b/tests/component_tests/config/CMakeLists.txt @@ -1,12 +1,69 @@ -# Define project -project(ConfigTests VERSION 1.0 LANGUAGES CXX) +#******************************************************************************* +# 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 target -add_executable(ComponentTest_Config config_tests.cpp) +# Define project +project (ConfigTests VERSION 1.0 LANGUAGES CXX) + +# Get CRC++ from github +include(FetchContent) +FetchContent_Declare( + CRCpp + GIT_REPOSITORY https://github.com/d-bahr/CRCpp + GIT_TAG release-1.2.0.0 +) +FetchContent_MakeAvailable(CRCpp) + +# Add include directories +include_directories(../export ${CRCpp_SOURCE_DIR}) + +## build module example for the test +#add_library(ComponentTest_ConfigInstall_Module SHARED +# "test_component.cpp" +# ) +# +#if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") +# target_link_options(ComponentTest_ConfigInstall_Module PUBLIC -fPIC) +# target_link_libraries(ComponentTest_ConfigInstall_Module GTest::GTest) +# if (WIN32) +# target_link_libraries(ComponentTest_ConfigInstall_Module Ws2_32 Winmm Rpcrt4.lib) +# else() +# target_link_libraries(ComponentTest_ConfigInstall_Module ${CMAKE_DL_LIBS} rt) +# endif() +#else() +# target_link_libraries(ComponentTest_ConfigInstall_Module GTest::GTest Rpcrt4.lib) +#endif() +# +#set_target_properties(ComponentTest_ConfigInstall_Module PROPERTIES PREFIX "") +#set_target_properties(ComponentTest_ConfigInstall_Module PROPERTIES SUFFIX ".sdv") +# +## Execute the sdv_packager utility to install the component in instance #2006. +#add_custom_target(ComponentTest_ConfigInstall_install_manifest +## TODO EVE +## COMMAND "$" -O. --instance2006 -NComponentTest_ConfigInstall "$" "-I$" --settings --create_configtest.toml +# COMMAND "$" DIRECT_INSTALL ComponentTest_Config --instance2006 "$" "-I$" --overwrite --user_config +# DEPENDS ComponentTest_ConfigInstall_Module +# WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} +#) + +# Module control test executable +add_executable(ComponentTest_Config + "main.cpp" + "config_tests.cpp" + ) target_link_libraries(ComponentTest_Config ${CMAKE_DL_LIBS} GTest::GTest) -# Add the test +# Add the Data Dispatch Service unittest add_test(NAME ComponentTest_Config COMMAND ComponentTest_Config WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) # Execute the test @@ -17,3 +74,5 @@ add_custom_command(TARGET ComponentTest_Config POST_BUILD # Build dependencies add_dependencies(ComponentTest_Config dependency_sdv_components) +add_dependencies(ComponentTest_Config ComponentTest_Repository) # Use the repository components for this test + diff --git a/tests/component_tests/config/config_tests.cpp b/tests/component_tests/config/config_tests.cpp index c580f6e..2c9c3fb 100644 --- a/tests/component_tests/config/config_tests.cpp +++ b/tests/component_tests/config/config_tests.cpp @@ -1,965 +1,93 @@ -#include -#include +/******************************************************************************** + * 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 #include +#include +#include #include -#include "../../../global/process_watchdog.h" +#include +#include "../../../global/exec_dir_helper.h" +#include "../../../global/base64.h" -#ifdef _MSC_VER -#pragma warning(disable : 4566) +#ifdef _WIN32 +#include #endif -#if defined(_WIN32) && defined(_UNICODE) -extern "C" int wmain(int argc, wchar_t* argv[]) -#else -extern "C" int main(int argc, char* argv[]) -#endif -{ - CProcessWatchdog watchdog; +// TODO: +// - Load config (server/standalone) +// - Load config with dependencies (server/standalone) +// - Close config (server/standalone) +// - Load + reload same config (server/standalone) +// - Load + load different config (server/standalone) +// - Save config (standalone) +// - Start + Load + Shutdown + Start (server) +// - System config + load user config (server) +// - System config + reload user config (server) +// - System config + load and close and load different user config (server) +// - System config with changed parameters + load config (server) - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); +// Application config tests +// Load config file - Save config file - identical? +// Load config file - add module - save config file - module added? +// Load config file - add and delete module - identical? +// Load config file - add component - save config file - module added? +// Load config file - add and delete component - identical? +// Load config file - load another config file - Save config file - identical to last config file? +// Load config file - reset baseline - add module - save config file - only module in config? +// Load config file - reset baseline - add component - save config file - only component in config? +// Add module - save config - only module in config? +// Add component - save config - only component in config? +// Add config file in Startup - config loaded? +// For main app: add system config file in settings - config loaded? +// For main app: add multiple system config file in settings - config loaded? +// For main app: add application config file in settings - config loaded? +// For main app: add multiple system config files and an application config in settings - configs loaded? +// For main app: add application config file in settings - config loaded? - add component - auto save config - config updated with +// additional compoment? For main app: add multiple system config files and an application config file in settings - config loaded? +// - add component - auto save config - config updated with additional compoment? For main app: add system config file in settings - +// add config in application startup - configs loaded? For main app: add multiple system config file in settings - add config in +// application startup - configs loaded? For main app: add application config file in settings - add config in application startup - +// startup config loaded (and otherone not)? For main app: add application config file in settings - add config in application +// startup - add service - startup config not updated? Settings config also not? Test search algorithm with multiple modules with +// similar name in different location for standalone and essential Test search algorithm with multiple modules with similar name in +// different location for main and isolated Test configuration saving of module path shoudl not change when module is found +// somewhere else. Test not existing config Test partially existing config (some modules/components do not exist) Test multiple +// manifest for loading components + +TEST(ConfigTest, LoadConfigStandalone) +{ +// sdv::app::CAppControl control; +// bool bResult = control.Startup("[Application]\nMode = \"Standalone\""); +// EXPECT_TRUE(bResult); +// +// std::string ssConfig = R"toml([Configuration] +//Version = 100 +// +//)toml"; +// +// control +// .LoadConfig("") +// +// control.Shutdown(); } -TEST(Config, Instantiate) +TEST(ConfigTest, LoadConfigServer) { - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" + //sdv::app::CAppControl control; + //bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=4000"); + //EXPECT_TRUE(bResult); -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config; - EXPECT_TRUE(config.Process("")); - - config.Clear(); - appcontrol.Shutdown(); -} - -TEST(RecognizeTypes, Table) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config(R"( - [newTable] - [secondTable.nestedTable] - )"); - EXPECT_TRUE(config.IsValid()); - - auto table1 = config.GetDirect("newTable"); - EXPECT_TRUE(table1); - EXPECT_EQ(table1.GetType(), sdv::toml::ENodeType::node_table); - EXPECT_EQ(table1.GetName(), "newTable"); - EXPECT_EQ(table1.GetValue(), sdv::any_t()); - sdv::toml::CNodeCollection collection = table1; - EXPECT_TRUE(collection); - - sdv::toml::CNodeCollection table2 = config.GetDirect("secondTable"); - EXPECT_TRUE(table2); - EXPECT_EQ(table2.GetType(), sdv::toml::ENodeType::node_table); - EXPECT_EQ(table2.GetName(), "secondTable"); - EXPECT_EQ(table2.GetValue(), sdv::any_t()); - - sdv::toml::CNodeCollection table3 = config.GetDirect("secondTable.nestedTable"); - EXPECT_TRUE(table3); - EXPECT_EQ(table3.GetType(), sdv::toml::ENodeType::node_table); - EXPECT_EQ(table3.GetName(), "nestedTable"); - EXPECT_EQ(table3.GetValue(), sdv::any_t()); - - config.Clear(); - appcontrol.Shutdown(); -} - -TEST(RecognizeTypes, Key_Value) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config(R"( - name = "Hammer" - id = 42 - pi = 3.1415926 - boolean = true - array = [] - table = {} - )"); - - - auto value_name = config.GetDirect("name"); - EXPECT_EQ(value_name.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(value_name.GetValue(), sdv::any_t("Hammer")); - - auto value_id = config.GetDirect("id"); - EXPECT_EQ(value_id.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(value_id.GetValue(), 42); - - auto value_pi = config.GetDirect("pi"); - EXPECT_EQ(value_pi.GetType(), sdv::toml::ENodeType::node_floating_point); - EXPECT_EQ(value_pi.GetValue(), 3.1415926); - - auto value_boolean = config.GetDirect("boolean"); - EXPECT_EQ(value_boolean.GetType(), sdv::toml::ENodeType::node_boolean); - EXPECT_EQ(value_boolean.GetValue(), true); - - auto value_array = config.GetDirect("array"); - EXPECT_EQ(value_array.GetType(), sdv::toml::ENodeType::node_array); - EXPECT_EQ(value_array.GetValue(), sdv::any_t()); - - auto value_table = config.GetDirect("table"); - EXPECT_EQ(value_table.GetType(), sdv::toml::ENodeType::node_table); - EXPECT_EQ(value_table.GetValue(), sdv::any_t()); - - config.Clear(); - appcontrol.Shutdown(); -} - -TEST(RecognizeTypes, TableArray) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config(R"( - [[newTableArray]] - [[newTableArray]] - [[table.nestedTableArray]] - )"); - - sdv::toml::CNodeCollection tableArray1 = config.GetDirect("newTableArray"); - EXPECT_EQ(tableArray1.GetType(), sdv::toml::ENodeType::node_array); - EXPECT_TRUE(tableArray1); - EXPECT_EQ(tableArray1.GetName(), "newTableArray"); - EXPECT_EQ(tableArray1.GetCount(), 2u); - auto tableNode0 = tableArray1[0]; - EXPECT_TRUE(tableNode0); - auto tableNode0b = tableArray1.Get(0); - EXPECT_TRUE(tableNode0b); - auto tableNode1 = tableArray1[1]; - EXPECT_TRUE(tableNode1); - auto tableNode1b = tableArray1.Get(1); - EXPECT_TRUE(tableNode1b); - EXPECT_FALSE(tableArray1[2]); - EXPECT_FALSE(tableArray1.Get(2)); - - auto table1 = config.GetDirect("newTableArray[0]"); - EXPECT_EQ(table1.GetType(), sdv::toml::ENodeType::node_table); - EXPECT_EQ(table1.GetName(), "newTableArray"); - - auto table2 = config.GetDirect("newTableArray[1]"); - EXPECT_EQ(table2.GetType(), sdv::toml::ENodeType::node_table); - EXPECT_EQ(table2.GetName(), "newTableArray"); - - config.Clear(); - appcontrol.Shutdown(); -} - -TEST(NestedContent, Array) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config(R"( - arr_mixed = [ 1.0, 2, "test string", [ 1, 2, ], { pi = 3.14, e = 2.71828 }, true] - arr_ints = [ 1, 2, 3, 4] - arr_ints_trailing_comma = [ 1, 2, 3, 4, ] - arr_multiline = [ - "first line", - "second line", - "third_line", - ] - )"); - - { - - sdv::toml::CNodeCollection array_ints = config.GetDirect("arr_ints"); - EXPECT_EQ(array_ints.GetType(), sdv::toml::ENodeType::node_array); - ASSERT_TRUE(array_ints); - EXPECT_EQ(array_ints.GetCount(), 4u); - EXPECT_TRUE(array_ints[0]); - EXPECT_TRUE(array_ints[1]); - EXPECT_TRUE(array_ints[2]); - EXPECT_TRUE(array_ints[3]); - EXPECT_FALSE(array_ints[4]); - auto array_ints_0 = config.GetDirect("arr_ints[0]"); - ASSERT_TRUE(array_ints_0); - EXPECT_EQ(array_ints_0.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_ints_0.GetValue(), 1); - auto array_ints_1 = config.GetDirect("arr_ints[1]"); - ASSERT_TRUE(array_ints_1); - EXPECT_EQ(array_ints_1.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_ints_1.GetValue(), 2); - auto array_ints_2 = config.GetDirect("arr_ints[2]"); - ASSERT_TRUE(array_ints_2); - EXPECT_EQ(array_ints_2.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_ints_2.GetValue(), 3); - auto array_ints_3 = config.GetDirect("arr_ints[3]"); - ASSERT_TRUE(array_ints_3); - EXPECT_EQ(array_ints_3.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_ints_3.GetValue(), 4); - auto array_ints_4 = config.GetDirect("arr_ints[4]"); - EXPECT_FALSE(array_ints_4); - } - - { - auto array_ints_trailing_comma = config.GetDirect("arr_ints_trailing_comma"); - auto array_ints_trailing_comma_0 = config.GetDirect("arr_ints_trailing_comma[0]"); - auto array_ints_trailing_comma_1 = config.GetDirect("arr_ints_trailing_comma[1]"); - auto array_ints_trailing_comma_2 = config.GetDirect("arr_ints_trailing_comma[2]"); - auto array_ints_trailing_comma_3 = config.GetDirect("arr_ints_trailing_comma[3]"); - auto array_ints_trailing_comma_4 = config.GetDirect("arr_ints_trailing_comma[4]"); - - EXPECT_EQ(array_ints_trailing_comma.GetType(), sdv::toml::ENodeType::node_array); - ASSERT_TRUE(array_ints_trailing_comma_0); - EXPECT_EQ(array_ints_trailing_comma_0.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_ints_trailing_comma_0.GetValue(), 1); - ASSERT_TRUE(array_ints_trailing_comma_1); - EXPECT_EQ(array_ints_trailing_comma_1.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_ints_trailing_comma_1.GetValue(), 2); - ASSERT_TRUE(array_ints_trailing_comma_2); - EXPECT_EQ(array_ints_trailing_comma_2.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_ints_trailing_comma_2.GetValue(), 3); - ASSERT_TRUE(array_ints_trailing_comma_3); - EXPECT_EQ(array_ints_trailing_comma_3.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_ints_trailing_comma_3.GetValue(), 4); - EXPECT_FALSE(array_ints_trailing_comma_4); - } - - { - auto array_mixed = config.GetDirect("arr_mixed"); - auto array_mixed_0 = config.GetDirect("arr_mixed[0]"); - auto array_mixed_1 = config.GetDirect("arr_mixed[1]"); - auto array_mixed_2 = config.GetDirect("arr_mixed[2]"); - auto array_mixed_3 = config.GetDirect("arr_mixed[3]"); - auto array_mixed_3_2 = config.GetDirect("arr_mixed[3][1]"); - auto array_mixed_4 = config.GetDirect("arr_mixed[4]"); - auto array_mixed_4_pi = config.GetDirect("arr_mixed[4].pi"); - auto array_mixed_5 = config.GetDirect("arr_mixed[5]"); - auto array_mixed_6 = config.GetDirect("arr_mixed[6]"); - - EXPECT_EQ(array_mixed.GetType(), sdv::toml::ENodeType::node_array); - ASSERT_TRUE(array_mixed_0); - EXPECT_EQ(array_mixed_0.GetType(), sdv::toml::ENodeType::node_floating_point); - EXPECT_EQ(array_mixed_0.GetValue(), 1.0); - ASSERT_TRUE(array_mixed_1); - EXPECT_EQ(array_mixed_1.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_mixed_1.GetValue(), 2); - ASSERT_TRUE(array_mixed_2); - EXPECT_EQ(array_mixed_2.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(array_mixed_2.GetValue()), "test string"); - ASSERT_TRUE(array_mixed_3); - EXPECT_EQ(array_mixed_3.GetType(), sdv::toml::ENodeType::node_array); - EXPECT_EQ(array_mixed_3_2.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(array_mixed_3_2.GetValue(), 2); - ASSERT_TRUE(array_mixed_4); - EXPECT_EQ(array_mixed_4.GetType(), sdv::toml::ENodeType::node_table); - EXPECT_EQ(array_mixed_4_pi.GetType(), sdv::toml::ENodeType::node_floating_point); - EXPECT_EQ(array_mixed_4_pi.GetValue(), 3.14); - ASSERT_TRUE(array_mixed_5); - EXPECT_EQ(array_mixed_5.GetType(), sdv::toml::ENodeType::node_boolean); - EXPECT_EQ(array_mixed_5.GetValue(), true); - EXPECT_EQ(array_mixed_5.GetValue(), sdv::any_t()); - EXPECT_FALSE(array_mixed_6); - } - - { - auto array_multiline = config.GetDirect("arr_multiline"); - auto array_multiline_0 = config.GetDirect("arr_multiline[0]"); - auto array_multiline_1 = config.GetDirect("arr_multiline[1]"); - auto array_multiline_2 = config.GetDirect("arr_multiline[2]"); - auto array_multiline_3 = config.GetDirect("arr_multiline[3]"); - - EXPECT_EQ(array_multiline.GetType(), sdv::toml::ENodeType::node_array); - ASSERT_TRUE(array_multiline_0); - EXPECT_EQ(array_multiline_0.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(array_multiline_0.GetValue()), "first line"); - ASSERT_TRUE(array_multiline_1); - EXPECT_EQ(array_multiline_1.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(array_multiline_1.GetValue()), "second line"); - ASSERT_TRUE(array_multiline_2); - EXPECT_EQ(array_multiline_2.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(array_multiline_2.GetValue()), "third_line"); - EXPECT_FALSE(array_multiline_3); - } - - config.Clear(); - appcontrol.Shutdown(); -} - -TEST(NestedContent, Table) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config(R"( - [table] - a = 2 - b = 1.2 - [anotherTable] - a = 4 - c = false - [thirdTable.fourthTable] - a = "five" - d = [] - )"); - - auto table_a = config.GetDirect("table.a"); - auto table_b = config.GetDirect("table.b"); - auto anotherTable_a = config.GetDirect("anotherTable.a"); - auto anotherTable_c = config.GetDirect("anotherTable.c"); - auto fourthTable_a = config.GetDirect("thirdTable.fourthTable.a"); - auto fourthTable_d = config.GetDirect("thirdTable.fourthTable.d"); - - ASSERT_TRUE(table_a); - EXPECT_EQ(table_a.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(table_a.GetValue(), 2); - ASSERT_TRUE(table_b); - EXPECT_EQ(table_b.GetType(), sdv::toml::ENodeType::node_floating_point); - EXPECT_EQ(table_b.GetValue(), 1.2); - ASSERT_TRUE(anotherTable_a); - EXPECT_EQ(anotherTable_a.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(anotherTable_a.GetValue(), 4); - ASSERT_TRUE(anotherTable_c); - EXPECT_EQ(anotherTable_c.GetType(), sdv::toml::ENodeType::node_boolean); - EXPECT_EQ(anotherTable_c.GetValue(), false); - ASSERT_TRUE(fourthTable_a); - EXPECT_EQ(fourthTable_a.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(fourthTable_a.GetValue()), "five"); - ASSERT_TRUE(fourthTable_d); - EXPECT_EQ(fourthTable_d.GetType(), sdv::toml::ENodeType::node_array); - - config.Clear(); - appcontrol.Shutdown(); -} - -TEST(NestedContent, TableArray) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config(R"( - [[table.test]] - a = 2 - b = 1.2 - [[table.test]] - a = 4 - c = false - [[table.test]] - a = "five" - d = [] - )"); - - auto table_test_1_a = config.GetDirect("table.test[0].a"); - auto table_test_1_b = config.GetDirect("table.test[0].b"); - auto table_test_2_a = config.GetDirect("table.test[1].a"); - auto table_test_2_c = config.GetDirect("table.test[1].c"); - auto table_test_3_a = config.GetDirect("table.test[2].a"); - auto table_test_3_d = config.GetDirect("table.test[2].d"); - - ASSERT_TRUE(table_test_1_a); - EXPECT_EQ(table_test_1_a.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(table_test_1_a.GetValue(), 2); - ASSERT_TRUE(table_test_1_b); - EXPECT_EQ(table_test_1_b.GetType(), sdv::toml::ENodeType::node_floating_point); - EXPECT_EQ(table_test_1_b.GetValue(), 1.2); - ASSERT_TRUE(table_test_2_a); - EXPECT_EQ(table_test_2_a.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(table_test_2_a.GetValue(), 4); - ASSERT_TRUE(table_test_2_c); - EXPECT_EQ(table_test_2_c.GetType(), sdv::toml::ENodeType::node_boolean); - EXPECT_EQ(table_test_2_c.GetValue(), false); - ASSERT_TRUE(table_test_3_a); - EXPECT_EQ(table_test_3_a.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(table_test_3_a.GetValue()), "five"); - ASSERT_TRUE(table_test_3_d); - EXPECT_EQ(table_test_3_d.GetType(), sdv::toml::ENodeType::node_array); - - config.Clear(); - appcontrol.Shutdown(); -} - -TEST(NestedContent, InlineTable) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config(R"( - table1 = { a = 0, b = 1.2, c = "string" } - table2 = { a = [], b = true, e = 2.71828 } - table3 = { a = { a = "a", b = "A" }, b = {a = "b", b = "B"}, e = {a = "e", b = "E"} } - )"); - - auto table1_a = config.GetDirect("table1.a"); - auto table1_b = config.GetDirect("table1.b"); - auto table1_c = config.GetDirect("table1.c"); - auto table2_a = config.GetDirect("table2.a"); - auto table2_b = config.GetDirect("table2.b"); - auto table2_e = config.GetDirect("table2.e"); - auto table3_a_a = config.GetDirect("table3.a.a"); - auto table3_a_b = config.GetDirect("table3.a.b"); - auto table3_b_a = config.GetDirect("table3.b.a"); - auto table3_b_b = config.GetDirect("table3.b.b"); - auto table3_e_a = config.GetDirect("table3.e.a"); - auto table3_e_b = config.GetDirect("table3.e.b"); - - ASSERT_TRUE(table1_a); - EXPECT_EQ(table1_a.GetType(), sdv::toml::ENodeType::node_integer); - EXPECT_EQ(table1_a.GetValue(), 0); - ASSERT_TRUE(table1_b); - EXPECT_EQ(table1_b.GetType(), sdv::toml::ENodeType::node_floating_point); - EXPECT_EQ(table1_b.GetValue(), 1.2); - ASSERT_TRUE(table1_c); - EXPECT_EQ(table1_c.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(table1_c.GetValue()), "string"); - ASSERT_TRUE(table2_a); - EXPECT_EQ(table2_a.GetType(), sdv::toml::ENodeType::node_array); - ASSERT_TRUE(table2_b); - EXPECT_EQ(table2_b.GetType(), sdv::toml::ENodeType::node_boolean); - EXPECT_EQ(table2_b.GetValue(), true); - ASSERT_TRUE(table2_e); - EXPECT_EQ(table2_e.GetType(), sdv::toml::ENodeType::node_floating_point); - EXPECT_EQ(table2_e.GetValue(), 2.71828); - ASSERT_TRUE(table3_a_a); - EXPECT_EQ(table3_a_a.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(table3_a_a.GetValue()), "a"); - ASSERT_TRUE(table3_a_b); - EXPECT_EQ(table3_a_b.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(table3_a_b.GetValue()), "A"); - ASSERT_TRUE(table3_b_a); - EXPECT_EQ(table3_b_a.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(table3_b_a.GetValue()), "b"); - ASSERT_TRUE(table3_b_b); - EXPECT_EQ(table3_b_b.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(table3_b_b.GetValue()), "B"); - ASSERT_TRUE(table3_e_a); - EXPECT_EQ(table3_e_a.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(table3_e_a.GetValue()), "e"); - ASSERT_TRUE(table3_e_b); - EXPECT_EQ(table3_e_b.GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(static_cast(table3_e_b.GetValue()), "E"); - - config.Clear(); - appcontrol.Shutdown(); -} - -TEST(SpecialCases, Keys) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - EXPECT_TRUE(sdv::toml::CTOMLParser(u8R"( - "127.0.0.1" = "value" - "character encoding" = "value" - "ʎǝʞ" = "value" - 'key2' = "value" - 'quoted "value"' = "value" - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - key = "value" - bare_key = "value" - bare-key = "value" - 1234 = "value" - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - "" = "blank" # VALID but discouraged - )")); - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - '' = 'blank' # VALID but discouraged - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - name = "Orange" - physical.color = "orange" - physical.shape = "round" - site."google.com" = true - )")); - - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - fruit.name = "banana" # this is best practice - fruit. color = "yellow" # same as fruit.color - fruit . flavor = "banana" # same as fruit.flavor - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - # This makes the key "fruit" into a table. - fruit.apple.smooth = true - # So then you can add to the table "fruit" like so: - fruit.orange = 2 - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - # VALID BUT DISCOURAGED - apple.type = "fruit" - orange.type = "fruit" - apple.skin = "thin" - orange.skin = "thick" - apple.color = "red" - orange.color = "orange" - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - 3.1415 = 3.1415 - )")); - { - sdv::toml::CTOMLParser config(R"( - 3.1415 = 3.1415 - )"); - auto table = config.GetDirect("3"); - auto pi = config.GetDirect("3.1415"); - ASSERT_TRUE(table); - EXPECT_EQ(table.GetType(), sdv::toml::ENodeType::node_table); - ASSERT_TRUE(pi); - EXPECT_EQ(pi.GetType(), sdv::toml::ENodeType::node_floating_point); - EXPECT_EQ(pi.GetValue(), 3.1415); - } - - appcontrol.Shutdown(); -} - -TEST(SpecialCases, Arrays) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - integers = [ 1, 2, 3 ] - colors = [ "red", "yellow", "green" ] - nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] - nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] - string_array = [ "all", 'strings', """are the same""", '''type''' ] - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] - contributors = [ - "Foo Bar ", - { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } - ] - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - integers3 = [ - 1, - 2, # this is ok - ] - )")); - - appcontrol.Shutdown(); -} - -TEST(SpecialCases, Tables) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - [table-1] - key1 = "some string" - key2 = 123 - - [table-2] - key1 = "another string" - key2 = 456 - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - [dog."tater.man"] - type.name = "pug" - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(u8R"( - [a.b.c] # this is best practice - [ d.e.f ] # same as [d.e.f] - [ g . h . i ] # same as [g.h.i] - [ j . "ʞ" . 'l' ] # same as [j."ʞ".'l'] - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - # [x] you - # [x.y] don't - # [x.y.z] need these - [x.y.z.w] # for this to work - [x] # defining a super-table afterward is ok - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - # VALID BUT DISCOURAGED - [fruit.apple] - [animal] - [fruit.orange] - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - [fruit] - apple.color = "red" - apple.taste.sweet = true - [fruit.apple.texture] # you can add sub-tables - )")); - - appcontrol.Shutdown(); -} - -TEST(SpecialCases, TableArrays) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - [[products]] - name = "Hammer" - sku = 738594937 - [[products]] # empty table within the array - [[products]] - name = "Nail" - sku = 284758393 - color = "gray" - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - [[fruits]] - name = "apple" - [fruits.physical] # subtable - color = "red" - shape = "round" - [[fruits.varieties]] # nested array of tables - name = "red delicious" - [[fruits.varieties]] - name = "granny smith" - [[fruits]] - name = "banana" - [[fruits.varieties]] - name = "plantain" - )")); - - EXPECT_TRUE(sdv::toml::CTOMLParser(R"( - points = [ { x = 1, y = 2, z = 3 }, - { x = 7, y = 8, z = 9 }, - { x = 2, y = 4, z = 8 } ] - )")); - - appcontrol.Shutdown(); -} - -TEST(ErrorCases, KeyValue) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"(key = # node_invalid)")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"(first = "Tom" last = "Preston-Werner" # node_invalid)")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"(= "no key name" # node_invalid)")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - name = "Tom" - name = "Pradyun" - )")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - fruit . flavor = "banana" # same as fruit.flavor - fruit.flavor = "banana" - )")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - spelling = "favorite" - "spelling" = "favourite" - )")); - - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - # This defines the value of fruit.apple to be an integer. - fruit.apple = 1 - # But then this treats fruit.apple like it's a table. - # You can't turn an integer into a table. - fruit.apple.smooth = true - )")); - - appcontrol.Shutdown(); -} - -TEST(ErrorCases, Tables) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - EXPECT_FALSE(sdv::toml::CTOMLParser(u8R"( - [ j . "ʞ" . 'l' ] - [j."ʞ".'l'] - )")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(u8R"( - [ j . "ʞ" . 'l' ] - ["j".'ʞ'."l"] - )")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - [fruit] - apple = "red" - [fruit] - orange = "orange" - )")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - [fruit] - apple = "red" - [fruit.apple] - texture = "smooth" - )")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - [fruit] - apple.color = "red" - apple.taste.sweet = true - [fruit.apple] # INVALID - )")); - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - [fruit] - apple.color = "red" - apple.taste.sweet = true - [fruit.apple.taste] # INVALID - )")); - - appcontrol.Shutdown(); -} - -TEST(ErrorCases, InlineTables) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - type = { name = "Nail" } - type.edible = false # INVALID - )")); - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - [product] - type.name = "Nail" - type = { edible = false } # INVALID - )")); - - appcontrol.Shutdown(); -} - -TEST(ErrorCases, TableArrays) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - [fruit.physical] # subtable, but to which parent element should it belong? - color = "red" - shape = "round" - [[fruit]] # parser must throw an error upon discovering that "fruit" is - # an array rather than a table - name = "apple" - )")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - fruits = [] - [[fruits]] # Not allowed - )")); - - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - [[fruits]] - name = "apple" - [[fruits.varieties]] - name = "red delicious" - # INVALID: This table conflicts with the previous array of tables - [fruits.varieties] - name = "granny smith" - )")); - EXPECT_FALSE(sdv::toml::CTOMLParser(R"( - [[fruits]] - name = "apple" - [fruits.physical] - color = "red" - shape = "round" - # INVALID: This array of tables conflicts with the previous table - [[fruits.physical]] - color = "green" - )")); - - appcontrol.Shutdown(); -} - -TEST(Ordering, Array) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config(R"( - array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - )"); - - auto two = config.GetDirect("array[2]"); - auto eleven = config.GetDirect("array[11]"); - const sdv::toml::CNodeCollection arr = config.GetDirect("array"); - - // with direct access - ASSERT_TRUE(two); - EXPECT_EQ(two.GetValue(), 2); - ASSERT_TRUE(eleven); - EXPECT_EQ(eleven.GetValue(), 11); - - // with indirect access through iterating - ASSERT_TRUE(arr); - for (std::size_t i = 0; i < arr.GetCount(); ++i) - { - EXPECT_EQ(arr[i].GetValue(), (int64_t)i); - } - - config.Clear(); - appcontrol.Shutdown(); -} - -TEST(Ordering, TableAray) -{ - sdv::app::CAppControl appcontrol(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config"); - ASSERT_TRUE(appcontrol.IsRunning()); - - sdv::toml::CTOMLParser config(R"( - [[tableArray]] - a = 0 - [[tableArray]] - a = 1 - [[tableArray]] - a = 2 - [[tableArray]] - a = 3 - [[tableArray]] - a = 4 - [[tableArray]] - a = 5 - [[tableArray]] - a = 6 - [[tableArray]] - a = 7 - [[tableArray]] - a = 8 - [[tableArray]] - a = 9 - [[tableArray]] - a = 10 - [[tableArray]] - a = 11 - )"); - - sdv::toml::CNodeCollection tableArray = config.GetDirect("tableArray"); - - ASSERT_TRUE(tableArray); - for (std::size_t i = 0; i < tableArray.GetCount(); ++i) - { - EXPECT_EQ(sdv::toml::CNodeCollection(tableArray[i])[0].GetValue(), (int64_t) i); - } - - config.Clear(); - appcontrol.Shutdown(); + //control.Shutdown(); } diff --git a/tests/component_tests/config/main.cpp b/tests/component_tests/config/main.cpp new file mode 100644 index 0000000..3da742d --- /dev/null +++ b/tests/component_tests/config/main.cpp @@ -0,0 +1,27 @@ +/******************************************************************************** + * 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 +#include "../../../global/process_watchdog.h" + +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/component_tests/config_install/CMakeLists.txt b/tests/component_tests/config_install/CMakeLists.txt index 319c90b..7141d59 100644 --- a/tests/component_tests/config_install/CMakeLists.txt +++ b/tests/component_tests/config_install/CMakeLists.txt @@ -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: +# Erik Verhoeven - initial API and implementation +#******************************************************************************* + # Define project -project (ModuleControlTests VERSION 1.0 LANGUAGES CXX) +project (ConfigInstallTests VERSION 1.0 LANGUAGES CXX) # Get CRC++ from github include(FetchContent) diff --git a/tests/component_tests/config_install/main.cpp b/tests/component_tests/config_install/main.cpp index 6dfdefb..3da742d 100644 --- a/tests/component_tests/config_install/main.cpp +++ b/tests/component_tests/config_install/main.cpp @@ -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 #include "../../../global/process_watchdog.h" diff --git a/tests/component_tests/config_install/manifest_helper_util_test.cpp b/tests/component_tests/config_install/manifest_helper_util_test.cpp index 780f31e..699fc84 100644 --- a/tests/component_tests/config_install/manifest_helper_util_test.cpp +++ b/tests/component_tests/config_install/manifest_helper_util_test.cpp @@ -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 #include #include diff --git a/tests/component_tests/config_install/module_install_test.cpp b/tests/component_tests/config_install/module_install_test.cpp index 726fd05..1f0becc 100644 --- a/tests/component_tests/config_install/module_install_test.cpp +++ b/tests/component_tests/config_install/module_install_test.cpp @@ -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 #include #include diff --git a/tests/component_tests/config_install/test_component.cpp b/tests/component_tests/config_install/test_component.cpp index 1962914..8152592 100644 --- a/tests/component_tests/config_install/test_component.cpp +++ b/tests/component_tests/config_install/test_component.cpp @@ -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 class CModuleTestComponent1 : public sdv::CSdvObject @@ -7,7 +20,7 @@ public: BEGIN_SDV_INTERFACE_MAP() END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Utility) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::utility) DECLARE_OBJECT_CLASS_NAME("ModuleTestComponent1") private: }; @@ -20,7 +33,7 @@ public: BEGIN_SDV_INTERFACE_MAP() END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Utility) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::utility) DECLARE_OBJECT_CLASS_NAME("ModuleTestComponent2") private: }; diff --git a/tests/component_tests/data_dispatch_service/CMakeLists.txt b/tests/component_tests/data_dispatch_service/CMakeLists.txt index 4277fb8..1f7202b 100644 --- a/tests/component_tests/data_dispatch_service/CMakeLists.txt +++ b/tests/component_tests/data_dispatch_service/CMakeLists.txt @@ -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 (DataDispatchServiceTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/component_tests/data_dispatch_service/data_dispatch_service_test.cpp b/tests/component_tests/data_dispatch_service/data_dispatch_service_test.cpp index 15a4f4c..4233c03 100644 --- a/tests/component_tests/data_dispatch_service/data_dispatch_service_test.cpp +++ b/tests/component_tests/data_dispatch_service/data_dispatch_service_test.cpp @@ -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 #include #include diff --git a/tests/component_tests/data_dispatch_service/main.cpp b/tests/component_tests/data_dispatch_service/main.cpp index b916ff6..6928875 100644 --- a/tests/component_tests/data_dispatch_service/main.cpp +++ b/tests/component_tests/data_dispatch_service/main.cpp @@ -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 #include #include "../../../global/process_watchdog.h" diff --git a/tests/component_tests/data_dispatch_service/transaction_test.cpp b/tests/component_tests/data_dispatch_service/transaction_test.cpp index b3e64fb..6f58d63 100644 --- a/tests/component_tests/data_dispatch_service/transaction_test.cpp +++ b/tests/component_tests/data_dispatch_service/transaction_test.cpp @@ -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 #include #include diff --git a/tests/component_tests/data_dispatch_service/trigger_test.cpp b/tests/component_tests/data_dispatch_service/trigger_test.cpp index fbcd80c..6949e61 100644 --- a/tests/component_tests/data_dispatch_service/trigger_test.cpp +++ b/tests/component_tests/data_dispatch_service/trigger_test.cpp @@ -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 #include #include diff --git a/tests/component_tests/dbc_util/CMakeLists.txt b/tests/component_tests/dbc_util/CMakeLists.txt index 8991408..f15792a 100644 --- a/tests/component_tests/dbc_util/CMakeLists.txt +++ b/tests/component_tests/dbc_util/CMakeLists.txt @@ -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 (DbcUtilTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/component_tests/dbc_util/dbc_util_test.cpp b/tests/component_tests/dbc_util/dbc_util_test.cpp index 5475140..8b55ef4 100644 --- a/tests/component_tests/dbc_util/dbc_util_test.cpp +++ b/tests/component_tests/dbc_util/dbc_util_test.cpp @@ -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 #include #include @@ -93,11 +106,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -125,7 +138,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalBE1.Reset(); signalBE2.Reset(); @@ -203,11 +216,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmit64BitBigEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -226,7 +239,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmit64BitBigEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalInt64BE.Reset(); signalUint64BE.Reset(); @@ -347,11 +360,11 @@ TEST(DbcUtilCanDLTest, ReceiveBigEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -384,7 +397,7 @@ TEST(DbcUtilCanDLTest, ReceiveBigEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalBE1.Reset(); signalBE2.Reset(); @@ -465,11 +478,11 @@ TEST(DbcUtilCanDLTest, Receive64BitBigEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -493,7 +506,7 @@ TEST(DbcUtilCanDLTest, Receive64BitBigEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalInt64BE.Reset(); signalUint64BE.Reset(); @@ -525,11 +538,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -557,7 +570,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalLE1.Reset(); signalLE2.Reset(); @@ -635,11 +648,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmit64BitLittleEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -658,7 +671,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmit64BitLittleEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalInt64LE.Reset(); signalUint64LE.Reset(); @@ -779,11 +792,11 @@ TEST(DbcUtilCanDLTest, ReceiveLittleEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -816,7 +829,7 @@ TEST(DbcUtilCanDLTest, ReceiveLittleEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalLE1.Reset(); signalLE2.Reset(); @@ -897,11 +910,11 @@ TEST(DbcUtilCanDLTest, Receive64BitLittleEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -925,7 +938,7 @@ TEST(DbcUtilCanDLTest, Receive64BitLittleEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalInt64LE.Reset(); signalUint64LE.Reset(); @@ -957,11 +970,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransactionalTransmitBigEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -992,7 +1005,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransactionalTransmitBigEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalBE1.Reset(); signalBE2.Reset(); @@ -1050,11 +1063,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransactionalTransmitLittleEndian) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -1085,7 +1098,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransactionalTransmitLittleEndian) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalLE1.Reset(); signalLE2.Reset(); @@ -1143,11 +1156,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndianAllDataTypes) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to integer based signals sdv::core::CDispatchService dispatch; @@ -1222,7 +1235,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndianAllDataTypes) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalIntBE1.Reset(); signalIntBE2.Reset(); @@ -1429,11 +1442,11 @@ TEST(DbcUtilCanDLTest, ReceiveBigEndianAllDataTypes) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -1568,7 +1581,7 @@ TEST(DbcUtilCanDLTest, ReceiveBigEndianAllDataTypes) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalIntBE1.Reset(); signalIntBE2.Reset(); @@ -1615,11 +1628,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndianAllDataTypes) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to integer based signals sdv::core::CDispatchService dispatch; @@ -1694,7 +1707,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndianAllDataTypes) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalIntLE1.Reset(); signalIntLE2.Reset(); @@ -1901,11 +1914,11 @@ TEST(DbcUtilCanDLTest, ReceiveLittleEndianAllDataTypes) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -2040,7 +2053,7 @@ TEST(DbcUtilCanDLTest, ReceiveLittleEndianAllDataTypes) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalIntLE1.Reset(); signalIntLE2.Reset(); @@ -2087,11 +2100,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndianScaledDataTypes) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to integer based signals sdv::core::CDispatchService dispatch; @@ -2127,7 +2140,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndianScaledDataTypes) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalUintBE1.Reset(); signalUintBE2.Reset(); @@ -2257,11 +2270,11 @@ TEST(DbcUtilCanDLTest, ReceiveBigEndianScaledDataType) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -2328,7 +2341,7 @@ TEST(DbcUtilCanDLTest, ReceiveBigEndianScaledDataType) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalUintBE1.Reset(); signalUintBE2.Reset(); @@ -2364,11 +2377,11 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndianScaledDataTypes) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to integer based signals sdv::core::CDispatchService dispatch; @@ -2404,7 +2417,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndianScaledDataTypes) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalUintLE1.Reset(); signalUintLE2.Reset(); @@ -2534,11 +2547,11 @@ TEST(DbcUtilCanDLTest, ReceiveLittleEndianScaledDataType) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -2605,7 +2618,7 @@ TEST(DbcUtilCanDLTest, ReceiveLittleEndianScaledDataType) // Shutdown appcontrol.SetConfigMode(); dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalUintLE1.Reset(); signalUintLE2.Reset(); @@ -2641,11 +2654,11 @@ TEST(DbcUtilCanDLTest, CyclicTransmit) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -2665,14 +2678,14 @@ TEST(DbcUtilCanDLTest, CyclicTransmit) }); appcontrol.SetRunningMode(); std::unique_lock lock(mtx); - cv.wait(lock); + cv.wait_for(lock, std::chrono::milliseconds(1000)); timer.Reset(); lock.unlock(); appcontrol.SetConfigMode(); // Shutdown dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalCounter.Reset(); @@ -2790,11 +2803,11 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -2815,13 +2828,13 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit) }); appcontrol.SetRunningMode(); std::unique_lock lock(mtx); - cv.wait(lock); + cv.wait_for(lock, std::chrono::milliseconds(1000)); appcontrol.SetConfigMode(); timer.Reset(); // Shutdown dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalCounter.Reset(); @@ -2957,11 +2970,11 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousTransmit) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -2981,13 +2994,13 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousTransmit) }); appcontrol.SetRunningMode(); std::unique_lock lock(mtx); - cv.wait(lock); + cv.wait_for(lock, std::chrono::milliseconds(1000)); appcontrol.SetConfigMode(); timer.Reset(); // Shutdown dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalCounter.Reset(); @@ -3098,11 +3111,11 @@ TEST(DbcUtilCanDLTest, SpontaneousDelayTransmit) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -3126,13 +3139,13 @@ TEST(DbcUtilCanDLTest, SpontaneousDelayTransmit) }); appcontrol.SetRunningMode(); std::unique_lock lock(mtx); - cv.wait(lock); + cv.wait_for(lock, std::chrono::milliseconds(1000)); appcontrol.SetConfigMode(); timer.Reset(); // Shutdown dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalCounter.Reset(); @@ -3236,11 +3249,11 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousDelayTransmit) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -3263,13 +3276,13 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousDelayTransmit) }); appcontrol.SetRunningMode(); std::unique_lock lock(mtx); - cv.wait(lock); + cv.wait_for(lock, std::chrono::milliseconds(5000)); appcontrol.SetConfigMode(); timer.Reset(); // Shutdown dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalCounter.Reset(); @@ -3380,11 +3393,11 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit) // Start the data link CDbcStructDataLink dl; - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); dl.Initialize(""); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); // Subscribe to several signals sdv::core::CDispatchService dispatch; @@ -3404,13 +3417,13 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit) }); appcontrol.SetRunningMode(); std::unique_lock lock(mtx); - cv.wait(lock); + cv.wait_for(lock, std::chrono::milliseconds(1000)); appcontrol.SetConfigMode(); timer.Reset(); // Shutdown dl.Shutdown(); - EXPECT_EQ(dl.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::destruction_pending); signalCounter.Reset(); diff --git a/tests/component_tests/dbc_util/test_dbc_util_config_asc.toml b/tests/component_tests/dbc_util/test_dbc_util_config_asc.toml index 58ea8fe..afa18db 100644 --- a/tests/component_tests/dbc_util/test_dbc_util_config_asc.toml +++ b/tests/component_tests/dbc_util/test_dbc_util_config_asc.toml @@ -12,6 +12,7 @@ Class = "DataDispatchService" [[Component]] Path = "can_com_sim.sdv" Class = "CAN_Com_Sim" +[Component.Parameters] Source="asc_reader_test.asc" Target="asc_writer_test.asc" diff --git a/tests/component_tests/dbc_util/test_dbc_util_config_rx.toml b/tests/component_tests/dbc_util/test_dbc_util_config_rx.toml index 727965f..e1c753c 100644 --- a/tests/component_tests/dbc_util/test_dbc_util_config_rx.toml +++ b/tests/component_tests/dbc_util/test_dbc_util_config_rx.toml @@ -12,5 +12,6 @@ Class = "DataDispatchService" [[Component]] Path = "can_com_sim.sdv" Class = "CAN_Com_Sim" +[Component.Parameters] Source="receiver_test.asc" diff --git a/tests/component_tests/dbc_util/test_dbc_util_config_tx.toml b/tests/component_tests/dbc_util/test_dbc_util_config_tx.toml index 0504842..da579ce 100644 --- a/tests/component_tests/dbc_util/test_dbc_util_config_tx.toml +++ b/tests/component_tests/dbc_util/test_dbc_util_config_tx.toml @@ -12,4 +12,5 @@ Class = "DataDispatchService" [[Component]] Path = "can_com_sim.sdv" Class = "CAN_Com_Sim" +[Component.Parameters] Target="transmitter_test.asc" diff --git a/tests/component_tests/logger/CMakeLists.txt b/tests/component_tests/logger/CMakeLists.txt index 59e1d84..217a4ab 100644 --- a/tests/component_tests/logger/CMakeLists.txt +++ b/tests/component_tests/logger/CMakeLists.txt @@ -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 +#******************************************************************************* + project(LoggerTests) set(CMAKE_CXX_STANDARD 17) diff --git a/tests/component_tests/logger/logger_test.cpp b/tests/component_tests/logger/logger_test.cpp index c62ed4d..1f7062f 100644 --- a/tests/component_tests/logger/logger_test.cpp +++ b/tests/component_tests/logger/logger_test.cpp @@ -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 #include diff --git a/tests/component_tests/logger/logger_test_service.cpp b/tests/component_tests/logger/logger_test_service.cpp index baf19ef..7a72668 100644 --- a/tests/component_tests/logger/logger_test_service.cpp +++ b/tests/component_tests/logger/logger_test_service.cpp @@ -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 "logger_test_service.h" CLoggerTestService::CLoggerTestService() diff --git a/tests/component_tests/logger/logger_test_service.h b/tests/component_tests/logger/logger_test_service.h index 30ab761..06e6add 100644 --- a/tests/component_tests/logger/logger_test_service.h +++ b/tests/component_tests/logger/logger_test_service.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 VAPI_DUMMY_TEST_SERVICE_H #define VAPI_DUMMY_TEST_SERVICE_H @@ -10,7 +23,7 @@ class CLoggerTestService : public sdv::CSdvObject { public: - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::BasicService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::basic_service) DECLARE_OBJECT_CLASS_NAME("LoggerTestService") CLoggerTestService(); diff --git a/tests/component_tests/repository/CMakeLists.txt b/tests/component_tests/repository/CMakeLists.txt index bea2b1e..eff7440 100644 --- a/tests/component_tests/repository/CMakeLists.txt +++ b/tests/component_tests/repository/CMakeLists.txt @@ -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(RepositoryTests VERSION 1.0 LANGUAGES CXX) @@ -10,7 +23,7 @@ message("Use IDL compiler: ${SDVIDL}") # Compile the IDL add_custom_command( - OUTPUT ${PROJECT_SOURCE_DIR}/generated/test_component.h + OUTPUT ${PROJECT_SOURCE_DIR}/generated/test_component.h ${PROJECT_SOURCE_DIR}/generated/ps/proxystub.cpp ${PROJECT_SOURCE_DIR}/generated/serdes/test_component_serdes.h DEPENDS sdv_idl_compiler MAIN_DEPENDENCY test_component.idl COMMENT "Build test_component.idl" @@ -19,9 +32,10 @@ add_custom_command( ) set_source_files_properties(test_component_ps.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h) +set_source_files_properties(test_component_ps.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/ps/proxystub.cpp) +set_source_files_properties(test_component_ps.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/serdes/test_component_serdes.h) set_source_files_properties(test_component.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h) - # Add the dynamic library add_library(ComponentTest_Repository_ps SHARED "test_component_ps.cpp" @@ -43,9 +57,7 @@ set_target_properties(ComponentTest_Repository_test_module PROPERTIES SUFFIX ".s # Execute the installation helper utility to create an installation manifest for the core files. add_custom_target(ComponentTest_Repository_install_manifest -# TODO EVE -# COMMAND "$" -O. --instance2005 -NComponentTest_Repository "$" "$" "-I$" --settings --create_configtest.toml --exclude_config_class"TestObject_CreateChain" --exclude_config_class"TestObject_CreateChainLock" --exclude_config_class"TestObject_CreateChainLockThread" --exclude_config_class"TestObject_CreateDuringShutdown" --exclude_config_class"TestObject_IObjectControlFail" - COMMAND "$" DIRECT_INSTALL ComponentTest_Repository --instance2005 "$" "$" "-I$" --overwrite --user_config + COMMAND "$" DIRECT_INSTALL ComponentTest_Repository -T. --instance2005 "$" "$" "-I$" --overwrite --user_config+TestObject_ComplexHelloService+TestObject_HelloUtility --interface_config+Example_Object+Example_Object_2+TestObject_HelloDevice+TestObject_BasicHelloService+TestObject_SystemHelloService DEPENDS ComponentTest_Repository_test_module ComponentTest_Repository_ps WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) @@ -53,7 +65,7 @@ add_custom_target(ComponentTest_Repository_install_manifest # Define target add_executable(ComponentTest_Repository "repository_test.cpp" - "isolation_test.cpp" + "isolation_test.cpp" ) target_link_libraries(ComponentTest_Repository ${CMAKE_DL_LIBS} GTest::GTest) diff --git a/tests/component_tests/repository/isolation_test.cpp b/tests/component_tests/repository/isolation_test.cpp index c8a198d..3cfe7eb 100644 --- a/tests/component_tests/repository/isolation_test.cpp +++ b/tests/component_tests/repository/isolation_test.cpp @@ -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 #include #include @@ -39,9 +52,8 @@ ViewFilter = "Fatal" Mode = "Main" Instance = 2005 )code"); - if (!bRet) - std::cout << "Failed to start the application for instance 1234." << std::endl; + std::cout << "Failed to start the application for instance 2005." << std::endl; } /** @@ -53,9 +65,7 @@ Instance = 2005 } }; -// TODO EVE: Disabled until the implementation of configuration installation is finished. See -// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/705891 -TEST_F(CIsolatedComponentTest, DISABLED_LoadNonIsolatedSystemService) +TEST_F(CIsolatedComponentTest, LoadNonIsolatedSystemService) { // Load the hello system service IHello* pHello = sdv::core::GetObject("TestObject_SystemHelloService"); @@ -67,9 +77,7 @@ TEST_F(CIsolatedComponentTest, DISABLED_LoadNonIsolatedSystemService) EXPECT_EQ(tPID, GetProcessID()); // Runs in main process } -// TODO EVE: Disabled until the implementation of configuration installation is finished. See -// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/705891 -TEST_F(CIsolatedComponentTest, DISABLED_LoadNonIsolatedDevice) +TEST_F(CIsolatedComponentTest, LoadNonIsolatedDevice) { // Load the hello device IHello* pHello = sdv::core::GetObject("TestObject_HelloDevice"); @@ -81,9 +89,7 @@ TEST_F(CIsolatedComponentTest, DISABLED_LoadNonIsolatedDevice) EXPECT_EQ(tPID, GetProcessID()); // Runs in main process } -// TODO EVE: Disabled until the implementation of configuration installation is finished. See -// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/705891 -TEST_F(CIsolatedComponentTest, DISABLED_LoadNonIsolatedBasicService) +TEST_F(CIsolatedComponentTest, LoadNonIsolatedBasicService) { // Load the hello basic service IHello* pHello = sdv::core::GetObject("TestObject_BasicHelloService"); @@ -95,9 +101,7 @@ TEST_F(CIsolatedComponentTest, DISABLED_LoadNonIsolatedBasicService) EXPECT_EQ(tPID, GetProcessID()); // Runs in main process } -// TODO EVE: Disabled until the implementation of configuration installation is finished. See -// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/705891 -TEST_F(CIsolatedComponentTest, DISABLED_LoadIsolatedComplexService) +TEST_F(CIsolatedComponentTest, LoadIsolatedComplexService) { // Load the hello basic service IHello* pHello = sdv::core::GetObject("TestObject_ComplexHelloService"); diff --git a/tests/component_tests/repository/repository_test.cpp b/tests/component_tests/repository/repository_test.cpp index febbce0..3781e42 100644 --- a/tests/component_tests/repository/repository_test.cpp +++ b/tests/component_tests/repository/repository_test.cpp @@ -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 #include #include @@ -29,38 +42,43 @@ public: SDV_INTERFACE_ENTRY(sdv::IObjectControl) END_SDV_INTERFACE_MAP() - virtual void Initialize([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual void Initialize([[maybe_unused]] const sdv::u8string& ssObjectConfig) override { FAIL() << "Error: Initialize should not be called by Repo Service!"; -// m_eObjectStatus = sdv::EObjectStatus::initialization_failure; +// m_eObjectState = sdv::EObjectState::initialization_failure; } - virtual sdv::EObjectStatus GetStatus() const + virtual sdv::EObjectState GetObjectState() const override { - return m_eObjectStatus; + return m_eObjectState; } - void SetOperationMode(sdv::EOperationMode eMode) + virtual void SetOperationMode(sdv::EOperationMode eMode) override { switch (eMode) { case sdv::EOperationMode::configuring: - m_eObjectStatus = sdv::EObjectStatus::configuring; + m_eObjectState = sdv::EObjectState::configuring; break; case sdv::EOperationMode::running: - m_eObjectStatus = sdv::EObjectStatus::running; + m_eObjectState = sdv::EObjectState::running; break; default: break; } } - virtual void Shutdown() + virtual sdv::u8string GetObjectConfig() const override { - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; + return {}; } - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; + virtual void Shutdown() override + { + m_eObjectState = sdv::EObjectState::destruction_pending; + } + + sdv::EObjectState m_eObjectState = sdv::EObjectState::initialization_pending; }; TEST(RepositoryTest, LoadNonexistentModule) @@ -366,7 +384,7 @@ Mode = "Essential" ASSERT_TRUE(pModuleControl->Load((GetExecDirectory() / "ComponentTest_Repository_test_module.sdv").generic_u8string())); - bool bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", nullptr, "ChainedObject"); + bool bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", nullptr, "chained_object = \"ChainedObject\""); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, pObjectAccess->GetObject("TestObject_CreateChain")); EXPECT_NE(nullptr, pObjectAccess->GetObject("ChainedObject")); @@ -404,7 +422,8 @@ Mode = "Essential" for (uint32_t i = 0; i < LoopCount; ++i) { std::string count = std::to_string(i); - bool bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", "Bar_" + count, "BarFoo_" + count); + bool bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", "Bar_" + count, + "chained_object = \"BarFoo_" + count + "\""); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, pObjectAccess->GetObject("BarFoo_" + count)); } @@ -413,7 +432,8 @@ Mode = "Essential" for (uint32_t i = 0; i < LoopCount; ++i) { std::string count = std::to_string(i); - bool bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", "Foo_" + count, "FooBar_" + count); + bool bRes = + pRepositoryControl->CreateObject("TestObject_CreateChain", "Foo_" + count, "chained_object =\"FooBar_" + count + "\""); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, pObjectAccess->GetObject("FooBar_" + count)); } @@ -460,7 +480,8 @@ Mode = "Essential" { std::string count = std::to_string(i); //locks using TestLockService during construction - bRes = pRepositoryControl->CreateObject("TestObject_CreateChainLock", "Bar_" + count, "BarFoo_" + count); + bRes = pRepositoryControl->CreateObject("TestObject_CreateChainLock", "Bar_" + count, + "chained_object = \"BarFoo_" + count + "\""); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, pObjectAccess->GetObject("BarFoo_" + count)); } @@ -470,7 +491,8 @@ Mode = "Essential" { std::string count = std::to_string(i); pLock->Lock(); - bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", "Foo_" + count, "FooBar_" + count); + bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", "Foo_" + count, + "chained_object = \"FooBar_" + count + "\""); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, pObjectAccess->GetObject("FooBar_" + count)); pLock->Unlock(); @@ -518,7 +540,8 @@ Mode = "Essential" { std::string count = std::to_string(i); //locks using TestLockService during construction in a seperate thread - bRes = pRepositoryControl->CreateObject("TestObject_CreateChainLockThread", "Bar_" + count, "BarFoo_" + count); + bRes = pRepositoryControl->CreateObject("TestObject_CreateChainLockThread", "Bar_" + count, + "chained_object = \"BarFoo_" + count + "\""); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, pObjectAccess->GetObject("BarFoo_" + count)); } @@ -528,7 +551,8 @@ Mode = "Essential" { std::string count = std::to_string(i); pLock->Lock(); - bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", "Foo_" + count, "FooBar_" + count); + bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", "Foo_" + count, + "chained_object = \"FooBar_" + count + "\""); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, pObjectAccess->GetObject("FooBar_" + count)); pLock->Unlock(); @@ -593,9 +617,7 @@ Mode = "Essential" control.Shutdown(); } -// TODO EVE: Disabled until the implementation of configuration installation is finished. See -// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/705891 -TEST(RepositoryTest, DISABLED_MainApplication_GetInstalledAndLoadedComponent) +TEST(RepositoryTest, MainApplication_GetInstalledAndLoadedComponent) { // Start the app control. The application automatically loads the installation manifests and the application configuration, // starting automatically the objects. @@ -618,9 +640,7 @@ Instance = 2005 control.Shutdown(); } -// TODO EVE: Disabled until the implementation of configuration installation is finished. See -// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/705891 -TEST(RepositoryTest, DISABLED_MainApplication_GetInstalledComponent) +TEST(RepositoryTest, MainApplication_GetInstalledComponent) { // Start the app control. The application automatically loads the installation manifests, but since there is no application // configuration, it doesn't load the objects. diff --git a/tests/component_tests/repository/test_component.cpp b/tests/component_tests/repository/test_component.cpp index 3640531..414c9df 100644 --- a/tests/component_tests/repository/test_component.cpp +++ b/tests/component_tests/repository/test_component.cpp @@ -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 #include #include @@ -27,7 +40,7 @@ public: SDV_INTERFACE_ENTRY(ITestLock) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("TestLockService") void Lock() override @@ -49,88 +62,26 @@ DEFINE_SDV_OBJECT(CTestLockService) /** * @brief Example component testing IObjectControl */ - class CTestObjectControl - : public sdv::CSdvObject - , public sdv::IObjectControl +class CTestObjectControl : public sdv::CSdvObject { public: - - ~CTestObjectControl() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("Example_Object") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; + virtual void OnShutdown() override + {} }; DEFINE_SDV_OBJECT(CTestObjectControl) @@ -138,87 +89,26 @@ DEFINE_SDV_OBJECT(CTestObjectControl) /** * @brief Example component testing IObjectControl - 2nd component to test duplicate instantiation */ -class CTestObjectControl2 - : public sdv::CSdvObject - , public sdv::IObjectControl +class CTestObjectControl2 : public sdv::CSdvObject { public: - ~CTestObjectControl2() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("Example_Object_2") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @param[in] ssObjectConfig Optional configuration string. - */ - virtual void Initialize([[maybe_unused]] const sdv::u8string& ssObjectConfig) - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; - } - - /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object - */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) + virtual bool OnInitialize() override { - 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; - } + return true; } /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. + */ + virtual void OnShutdown() override + {} }; DEFINE_SDV_OBJECT(CTestObjectControl2) @@ -226,70 +116,26 @@ DEFINE_SDV_OBJECT(CTestObjectControl2) /** * @brief Example component testing IObjectControl */ - class CTestObjectControlFail - : public sdv::CSdvObject - , public sdv::IObjectControl +class CTestObjectControlFail : public sdv::CSdvObject { public: - - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("TestObject_IObjectControlFail") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - m_eObjectStatus = sdv::EObjectStatus::initialization_failure; + return false; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() + virtual void OnShutdown() override {} - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; }; DEFINE_SDV_OBJECT(CTestObjectControlFail) @@ -297,95 +143,45 @@ DEFINE_SDV_OBJECT(CTestObjectControlFail) /** * @brief Example component testing chained object creation in Initialize */ - class CTestObjectCreate - : public sdv::CSdvObject - , public sdv::IObjectControl +class CTestObjectCreate : public sdv::CSdvObject { public: - - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("TestObject_CreateChain") + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_ssChainedObject, "chained_object", "", "", "Name of the chained object") + END_SDV_PARAM_MAP() + /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - //create new example object with name ssObjectConfig - auto pRepo = sdv::core::GetObject("RepositoryService"); - ASSERT_TRUE(pRepo); + if (!pRepo) return false; auto pCreate = sdv::TInterfaceAccessPtr(pRepo).GetInterface(); - ASSERT_TRUE(pCreate); + if (!pCreate) return false; - ASSERT_FALSE(ssObjectConfig.empty()); + if (m_ssChainedObject.empty()) + return false; - ASSERT_TRUE(pCreate->CreateObject("Example_Object", ssObjectConfig,nullptr)); + if (!pCreate->CreateObject("Example_Object", m_ssChainedObject, nullptr)) + return false; - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } + virtual void OnShutdown() 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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; + std::string m_ssChainedObject; ///< Name of the chained object }; DEFINE_SDV_OBJECT(CTestObjectCreate) @@ -393,31 +189,26 @@ DEFINE_SDV_OBJECT(CTestObjectCreate) /** * @brief Example component testing chained object creation in Initialize while holding a lock */ - class CTestObjectCreateLock - : public sdv::CSdvObject - , public sdv::IObjectControl +class CTestObjectCreateLock: public sdv::CSdvObject { public: - - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("TestObject_CreateChainLock") - /** - * @brief Initialize the object. Overload of sdv::IObjectControl::Initialize. - * @param[in] ssObjectConfig Optional configuration string. - */ - virtual void Initialize(const sdv::u8string& ssObjectConfig) - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_ssChainedObject, "chained_object", "", "", "Name of the chained object") + END_SDV_PARAM_MAP() + /** + * @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 bool OnInitialize() override + { //create new example object with name ssObjectConfig while holding lock auto pLock = sdv::core::GetObject("TestLockService"); - ASSERT_NE(pLock,nullptr); + if (!pLock) return false; struct SAutoLock { @@ -427,67 +218,25 @@ public: } sAutoLock(pLock); auto pRepo = sdv::core::GetObject("RepositoryService"); - ASSERT_TRUE(pRepo); + if (!pRepo) return false; auto pCreate = sdv::TInterfaceAccessPtr(pRepo).GetInterface(); - ASSERT_TRUE(pCreate); + if (!pCreate) return false; - ASSERT_FALSE(ssObjectConfig.empty()); + if (m_ssChainedObject.empty()) return false; - ASSERT_TRUE(pCreate->CreateObject("Example_Object", ssObjectConfig, nullptr)); + if (!pCreate->CreateObject("Example_Object", m_ssChainedObject, nullptr)) return false; - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @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 - { - return m_eObjectStatus; - } + virtual void OnShutdown() 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) - { - 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; - } - } - - /** - * @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown. - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; + std::string m_ssChainedObject; ///< Name of the chained object }; DEFINE_SDV_OBJECT(CTestObjectCreateLock) @@ -495,33 +244,33 @@ DEFINE_SDV_OBJECT(CTestObjectCreateLock) /** * @brief Example component testing chained object creation in Initialize in extra thread holding a lock */ - class CTestObjectCreateLockThread - : public sdv::CSdvObject - , public sdv::IObjectControl +class CTestObjectCreateLockThread : public sdv::CSdvObject { public: - - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("TestObject_CreateChainLockThread") - /** - * @brief Initialize the object. Overload of sdv::IObjectControl::Initialize. - * @param[in] ssObjectConfig Optional configuration string. - */ - virtual void Initialize(const sdv::u8string& ssObjectConfig) - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_ssChainedObject, "chained_object", "", "", "Name of the chained object") + END_SDV_PARAM_MAP() - auto fun = [ssObjectConfig]() + /** + * @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 bool OnInitialize() override + { + bool bResult = true; + auto fun = [this, &bResult]() { //create new example object with name ssObjectConfig while holding lock auto pLock = sdv::core::GetObject("TestLockService"); - ASSERT_NE(pLock, nullptr); + if (!pLock) + { + bResult = false; + return; + } struct SAutoLock { @@ -531,72 +280,46 @@ public: } sAutoLock(pLock); auto pRepo = sdv::core::GetObject("RepositoryService"); - ASSERT_TRUE(pRepo); + if (!pRepo) + { + bResult = false; + return; + } auto pCreate = sdv::TInterfaceAccessPtr(pRepo).GetInterface(); - ASSERT_TRUE(pCreate); + if (!pCreate) + { + bResult = false; + return; + } - ASSERT_FALSE(ssObjectConfig.empty()); + if (m_ssChainedObject.empty()) + { + bResult = false; + return; + } - ASSERT_TRUE(pCreate->CreateObject("Example_Object", ssObjectConfig, nullptr)); + if (!pCreate->CreateObject("Example_Object", m_ssChainedObject, nullptr)) + { + bResult = false; + return; + } }; std::thread testThread(fun); testThread.join(); - m_eObjectStatus = sdv::EObjectStatus::initialized; + return bResult; } /** - * @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 - { - return m_eObjectStatus; - } + virtual void OnShutdown() 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) - { - 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; - } - } - - /** - * @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown. - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; + std::string m_ssChainedObject; ///< Name of the chained object }; DEFINE_SDV_OBJECT(CTestObjectCreateLockThread) @@ -605,78 +328,30 @@ DEFINE_SDV_OBJECT(CTestObjectCreateLockThread) /** * @brief Example component testing IObjectControl */ - class CTestObjectCreateDuringShutdown - : public sdv::CSdvObject - , public sdv::IObjectControl +class CTestObjectCreateDuringShutdown : public sdv::CSdvObject { public: - - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("TestObject_CreateDuringShutdown") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object - */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. + * @brief Shutdown method called before the object is destroyed. Overload of sdv::CSdvObject::OnShutdown. * @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() + virtual void OnShutdown() override { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - //create new example object //this is allowed during shutdown as it's needed in case new proxies are created @@ -687,12 +362,7 @@ public: ASSERT_TRUE(pCreate); ASSERT_FALSE(pCreate->CreateObject("Example_Object", nullptr, nullptr)); - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; } - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; }; DEFINE_SDV_OBJECT(CTestObjectCreateDuringShutdown) @@ -707,7 +377,7 @@ public: SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("TestObject_HelloDevice") /** @@ -740,7 +410,7 @@ public: SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::BasicService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::basic_service) DECLARE_OBJECT_CLASS_NAME("TestObject_BasicHelloService") /** @@ -781,7 +451,7 @@ public: SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::complex_service) DECLARE_OBJECT_CLASS_NAME("TestObject_ComplexHelloService") /** @@ -814,7 +484,7 @@ public: SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Utility) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::utility) DECLARE_OBJECT_CLASS_NAME("TestObject_HelloUtility") /** @@ -847,7 +517,7 @@ public: SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) DECLARE_OBJECT_CLASS_NAME("TestObject_SystemHelloService") /** diff --git a/tests/component_tests/repository/test_component.idl b/tests/component_tests/repository/test_component.idl index 22164c6..ae55d15 100644 --- a/tests/component_tests/repository/test_component.idl +++ b/tests/component_tests/repository/test_component.idl @@ -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 #include diff --git a/tests/component_tests/repository/test_component_ps.cpp b/tests/component_tests/repository/test_component_ps.cpp index eec8f5a..11b64ac 100644 --- a/tests/component_tests/repository/test_component_ps.cpp +++ b/tests/component_tests/repository/test_component_ps.cpp @@ -1 +1,14 @@ +/******************************************************************************** + * 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 "generated/ps/proxystub.cpp" \ No newline at end of file diff --git a/tests/component_tests/task_timer/CMakeLists.txt b/tests/component_tests/task_timer/CMakeLists.txt index 2fdd550..0bc1f49 100644 --- a/tests/component_tests/task_timer/CMakeLists.txt +++ b/tests/component_tests/task_timer/CMakeLists.txt @@ -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 (multiple EXEs) project(TaskTimerTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/component_tests/task_timer/task_simulation_timer_test.cpp b/tests/component_tests/task_timer/task_simulation_timer_test.cpp index 616b010..60b8462 100644 --- a/tests/component_tests/task_timer/task_simulation_timer_test.cpp +++ b/tests/component_tests/task_timer/task_simulation_timer_test.cpp @@ -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 "gtest/gtest.h" #include #include @@ -66,13 +79,13 @@ TEST(TaskSimulationTimerTest, BasicCounterTestInterface) CTestTask task; - sdv::core::CTaskTimer timer(3, &task); + sdv::core::CTaskTimer timer(3, &task, true); EXPECT_TRUE(timer); appcontrol.SetRunningMode(); // Get the simulation task timer service. sdv::core::ITimerSimulationStep* pTimerSimulationStep = sdv::core::GetObject("SimulationTaskTimerService"); - EXPECT_TRUE(pTimerSimulationStep); + ASSERT_TRUE(pTimerSimulationStep); uint32_t expected = 5; uint32_t expectedAdditional = 11; @@ -122,9 +135,9 @@ TEST(TaskSimulationTimerTest, LargerTaskPeriodTest) CTestTask task1150; CTestTask task3100; - sdv::core::CTaskTimer timer150(150, &task150); - sdv::core::CTaskTimer timer1150(1150, &task1150); - sdv::core::CTaskTimer timer3100(3100, &task3100); + sdv::core::CTaskTimer timer150(150, &task150, true); + sdv::core::CTaskTimer timer1150(1150, &task1150, true); + sdv::core::CTaskTimer timer3100(3100, &task3100, true); EXPECT_TRUE(timer150); EXPECT_TRUE(timer1150); EXPECT_TRUE(timer3100); @@ -132,7 +145,7 @@ TEST(TaskSimulationTimerTest, LargerTaskPeriodTest) // Get the simulation task timer service. sdv::core::ITimerSimulationStep* pTimerSimulationStep = sdv::core::GetObject("SimulationTaskTimerService"); - EXPECT_TRUE(pTimerSimulationStep); + ASSERT_TRUE(pTimerSimulationStep); uint32_t expected150 = 33; uint32_t expected1150 = 4; @@ -179,9 +192,9 @@ TEST(TaskSimulationTimerTest, MultipleTimerIdenticalTaskPeriodTest) CTestTask task2; CTestTask task3; - sdv::core::CTaskTimer timer1(3, &task1); - sdv::core::CTaskTimer timer2(3, &task2); - sdv::core::CTaskTimer timer3(3, &task3); + sdv::core::CTaskTimer timer1(3, &task1, true); + sdv::core::CTaskTimer timer2(3, &task2, true); + sdv::core::CTaskTimer timer3(3, &task3, true); EXPECT_TRUE(timer1); EXPECT_TRUE(timer2); EXPECT_TRUE(timer3); @@ -189,7 +202,7 @@ TEST(TaskSimulationTimerTest, MultipleTimerIdenticalTaskPeriodTest) // Get the simulation task timer service. sdv::core::ITimerSimulationStep* pTimerSimulationStep = sdv::core::GetObject("SimulationTaskTimerService"); - EXPECT_TRUE(pTimerSimulationStep); + ASSERT_TRUE(pTimerSimulationStep); uint32_t expected = 5; uint32_t expectedAdditional = 11; @@ -248,9 +261,9 @@ TEST(TaskSimulationTimerTest, MultipleTimerDifferentTaskPeriodTest) CTestTask task3; CTestTask task4; - sdv::core::CTaskTimer timer2(2, &task2); - sdv::core::CTaskTimer timer3(3, &task3); - sdv::core::CTaskTimer timer4(4, &task4); + sdv::core::CTaskTimer timer2(2, &task2, true); + sdv::core::CTaskTimer timer3(3, &task3, true); + sdv::core::CTaskTimer timer4(4, &task4, true); EXPECT_TRUE(timer2); EXPECT_TRUE(timer3); EXPECT_TRUE(timer4); @@ -258,7 +271,7 @@ TEST(TaskSimulationTimerTest, MultipleTimerDifferentTaskPeriodTest) // Get the simulation task timer service. sdv::core::ITimerSimulationStep* pTimerSimulationStep = sdv::core::GetObject("SimulationTaskTimerService"); - EXPECT_TRUE(pTimerSimulationStep); + ASSERT_TRUE(pTimerSimulationStep); uint32_t expected2 = 7; uint32_t expected3 = 5; @@ -322,9 +335,9 @@ TEST(TaskSimulationTimerTest, MultipleTimerLargeSimulationStepTest) CTestTask task150; CTestTask task450; - sdv::core::CTaskTimer timer1(1, &task1); - sdv::core::CTaskTimer timer150(150, &task150); - sdv::core::CTaskTimer timer450(450, &task450); + sdv::core::CTaskTimer timer1(1, &task1, true); + sdv::core::CTaskTimer timer150(150, &task150, true); + sdv::core::CTaskTimer timer450(450, &task450, true); EXPECT_TRUE(timer1); EXPECT_TRUE(timer150); EXPECT_TRUE(timer450); @@ -332,7 +345,7 @@ TEST(TaskSimulationTimerTest, MultipleTimerLargeSimulationStepTest) // Get the simulation task timer service. sdv::core::ITimerSimulationStep* pTimerSimulationStep = sdv::core::GetObject("SimulationTaskTimerService"); - EXPECT_TRUE(pTimerSimulationStep); + ASSERT_TRUE(pTimerSimulationStep); uint32_t expected1 = 480; uint32_t expected150 = 3; @@ -369,9 +382,9 @@ TEST(TaskSimulationTimerTest, MultipleTimerLargeAndDifferentSimulationStepTest) CTestTask task150; CTestTask task450; - sdv::core::CTaskTimer timer1(1, &task1); - sdv::core::CTaskTimer timer150(150, &task150); - sdv::core::CTaskTimer timer450(450, &task450); + sdv::core::CTaskTimer timer1(1, &task1, true); + sdv::core::CTaskTimer timer150(150, &task150, true); + sdv::core::CTaskTimer timer450(450, &task450, true); EXPECT_TRUE(timer1); EXPECT_TRUE(timer150); EXPECT_TRUE(timer450); @@ -379,7 +392,7 @@ TEST(TaskSimulationTimerTest, MultipleTimerLargeAndDifferentSimulationStepTest) // Get the simulation task timer service. sdv::core::ITimerSimulationStep* pTimerSimulationStep = sdv::core::GetObject("SimulationTaskTimerService"); - EXPECT_TRUE(pTimerSimulationStep); + ASSERT_TRUE(pTimerSimulationStep); uint32_t expected1 = 480; uint32_t expected150 = 3; diff --git a/tests/component_tests/task_timer/task_timer_test.cpp b/tests/component_tests/task_timer/task_timer_test.cpp index 07d8a9e..7ddce7e 100644 --- a/tests/component_tests/task_timer/task_timer_test.cpp +++ b/tests/component_tests/task_timer/task_timer_test.cpp @@ -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 "gtest/gtest.h" #include #include diff --git a/tests/component_tests/toml_parser/CMakeLists.txt b/tests/component_tests/toml_parser/CMakeLists.txt new file mode 100644 index 0000000..5631d72 --- /dev/null +++ b/tests/component_tests/toml_parser/CMakeLists.txt @@ -0,0 +1,32 @@ +#******************************************************************************* +# 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(ConfigTests VERSION 1.0 LANGUAGES CXX) + +# Define target +add_executable(ComponentTest_TOMLParser toml_parser_tests.cpp) + +target_link_libraries(ComponentTest_TOMLParser ${CMAKE_DL_LIBS} GTest::GTest) + +# Add the test +add_test(NAME ComponentTest_TOMLParser COMMAND ComponentTest_TOMLParser WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + +# Execute the test +add_custom_command(TARGET ComponentTest_TOMLParser POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ComponentTest_TOMLParser.xml + VERBATIM +) + +# Build dependencies +add_dependencies(ComponentTest_TOMLParser dependency_sdv_components) diff --git a/tests/component_tests/toml_parser/toml_parser_tests.cpp b/tests/component_tests/toml_parser/toml_parser_tests.cpp new file mode 100644 index 0000000..8573d14 --- /dev/null +++ b/tests/component_tests/toml_parser/toml_parser_tests.cpp @@ -0,0 +1,980 @@ +/******************************************************************************** + * 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 +#include +#include +#include +#include "../../../global/process_watchdog.h" + +// TODO: parser functions for updating/writing nodes and combining/subtracting + +#ifdef _MSC_VER +#pragma warning(disable : 4566) +#endif + +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +TEST(TOMLParser, Instantiate) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config; + EXPECT_TRUE(config.Process("")); + + config.Clear(); + appcontrol.Shutdown(); +} + +TEST(RecognizeTypes, Table) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config(R"( + [newTable] + [secondTable.nestedTable] + )"); + EXPECT_TRUE(config.IsValid()); + + auto table1 = config.GetDirect("newTable"); + EXPECT_TRUE(table1); + EXPECT_EQ(table1.GetType(), sdv::toml::ENodeType::node_table); + EXPECT_EQ(table1.GetName(), "newTable"); + EXPECT_EQ(table1.GetValue(), sdv::any_t()); + sdv::toml::CNodeCollection collection = table1; + EXPECT_TRUE(collection); + + sdv::toml::CNodeCollection table2 = config.GetDirect("secondTable"); + EXPECT_TRUE(table2); + EXPECT_EQ(table2.GetType(), sdv::toml::ENodeType::node_table); + EXPECT_EQ(table2.GetName(), "secondTable"); + EXPECT_EQ(table2.GetValue(), sdv::any_t()); + + sdv::toml::CNodeCollection table3 = config.GetDirect("secondTable.nestedTable"); + EXPECT_TRUE(table3); + EXPECT_EQ(table3.GetType(), sdv::toml::ENodeType::node_table); + EXPECT_EQ(table3.GetName(), "nestedTable"); + EXPECT_EQ(table3.GetValue(), sdv::any_t()); + + config.Clear(); + appcontrol.Shutdown(); +} + +TEST(RecognizeTypes, Key_Value) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config(R"( + name = "Hammer" + id = 42 + pi = 3.1415926 + boolean = true + array = [] + table = {} + )"); + + + auto value_name = config.GetDirect("name"); + EXPECT_EQ(value_name.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(value_name.GetValue(), "Hammer"); + + auto value_id = config.GetDirect("id"); + EXPECT_EQ(value_id.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(value_id.GetValue(), 42); + + auto value_pi = config.GetDirect("pi"); + EXPECT_EQ(value_pi.GetType(), sdv::toml::ENodeType::node_floating_point); + EXPECT_EQ(value_pi.GetValue(), 3.1415926); + + auto value_boolean = config.GetDirect("boolean"); + EXPECT_EQ(value_boolean.GetType(), sdv::toml::ENodeType::node_boolean); + EXPECT_EQ(value_boolean.GetValue(), true); + + auto value_array = config.GetDirect("array"); + EXPECT_EQ(value_array.GetType(), sdv::toml::ENodeType::node_array); + EXPECT_EQ(value_array.GetValue(), sdv::any_t()); + + auto value_table = config.GetDirect("table"); + EXPECT_EQ(value_table.GetType(), sdv::toml::ENodeType::node_table); + EXPECT_EQ(value_table.GetValue(), sdv::any_t()); + + config.Clear(); + appcontrol.Shutdown(); +} + +TEST(RecognizeTypes, TableArray) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config(R"( + [[newTableArray]] + [[newTableArray]] + [[table.nestedTableArray]] + )"); + + sdv::toml::CNodeCollection tableArray1 = config.GetDirect("newTableArray"); + EXPECT_EQ(tableArray1.GetType(), sdv::toml::ENodeType::node_array); + EXPECT_TRUE(tableArray1); + EXPECT_EQ(tableArray1.GetName(), "newTableArray"); + EXPECT_EQ(tableArray1.GetCount(), 2u); + auto tableNode0 = tableArray1[0]; + EXPECT_TRUE(tableNode0); + auto tableNode0b = tableArray1.Get(0); + EXPECT_TRUE(tableNode0b); + auto tableNode1 = tableArray1[1]; + EXPECT_TRUE(tableNode1); + auto tableNode1b = tableArray1.Get(1); + EXPECT_TRUE(tableNode1b); + EXPECT_FALSE(tableArray1[2]); + EXPECT_FALSE(tableArray1.Get(2)); + + auto table1 = config.GetDirect("newTableArray[0]"); + EXPECT_EQ(table1.GetType(), sdv::toml::ENodeType::node_table); + EXPECT_EQ(table1.GetName(), "newTableArray"); + + auto table2 = config.GetDirect("newTableArray[1]"); + EXPECT_EQ(table2.GetType(), sdv::toml::ENodeType::node_table); + EXPECT_EQ(table2.GetName(), "newTableArray"); + + config.Clear(); + appcontrol.Shutdown(); +} + +TEST(NestedContent, Array) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config(R"( + arr_mixed = [ 1.0, 2, "test string", [ 1, 2, ], { pi = 3.14, e = 2.71828 }, true] + arr_ints = [ 1, 2, 3, 4] + arr_ints_trailing_comma = [ 1, 2, 3, 4, ] + arr_multiline = [ + "first line", + "second line", + "third_line", + ] + )"); + + { + + sdv::toml::CNodeCollection array_ints = config.GetDirect("arr_ints"); + EXPECT_EQ(array_ints.GetType(), sdv::toml::ENodeType::node_array); + ASSERT_TRUE(array_ints); + EXPECT_EQ(array_ints.GetCount(), 4u); + EXPECT_TRUE(array_ints[0]); + EXPECT_TRUE(array_ints[1]); + EXPECT_TRUE(array_ints[2]); + EXPECT_TRUE(array_ints[3]); + EXPECT_FALSE(array_ints[4]); + auto array_ints_0 = config.GetDirect("arr_ints[0]"); + ASSERT_TRUE(array_ints_0); + EXPECT_EQ(array_ints_0.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_ints_0.GetValue(), 1); + auto array_ints_1 = config.GetDirect("arr_ints[1]"); + ASSERT_TRUE(array_ints_1); + EXPECT_EQ(array_ints_1.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_ints_1.GetValue(), 2); + auto array_ints_2 = config.GetDirect("arr_ints[2]"); + ASSERT_TRUE(array_ints_2); + EXPECT_EQ(array_ints_2.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_ints_2.GetValue(), 3); + auto array_ints_3 = config.GetDirect("arr_ints[3]"); + ASSERT_TRUE(array_ints_3); + EXPECT_EQ(array_ints_3.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_ints_3.GetValue(), 4); + auto array_ints_4 = config.GetDirect("arr_ints[4]"); + EXPECT_FALSE(array_ints_4); + } + + { + auto array_ints_trailing_comma = config.GetDirect("arr_ints_trailing_comma"); + auto array_ints_trailing_comma_0 = config.GetDirect("arr_ints_trailing_comma[0]"); + auto array_ints_trailing_comma_1 = config.GetDirect("arr_ints_trailing_comma[1]"); + auto array_ints_trailing_comma_2 = config.GetDirect("arr_ints_trailing_comma[2]"); + auto array_ints_trailing_comma_3 = config.GetDirect("arr_ints_trailing_comma[3]"); + auto array_ints_trailing_comma_4 = config.GetDirect("arr_ints_trailing_comma[4]"); + + EXPECT_EQ(array_ints_trailing_comma.GetType(), sdv::toml::ENodeType::node_array); + ASSERT_TRUE(array_ints_trailing_comma_0); + EXPECT_EQ(array_ints_trailing_comma_0.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_ints_trailing_comma_0.GetValue(), 1); + ASSERT_TRUE(array_ints_trailing_comma_1); + EXPECT_EQ(array_ints_trailing_comma_1.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_ints_trailing_comma_1.GetValue(), 2); + ASSERT_TRUE(array_ints_trailing_comma_2); + EXPECT_EQ(array_ints_trailing_comma_2.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_ints_trailing_comma_2.GetValue(), 3); + ASSERT_TRUE(array_ints_trailing_comma_3); + EXPECT_EQ(array_ints_trailing_comma_3.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_ints_trailing_comma_3.GetValue(), 4); + EXPECT_FALSE(array_ints_trailing_comma_4); + } + + { + auto array_mixed = config.GetDirect("arr_mixed"); + auto array_mixed_0 = config.GetDirect("arr_mixed[0]"); + auto array_mixed_1 = config.GetDirect("arr_mixed[1]"); + auto array_mixed_2 = config.GetDirect("arr_mixed[2]"); + auto array_mixed_3 = config.GetDirect("arr_mixed[3]"); + auto array_mixed_3_2 = config.GetDirect("arr_mixed[3][1]"); + auto array_mixed_4 = config.GetDirect("arr_mixed[4]"); + auto array_mixed_4_pi = config.GetDirect("arr_mixed[4].pi"); + auto array_mixed_5 = config.GetDirect("arr_mixed[5]"); + auto array_mixed_6 = config.GetDirect("arr_mixed[6]"); + + EXPECT_EQ(array_mixed.GetType(), sdv::toml::ENodeType::node_array); + ASSERT_TRUE(array_mixed_0); + EXPECT_EQ(array_mixed_0.GetType(), sdv::toml::ENodeType::node_floating_point); + EXPECT_EQ(array_mixed_0.GetValue(), 1.0); + ASSERT_TRUE(array_mixed_1); + EXPECT_EQ(array_mixed_1.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_mixed_1.GetValue(), 2); + ASSERT_TRUE(array_mixed_2); + EXPECT_EQ(array_mixed_2.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(array_mixed_2.GetValue()), "test string"); + ASSERT_TRUE(array_mixed_3); + EXPECT_EQ(array_mixed_3.GetType(), sdv::toml::ENodeType::node_array); + EXPECT_EQ(array_mixed_3_2.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(array_mixed_3_2.GetValue(), 2); + ASSERT_TRUE(array_mixed_4); + EXPECT_EQ(array_mixed_4.GetType(), sdv::toml::ENodeType::node_table); + EXPECT_EQ(array_mixed_4_pi.GetType(), sdv::toml::ENodeType::node_floating_point); + EXPECT_EQ(array_mixed_4_pi.GetValue(), 3.14); + ASSERT_TRUE(array_mixed_5); + EXPECT_EQ(array_mixed_5.GetType(), sdv::toml::ENodeType::node_boolean); + EXPECT_EQ(array_mixed_5.GetValue(), true); + EXPECT_EQ(array_mixed_5.GetValue(), sdv::any_t()); + EXPECT_FALSE(array_mixed_6); + } + + { + auto array_multiline = config.GetDirect("arr_multiline"); + auto array_multiline_0 = config.GetDirect("arr_multiline[0]"); + auto array_multiline_1 = config.GetDirect("arr_multiline[1]"); + auto array_multiline_2 = config.GetDirect("arr_multiline[2]"); + auto array_multiline_3 = config.GetDirect("arr_multiline[3]"); + + EXPECT_EQ(array_multiline.GetType(), sdv::toml::ENodeType::node_array); + ASSERT_TRUE(array_multiline_0); + EXPECT_EQ(array_multiline_0.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(array_multiline_0.GetValue()), "first line"); + ASSERT_TRUE(array_multiline_1); + EXPECT_EQ(array_multiline_1.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(array_multiline_1.GetValue()), "second line"); + ASSERT_TRUE(array_multiline_2); + EXPECT_EQ(array_multiline_2.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(array_multiline_2.GetValue()), "third_line"); + EXPECT_FALSE(array_multiline_3); + } + + config.Clear(); + appcontrol.Shutdown(); +} + +TEST(NestedContent, Table) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config(R"( + [table] + a = 2 + b = 1.2 + [anotherTable] + a = 4 + c = false + [thirdTable.fourthTable] + a = "five" + d = [] + )"); + + auto table_a = config.GetDirect("table.a"); + auto table_b = config.GetDirect("table.b"); + auto anotherTable_a = config.GetDirect("anotherTable.a"); + auto anotherTable_c = config.GetDirect("anotherTable.c"); + auto fourthTable_a = config.GetDirect("thirdTable.fourthTable.a"); + auto fourthTable_d = config.GetDirect("thirdTable.fourthTable.d"); + + ASSERT_TRUE(table_a); + EXPECT_EQ(table_a.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(table_a.GetValue(), 2); + ASSERT_TRUE(table_b); + EXPECT_EQ(table_b.GetType(), sdv::toml::ENodeType::node_floating_point); + EXPECT_EQ(table_b.GetValue(), 1.2); + ASSERT_TRUE(anotherTable_a); + EXPECT_EQ(anotherTable_a.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(anotherTable_a.GetValue(), 4); + ASSERT_TRUE(anotherTable_c); + EXPECT_EQ(anotherTable_c.GetType(), sdv::toml::ENodeType::node_boolean); + EXPECT_EQ(anotherTable_c.GetValue(), false); + ASSERT_TRUE(fourthTable_a); + EXPECT_EQ(fourthTable_a.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(fourthTable_a.GetValue()), "five"); + ASSERT_TRUE(fourthTable_d); + EXPECT_EQ(fourthTable_d.GetType(), sdv::toml::ENodeType::node_array); + + config.Clear(); + appcontrol.Shutdown(); +} + +TEST(NestedContent, TableArray) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config(R"( + [[table.test]] + a = 2 + b = 1.2 + [[table.test]] + a = 4 + c = false + [[table.test]] + a = "five" + d = [] + )"); + + auto table_test_1_a = config.GetDirect("table.test[0].a"); + auto table_test_1_b = config.GetDirect("table.test[0].b"); + auto table_test_2_a = config.GetDirect("table.test[1].a"); + auto table_test_2_c = config.GetDirect("table.test[1].c"); + auto table_test_3_a = config.GetDirect("table.test[2].a"); + auto table_test_3_d = config.GetDirect("table.test[2].d"); + + ASSERT_TRUE(table_test_1_a); + EXPECT_EQ(table_test_1_a.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(table_test_1_a.GetValue(), 2); + ASSERT_TRUE(table_test_1_b); + EXPECT_EQ(table_test_1_b.GetType(), sdv::toml::ENodeType::node_floating_point); + EXPECT_EQ(table_test_1_b.GetValue(), 1.2); + ASSERT_TRUE(table_test_2_a); + EXPECT_EQ(table_test_2_a.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(table_test_2_a.GetValue(), 4); + ASSERT_TRUE(table_test_2_c); + EXPECT_EQ(table_test_2_c.GetType(), sdv::toml::ENodeType::node_boolean); + EXPECT_EQ(table_test_2_c.GetValue(), false); + ASSERT_TRUE(table_test_3_a); + EXPECT_EQ(table_test_3_a.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(table_test_3_a.GetValue()), "five"); + ASSERT_TRUE(table_test_3_d); + EXPECT_EQ(table_test_3_d.GetType(), sdv::toml::ENodeType::node_array); + + config.Clear(); + appcontrol.Shutdown(); +} + +TEST(NestedContent, InlineTable) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config(R"( + table1 = { a = 0, b = 1.2, c = "string" } + table2 = { a = [], b = true, e = 2.71828 } + table3 = { a = { a = "a", b = "A" }, b = {a = "b", b = "B"}, e = {a = "e", b = "E"} } + )"); + + auto table1_a = config.GetDirect("table1.a"); + auto table1_b = config.GetDirect("table1.b"); + auto table1_c = config.GetDirect("table1.c"); + auto table2_a = config.GetDirect("table2.a"); + auto table2_b = config.GetDirect("table2.b"); + auto table2_e = config.GetDirect("table2.e"); + auto table3_a_a = config.GetDirect("table3.a.a"); + auto table3_a_b = config.GetDirect("table3.a.b"); + auto table3_b_a = config.GetDirect("table3.b.a"); + auto table3_b_b = config.GetDirect("table3.b.b"); + auto table3_e_a = config.GetDirect("table3.e.a"); + auto table3_e_b = config.GetDirect("table3.e.b"); + + ASSERT_TRUE(table1_a); + EXPECT_EQ(table1_a.GetType(), sdv::toml::ENodeType::node_integer); + EXPECT_EQ(table1_a.GetValue(), 0); + ASSERT_TRUE(table1_b); + EXPECT_EQ(table1_b.GetType(), sdv::toml::ENodeType::node_floating_point); + EXPECT_EQ(table1_b.GetValue(), 1.2); + ASSERT_TRUE(table1_c); + EXPECT_EQ(table1_c.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(table1_c.GetValue()), "string"); + ASSERT_TRUE(table2_a); + EXPECT_EQ(table2_a.GetType(), sdv::toml::ENodeType::node_array); + ASSERT_TRUE(table2_b); + EXPECT_EQ(table2_b.GetType(), sdv::toml::ENodeType::node_boolean); + EXPECT_EQ(table2_b.GetValue(), true); + ASSERT_TRUE(table2_e); + EXPECT_EQ(table2_e.GetType(), sdv::toml::ENodeType::node_floating_point); + EXPECT_EQ(table2_e.GetValue(), 2.71828); + ASSERT_TRUE(table3_a_a); + EXPECT_EQ(table3_a_a.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(table3_a_a.GetValue()), "a"); + ASSERT_TRUE(table3_a_b); + EXPECT_EQ(table3_a_b.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(table3_a_b.GetValue()), "A"); + ASSERT_TRUE(table3_b_a); + EXPECT_EQ(table3_b_a.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(table3_b_a.GetValue()), "b"); + ASSERT_TRUE(table3_b_b); + EXPECT_EQ(table3_b_b.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(table3_b_b.GetValue()), "B"); + ASSERT_TRUE(table3_e_a); + EXPECT_EQ(table3_e_a.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(table3_e_a.GetValue()), "e"); + ASSERT_TRUE(table3_e_b); + EXPECT_EQ(table3_e_b.GetType(), sdv::toml::ENodeType::node_string); + EXPECT_EQ(static_cast(table3_e_b.GetValue()), "E"); + + config.Clear(); + appcontrol.Shutdown(); +} + +TEST(SpecialCases, Keys) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + EXPECT_TRUE(sdv::toml::CTOMLParser(u8R"( + "127.0.0.1" = "value" + "character encoding" = "value" + "ʎǝʞ" = "value" + 'key2' = "value" + 'quoted "value"' = "value" + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + key = "value" + bare_key = "value" + bare-key = "value" + 1234 = "value" + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + "" = "blank" # VALID but discouraged + )")); + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + '' = 'blank' # VALID but discouraged + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + name = "Orange" + physical.color = "orange" + physical.shape = "round" + site."google.com" = true + )")); + + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + fruit.name = "banana" # this is best practice + fruit. color = "yellow" # same as fruit.color + fruit . flavor = "banana" # same as fruit.flavor + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + # This makes the key "fruit" into a table. + fruit.apple.smooth = true + # So then you can add to the table "fruit" like so: + fruit.orange = 2 + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + # VALID BUT DISCOURAGED + apple.type = "fruit" + orange.type = "fruit" + apple.skin = "thin" + orange.skin = "thick" + apple.color = "red" + orange.color = "orange" + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + 3.1415 = 3.1415 + )")); + { + sdv::toml::CTOMLParser config(R"( + 3.1415 = 3.1415 + )"); + auto table = config.GetDirect("3"); + auto pi = config.GetDirect("3.1415"); + ASSERT_TRUE(table); + EXPECT_EQ(table.GetType(), sdv::toml::ENodeType::node_table); + ASSERT_TRUE(pi); + EXPECT_EQ(pi.GetType(), sdv::toml::ENodeType::node_floating_point); + EXPECT_EQ(pi.GetValue(), 3.1415); + } + + appcontrol.Shutdown(); +} + +TEST(SpecialCases, Arrays) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + integers = [ 1, 2, 3 ] + colors = [ "red", "yellow", "green" ] + nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] + nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] + string_array = [ "all", 'strings', """are the same""", '''type''' ] + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] + contributors = [ + "Foo Bar ", + { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } + ] + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + integers3 = [ + 1, + 2, # this is ok + ] + )")); + + appcontrol.Shutdown(); +} + +TEST(SpecialCases, Tables) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + [table-1] + key1 = "some string" + key2 = 123 + + [table-2] + key1 = "another string" + key2 = 456 + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + [dog."tater.man"] + type.name = "pug" + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(u8R"( + [a.b.c] # this is best practice + [ d.e.f ] # same as [d.e.f] + [ g . h . i ] # same as [g.h.i] + [ j . "ʞ" . 'l' ] # same as [j."ʞ".'l'] + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + # [x] you + # [x.y] don't + # [x.y.z] need these + [x.y.z.w] # for this to work + [x] # defining a super-table afterward is ok + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + # VALID BUT DISCOURAGED + [fruit.apple] + [animal] + [fruit.orange] + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + [fruit] + apple.color = "red" + apple.taste.sweet = true + [fruit.apple.texture] # you can add sub-tables + )")); + + appcontrol.Shutdown(); +} + +TEST(SpecialCases, TableArrays) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + [[products]] + name = "Hammer" + sku = 738594937 + [[products]] # empty table within the array + [[products]] + name = "Nail" + sku = 284758393 + color = "gray" + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + [[fruits]] + name = "apple" + [fruits.physical] # subtable + color = "red" + shape = "round" + [[fruits.varieties]] # nested array of tables + name = "red delicious" + [[fruits.varieties]] + name = "granny smith" + [[fruits]] + name = "banana" + [[fruits.varieties]] + name = "plantain" + )")); + + EXPECT_TRUE(sdv::toml::CTOMLParser(R"( + points = [ { x = 1, y = 2, z = 3 }, + { x = 7, y = 8, z = 9 }, + { x = 2, y = 4, z = 8 } ] + )")); + + appcontrol.Shutdown(); +} + +TEST(ErrorCases, KeyValue) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"(key = # node_invalid)")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"(first = "Tom" last = "Preston-Werner" # node_invalid)")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"(= "no key name" # node_invalid)")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + name = "Tom" + name = "Pradyun" + )")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + fruit . flavor = "banana" # same as fruit.flavor + fruit.flavor = "banana" + )")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + spelling = "favorite" + "spelling" = "favourite" + )")); + + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + # This defines the value of fruit.apple to be an integer. + fruit.apple = 1 + # But then this treats fruit.apple like it's a table. + # You can't turn an integer into a table. + fruit.apple.smooth = true + )")); + + appcontrol.Shutdown(); +} + +TEST(ErrorCases, Tables) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + EXPECT_FALSE(sdv::toml::CTOMLParser(u8R"( + [ j . "ʞ" . 'l' ] + [j."ʞ".'l'] + )")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(u8R"( + [ j . "ʞ" . 'l' ] + ["j".'ʞ'."l"] + )")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + [fruit] + apple = "red" + [fruit] + orange = "orange" + )")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + [fruit] + apple = "red" + [fruit.apple] + texture = "smooth" + )")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + [fruit] + apple.color = "red" + apple.taste.sweet = true + [fruit.apple] # INVALID + )")); + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + [fruit] + apple.color = "red" + apple.taste.sweet = true + [fruit.apple.taste] # INVALID + )")); + + appcontrol.Shutdown(); +} + +TEST(ErrorCases, InlineTables) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + type = { name = "Nail" } + type.edible = false # INVALID + )")); + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + [product] + type.name = "Nail" + type = { edible = false } # INVALID + )")); + + appcontrol.Shutdown(); +} + +TEST(ErrorCases, TableArrays) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + [fruit.physical] # subtable, but to which parent element should it belong? + color = "red" + shape = "round" + [[fruit]] # parser must throw an error upon discovering that "fruit" is + # an array rather than a table + name = "apple" + )")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + fruits = [] + [[fruits]] # Not allowed + )")); + + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + [[fruits]] + name = "apple" + [[fruits.varieties]] + name = "red delicious" + # INVALID: This table conflicts with the previous array of tables + [fruits.varieties] + name = "granny smith" + )")); + EXPECT_FALSE(sdv::toml::CTOMLParser(R"( + [[fruits]] + name = "apple" + [fruits.physical] + color = "red" + shape = "round" + # INVALID: This array of tables conflicts with the previous table + [[fruits.physical]] + color = "green" + )")); + + appcontrol.Shutdown(); +} + +TEST(Ordering, Array) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config(R"( + array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + )"); + + auto two = config.GetDirect("array[2]"); + auto eleven = config.GetDirect("array[11]"); + const sdv::toml::CNodeCollection arr = config.GetDirect("array"); + + // with direct access + ASSERT_TRUE(two); + EXPECT_EQ(two.GetValue(), 2); + ASSERT_TRUE(eleven); + EXPECT_EQ(eleven.GetValue(), 11); + + // with indirect access through iterating + ASSERT_TRUE(arr); + for (std::size_t i = 0; i < arr.GetCount(); ++i) + { + EXPECT_EQ(arr[i].GetValue(), (int64_t)i); + } + + config.Clear(); + appcontrol.Shutdown(); +} + +TEST(Ordering, TableAray) +{ + sdv::app::CAppControl appcontrol(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config"); + ASSERT_TRUE(appcontrol.IsRunning()); + + sdv::toml::CTOMLParser config(R"( + [[tableArray]] + a = 0 + [[tableArray]] + a = 1 + [[tableArray]] + a = 2 + [[tableArray]] + a = 3 + [[tableArray]] + a = 4 + [[tableArray]] + a = 5 + [[tableArray]] + a = 6 + [[tableArray]] + a = 7 + [[tableArray]] + a = 8 + [[tableArray]] + a = 9 + [[tableArray]] + a = 10 + [[tableArray]] + a = 11 + )"); + + sdv::toml::CNodeCollection tableArray = config.GetDirect("tableArray"); + + ASSERT_TRUE(tableArray); + for (std::size_t i = 0; i < tableArray.GetCount(); ++i) + { + EXPECT_EQ(sdv::toml::CNodeCollection(tableArray[i])[0].GetValue(), (int64_t) i); + } + + config.Clear(); + appcontrol.Shutdown(); +} + diff --git a/tests/component_tests/vss_util/CMakeLists.txt b/tests/component_tests/vss_util/CMakeLists.txt index 8046416..ef0b7c5 100644 --- a/tests/component_tests/vss_util/CMakeLists.txt +++ b/tests/component_tests/vss_util/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Define project project (VSSUtilTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/component_tests/vss_util/vss_util_test.cpp b/tests/component_tests/vss_util/vss_util_test.cpp index 0eba34d..2f455b2 100644 --- a/tests/component_tests/vss_util/vss_util_test.cpp +++ b/tests/component_tests/vss_util/vss_util_test.cpp @@ -1,3 +1,13 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + #include #include #include diff --git a/tests/include/basic_test_helper.h b/tests/include/basic_test_helper.h index a8c1cc0..3493165 100644 --- a/tests/include/basic_test_helper.h +++ b/tests/include/basic_test_helper.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 "../../global/exec_dir_helper.h" #include diff --git a/tests/include/gtest_custom.h b/tests/include/gtest_custom.h index fbd9a38..36aa6d2 100644 --- a/tests/include/gtest_custom.h +++ b/tests/include/gtest_custom.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 GTEST_CUSTOM_H #define GTEST_CUSTOM_H diff --git a/tests/include/logger_test_helper.h b/tests/include/logger_test_helper.h index 6edb861..2de71df 100644 --- a/tests/include/logger_test_helper.h +++ b/tests/include/logger_test_helper.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(_WIN32) #include "../../global/exec_dir_helper.h" diff --git a/tests/include/sdv_test_macro.h b/tests/include/sdv_test_macro.h index 9c2ce54..0a20f1f 100644 --- a/tests/include/sdv_test_macro.h +++ b/tests/include/sdv_test_macro.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 SDV_TEST_MACRO_H #define SDV_TEST_MACRO_H diff --git a/tests/include/simple_cpp_decomposer.h b/tests/include/simple_cpp_decomposer.h index 33518f6..02dd45e 100644 --- a/tests/include/simple_cpp_decomposer.h +++ b/tests/include/simple_cpp_decomposer.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 SIMPLE_CPP_DECOMPOSER_H #define SIMPLE_CPP_DECOMPOSER_H diff --git a/tests/include/simple_cpp_decomposer.inl b/tests/include/simple_cpp_decomposer.inl index cf77ea9..6081d10 100644 --- a/tests/include/simple_cpp_decomposer.inl +++ b/tests/include/simple_cpp_decomposer.inl @@ -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 SIMPLE_CPP_DECOMPOSER_INL #define SIMPLE_CPP_DECOMPOSER_INL diff --git a/tests/include/test_watchdog.h b/tests/include/test_watchdog.h index 2f515de..af8cc32 100644 --- a/tests/include/test_watchdog.h +++ b/tests/include/test_watchdog.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 TEST_WATCHDOG_H #define TEST_WATCHDOG_H diff --git a/tests/loop_test_runner_linux.sh b/tests/loop_test_runner_linux.sh index fc02733..2f6cab0 100644 --- a/tests/loop_test_runner_linux.sh +++ b/tests/loop_test_runner_linux.sh @@ -1,3 +1,14 @@ +# /******************************************************************************** +# 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 +# ********************************************************************************/ +# +# ################################## ATTENTION ################################# # Shell Script to execute the tests on Linux # # Adjust the permission with: chmod +x loop_test_runner_linux.sh # @@ -19,7 +30,7 @@ TEST_NAME="$1" MAX_RUNS="$2" # Build the full path to the test executable -TARGET_TEST="../build/tests/bin/$TEST_NAME" +TARGET_TEST="./bin/$TEST_NAME" # Check if the test executable exists if [ -f "$TARGET_TEST" ]; then diff --git a/tests/loop_test_runner_windows.ps1 b/tests/loop_test_runner_windows.ps1 index 18da151..07c59b7 100644 --- a/tests/loop_test_runner_windows.ps1 +++ b/tests/loop_test_runner_windows.ps1 @@ -1,3 +1,15 @@ +# /******************************************************************************** +# 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 +# ********************************************************************************/ +# +# +# <# .SYNOPSIS PowerShell script to loop a specific test executable multiple times to catch intermittent failures. @@ -16,7 +28,7 @@ param ( ) # Build the full path to the test executable -$TargetTest = Join-Path "..\build\tests\bin" $TestName +$TargetTest = Join-Path "bin" $TestName # Check if the test executable exists if (Test-Path $TargetTest) { diff --git a/tests/manual_tests/config_files/test_manual_can_com_silkit.toml b/tests/manual_tests/config_files/test_manual_can_com_silkit.toml index d694edc..073915a 100644 --- a/tests/manual_tests/config_files/test_manual_can_com_silkit.toml +++ b/tests/manual_tests/config_files/test_manual_can_com_silkit.toml @@ -12,6 +12,7 @@ Class = "DataDispatchService" [[Component]] Path = "can_com_silkit.sdv" Class = "CAN_Com_SilKit" +[Component.Parameters] DebugInfo = true SyncMode = true SilKitParticipantName = "CAN1" diff --git a/tests/manual_tests/silkit_can_com_tests/CMakeLists.txt b/tests/manual_tests/silkit_can_com_tests/CMakeLists.txt index 1ae7bea..5ed457b 100644 --- a/tests/manual_tests/silkit_can_com_tests/CMakeLists.txt +++ b/tests/manual_tests/silkit_can_com_tests/CMakeLists.txt @@ -1,5 +1,15 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # 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")) project(ManualTestCanComSilKit) diff --git a/tests/manual_tests/silkit_can_com_tests/include/can_com_test_helper.h b/tests/manual_tests/silkit_can_com_tests/include/can_com_test_helper.h index 9654650..e308798 100644 --- a/tests/manual_tests/silkit_can_com_tests/include/can_com_test_helper.h +++ b/tests/manual_tests/silkit_can_com_tests/include/can_com_test_helper.h @@ -1,3 +1,13 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + #ifndef TEST_CAN_SILKIT_HELPER_H #define TEST_CAN_SILKIT_HELPER_H @@ -22,9 +32,9 @@ class CTestCanSilKit : public CCANSilKit return CCANSilKit::Initialize(ssObjectConfig); } - virtual sdv::EObjectStatus GetStatus() const override + virtual sdv::EObjectState GetObjectState() const override { - return CCANSilKit::GetStatus(); + return CCANSilKit::GetObjectState(); } virtual void Shutdown() override diff --git a/tests/manual_tests/silkit_can_com_tests/run_silkit_util.bat b/tests/manual_tests/silkit_can_com_tests/run_silkit_util.bat index f7dfd3b..ea95a12 100644 --- a/tests/manual_tests/silkit_can_com_tests/run_silkit_util.bat +++ b/tests/manual_tests/silkit_can_com_tests/run_silkit_util.bat @@ -1,3 +1,15 @@ +REM /******************************************************************************** +REM * Copyright (c) 2025-2026 ZF Friedrichshafen AG +REM * +REM * This program and the accompanying materials are made available under the +REM * terms of the Apache License Version 2.0 which is available at +REM * https://www.apache.org/licenses/LICENSE-2.0 +REM * +REM * SPDX-License-Identifier: Apache-2.0 +REM ********************************************************************************/ +REM +REM +REM @echo off REM Run sil-kit-registry in a new Command Prompt window start cmd /k "..\..\_deps\silkit-src\SilKit\bin\sil-kit-registry.exe" diff --git a/tests/manual_tests/silkit_can_com_tests/run_silkit_util.sh b/tests/manual_tests/silkit_can_com_tests/run_silkit_util.sh index cd0e6a9..e68b5f5 100644 --- a/tests/manual_tests/silkit_can_com_tests/run_silkit_util.sh +++ b/tests/manual_tests/silkit_can_com_tests/run_silkit_util.sh @@ -1,3 +1,14 @@ +# /******************************************************************************** +# 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 +# ********************************************************************************/ +# +# #!/bin/bash # Run sil-kit-registry in a new terminal window diff --git a/tests/manual_tests/silkit_can_com_tests/src/can_com_manual_test_silkit.cpp b/tests/manual_tests/silkit_can_com_tests/src/can_com_manual_test_silkit.cpp index f94da55..779b121 100644 --- a/tests/manual_tests/silkit_can_com_tests/src/can_com_manual_test_silkit.cpp +++ b/tests/manual_tests/silkit_can_com_tests/src/can_com_manual_test_silkit.cpp @@ -1,3 +1,13 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + #include "../../../../global/process_watchdog.h" #include "../include/can_com_test_helper.h" @@ -52,9 +62,9 @@ class CTestCANSilkit : public CCANSilKit return CCANSilKit::GetInterfaces(); } - sdv::EObjectStatus GetTestStatus() const + sdv::EObjectState GetTestStatus() const { - return CCANSilKit::GetStatus(); + return CCANSilKit::GetObjectState(); } uint64_t GetMessagesSent() const @@ -104,7 +114,7 @@ bool InitializeAppControl(sdv::app::CAppControl* appcontrol, const std::string& void InitializeCanComObject(CTestCANSilkit& canComObj, const std::string config, MockCANReceiver& mockRcv) { ASSERT_NO_THROW(canComObj.Initialize(config.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.SetOperationMode(sdv::EOperationMode::configuring)); ASSERT_NO_THROW(canComObj.RegisterReceiver(&mockRcv)); EXPECT_NO_THROW(canComObj.SetOperationMode(sdv::EOperationMode::running)); @@ -147,12 +157,12 @@ TEST_F(CANSilkitTest, ValidConfigString) )"; CTestCANSilkit canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); - ASSERT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized); + ASSERT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.Send(testMsg, 0)); ASSERT_NO_THROW(canComObj.Shutdown()); - ASSERT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::shutdown_in_progress); + ASSERT_EQ(canComObj.GetObjectState(), sdv::EObjectState::shutdown_in_progress); } TEST_F(CANSilkitTest, InvalidConfigIdentifier) @@ -176,10 +186,10 @@ TEST_F(CANSilkitTest, InvalidConfigIdentifier) CTestCANSilkit canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialization_failure); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialization_failure); ASSERT_NO_THROW(canComObj.Shutdown()); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::shutdown_in_progress); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::shutdown_in_progress); } TEST_F(CANSilkitTest, SendReceiveTest) @@ -219,9 +229,9 @@ TEST_F(CANSilkitTest, SendReceiveTest) MockCANReceiver mockRcv; canComObj1.Initialize(ssConfig1.c_str()); - ASSERT_EQ(canComObj1.GetStatus(), sdv::EObjectStatus::initialized); + ASSERT_EQ(canComObj1.GetObjectState(), sdv::EObjectState::initialized); canComObj2.Initialize(ssConfig2.c_str()); - ASSERT_EQ(canComObj2.GetStatus(), sdv::EObjectStatus::initialized); + ASSERT_EQ(canComObj2.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj1.SetOperationMode(sdv::EOperationMode::running)); ASSERT_NO_THROW(canComObj2.SetOperationMode(sdv::EOperationMode::configuring)); diff --git a/tests/manual_tests/silkit_can_com_tests/src/can_com_test_silkit.cpp b/tests/manual_tests/silkit_can_com_tests/src/can_com_test_silkit.cpp index 4c407c9..1f1c3a5 100644 --- a/tests/manual_tests/silkit_can_com_tests/src/can_com_test_silkit.cpp +++ b/tests/manual_tests/silkit_can_com_tests/src/can_com_test_silkit.cpp @@ -1,3 +1,13 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + #include #include #include @@ -20,7 +30,6 @@ */ class CDummyCANSilKit : public sdv::CSdvObject - , public sdv::IObjectControl , public sdv::can::IRegisterReceiver , public sdv::can::ISend { @@ -28,46 +37,30 @@ public: // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(sdv::can::IRegisterReceiver) SDV_INTERFACE_ENTRY(sdv::can::ISend) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::vehicle_bus) DECLARE_OBJECT_CLASS_NAME("Dummy_CAN_Sockets") DECLARE_DEFAULT_OBJECT_NAME("CAN_Communication_Object") DECLARE_OBJECT_SINGLETON() - virtual void Initialize(const sdv::u8string& ) override + /** + * @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 bool OnInitialize() override { m_StopThread = false; //m_thSend2DatalinkThread = std::thread(&CDummyCANSockets::Send2DatalinkThread, this); - m_status = sdv::EObjectStatus::initialized; + return true; } - virtual sdv::EObjectStatus GetStatus() const override - { - return m_status; - } - - void SetOperationMode(sdv::EOperationMode eMode) override - { - switch (eMode) - { - case sdv::EOperationMode::configuring: - if (m_status == sdv::EObjectStatus::running || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::configuring; - break; - case sdv::EOperationMode::running: - if (m_status == sdv::EObjectStatus::configuring || m_status == sdv::EObjectStatus::initialized) - m_status = sdv::EObjectStatus::running; - break; - default: - break; - } - } - - virtual void Shutdown() override + /** + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. + */ + virtual void OnShutdown() override { m_StopThread = true; if (m_thSend2DatalinkThread.joinable()) @@ -126,7 +119,6 @@ private: std::thread m_thSend2DatalinkThread; mutable std::mutex m_mtxReceivers; std::set m_setReceivers; - std::atomic m_status = { sdv::EObjectStatus::initialization_pending }; }; class MockCANReceiver : public sdv::can::IReceive @@ -245,37 +237,37 @@ TEST_F(CANCommunicationTest, BasicSendAndReceiveMessageTest) testMsg.seqData = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x77, 0x88}; // Configuration content as a string - std::string testConfigFileContent = R"( - [Configuration] - Version = 100 + const std::string testConfigFileContent = R"toml( +[Configuration] +Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml( - [[Component]] - Path = "task_timer.sdv" - Class = "TaskTimerService" +[[Component]] +Path = "task_timer.sdv" +Class = "TaskTimerService" - [[Component]] - Path = "data_dispatch_service.sdv" - Class = "DataDispatchService" +[[Component]] +Path = "data_dispatch_service.sdv" +Class = "DataDispatchService" - [[Component]] - Path = "can_com_silkit.sdv" - Class = "CAN_Com_SilKit" - DebugInfo = true - SyncMode = true - CanSilKitChannel = "CAN1" - CanSilKitNetwork = "PrivateCAN" - RegistryURI = "silkit://localhost:8500" - SilKitConfig = """{ - "Logging": { - "Sinks": [ { "Type": "Stdout", "Level": "Info" } ] - }, - }""" +[[Component]] +Path = "can_com_silkit.sdv" +Class = "CAN_Com_SilKit" +DebugInfo = true +SyncMode = true +CanSilKitChannel = "CAN1" +CanSilKitNetwork = "PrivateCAN" +RegistryURI = "silkit://localhost:8500" +SilKitConfig = """{ + "Logging": { + "Sinks": [ { "Type": "Stdout", "Level": "Info" } ] + }, + }""" - [[Component]] - Path = "can_datalink.sdv" - Class = "CAN_data_link" - """ - )"; +[[Component]] +Path = "can_datalink.sdv" +Class = "CAN_data_link" +""" +)toml"; sdv::app::CAppControl appControl; auto bResult = InitializeAppControl(&appControl, "test_manual_can_com_silkit.toml"); diff --git a/tests/manual_tests/silkit_can_com_tests/src/can_reader.cpp b/tests/manual_tests/silkit_can_com_tests/src/can_reader.cpp index 9d38e78..98c84f7 100644 --- a/tests/manual_tests/silkit_can_com_tests/src/can_reader.cpp +++ b/tests/manual_tests/silkit_can_com_tests/src/can_reader.cpp @@ -1,3 +1,13 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + #include "../include/can_com_test_helper.h" void ReceiveData() diff --git a/tests/manual_tests/silkit_can_com_tests/src/can_writer.cpp b/tests/manual_tests/silkit_can_com_tests/src/can_writer.cpp index d090c2a..ca3683d 100644 --- a/tests/manual_tests/silkit_can_com_tests/src/can_writer.cpp +++ b/tests/manual_tests/silkit_can_com_tests/src/can_writer.cpp @@ -1,3 +1,13 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + #include "../include/can_com_test_helper.h" int main() diff --git a/tests/run_tests_on_linux.sh b/tests/run_tests_on_linux.sh index 6771568..33f8e95 100644 --- a/tests/run_tests_on_linux.sh +++ b/tests/run_tests_on_linux.sh @@ -1,3 +1,14 @@ +# /******************************************************************************** +# 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 +# ********************************************************************************/ +# +# ############################## ATTENTION ############################## # Shell Script to execute the tests on Linux # # Adjust the permission with: chmod +x run_tests_on_linux.sh # diff --git a/tests/run_tests_on_windows.ps1 b/tests/run_tests_on_windows.ps1 index 38793f8..587cfce 100644 --- a/tests/run_tests_on_windows.ps1 +++ b/tests/run_tests_on_windows.ps1 @@ -1,3 +1,14 @@ +# /******************************************************************************** +# 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 +# ********************************************************************************/ +# +# # PowerShell Script to execute the tests on Windows # Save this script as run_tests_on_windows.ps1 # Run the script with: .\run_tests_on_windows.ps1 diff --git a/tests/unit_tests/app_connect/CMakeLists.txt b/tests/unit_tests/app_connect/CMakeLists.txt index a22d6ea..065bea0 100644 --- a/tests/unit_tests/app_connect/CMakeLists.txt +++ b/tests/unit_tests/app_connect/CMakeLists.txt @@ -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 (UnitTest_App_Connect VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/app_connect/app_connect_local.cpp b/tests/unit_tests/app_connect/app_connect_local.cpp index 12d4f60..2b6c398 100644 --- a/tests/unit_tests/app_connect/app_connect_local.cpp +++ b/tests/unit_tests/app_connect/app_connect_local.cpp @@ -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 + ********************************************************************************/ + /** - Default instantiate listener - Instantiate with specific info diff --git a/tests/unit_tests/app_connect/main.cpp b/tests/unit_tests/app_connect/main.cpp index d934a84..e988681 100644 --- a/tests/unit_tests/app_connect/main.cpp +++ b/tests/unit_tests/app_connect/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../../global/localmemmgr.h" diff --git a/tests/unit_tests/asc_format/CMakeLists.txt b/tests/unit_tests/asc_format/CMakeLists.txt index 48a901f..840a0b1 100644 --- a/tests/unit_tests/asc_format/CMakeLists.txt +++ b/tests/unit_tests/asc_format/CMakeLists.txt @@ -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 (UnitTest_ASC_Format VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/asc_format/asc_reader_test.cpp b/tests/unit_tests/asc_format/asc_reader_test.cpp index 93913bc..03e50b1 100644 --- a/tests/unit_tests/asc_format/asc_reader_test.cpp +++ b/tests/unit_tests/asc_format/asc_reader_test.cpp @@ -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 "../../include/gtest_custom.h" #include "../../../global/ascformat/ascreader.cpp" #include @@ -413,7 +426,7 @@ TEST(CAscReaderTest, RepeatPlaybackCheckLoopExecution) std::this_thread::sleep_for(std::chrono::milliseconds(400)); reader.StopPlayback(); - // Second loop must run in the same speed, therfore LoopCount = 2, not mmore + // Second loop must run in the same speed, therefore LoopCount = 2, not mmore EXPECT_TRUE((readerOneLoop.GetLoopCount() + 1) == reader.GetLoopCount()); } diff --git a/tests/unit_tests/asc_format/asc_writer_test.cpp b/tests/unit_tests/asc_format/asc_writer_test.cpp index 1672fbe..dce28d8 100644 --- a/tests/unit_tests/asc_format/asc_writer_test.cpp +++ b/tests/unit_tests/asc_format/asc_writer_test.cpp @@ -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 "../../include/gtest_custom.h" #include "../../../global/ascformat/ascwriter.cpp" #include diff --git a/tests/unit_tests/asc_format/main.cpp b/tests/unit_tests/asc_format/main.cpp index d934a84..2cd8af6 100644 --- a/tests/unit_tests/asc_format/main.cpp +++ b/tests/unit_tests/asc_format/main.cpp @@ -1,3 +1,13 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + #include #include "../../../global/process_watchdog.h" #include "../../../global/localmemmgr.h" diff --git a/tests/unit_tests/basic_types/CMakeLists.txt b/tests/unit_tests/basic_types/CMakeLists.txt index b401c95..87c46eb 100644 --- a/tests/unit_tests/basic_types/CMakeLists.txt +++ b/tests/unit_tests/basic_types/CMakeLists.txt @@ -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 (multiple EXEs) project(BasicTypesTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/basic_types/any.cpp b/tests/unit_tests/basic_types/any.cpp index 6bc9d66..1eeb9ad 100644 --- a/tests/unit_tests/basic_types/any.cpp +++ b/tests/unit_tests/basic_types/any.cpp @@ -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 #include #include "basic_types_test.h" diff --git a/tests/unit_tests/basic_types/basic_types_test.cpp b/tests/unit_tests/basic_types/basic_types_test.cpp index c697b01..db49a0c 100644 --- a/tests/unit_tests/basic_types/basic_types_test.cpp +++ b/tests/unit_tests/basic_types/basic_types_test.cpp @@ -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 "basic_types_test.h" void CBasicTypesTest::SetUpTestCase() diff --git a/tests/unit_tests/basic_types/basic_types_test.h b/tests/unit_tests/basic_types/basic_types_test.h index 04c8fa3..0137224 100644 --- a/tests/unit_tests/basic_types/basic_types_test.h +++ b/tests/unit_tests/basic_types/basic_types_test.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 BASIC_TYPES_TEST_H #define BASIC_TYPES_TEST_H diff --git a/tests/unit_tests/basic_types/crc.cpp b/tests/unit_tests/basic_types/crc.cpp index 3b17f83..819af79 100644 --- a/tests/unit_tests/basic_types/crc.cpp +++ b/tests/unit_tests/basic_types/crc.cpp @@ -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 #include "basic_types_test.h" diff --git a/tests/unit_tests/basic_types/interface.cpp b/tests/unit_tests/basic_types/interface.cpp index cb47c03..7d6e3e4 100644 --- a/tests/unit_tests/basic_types/interface.cpp +++ b/tests/unit_tests/basic_types/interface.cpp @@ -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 #include #include "basic_types_test.h" diff --git a/tests/unit_tests/basic_types/main.cpp b/tests/unit_tests/basic_types/main.cpp index e3120f9..84f0957 100644 --- a/tests/unit_tests/basic_types/main.cpp +++ b/tests/unit_tests/basic_types/main.cpp @@ -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 #include "../../../global/process_watchdog.h" diff --git a/tests/unit_tests/basic_types/ptr.cpp b/tests/unit_tests/basic_types/ptr.cpp index 728b20e..559ae88 100644 --- a/tests/unit_tests/basic_types/ptr.cpp +++ b/tests/unit_tests/basic_types/ptr.cpp @@ -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 #include diff --git a/tests/unit_tests/basic_types/ptr_complex.cpp b/tests/unit_tests/basic_types/ptr_complex.cpp index 2ad1bcb..d8bcb70 100644 --- a/tests/unit_tests/basic_types/ptr_complex.cpp +++ b/tests/unit_tests/basic_types/ptr_complex.cpp @@ -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 #include diff --git a/tests/unit_tests/basic_types/ptr_simple.cpp b/tests/unit_tests/basic_types/ptr_simple.cpp index b295295..459b2b8 100644 --- a/tests/unit_tests/basic_types/ptr_simple.cpp +++ b/tests/unit_tests/basic_types/ptr_simple.cpp @@ -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 #include diff --git a/tests/unit_tests/basic_types/sequence_complex.cpp b/tests/unit_tests/basic_types/sequence_complex.cpp index 30b3e1c..cc5dfb8 100644 --- a/tests/unit_tests/basic_types/sequence_complex.cpp +++ b/tests/unit_tests/basic_types/sequence_complex.cpp @@ -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 #include diff --git a/tests/unit_tests/basic_types/sequence_simple.cpp b/tests/unit_tests/basic_types/sequence_simple.cpp index e4887ff..48331c6 100644 --- a/tests/unit_tests/basic_types/sequence_simple.cpp +++ b/tests/unit_tests/basic_types/sequence_simple.cpp @@ -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 #include diff --git a/tests/unit_tests/basic_types/serdes.cpp b/tests/unit_tests/basic_types/serdes.cpp index c099629..363b45a 100644 --- a/tests/unit_tests/basic_types/serdes.cpp +++ b/tests/unit_tests/basic_types/serdes.cpp @@ -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 #include "basic_types_test.h" diff --git a/tests/unit_tests/basic_types/string.cpp b/tests/unit_tests/basic_types/string.cpp index 982cb56..4d3744d 100644 --- a/tests/unit_tests/basic_types/string.cpp +++ b/tests/unit_tests/basic_types/string.cpp @@ -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 #include #include "basic_types_test.h" diff --git a/tests/unit_tests/basic_types/u16string.cpp b/tests/unit_tests/basic_types/u16string.cpp index 7cfb002..461af13 100644 --- a/tests/unit_tests/basic_types/u16string.cpp +++ b/tests/unit_tests/basic_types/u16string.cpp @@ -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 #include #include "basic_types_test.h" diff --git a/tests/unit_tests/basic_types/u32string.cpp b/tests/unit_tests/basic_types/u32string.cpp index 0baf44c..f45f52b 100644 --- a/tests/unit_tests/basic_types/u32string.cpp +++ b/tests/unit_tests/basic_types/u32string.cpp @@ -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 #include #include "basic_types_test.h" diff --git a/tests/unit_tests/basic_types/u8string.cpp b/tests/unit_tests/basic_types/u8string.cpp index 8a09aad..5ecba66 100644 --- a/tests/unit_tests/basic_types/u8string.cpp +++ b/tests/unit_tests/basic_types/u8string.cpp @@ -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 #include #include "basic_types_test.h" diff --git a/tests/unit_tests/basic_types/wstring.cpp b/tests/unit_tests/basic_types/wstring.cpp index 6a49167..cc7c877 100644 --- a/tests/unit_tests/basic_types/wstring.cpp +++ b/tests/unit_tests/basic_types/wstring.cpp @@ -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 #include #include "basic_types_test.h" diff --git a/tests/unit_tests/commandline_parser/CMakeLists.txt b/tests/unit_tests/commandline_parser/CMakeLists.txt index 7335198..276b840 100644 --- a/tests/unit_tests/commandline_parser/CMakeLists.txt +++ b/tests/unit_tests/commandline_parser/CMakeLists.txt @@ -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 (UnitTest_CommandLine_Parser VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/commandline_parser/commandline_parser_test.cpp b/tests/unit_tests/commandline_parser/commandline_parser_test.cpp index f988031..032172d 100644 --- a/tests/unit_tests/commandline_parser/commandline_parser_test.cpp +++ b/tests/unit_tests/commandline_parser/commandline_parser_test.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.cpp" diff --git a/tests/unit_tests/commandline_parser/commandline_parser_test.h b/tests/unit_tests/commandline_parser/commandline_parser_test.h index af30c99..1601657 100644 --- a/tests/unit_tests/commandline_parser/commandline_parser_test.h +++ b/tests/unit_tests/commandline_parser/commandline_parser_test.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 COMMANDLINE_PARSER_TEST_H #define COMMANDLINE_PARSER_TEST_H diff --git a/tests/unit_tests/commandline_parser/defaultarg_parser_test.cpp b/tests/unit_tests/commandline_parser/defaultarg_parser_test.cpp index ebc2017..b400715 100644 --- a/tests/unit_tests/commandline_parser/defaultarg_parser_test.cpp +++ b/tests/unit_tests/commandline_parser/defaultarg_parser_test.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" diff --git a/tests/unit_tests/commandline_parser/incompatible_arguments.cpp b/tests/unit_tests/commandline_parser/incompatible_arguments.cpp index f9be394..d0edbef 100644 --- a/tests/unit_tests/commandline_parser/incompatible_arguments.cpp +++ b/tests/unit_tests/commandline_parser/incompatible_arguments.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" diff --git a/tests/unit_tests/commandline_parser/main.cpp b/tests/unit_tests/commandline_parser/main.cpp index d934a84..e988681 100644 --- a/tests/unit_tests/commandline_parser/main.cpp +++ b/tests/unit_tests/commandline_parser/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../../global/localmemmgr.h" diff --git a/tests/unit_tests/commandline_parser/option_argument_selective_group.cpp b/tests/unit_tests/commandline_parser/option_argument_selective_group.cpp index 3770d98..103e4e6 100644 --- a/tests/unit_tests/commandline_parser/option_argument_selective_group.cpp +++ b/tests/unit_tests/commandline_parser/option_argument_selective_group.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" diff --git a/tests/unit_tests/commandline_parser/option_parser_test.cpp b/tests/unit_tests/commandline_parser/option_parser_test.cpp index 8cc20f3..e7485ae 100644 --- a/tests/unit_tests/commandline_parser/option_parser_test.cpp +++ b/tests/unit_tests/commandline_parser/option_parser_test.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" diff --git a/tests/unit_tests/commandline_parser/option_parser_test_assignment_next_arg.cpp b/tests/unit_tests/commandline_parser/option_parser_test_assignment_next_arg.cpp index 6bd0828..e24bd22 100644 --- a/tests/unit_tests/commandline_parser/option_parser_test_assignment_next_arg.cpp +++ b/tests/unit_tests/commandline_parser/option_parser_test_assignment_next_arg.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" diff --git a/tests/unit_tests/commandline_parser/option_parser_test_no_assignment_char.cpp b/tests/unit_tests/commandline_parser/option_parser_test_no_assignment_char.cpp index 6edcc58..cb783ab 100644 --- a/tests/unit_tests/commandline_parser/option_parser_test_no_assignment_char.cpp +++ b/tests/unit_tests/commandline_parser/option_parser_test_no_assignment_char.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" @@ -562,22 +575,24 @@ TEST_F(CCommandLineParserTestNoAssignment, OptionParseFlags) TEST_F(CCommandLineParserTestNoAssignment, OptionOverlappingArg) { // Mixing up -server and -s - const char* rgszCommandLine1[] = {"this_exe.app", "-server"}; - CCommandLine cl1(static_cast(CCommandLine::EParseFlags::no_assignment_character)); - bool bSilence = false; cl1.DefineOption("s", bSilence, "silence flag"); - bool bServer = false; cl1.DefineOption("server", bServer, "server enabled"); - EXPECT_NO_THROW(cl1.Parse(std::extent::value, rgszCommandLine1)); - EXPECT_FALSE(bSilence); - EXPECT_TRUE(bServer); + // CHANGE EVE: 25.03.2025: This is allowed by now. + //const char* rgszCommandLine1[] = {"this_exe.app", "-server"}; + //CCommandLine cl1(static_cast(CCommandLine::EParseFlags::no_assignment_character)); + //bool bSilence = false; cl1.DefineOption("s", bSilence, "silence flag"); + //bool bServer = false; cl1.DefineOption("server", bServer, "server enabled"); + //EXPECT_NO_THROW(cl1.Parse(std::extent::value, rgszCommandLine1)); + //EXPECT_FALSE(bSilence); + //EXPECT_TRUE(bServer); // Mixing up -instance and -instance_a - const char* rgszCommandLine2[] = {"this_exe.app", "-instance_a"}; - CCommandLine cl2(static_cast(CCommandLine::EParseFlags::no_assignment_character)); - std::string ssInstance; cl2.DefineOption("instance", ssInstance, "Instance ID"); - bool bInstance_a = false; cl2.DefineOption("instance_a", bInstance_a, "Instance A is activited"); - EXPECT_NO_THROW(cl2.Parse(std::extent::value, rgszCommandLine2)); - EXPECT_TRUE(ssInstance.empty()); - EXPECT_TRUE(bInstance_a); + // CHANGE EVE: 25.03.2025: This is allowed by now. + //const char* rgszCommandLine2[] = {"this_exe.app", "-instance_a"}; + //CCommandLine cl2(static_cast(CCommandLine::EParseFlags::no_assignment_character)); + //std::string ssInstance; cl2.DefineOption("instance", ssInstance, "Instance ID"); + //bool bInstance_a = false; cl2.DefineOption("instance_a", bInstance_a, "Instance A is activited"); + //EXPECT_NO_THROW(cl2.Parse(std::extent::value, rgszCommandLine2)); + //EXPECT_TRUE(ssInstance.empty()); + //EXPECT_TRUE(bInstance_a); // Mixiong up -enable_opt+ and -enable_options const char* rgszCommandLine3[] = {"this_exe.app", "-enable_options"}; @@ -591,9 +606,9 @@ TEST_F(CCommandLineParserTestNoAssignment, OptionOverlappingArg) // Mixing up -server and -s when double assignment const char* rgszCommandLine4[] = {"this_exe.app", "-s"}; CCommandLine cl4(static_cast(CCommandLine::EParseFlags::no_assignment_character)); - bSilence = false; auto rSilenceOption = cl4.DefineSubOption("silence", bSilence, "silence flag"); + bool bSilence = false; auto rSilenceOption = cl4.DefineSubOption("silence", bSilence, "silence flag"); rSilenceOption.AddOptionName("s"); - bServer = false; cl4.DefineOption("server", bServer, "server enabled"); + bool bServer = false; cl4.DefineOption("server", bServer, "server enabled"); EXPECT_NO_THROW(cl4.Parse(std::extent::value, rgszCommandLine4)); EXPECT_TRUE(bSilence); EXPECT_FALSE(bServer); diff --git a/tests/unit_tests/commandline_parser/suboption_argument_selective_group.cpp b/tests/unit_tests/commandline_parser/suboption_argument_selective_group.cpp index 74a0af3..1e40a0e 100644 --- a/tests/unit_tests/commandline_parser/suboption_argument_selective_group.cpp +++ b/tests/unit_tests/commandline_parser/suboption_argument_selective_group.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" diff --git a/tests/unit_tests/commandline_parser/suboption_parser_test.cpp b/tests/unit_tests/commandline_parser/suboption_parser_test.cpp index 5c97ddf..e62f042 100644 --- a/tests/unit_tests/commandline_parser/suboption_parser_test.cpp +++ b/tests/unit_tests/commandline_parser/suboption_parser_test.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" diff --git a/tests/unit_tests/commandline_parser/suboption_parser_test_assignment_next_arg.cpp b/tests/unit_tests/commandline_parser/suboption_parser_test_assignment_next_arg.cpp index 340d19d..6b287eb 100644 --- a/tests/unit_tests/commandline_parser/suboption_parser_test_assignment_next_arg.cpp +++ b/tests/unit_tests/commandline_parser/suboption_parser_test_assignment_next_arg.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" diff --git a/tests/unit_tests/commandline_parser/suboption_parser_test_no_assignment_char.cpp b/tests/unit_tests/commandline_parser/suboption_parser_test_no_assignment_char.cpp index 8d512d4..6363603 100644 --- a/tests/unit_tests/commandline_parser/suboption_parser_test_no_assignment_char.cpp +++ b/tests/unit_tests/commandline_parser/suboption_parser_test_no_assignment_char.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" @@ -557,13 +570,15 @@ TEST_F(CCommandLineParserTestNoAssignment, SubOptionOverlappingArg) EXPECT_TRUE(bServer); // Mixing up -instance and -instance_a - const char* rgszCommandLine2[] = {"this_exe.app", "--instance_a"}; - CCommandLine cl2(static_cast(CCommandLine::EParseFlags::no_assignment_character)); - std::string ssInstance; cl2.DefineSubOption("instance", ssInstance, "Instance ID"); - bool bInstance_a = false; cl2.DefineSubOption("instance_a", bInstance_a, "Instance A is activited"); - EXPECT_NO_THROW(cl2.Parse(std::extent::value, rgszCommandLine2)); - EXPECT_TRUE(ssInstance.empty()); - EXPECT_TRUE(bInstance_a); + // CHANGE EVE 26.03.2026: The multiple assignment of options was enabled to allow several option sets with identical names but + // different assignments to be used in different groups. This, however doesn't protect against overlapping options any more. + //const char* rgszCommandLine2[] = {"this_exe.app", "--instance_a"}; + //CCommandLine cl2(static_cast(CCommandLine::EParseFlags::no_assignment_character)); + //std::string ssInstance; cl2.DefineSubOption("instance", ssInstance, "Instance ID"); + //bool bInstance_a = false; cl2.DefineSubOption("instance_a", bInstance_a, "Instance A is activited"); + //EXPECT_NO_THROW(cl2.Parse(std::extent::value, rgszCommandLine2)); + //EXPECT_TRUE(ssInstance.empty()); + //EXPECT_TRUE(bInstance_a); // Mixiong up -enable_opt+ and -enable_options const char* rgszCommandLine3[] = {"this_exe.app", "--enable_options"}; diff --git a/tests/unit_tests/commandline_parser/windows_option_parser_test.cpp b/tests/unit_tests/commandline_parser/windows_option_parser_test.cpp index e96adc8..d1cea64 100644 --- a/tests/unit_tests/commandline_parser/windows_option_parser_test.cpp +++ b/tests/unit_tests/commandline_parser/windows_option_parser_test.cpp @@ -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 "../../include/gtest_custom.h" #include "commandline_parser_test.h" #include "../../../global/cmdlnparser/cmdlnparser.h" diff --git a/tests/unit_tests/concurrency/CMakeLists.txt b/tests/unit_tests/concurrency/CMakeLists.txt index f7a7e6c..ac118e5 100644 --- a/tests/unit_tests/concurrency/CMakeLists.txt +++ b/tests/unit_tests/concurrency/CMakeLists.txt @@ -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(ConcurrencyTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/concurrency/concurrency_tests.cpp b/tests/unit_tests/concurrency/concurrency_tests.cpp index a443e4b..9425435 100644 --- a/tests/unit_tests/concurrency/concurrency_tests.cpp +++ b/tests/unit_tests/concurrency/concurrency_tests.cpp @@ -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 #include #include diff --git a/tests/unit_tests/core_loader/CMakeLists.txt b/tests/unit_tests/core_loader/CMakeLists.txt index 00522b1..fd1e14f 100644 --- a/tests/unit_tests/core_loader/CMakeLists.txt +++ b/tests/unit_tests/core_loader/CMakeLists.txt @@ -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 (multiple EXEs) project(CoreLoaderTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/core_loader/core_loader_test.cpp b/tests/unit_tests/core_loader/core_loader_test.cpp index 3a6de66..093fc54 100644 --- a/tests/unit_tests/core_loader/core_loader_test.cpp +++ b/tests/unit_tests/core_loader/core_loader_test.cpp @@ -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 #include #include "../../../global/exec_dir_helper.h" diff --git a/tests/unit_tests/core_loader/main.cpp b/tests/unit_tests/core_loader/main.cpp index e3120f9..84f0957 100644 --- a/tests/unit_tests/core_loader/main.cpp +++ b/tests/unit_tests/core_loader/main.cpp @@ -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 #include "../../../global/process_watchdog.h" diff --git a/tests/unit_tests/core_loader/test_app.cpp b/tests/unit_tests/core_loader/test_app.cpp index a022796..fdfcd71 100644 --- a/tests/unit_tests/core_loader/test_app.cpp +++ b/tests/unit_tests/core_loader/test_app.cpp @@ -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 #include #include "../../../global/exec_dir_helper.h" diff --git a/tests/unit_tests/dbc_parser/CMakeLists.txt b/tests/unit_tests/dbc_parser/CMakeLists.txt index ea51377..85b8aec 100644 --- a/tests/unit_tests/dbc_parser/CMakeLists.txt +++ b/tests/unit_tests/dbc_parser/CMakeLists.txt @@ -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 (UnitTest_DBC_Parser VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/dbc_parser/dbc_parser_test.cpp b/tests/unit_tests/dbc_parser/dbc_parser_test.cpp index 18753c1..752e8bc 100644 --- a/tests/unit_tests/dbc_parser/dbc_parser_test.cpp +++ b/tests/unit_tests/dbc_parser/dbc_parser_test.cpp @@ -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 "../../include/gtest_custom.h" #include "dbc_parser_test.h" #include "../../../global/dbcparser/dbcparser.cpp" diff --git a/tests/unit_tests/dbc_parser/dbc_parser_test.h b/tests/unit_tests/dbc_parser/dbc_parser_test.h index 0db7129..2f854c2 100644 --- a/tests/unit_tests/dbc_parser/dbc_parser_test.h +++ b/tests/unit_tests/dbc_parser/dbc_parser_test.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 COMMANDLINE_PARSER_TEST_H #define COMMANDLINE_PARSER_TEST_H diff --git a/tests/unit_tests/dbc_parser/main.cpp b/tests/unit_tests/dbc_parser/main.cpp index d934a84..e988681 100644 --- a/tests/unit_tests/dbc_parser/main.cpp +++ b/tests/unit_tests/dbc_parser/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../../global/localmemmgr.h" diff --git a/tests/unit_tests/flags_type/CMakeLists.txt b/tests/unit_tests/flags_type/CMakeLists.txt index d6b57ca..3c73e07 100644 --- a/tests/unit_tests/flags_type/CMakeLists.txt +++ b/tests/unit_tests/flags_type/CMakeLists.txt @@ -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(FlagsTypeTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/flags_type/flags_test.cpp b/tests/unit_tests/flags_type/flags_test.cpp index d8ca613..18e32a4 100644 --- a/tests/unit_tests/flags_type/flags_test.cpp +++ b/tests/unit_tests/flags_type/flags_test.cpp @@ -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 #include #include "../../../global/process_watchdog.h" diff --git a/tests/unit_tests/idl_compiler/CMakeLists.txt b/tests/unit_tests/idl_compiler/CMakeLists.txt index a64ccaf..c715cb1 100644 --- a/tests/unit_tests/idl_compiler/CMakeLists.txt +++ b/tests/unit_tests/idl_compiler/CMakeLists.txt @@ -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 (UnitTest_IDLCompiler VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/idl_compiler/array.idl b/tests/unit_tests/idl_compiler/array.idl index 54fc22e..959385e 100644 --- a/tests/unit_tests/idl_compiler/array.idl +++ b/tests/unit_tests/idl_compiler/array.idl @@ -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 + ********************************************************************************/ + const int32 iGlobalLength = 32; const char szGlobalArray1[] = "12345678901234567890123456789801"; diff --git a/tests/unit_tests/idl_compiler/code_to_text_test.cpp b/tests/unit_tests/idl_compiler/code_to_text_test.cpp index 21b7586..2565697 100644 --- a/tests/unit_tests/idl_compiler/code_to_text_test.cpp +++ b/tests/unit_tests/idl_compiler/code_to_text_test.cpp @@ -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 "includes.h" #include "lexer_test.h" #include "../../../sdv_executables/sdv_idl_compiler/support.h" diff --git a/tests/unit_tests/idl_compiler/commandline_test.cpp b/tests/unit_tests/idl_compiler/commandline_test.cpp index 1038451..65a4999 100644 --- a/tests/unit_tests/idl_compiler/commandline_test.cpp +++ b/tests/unit_tests/idl_compiler/commandline_test.cpp @@ -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 "includes.h" #include "lexer_test.h" #include "../../../sdv_executables/sdv_idl_compiler/core_idl_backup.h" diff --git a/tests/unit_tests/idl_compiler/compiler_test.cpp b/tests/unit_tests/idl_compiler/compiler_test.cpp index 030c350..ef22e20 100644 --- a/tests/unit_tests/idl_compiler/compiler_test.cpp +++ b/tests/unit_tests/idl_compiler/compiler_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/constvariant_test.cpp b/tests/unit_tests/idl_compiler/constvariant_test.cpp index 428c55f..ac7aa46 100644 --- a/tests/unit_tests/idl_compiler/constvariant_test.cpp +++ b/tests/unit_tests/idl_compiler/constvariant_test.cpp @@ -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 "includes.h" #include "lexer_test.h" #include "../../../sdv_executables/sdv_idl_compiler/constvariant.cpp" diff --git a/tests/unit_tests/idl_compiler/generator_interface_id_test.cpp b/tests/unit_tests/idl_compiler/generator_interface_id_test.cpp index 2763f1c..29cb04d 100644 --- a/tests/unit_tests/idl_compiler/generator_interface_id_test.cpp +++ b/tests/unit_tests/idl_compiler/generator_interface_id_test.cpp @@ -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 "includes.h" #include #include "parser_test.h" diff --git a/tests/unit_tests/idl_compiler/generator_ps_interface_test.cpp b/tests/unit_tests/idl_compiler/generator_ps_interface_test.cpp index 9d5420d..b9460fa 100644 --- a/tests/unit_tests/idl_compiler/generator_ps_interface_test.cpp +++ b/tests/unit_tests/idl_compiler/generator_ps_interface_test.cpp @@ -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 "includes.h" #include "generated/ps/interfaces_proxy.cpp" #include "generated/ps/interfaces_stub.cpp" diff --git a/tests/unit_tests/idl_compiler/generator_ps_test.cpp b/tests/unit_tests/idl_compiler/generator_ps_test.cpp index 45dc3ac..1f88201 100644 --- a/tests/unit_tests/idl_compiler/generator_ps_test.cpp +++ b/tests/unit_tests/idl_compiler/generator_ps_test.cpp @@ -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 "includes.h" #include "generated/array.h" #include "generated/ps/test_proxy.cpp" @@ -991,7 +1004,7 @@ TEST_F(CGeneratorTest, OperationAny) EXPECT_TRUE(anyVal.empty()); - proxy.Access().SetAny(sdv::any_t("ansi")); + proxy.Access().SetAny("ansi"); proxy.Access().GetAny(anyVal); @@ -1188,7 +1201,7 @@ TEST_F(CGeneratorTest, AttributeAny) EXPECT_TRUE(anyVal.empty()); - proxy.Access().set_anyMyValue(sdv::any_t("ansi")); + proxy.Access().set_anyMyValue("ansi"); anyVal = proxy.Access().get_anyMyValue(); diff --git a/tests/unit_tests/idl_compiler/generator_test.cpp b/tests/unit_tests/idl_compiler/generator_test.cpp index 13f6ebf..354e883 100644 --- a/tests/unit_tests/idl_compiler/generator_test.cpp +++ b/tests/unit_tests/idl_compiler/generator_test.cpp @@ -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 "includes.h" #include "generator_test.h" #include diff --git a/tests/unit_tests/idl_compiler/generator_test.h b/tests/unit_tests/idl_compiler/generator_test.h index abddc84..e9bf905 100644 --- a/tests/unit_tests/idl_compiler/generator_test.h +++ b/tests/unit_tests/idl_compiler/generator_test.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 GENERATOR_TEST_H #define GENERATOR_TEST_H diff --git a/tests/unit_tests/idl_compiler/idl_interface_id_compatibility.cpp b/tests/unit_tests/idl_compiler/idl_interface_id_compatibility.cpp index 9608158..c12b078 100644 --- a/tests/unit_tests/idl_compiler/idl_interface_id_compatibility.cpp +++ b/tests/unit_tests/idl_compiler/idl_interface_id_compatibility.cpp @@ -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 "includes.h" #include "parser_test.h" #include diff --git a/tests/unit_tests/idl_compiler/includes.h b/tests/unit_tests/idl_compiler/includes.h index 65c8098..12e716f 100644 --- a/tests/unit_tests/idl_compiler/includes.h +++ b/tests/unit_tests/idl_compiler/includes.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 INCLUDES_H #define INCLUDES_H diff --git a/tests/unit_tests/idl_compiler/interfaces.idl b/tests/unit_tests/idl_compiler/interfaces.idl index 5cad758..86d9f1c 100644 --- a/tests/unit_tests/idl_compiler/interfaces.idl +++ b/tests/unit_tests/idl_compiler/interfaces.idl @@ -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 enum EHello diff --git a/tests/unit_tests/idl_compiler/lexer_test.cpp b/tests/unit_tests/idl_compiler/lexer_test.cpp index 1f60c59..4f04d4b 100644 --- a/tests/unit_tests/idl_compiler/lexer_test.cpp +++ b/tests/unit_tests/idl_compiler/lexer_test.cpp @@ -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 "includes.h" #include "lexer_test.h" #include "../../../sdv_executables/sdv_idl_compiler/lexer.cpp" diff --git a/tests/unit_tests/idl_compiler/lexer_test.h b/tests/unit_tests/idl_compiler/lexer_test.h index 9a559a6..c14b4bc 100644 --- a/tests/unit_tests/idl_compiler/lexer_test.h +++ b/tests/unit_tests/idl_compiler/lexer_test.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 LEXER_TEST_H #define LEXER_TEST_H diff --git a/tests/unit_tests/idl_compiler/linked.idl b/tests/unit_tests/idl_compiler/linked.idl index c694ad2..e9670b5 100644 --- a/tests/unit_tests/idl_compiler/linked.idl +++ b/tests/unit_tests/idl_compiler/linked.idl @@ -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 + ********************************************************************************/ + exception InsufficientFunds { int32 i; diff --git a/tests/unit_tests/idl_compiler/main.cpp b/tests/unit_tests/idl_compiler/main.cpp index 308cd60..7fce3d7 100644 --- a/tests/unit_tests/idl_compiler/main.cpp +++ b/tests/unit_tests/idl_compiler/main.cpp @@ -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 "includes.h" #include "../../../global/process_watchdog.h" #include "generated/test.h" diff --git a/tests/unit_tests/idl_compiler/parser_const_assignment_test.cpp b/tests/unit_tests/idl_compiler/parser_const_assignment_test.cpp index ffe48a0..9bc1e63 100644 --- a/tests/unit_tests/idl_compiler/parser_const_assignment_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_const_assignment_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/parser_enum_test.cpp b/tests/unit_tests/idl_compiler/parser_enum_test.cpp index 6f01dd8..df7e696 100644 --- a/tests/unit_tests/idl_compiler/parser_enum_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_enum_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/parser_exception_test.cpp b/tests/unit_tests/idl_compiler/parser_exception_test.cpp index 11f4c05..2305151 100644 --- a/tests/unit_tests/idl_compiler/parser_exception_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_exception_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/parser_ext_test.cpp b/tests/unit_tests/idl_compiler/parser_ext_test.cpp index 6320d50..d79c83d 100644 --- a/tests/unit_tests/idl_compiler/parser_ext_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_ext_test.cpp @@ -1,3 +1,15 @@ +/******************************************************************************** + * 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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/parser_interface_test.cpp b/tests/unit_tests/idl_compiler/parser_interface_test.cpp index f3d77a4..068ff02 100644 --- a/tests/unit_tests/idl_compiler/parser_interface_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_interface_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/parser_meta_test.cpp b/tests/unit_tests/idl_compiler/parser_meta_test.cpp index 782a1d7..d2d93b2 100644 --- a/tests/unit_tests/idl_compiler/parser_meta_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_meta_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/entities/meta_entity.cpp" diff --git a/tests/unit_tests/idl_compiler/parser_module_test.cpp b/tests/unit_tests/idl_compiler/parser_module_test.cpp index e39445f..f56a62b 100644 --- a/tests/unit_tests/idl_compiler/parser_module_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_module_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/entities/module_entity.cpp" diff --git a/tests/unit_tests/idl_compiler/parser_struct_test.cpp b/tests/unit_tests/idl_compiler/parser_struct_test.cpp index 93d50cd..643da7a 100644 --- a/tests/unit_tests/idl_compiler/parser_struct_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_struct_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/parser_test.cpp b/tests/unit_tests/idl_compiler/parser_test.cpp index 1892293..92de37d 100644 --- a/tests/unit_tests/idl_compiler/parser_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.cpp" diff --git a/tests/unit_tests/idl_compiler/parser_test.h b/tests/unit_tests/idl_compiler/parser_test.h index b35ae60..2851f25 100644 --- a/tests/unit_tests/idl_compiler/parser_test.h +++ b/tests/unit_tests/idl_compiler/parser_test.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 PARSER_TEST_H #define PARSER_TEST_H diff --git a/tests/unit_tests/idl_compiler/parser_typedef_test.cpp b/tests/unit_tests/idl_compiler/parser_typedef_test.cpp index b3a6080..7651563 100644 --- a/tests/unit_tests/idl_compiler/parser_typedef_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_typedef_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/parser_union_test.cpp b/tests/unit_tests/idl_compiler/parser_union_test.cpp index 0920e01..312a1b4 100644 --- a/tests/unit_tests/idl_compiler/parser_union_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_union_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/parser_var_assignment_test.cpp b/tests/unit_tests/idl_compiler/parser_var_assignment_test.cpp index 7287247..73ae612 100644 --- a/tests/unit_tests/idl_compiler/parser_var_assignment_test.cpp +++ b/tests/unit_tests/idl_compiler/parser_var_assignment_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/parser.h" diff --git a/tests/unit_tests/idl_compiler/preproc_test.cpp b/tests/unit_tests/idl_compiler/preproc_test.cpp index 8628208..019919d 100644 --- a/tests/unit_tests/idl_compiler/preproc_test.cpp +++ b/tests/unit_tests/idl_compiler/preproc_test.cpp @@ -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 "includes.h" #include "parser_test.h" #include "../../../sdv_executables/sdv_idl_compiler/preproc.cpp" diff --git a/tests/unit_tests/idl_compiler/source_test.cpp b/tests/unit_tests/idl_compiler/source_test.cpp index 9be3f2d..47f1249 100644 --- a/tests/unit_tests/idl_compiler/source_test.cpp +++ b/tests/unit_tests/idl_compiler/source_test.cpp @@ -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 "includes.h" #include "lexer_test.h" #include "../../../sdv_executables/sdv_idl_compiler/source.cpp" diff --git a/tests/unit_tests/idl_compiler/test.idl b/tests/unit_tests/idl_compiler/test.idl index cc0f2b9..6e75925 100644 --- a/tests/unit_tests/idl_compiler/test.idl +++ b/tests/unit_tests/idl_compiler/test.idl @@ -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 #include "linked.idl" // Test: split nested module definitions diff --git a/tests/unit_tests/idl_compiler/test_ifc_id1.idl b/tests/unit_tests/idl_compiler/test_ifc_id1.idl index 015b355..328621e 100644 --- a/tests/unit_tests/idl_compiler/test_ifc_id1.idl +++ b/tests/unit_tests/idl_compiler/test_ifc_id1.idl @@ -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 /** diff --git a/tests/unit_tests/idl_compiler/test_ifc_id2.idl b/tests/unit_tests/idl_compiler/test_ifc_id2.idl index 60f219b..6eca0e1 100644 --- a/tests/unit_tests/idl_compiler/test_ifc_id2.idl +++ b/tests/unit_tests/idl_compiler/test_ifc_id2.idl @@ -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 /** diff --git a/tests/unit_tests/idl_compiler/union.idl b/tests/unit_tests/idl_compiler/union.idl index c2ccc7e..a260a21 100644 --- a/tests/unit_tests/idl_compiler/union.idl +++ b/tests/unit_tests/idl_compiler/union.idl @@ -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 + ********************************************************************************/ + // Several issues exist with the code generation of the unions: // - Forward declaration of unions with value based switch --> forward declarated type is union and impl. type is struct // - Unions with complex members --> constructor and destructor are deleted diff --git a/tests/unit_tests/install_package_composer/CMakeLists.txt b/tests/unit_tests/install_package_composer/CMakeLists.txt index 2b1d4d6..987ad5b 100644 --- a/tests/unit_tests/install_package_composer/CMakeLists.txt +++ b/tests/unit_tests/install_package_composer/CMakeLists.txt @@ -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(InstallPackageComposerTests VERSION 1.0 LANGUAGES CXX) @@ -38,7 +51,6 @@ set_target_properties(UnitTest_InstallPackageComposer_Component2 PROPERTIES PREF set_target_properties(UnitTest_InstallPackageComposer_Component2 PROPERTIES SUFFIX ".sdv") # Define target -# TODO EVE add_executable(UnitTest_InstallPackageComposer composer_test_suite.cpp composer_test_suite.h diff --git a/tests/unit_tests/install_package_composer/composer_test_suite.cpp b/tests/unit_tests/install_package_composer/composer_test_suite.cpp index c14c1cf..29bc8ad 100644 --- a/tests/unit_tests/install_package_composer/composer_test_suite.cpp +++ b/tests/unit_tests/install_package_composer/composer_test_suite.cpp @@ -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 "composer_test_suite.h" #include #include @@ -13,6 +26,7 @@ #include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp" #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/miscellaneous.cpp" +#include "../../../sdv_services/core/toml_parser/code_snippet.cpp" #include #if defined(_WIN32) && defined(_UNICODE) diff --git a/tests/unit_tests/install_package_composer/composer_test_suite.h b/tests/unit_tests/install_package_composer/composer_test_suite.h index b83f98a..1043d70 100644 --- a/tests/unit_tests/install_package_composer/composer_test_suite.h +++ b/tests/unit_tests/install_package_composer/composer_test_suite.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 COMPOSER_TEST_SUITE_H #define COMPOSER_TEST_SUITE_H diff --git a/tests/unit_tests/install_package_composer/composer_tests.cpp b/tests/unit_tests/install_package_composer/composer_tests.cpp index fade90c..7983044 100644 --- a/tests/unit_tests/install_package_composer/composer_tests.cpp +++ b/tests/unit_tests/install_package_composer/composer_tests.cpp @@ -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 "composer_test_suite.h" #include "../../../sdv_services/core/installation_composer.h" #include "../../../global/exec_dir_helper.h" @@ -2412,17 +2425,17 @@ TEST_F(CInstallPackageComposerTest, ComposeSDVPackage) // The amount of modules should be 2 auto vecModules = manifest.ModuleList(); EXPECT_EQ(vecModules.size(), 2); - auto vecComponents = manifest.ComponentList(); - EXPECT_EQ(vecComponents.size(), 3); + auto vecClasses = manifest.ClassList(); + EXPECT_EQ(vecClasses.size(), 3); // The name of the module should fit to the expected file std::string szssComponents[] = { "DummyService #1", "DummyDevice", "DummyService #2" }; for (auto& rssComponent : szssComponents) { - EXPECT_NE(std::find_if(vecComponents.begin(), vecComponents.end(), [&](const CInstallManifest::SComponent& rsComponent) + EXPECT_NE(std::find_if(vecClasses.begin(), vecClasses.end(), [&](const sdv::SClassInfo& rsClass) { - return rsComponent.ssClassName == rssComponent; - }), vecComponents.end()); + return rsClass.ssName == rssComponent; + }), vecClasses.end()); } } diff --git a/tests/unit_tests/install_package_composer/dummy_component1.cpp b/tests/unit_tests/install_package_composer/dummy_component1.cpp index c542634..5b66f52 100644 --- a/tests/unit_tests/install_package_composer/dummy_component1.cpp +++ b/tests/unit_tests/install_package_composer/dummy_component1.cpp @@ -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 /** @@ -10,7 +23,7 @@ public: BEGIN_SDV_INTERFACE_MAP() END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::complex_service) DECLARE_OBJECT_CLASS_NAME("DummyService #1") DECLARE_OBJECT_CLASS_ALIAS("Dummy1", "DummySvc1") DECLARE_DEFAULT_OBJECT_NAME("MyDummy") @@ -29,7 +42,7 @@ public: BEGIN_SDV_INTERFACE_MAP() END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("DummyDevice") }; DEFINE_SDV_OBJECT(CDummyDevice) diff --git a/tests/unit_tests/install_package_composer/dummy_component2.cpp b/tests/unit_tests/install_package_composer/dummy_component2.cpp index a5e1c15..ff57e1e 100644 --- a/tests/unit_tests/install_package_composer/dummy_component2.cpp +++ b/tests/unit_tests/install_package_composer/dummy_component2.cpp @@ -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 class CDummyService2 : public sdv::CSdvObject @@ -7,7 +20,7 @@ public: BEGIN_SDV_INTERFACE_MAP() END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::complex_service) DECLARE_OBJECT_CLASS_NAME("DummyService #2") }; diff --git a/tests/unit_tests/install_package_composer/environment_tests.cpp b/tests/unit_tests/install_package_composer/environment_tests.cpp index 0615b4b..8efc4cc 100644 --- a/tests/unit_tests/install_package_composer/environment_tests.cpp +++ b/tests/unit_tests/install_package_composer/environment_tests.cpp @@ -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 + ********************************************************************************/ + #ifdef _WIN32 #include #include @@ -532,7 +545,7 @@ TEST_F(CEnvironmentTest, INSTALL_Directories) std::filesystem::path pathTargetDir = GetExecDirectory() / "install_package_composer_targets"; CCmdlnStr cmdlnLocalMissingTarget(std::string("INSTALL \"") + pathPackage1.generic_u8string() + "\" --local"); - CCmdlnStr cmdlnTargetMissingLocal(std::string("INSTALL \"") + pathPackage1.generic_u8string() + "\" \"-T" + pathTargetDir.generic_u8string() + "\""); + CCmdlnStr cmdlnAutomaticTargetServer(std::string("INSTALL \"") + pathPackage1.generic_u8string() + "\" \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnAvailable(std::string("INSTALL \"") + pathPackage1.generic_u8string() + "\" --local \"-T" + pathTargetDir.generic_u8string() + "\""); CSdvPackagerEnvironment environment; @@ -542,11 +555,11 @@ TEST_F(CEnvironmentTest, INSTALL_Directories) EXPECT_TRUE(environment.TargetLocation().empty()); EXPECT_EQ(environment.Error(), CMDLN_TARGET_LOCATION_ERROR); - EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnTargetMissingLocal)); + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAutomaticTargetServer)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::install); EXPECT_FALSE(environment.Local()); EXPECT_EQ(environment.TargetLocation(), pathTargetDir); - EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); + EXPECT_EQ(environment.Error(), NO_ERROR); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAvailable)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::install); @@ -644,29 +657,24 @@ TEST_F(CEnvironmentTest, INSTALL_LocalConfig) pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnConfigFileMissingLocal(std::string("INSTALL \"") + pathPackage1.generic_u8string() + "\" \"--config_file" + pathConfig.generic_u8string() + "\" \"-T" + pathTargetDir.generic_u8string() + "\""); - CCmdlnStr cmdlnConfigActivateConfigMissingLocal(std::string("INSTALL \"") + pathPackage1.generic_u8string() + - "\" \"--config_file" + pathConfig.generic_u8string() + "\" --activate_config \"-T" + pathTargetDir.generic_u8string() + "\""); - CCmdlnStr cmdlnConfigActivateConfigMissingConfig(std::string("INSTALL \"") + pathPackage1.generic_u8string() + - "\" --local --activate_config \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnAvailable(std::string("INSTALL \"") + pathPackage1.generic_u8string() + "\" \"--config_dir" + pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"--config_file" + - pathConfig.filename().generic_u8string() + "\" --activate_config --local \"-T" + pathTargetDir.generic_u8string() + "\""); + pathConfig.filename().generic_u8string() + "\" --local \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnAvailableExtendedPlus(std::string("INSTALL \"") + pathPackage1.generic_u8string() + "\" \"--config_dir" + pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"--config_file" + - pathConfig.filename().generic_u8string() + "+abc+def\" --activate_config --local \"-T" + pathTargetDir.generic_u8string() + "\""); + pathConfig.filename().generic_u8string() + "+abc+def\" --local \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnAvailableExtendedComma(std::string("INSTALL \"") + pathPackage1.generic_u8string() + "\" \"--config_dir" + pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"--config_file" + - pathConfig.filename().generic_u8string() + "+abc,def\" --activate_config --local \"-T" + pathTargetDir.generic_u8string() + "\""); + pathConfig.filename().generic_u8string() + "+abc,def\" --local \"-T" + pathTargetDir.generic_u8string() + "\""); CSdvPackagerEnvironment environment; - std::vector vecComponents; + CSdvPackagerEnvironment::CComponentVector vecComponents; EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigDirMissingLocal)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::install); EXPECT_FALSE(environment.Local()); EXPECT_EQ(environment.LocalConfigLocations().size(), 2); EXPECT_TRUE(environment.LocalConfigFile(vecComponents).empty()); EXPECT_TRUE(vecComponents.empty()); - EXPECT_FALSE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigFileMissingLocal)); @@ -675,25 +683,6 @@ TEST_F(CEnvironmentTest, INSTALL_LocalConfig) EXPECT_TRUE(environment.LocalConfigLocations().empty()); EXPECT_EQ(environment.LocalConfigFile(vecComponents), pathConfig); EXPECT_TRUE(vecComponents.empty()); - EXPECT_FALSE(environment.ActivateLocalConfig()); - EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); - - EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigActivateConfigMissingLocal)); - EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::install); - EXPECT_FALSE(environment.Local()); - EXPECT_TRUE(environment.LocalConfigLocations().empty()); - EXPECT_EQ(environment.LocalConfigFile(vecComponents), pathConfig); - EXPECT_TRUE(vecComponents.empty()); - EXPECT_TRUE(environment.ActivateLocalConfig()); - EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); - - EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigActivateConfigMissingConfig)); - EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::install); - EXPECT_TRUE(environment.Local()); - EXPECT_TRUE(environment.LocalConfigLocations().empty()); - EXPECT_TRUE(environment.LocalConfigFile(vecComponents).empty()); - EXPECT_TRUE(vecComponents.empty()); - EXPECT_TRUE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAvailable)); @@ -702,7 +691,6 @@ TEST_F(CEnvironmentTest, INSTALL_LocalConfig) EXPECT_EQ(environment.LocalConfigLocations().size(), 2); EXPECT_EQ(environment.LocalConfigFile(vecComponents), "config.toml"); EXPECT_TRUE(vecComponents.empty()); - EXPECT_TRUE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), NO_ERROR); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAvailableExtendedPlus)); @@ -711,7 +699,6 @@ TEST_F(CEnvironmentTest, INSTALL_LocalConfig) EXPECT_EQ(environment.LocalConfigLocations().size(), 2); EXPECT_EQ(environment.LocalConfigFile(vecComponents), "config.toml"); EXPECT_EQ(vecComponents.size(), 2); - EXPECT_TRUE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), NO_ERROR); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAvailableExtendedComma)); @@ -720,7 +707,6 @@ TEST_F(CEnvironmentTest, INSTALL_LocalConfig) EXPECT_EQ(environment.LocalConfigLocations().size(), 2); EXPECT_EQ(environment.LocalConfigFile(vecComponents), "config.toml"); EXPECT_EQ(vecComponents.size(), 2); - EXPECT_TRUE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), NO_ERROR); } @@ -766,7 +752,7 @@ TEST_F(CEnvironmentTest, INSTALL_ServerConfig) "\""); CSdvPackagerEnvironment environment; - std::vector vecComponents; + CSdvPackagerEnvironment::CComponentVector vecComponents; EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnMissing)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::install); EXPECT_FALSE(environment.Local()); @@ -1230,7 +1216,7 @@ TEST_F(CEnvironmentTest, DIRECT_INSTALL_Directories) CCmdlnStr cmdlnNoSourceDir("DIRECT_INSTALL abc file1.bin"); CCmdlnStr cmdlnAvailable(std::string("DIRECT_INSTALL abc file1.bin \"-I") + pathSrcDir.generic_u8string() + "\" "); CCmdlnStr cmdlnLocalMissingTarget(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" --local"); - CCmdlnStr cmdlnTargetMissingLocal(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"-T" + pathTargetDir.generic_u8string() + "\""); + CCmdlnStr cmdlnAutomaticTargetServer(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnLocalAvailable(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" --local \"-T" + pathTargetDir.generic_u8string() + "\""); CSdvPackagerEnvironment environment; @@ -1255,11 +1241,11 @@ TEST_F(CEnvironmentTest, DIRECT_INSTALL_Directories) EXPECT_TRUE(environment.TargetLocation().empty()); EXPECT_EQ(environment.Error(), CMDLN_TARGET_LOCATION_ERROR); - EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnTargetMissingLocal)); + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAutomaticTargetServer)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::direct_install); EXPECT_FALSE(environment.Local()); EXPECT_EQ(environment.TargetLocation(), pathTargetDir); - EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); + EXPECT_EQ(environment.Error(), NO_ERROR); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnLocalAvailable)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::direct_install); @@ -1360,29 +1346,24 @@ TEST_F(CEnvironmentTest, DIRECT_INSTALL_LocalConfig) pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnConfigFileMissingLocal(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"--config_file" + pathConfig.generic_u8string() + "\" \"-T" + pathTargetDir.generic_u8string() + "\""); - CCmdlnStr cmdlnConfigActivateConfigMissingLocal(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + - "\" \"--config_file" + pathConfig.generic_u8string() + "\" --activate_config \"-T" + pathTargetDir.generic_u8string() + "\""); - CCmdlnStr cmdlnConfigActivateConfigMissingConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + - "\" --local --activate_config \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnAvailable(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"--config_dir" + pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"--config_file" + - pathConfig.filename().generic_u8string() + "\" --activate_config --local \"-T" + pathTargetDir.generic_u8string() + "\""); + pathConfig.filename().generic_u8string() + "\" --local \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnAvailableExtendedPlus(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"--config_dir" + pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"--config_file" + - pathConfig.filename().generic_u8string() + "+abc+def\" --activate_config --local \"-T" + pathTargetDir.generic_u8string() + "\""); + pathConfig.filename().generic_u8string() + "+abc+def\" --local \"-T" + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnAvailableExtendedComma(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"--config_dir" + pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"--config_file" + - pathConfig.filename().generic_u8string() + "+abc,def\" --activate_config --local \"-T" + pathTargetDir.generic_u8string() + "\""); + pathConfig.filename().generic_u8string() + "+abc,def\" --local \"-T" + pathTargetDir.generic_u8string() + "\""); CSdvPackagerEnvironment environment; - std::vector vecComponents; + CSdvPackagerEnvironment::CComponentVector vecComponents; EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigDirMissingLocal)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::direct_install); EXPECT_FALSE(environment.Local()); EXPECT_EQ(environment.LocalConfigLocations().size(), 2); EXPECT_TRUE(environment.LocalConfigFile(vecComponents).empty()); EXPECT_TRUE(vecComponents.empty()); - EXPECT_FALSE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigFileMissingLocal)); @@ -1391,25 +1372,6 @@ TEST_F(CEnvironmentTest, DIRECT_INSTALL_LocalConfig) EXPECT_TRUE(environment.LocalConfigLocations().empty()); EXPECT_EQ(environment.LocalConfigFile(vecComponents), pathConfig); EXPECT_TRUE(vecComponents.empty()); - EXPECT_FALSE(environment.ActivateLocalConfig()); - EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); - - EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigActivateConfigMissingLocal)); - EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::direct_install); - EXPECT_FALSE(environment.Local()); - EXPECT_TRUE(environment.LocalConfigLocations().empty()); - EXPECT_EQ(environment.LocalConfigFile(vecComponents), pathConfig); - EXPECT_TRUE(vecComponents.empty()); - EXPECT_TRUE(environment.ActivateLocalConfig()); - EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); - - EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigActivateConfigMissingConfig)); - EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::direct_install); - EXPECT_TRUE(environment.Local()); - EXPECT_TRUE(environment.LocalConfigLocations().empty()); - EXPECT_TRUE(environment.LocalConfigFile(vecComponents).empty()); - EXPECT_TRUE(vecComponents.empty()); - EXPECT_TRUE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAvailable)); @@ -1418,7 +1380,6 @@ TEST_F(CEnvironmentTest, DIRECT_INSTALL_LocalConfig) EXPECT_EQ(environment.LocalConfigLocations().size(), 2); EXPECT_EQ(environment.LocalConfigFile(vecComponents), "config.toml"); EXPECT_TRUE(vecComponents.empty()); - EXPECT_TRUE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), NO_ERROR); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAvailableExtendedPlus)); @@ -1427,7 +1388,6 @@ TEST_F(CEnvironmentTest, DIRECT_INSTALL_LocalConfig) EXPECT_EQ(environment.LocalConfigLocations().size(), 2); EXPECT_EQ(environment.LocalConfigFile(vecComponents), "config.toml"); EXPECT_EQ(vecComponents.size(), 2); - EXPECT_TRUE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), NO_ERROR); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAvailableExtendedComma)); @@ -1436,7 +1396,6 @@ TEST_F(CEnvironmentTest, DIRECT_INSTALL_LocalConfig) EXPECT_EQ(environment.LocalConfigLocations().size(), 2); EXPECT_EQ(environment.LocalConfigFile(vecComponents), "config.toml"); EXPECT_EQ(vecComponents.size(), 2); - EXPECT_TRUE(environment.ActivateLocalConfig()); EXPECT_EQ(environment.Error(), NO_ERROR); } @@ -1467,7 +1426,7 @@ TEST_F(CEnvironmentTest, DIRECT_INSTALL_ServerConfig) CCmdlnStr cmdlnAllLocal(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" --user_config --platform_config --interface_config --abstract_config --local \"-T" + pathTargetDir.generic_u8string() + "\""); CSdvPackagerEnvironment environment; - std::vector vecComponents; + CSdvPackagerEnvironment::CComponentVector vecComponents; EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnMissing)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::direct_install); EXPECT_FALSE(environment.Local()); @@ -1716,6 +1675,270 @@ TEST_F(CEnvironmentTest, DIRECT_INSTALL_ServerConfig) EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); } +TEST_F(CEnvironmentTest, DIRECT_INSTALL_ConfigParameters) +{ + std::filesystem::path pathSrcDir = GetExecDirectory() / "install_package_composer_sources"; + std::filesystem::path pathSrcDir2 = GetExecDirectory() / "install_package_composer_sources" / "dummy_package"; + std::filesystem::path pathSrcFile = pathSrcDir2 / "file1.bin"; + std::filesystem::path pathConfig = pathSrcDir / "config.toml"; + std::filesystem::path pathTargetDir = GetExecDirectory() / "install_package_composer_targets"; + + CCmdlnStr cmdlnConfigMissing(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\"" + + " \"--parametersmy_component1:param1=10,param2=20,groupA.param3=string value\"" + + " \"--parametersmy_test21:param5=,groupB.param6=60\""); + CCmdlnStr cmdlnLocalConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"--config_dir" + + pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"--config_file" + + pathConfig.filename().generic_u8string() + "+my_component1+my_component2=mytest21\" --local \"-T" + + pathTargetDir.generic_u8string() + "\"" + " \"--parametersmy_component1:param1=10,param2=20,groupA.param3=string value\"" + + " \"--parametersmy_test21:param5=,groupB.param6=60\""); + CCmdlnStr cmdlnServerUserConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"--user_config" + + "+my_component1+my_component2=mytest21\" \"-T" + pathTargetDir.generic_u8string() + "\"" + + " \"--parametersmy_component1:param1=10,param2=20,groupA.param3=string value\"" + + " \"--parametersmy_test21:param5=,groupB.param6=60\""); + CCmdlnStr cmdlnServerPlatformConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + + "\" \"--platform_config" + "+my_component1+my_component2=mytest21\" \"-T" + pathTargetDir.generic_u8string() + "\"" + + " \"--parametersmy_component1:param1=10,param2=20,groupA.param3=string value\"" + + " \"--parametersmy_test21:param5=,groupB.param6=60\""); + CCmdlnStr cmdlnServerInterfaceConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + + "\" \"--interface_config" + "+my_component1+my_component2=mytest21\" \"-T" + pathTargetDir.generic_u8string() + "\"" + + " \"--parametersmy_component1:param1=10,param2=20,groupA.param3=string value\"" + + " \"--parametersmy_test21:param5=,groupB.param6=60\""); + CCmdlnStr cmdlnServerAbstractConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + + "\" \"--abstract_config" + "+my_component1+my_component2=mytest21\" \"-T" + pathTargetDir.generic_u8string() + "\"" + + " \"--parametersmy_component1:param1=10,param2=20,groupA.param3=string value\"" + + " \"--parametersmy_test21:param5=,groupB.param6=60\""); + + CSdvPackagerEnvironment environment; + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigMissing)); + EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnLocalConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + auto vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_TRUE(vecParameters[0].second.empty()); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerUserConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_TRUE(vecParameters[0].second.empty()); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerPlatformConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_TRUE(vecParameters[0].second.empty()); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerInterfaceConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_TRUE(vecParameters[0].second.empty()); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerAbstractConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_TRUE(vecParameters[0].second.empty()); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + // TODO: + // - Check for invalid parameter string + // - Check for duplicate parameters (which is not an error). +} + +TEST_F(CEnvironmentTest, DIRECT_INSTALL_ConfigParameterFile) +{ + std::filesystem::path pathSrcDir = GetExecDirectory() / "install_package_composer_sources"; + std::filesystem::path pathSrcDir2 = GetExecDirectory() / "install_package_composer_sources" / "dummy_package"; + std::filesystem::path pathSrcFile = pathSrcDir2 / "file1.bin"; + std::filesystem::path pathConfig = pathSrcDir / "config.toml"; + std::filesystem::path pathTargetDir = GetExecDirectory() / "install_package_composer_targets"; + + std::ofstream fstream("param_test_file.toml"); + ASSERT_TRUE(fstream.is_open()); + fstream << R"toml(# Parameters for my_component1 +[my_component1] +param1 = 10 +param2 = 20 +[my_component1.groupA] # Example with additional table for the GroupA parameters +param3 = "string value" + +# Parameters for test21 (instance of my_component2) +[my_test21] +param5=50 +groupB.param6=60 # Grouping using inline table for GroupB parameters +)toml"; + fstream.close(); + + CCmdlnStr cmdlnConfigMissing(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\"" + + " \"--param_fileparam_test_file.toml\""); + CCmdlnStr cmdlnLocalConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"--config_dir" + + pathSrcDir.generic_u8string() + "," + pathTargetDir.generic_u8string() + "\" \"--config_file" + + pathConfig.filename().generic_u8string() + "+my_component1+my_component2=my_test21\" --local \"-T" + + pathTargetDir.generic_u8string() + "\"" + " \"--param_fileparam_test_file.toml\""); + CCmdlnStr cmdlnServerUserConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + "\" \"--user_config" + + "+my_component1+my_component2=my_test21\" \"-T" + pathTargetDir.generic_u8string() + "\"" + + " \"--param_fileparam_test_file.toml\""); + CCmdlnStr cmdlnServerPlatformConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + + "\" \"--platform_config" + "+my_component1+my_component2=my_test21\" \"-T" + pathTargetDir.generic_u8string() + "\"" + + " \"--param_fileparam_test_file.toml\""); + CCmdlnStr cmdlnServerInterfaceConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + + "\" \"--interface_config" + "+my_component1+my_component2=my_test21\" \"-T" + pathTargetDir.generic_u8string() + "\"" + + " \"--param_fileparam_test_file.toml\""); + CCmdlnStr cmdlnServerAbstractConfig(std::string("DIRECT_INSTALL abc \"") + pathSrcFile.generic_u8string() + + "\" \"--abstract_config" + "+my_component1+my_component2=my_test21\" \"-T" + pathTargetDir.generic_u8string() + "\"" + + " \"--param_fileparam_test_file.toml\""); + + CSdvPackagerEnvironment environment; + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnConfigMissing)); + EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnLocalConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + auto vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_EQ(static_cast(vecParameters[0].second), 50); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerUserConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_EQ(static_cast(vecParameters[0].second), 50); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerPlatformConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_EQ(static_cast(vecParameters[0].second), 50); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerInterfaceConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_EQ(static_cast(vecParameters[0].second), 50); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerAbstractConfig)); + EXPECT_EQ(environment.Error(), NO_ERROR); + vecParameters = environment.ObjectParameters("my_component1"); + ASSERT_EQ(vecParameters.size(), 3); + EXPECT_EQ(vecParameters[0].first, "param1"); + EXPECT_EQ(static_cast(vecParameters[0].second), 10); + EXPECT_EQ(vecParameters[1].first, "param2"); + EXPECT_EQ(static_cast(vecParameters[1].second), 20); + EXPECT_EQ(vecParameters[2].first, "groupA.param3"); + EXPECT_EQ(static_cast(vecParameters[2].second), "string value"); + vecParameters = environment.ObjectParameters("my_test21"); + ASSERT_EQ(vecParameters.size(), 2); + EXPECT_EQ(vecParameters[0].first, "param5"); + EXPECT_EQ(static_cast(vecParameters[0].second), 50); + EXPECT_EQ(vecParameters[1].first, "groupB.param6"); + EXPECT_EQ(static_cast(vecParameters[1].second), 60); + + // TODO: + // - Check for invalid parameter string + // - Check for duplicate parameters (which is not an error). +} + TEST_F(CEnvironmentTest, UNINSTALL_InstallName) { CCmdlnStr cmdlnMissing("UNINSTALL"); @@ -1781,22 +2004,29 @@ TEST_F(CEnvironmentTest, UNINSTALL_Directories) { std::filesystem::path pathTargetDir = GetExecDirectory() / "install_package_composer_targets"; + CCmdlnStr cmdlnServerAutomaticTarget("UNINSTALL abc"); CCmdlnStr cmdlnLocalMissingTarget("UNINSTALL abc --local"); - CCmdlnStr cmdlnTargetMissingLocal(std::string("UNINSTALL abc \"-T") + pathTargetDir.generic_u8string() + "\""); + CCmdlnStr cmdlnServerAvailable(std::string("UNINSTALL abc \"-T") + pathTargetDir.generic_u8string() + "\""); CCmdlnStr cmdlnAvailable(std::string("UNINSTALL abc --local \"-T") + pathTargetDir.generic_u8string() + "\""); CSdvPackagerEnvironment environment; + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerAutomaticTarget)); + EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::uninstall); + EXPECT_FALSE(environment.Local()); + EXPECT_FALSE(environment.TargetLocation().empty()); + EXPECT_EQ(environment.Error(), NO_ERROR); + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnLocalMissingTarget)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::uninstall); EXPECT_TRUE(environment.Local()); EXPECT_TRUE(environment.TargetLocation().empty()); EXPECT_EQ(environment.Error(), CMDLN_TARGET_LOCATION_ERROR); - EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnTargetMissingLocal)); + EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnServerAvailable)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::uninstall); EXPECT_FALSE(environment.Local()); EXPECT_EQ(environment.TargetLocation(), pathTargetDir); - EXPECT_EQ(environment.Error(), CMDLN_INCOMPATIBLE_ARGUMENTS); + EXPECT_EQ(environment.Error(), NO_ERROR); EXPECT_NO_THROW(environment = CSdvPackagerEnvironment(cmdlnAvailable)); EXPECT_EQ(environment.OperatingMode(), CSdvPackagerEnvironment::EOperatingMode::uninstall); diff --git a/tests/unit_tests/install_package_composer/manifest_tests.cpp b/tests/unit_tests/install_package_composer/manifest_tests.cpp index 8cddb82..5a83a51 100644 --- a/tests/unit_tests/install_package_composer/manifest_tests.cpp +++ b/tests/unit_tests/install_package_composer/manifest_tests.cpp @@ -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 #include "composer_test_suite.h" #include "../../../global/exec_dir_helper.h" @@ -146,27 +159,27 @@ TEST_F(CInstallManifestTest, WriteReadComponents) EXPECT_EQ(vecModules.size(), 2); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component1.sdv"), vecModules.end()); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component2.sdv"), vecModules.end()); - auto vecComponents = manifestWrite.ComponentList(); - EXPECT_EQ(vecComponents.size(), 3); - auto itDummyService1 = std::find_if(vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #1"; }); - ASSERT_NE(itDummyService1, vecComponents.end()); - EXPECT_EQ(itDummyService1->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - EXPECT_FALSE(itDummyService1->ssManifest.empty()); - ASSERT_EQ(itDummyService1->seqAliases.size(), 2); - EXPECT_EQ(itDummyService1->seqAliases[0], "Dummy1"); - EXPECT_EQ(itDummyService1->seqAliases[1], "DummySvc1"); + auto vecClasses = manifestWrite.ClassList(); + EXPECT_EQ(vecClasses.size(), 3); + auto itDummyService1 = std::find_if(vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyService #1"; }); + ASSERT_NE(itDummyService1, vecClasses.end()); + EXPECT_EQ(itDummyService1->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + //EXPECT_FALSE(itDummyService1->ssManifest.empty()); + ASSERT_EQ(itDummyService1->seqClassAliases.size(), 2); + EXPECT_EQ(itDummyService1->seqClassAliases[0], "Dummy1"); + EXPECT_EQ(itDummyService1->seqClassAliases[1], "DummySvc1"); EXPECT_EQ(itDummyService1->ssDefaultObjectName, "MyDummy"); - EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::ComplexService); + EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::complex_service); EXPECT_EQ(itDummyService1->uiFlags, static_cast(sdv::EObjectFlags::singleton)); ASSERT_EQ(itDummyService1->seqDependencies .size(), 2); EXPECT_EQ(itDummyService1->seqDependencies[0], "DummyDevice"); EXPECT_EQ(itDummyService1->seqDependencies[1], "DummyService #2"); - auto itDummyDevice = std::find_if(vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyDevice"; }); - ASSERT_NE(itDummyDevice, vecComponents.end()); - EXPECT_EQ(itDummyDevice->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - auto itDummyService2 = std::find_if(vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #2"; }); - ASSERT_NE(itDummyService2, vecComponents.end()); - EXPECT_EQ(itDummyService2->pathRelModule, "UnitTest_InstallPackageComposer_Component2.sdv"); + auto itDummyDevice = std::find_if(vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyDevice"; }); + ASSERT_NE(itDummyDevice, vecClasses.end()); + EXPECT_EQ(itDummyDevice->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + auto itDummyService2 = std::find_if(vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyService #2"; }); + ASSERT_NE(itDummyService2, vecClasses.end()); + EXPECT_EQ(itDummyService2->ssModulePath, "UnitTest_InstallPackageComposer_Component2.sdv"); std::string ssManifest = manifestWrite.Write(); EXPECT_FALSE(ssManifest.empty()); @@ -177,27 +190,27 @@ TEST_F(CInstallManifestTest, WriteReadComponents) EXPECT_EQ(vecModules.size(), 2); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component1.sdv"), vecModules.end()); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component2.sdv"), vecModules.end()); - vecComponents = manifestRead.ComponentList(); - EXPECT_EQ(vecComponents.size(), 3); - itDummyService1 = std::find_if(vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #1"; }); - ASSERT_NE(itDummyService1, vecComponents.end()); - EXPECT_EQ(itDummyService1->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - EXPECT_FALSE(itDummyService1->ssManifest.empty()); - ASSERT_EQ(itDummyService1->seqAliases.size(), 2); - EXPECT_EQ(itDummyService1->seqAliases[0], "Dummy1"); - EXPECT_EQ(itDummyService1->seqAliases[1], "DummySvc1"); + vecClasses = manifestRead.ClassList(); + EXPECT_EQ(vecClasses.size(), 3); + itDummyService1 = std::find_if(vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyService #1"; }); + ASSERT_NE(itDummyService1, vecClasses.end()); + EXPECT_EQ(itDummyService1->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + //EXPECT_FALSE(itDummyService1->ssManifest.empty()); + ASSERT_EQ(itDummyService1->seqClassAliases.size(), 2); + EXPECT_EQ(itDummyService1->seqClassAliases[0], "Dummy1"); + EXPECT_EQ(itDummyService1->seqClassAliases[1], "DummySvc1"); EXPECT_EQ(itDummyService1->ssDefaultObjectName, "MyDummy"); - EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::ComplexService); + EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::complex_service); EXPECT_EQ(itDummyService1->uiFlags, static_cast(sdv::EObjectFlags::singleton)); ASSERT_EQ(itDummyService1->seqDependencies .size(), 2); EXPECT_EQ(itDummyService1->seqDependencies[0], "DummyDevice"); EXPECT_EQ(itDummyService1->seqDependencies[1], "DummyService #2"); - itDummyDevice = std::find_if(vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyDevice"; }); - ASSERT_NE(itDummyDevice, vecComponents.end()); - EXPECT_EQ(itDummyDevice->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - itDummyService2 = std::find_if(vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #2"; }); - ASSERT_NE(itDummyService2, vecComponents.end()); - EXPECT_EQ(itDummyService2->pathRelModule, "UnitTest_InstallPackageComposer_Component2.sdv"); + itDummyDevice = std::find_if(vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyDevice"; }); + ASSERT_NE(itDummyDevice, vecClasses.end()); + EXPECT_EQ(itDummyDevice->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + itDummyService2 = std::find_if(vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyService #2"; }); + ASSERT_NE(itDummyService2, vecClasses.end()); + EXPECT_EQ(itDummyService2->ssModulePath, "UnitTest_InstallPackageComposer_Component2.sdv"); } TEST_F(CInstallManifestTest, WriteReadFindComponents) @@ -214,32 +227,32 @@ TEST_F(CInstallManifestTest, WriteReadFindComponents) EXPECT_EQ(vecModules.size(), 2); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component1.sdv"), vecModules.end()); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component2.sdv"), vecModules.end()); - auto vecComponents = manifestWrite.ComponentList(); - EXPECT_EQ(vecComponents.size(), 3); - auto itDummyService1 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #1"; }); - ASSERT_NE(itDummyService1, vecComponents.end()); - EXPECT_EQ(itDummyService1->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - EXPECT_FALSE(itDummyService1->ssManifest.empty()); - ASSERT_EQ(itDummyService1->seqAliases.size(), 2); - EXPECT_EQ(itDummyService1->seqAliases[0], "Dummy1"); - EXPECT_EQ(itDummyService1->seqAliases[1], "DummySvc1"); + auto vecClasses = manifestWrite.ClassList(); + EXPECT_EQ(vecClasses.size(), 3); + auto itDummyService1 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #1"; }); + ASSERT_NE(itDummyService1, vecClasses.end()); + EXPECT_EQ(itDummyService1->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + //EXPECT_FALSE(itDummyService1->ssManifest.empty()); + ASSERT_EQ(itDummyService1->seqClassAliases.size(), 2); + EXPECT_EQ(itDummyService1->seqClassAliases[0], "Dummy1"); + EXPECT_EQ(itDummyService1->seqClassAliases[1], "DummySvc1"); EXPECT_EQ(itDummyService1->ssDefaultObjectName, "MyDummy"); - EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::ComplexService); + EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::complex_service); EXPECT_EQ(itDummyService1->uiFlags, static_cast(sdv::EObjectFlags::singleton)); ASSERT_EQ(itDummyService1->seqDependencies.size(), 2); EXPECT_EQ(itDummyService1->seqDependencies[0], "DummyDevice"); EXPECT_EQ(itDummyService1->seqDependencies[1], "DummyService #2"); auto itDummyDevice = std::find_if( - vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyDevice"; }); - ASSERT_NE(itDummyDevice, vecComponents.end()); - EXPECT_EQ(itDummyDevice->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - auto itDummyService2 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #2"; }); - ASSERT_NE(itDummyService2, vecComponents.end()); - EXPECT_EQ(itDummyService2->pathRelModule, "UnitTest_InstallPackageComposer_Component2.sdv"); + vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyDevice"; }); + ASSERT_NE(itDummyDevice, vecClasses.end()); + EXPECT_EQ(itDummyDevice->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + auto itDummyService2 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #2"; }); + ASSERT_NE(itDummyService2, vecClasses.end()); + EXPECT_EQ(itDummyService2->ssModulePath, "UnitTest_InstallPackageComposer_Component2.sdv"); std::string ssManifest = manifestWrite.Write(); EXPECT_FALSE(ssManifest.empty()); @@ -408,32 +421,32 @@ TEST_F(CInstallManifestTest, SaveLoadComponents) EXPECT_EQ(vecModules.size(), 2); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component1.sdv"), vecModules.end()); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component2.sdv"), vecModules.end()); - auto vecComponents = manifestWrite.ComponentList(); - EXPECT_EQ(vecComponents.size(), 3); - auto itDummyService1 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #1"; }); - ASSERT_NE(itDummyService1, vecComponents.end()); - EXPECT_EQ(itDummyService1->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - EXPECT_FALSE(itDummyService1->ssManifest.empty()); - ASSERT_EQ(itDummyService1->seqAliases.size(), 2); - EXPECT_EQ(itDummyService1->seqAliases[0], "Dummy1"); - EXPECT_EQ(itDummyService1->seqAliases[1], "DummySvc1"); + auto vecClasses = manifestWrite.ClassList(); + EXPECT_EQ(vecClasses.size(), 3); + auto itDummyService1 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #1"; }); + ASSERT_NE(itDummyService1, vecClasses.end()); + EXPECT_EQ(itDummyService1->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + //EXPECT_FALSE(itDummyService1->ssManifest.empty()); + ASSERT_EQ(itDummyService1->seqClassAliases.size(), 2); + EXPECT_EQ(itDummyService1->seqClassAliases[0], "Dummy1"); + EXPECT_EQ(itDummyService1->seqClassAliases[1], "DummySvc1"); EXPECT_EQ(itDummyService1->ssDefaultObjectName, "MyDummy"); - EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::ComplexService); + EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::complex_service); EXPECT_EQ(itDummyService1->uiFlags, static_cast(sdv::EObjectFlags::singleton)); ASSERT_EQ(itDummyService1->seqDependencies.size(), 2); EXPECT_EQ(itDummyService1->seqDependencies[0], "DummyDevice"); EXPECT_EQ(itDummyService1->seqDependencies[1], "DummyService #2"); auto itDummyDevice = std::find_if( - vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyDevice"; }); - ASSERT_NE(itDummyDevice, vecComponents.end()); - EXPECT_EQ(itDummyDevice->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - auto itDummyService2 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #2"; }); - ASSERT_NE(itDummyService2, vecComponents.end()); - EXPECT_EQ(itDummyService2->pathRelModule, "UnitTest_InstallPackageComposer_Component2.sdv"); + vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyDevice"; }); + ASSERT_NE(itDummyDevice, vecClasses.end()); + EXPECT_EQ(itDummyDevice->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + auto itDummyService2 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #2"; }); + ASSERT_NE(itDummyService2, vecClasses.end()); + EXPECT_EQ(itDummyService2->ssModulePath, "UnitTest_InstallPackageComposer_Component2.sdv"); EXPECT_TRUE(manifestWrite.Save(pathTgtPckDir)); EXPECT_TRUE(std::filesystem::exists(pathTgtPckDir / "install_manifest.toml")); @@ -444,32 +457,32 @@ TEST_F(CInstallManifestTest, SaveLoadComponents) EXPECT_EQ(vecModules.size(), 2); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component1.sdv"), vecModules.end()); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component2.sdv"), vecModules.end()); - vecComponents = manifestRead.ComponentList(); - EXPECT_EQ(vecComponents.size(), 3); - itDummyService1 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #1"; }); - ASSERT_NE(itDummyService1, vecComponents.end()); - EXPECT_EQ(itDummyService1->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - EXPECT_FALSE(itDummyService1->ssManifest.empty()); - ASSERT_EQ(itDummyService1->seqAliases.size(), 2); - EXPECT_EQ(itDummyService1->seqAliases[0], "Dummy1"); - EXPECT_EQ(itDummyService1->seqAliases[1], "DummySvc1"); + vecClasses = manifestRead.ClassList(); + EXPECT_EQ(vecClasses.size(), 3); + itDummyService1 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #1"; }); + ASSERT_NE(itDummyService1, vecClasses.end()); + EXPECT_EQ(itDummyService1->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + //EXPECT_FALSE(itDummyService1->ssManifest.empty()); + ASSERT_EQ(itDummyService1->seqClassAliases.size(), 2); + EXPECT_EQ(itDummyService1->seqClassAliases[0], "Dummy1"); + EXPECT_EQ(itDummyService1->seqClassAliases[1], "DummySvc1"); EXPECT_EQ(itDummyService1->ssDefaultObjectName, "MyDummy"); - EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::ComplexService); + EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::complex_service); EXPECT_EQ(itDummyService1->uiFlags, static_cast(sdv::EObjectFlags::singleton)); ASSERT_EQ(itDummyService1->seqDependencies.size(), 2); EXPECT_EQ(itDummyService1->seqDependencies[0], "DummyDevice"); EXPECT_EQ(itDummyService1->seqDependencies[1], "DummyService #2"); itDummyDevice = std::find_if( - vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyDevice"; }); - ASSERT_NE(itDummyDevice, vecComponents.end()); - EXPECT_EQ(itDummyDevice->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - itDummyService2 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #2"; }); - ASSERT_NE(itDummyService2, vecComponents.end()); - EXPECT_EQ(itDummyService2->pathRelModule, "UnitTest_InstallPackageComposer_Component2.sdv"); + vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyDevice"; }); + ASSERT_NE(itDummyDevice, vecClasses.end()); + EXPECT_EQ(itDummyDevice->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + itDummyService2 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #2"; }); + ASSERT_NE(itDummyService2, vecClasses.end()); + EXPECT_EQ(itDummyService2->ssModulePath, "UnitTest_InstallPackageComposer_Component2.sdv"); } TEST_F(CInstallManifestTest, SaveLoadFindModules) @@ -489,32 +502,32 @@ TEST_F(CInstallManifestTest, SaveLoadFindModules) EXPECT_EQ(vecModules.size(), 2); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component1.sdv"), vecModules.end()); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component2.sdv"), vecModules.end()); - auto vecComponents = manifestWrite.ComponentList(); - EXPECT_EQ(vecComponents.size(), 3); - auto itDummyService1 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #1"; }); - ASSERT_NE(itDummyService1, vecComponents.end()); - EXPECT_EQ(itDummyService1->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - EXPECT_FALSE(itDummyService1->ssManifest.empty()); - ASSERT_EQ(itDummyService1->seqAliases.size(), 2); - EXPECT_EQ(itDummyService1->seqAliases[0], "Dummy1"); - EXPECT_EQ(itDummyService1->seqAliases[1], "DummySvc1"); + auto vecClasses = manifestWrite.ClassList(); + EXPECT_EQ(vecClasses.size(), 3); + auto itDummyService1 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #1"; }); + ASSERT_NE(itDummyService1, vecClasses.end()); + EXPECT_EQ(itDummyService1->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + //EXPECT_FALSE(itDummyService1->ssManifest.empty()); + ASSERT_EQ(itDummyService1->seqClassAliases.size(), 2); + EXPECT_EQ(itDummyService1->seqClassAliases[0], "Dummy1"); + EXPECT_EQ(itDummyService1->seqClassAliases[1], "DummySvc1"); EXPECT_EQ(itDummyService1->ssDefaultObjectName, "MyDummy"); - EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::ComplexService); + EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::complex_service); EXPECT_EQ(itDummyService1->uiFlags, static_cast(sdv::EObjectFlags::singleton)); ASSERT_EQ(itDummyService1->seqDependencies.size(), 2); EXPECT_EQ(itDummyService1->seqDependencies[0], "DummyDevice"); EXPECT_EQ(itDummyService1->seqDependencies[1], "DummyService #2"); auto itDummyDevice = std::find_if( - vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyDevice"; }); - ASSERT_NE(itDummyDevice, vecComponents.end()); - EXPECT_EQ(itDummyDevice->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - auto itDummyService2 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #2"; }); - ASSERT_NE(itDummyService2, vecComponents.end()); - EXPECT_EQ(itDummyService2->pathRelModule, "UnitTest_InstallPackageComposer_Component2.sdv"); + vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyDevice"; }); + ASSERT_NE(itDummyDevice, vecClasses.end()); + EXPECT_EQ(itDummyDevice->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + auto itDummyService2 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #2"; }); + ASSERT_NE(itDummyService2, vecClasses.end()); + EXPECT_EQ(itDummyService2->ssModulePath, "UnitTest_InstallPackageComposer_Component2.sdv"); EXPECT_TRUE(manifestWrite.Save(pathTgtPckDir)); EXPECT_TRUE(std::filesystem::exists(pathTgtPckDir / "install_manifest.toml")); @@ -544,32 +557,32 @@ TEST_F(CInstallManifestTest, SaveLoadFindComponents) EXPECT_EQ(vecModules.size(), 2); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component1.sdv"), vecModules.end()); EXPECT_NE(std::find(vecModules.begin(), vecModules.end(), "UnitTest_InstallPackageComposer_Component2.sdv"), vecModules.end()); - auto vecComponents = manifestWrite.ComponentList(); - EXPECT_EQ(vecComponents.size(), 3); - auto itDummyService1 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #1"; }); - ASSERT_NE(itDummyService1, vecComponents.end()); - EXPECT_EQ(itDummyService1->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - EXPECT_FALSE(itDummyService1->ssManifest.empty()); - ASSERT_EQ(itDummyService1->seqAliases.size(), 2); - EXPECT_EQ(itDummyService1->seqAliases[0], "Dummy1"); - EXPECT_EQ(itDummyService1->seqAliases[1], "DummySvc1"); + auto vecClasses = manifestWrite.ClassList(); + EXPECT_EQ(vecClasses.size(), 3); + auto itDummyService1 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #1"; }); + ASSERT_NE(itDummyService1, vecClasses.end()); + EXPECT_EQ(itDummyService1->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + //EXPECT_FALSE(itDummyService1->ssManifest.empty()); + ASSERT_EQ(itDummyService1->seqClassAliases.size(), 2); + EXPECT_EQ(itDummyService1->seqClassAliases[0], "Dummy1"); + EXPECT_EQ(itDummyService1->seqClassAliases[1], "DummySvc1"); EXPECT_EQ(itDummyService1->ssDefaultObjectName, "MyDummy"); - EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::ComplexService); + EXPECT_EQ(itDummyService1->eType, sdv::EObjectType::complex_service); EXPECT_EQ(itDummyService1->uiFlags, static_cast(sdv::EObjectFlags::singleton)); ASSERT_EQ(itDummyService1->seqDependencies.size(), 2); EXPECT_EQ(itDummyService1->seqDependencies[0], "DummyDevice"); EXPECT_EQ(itDummyService1->seqDependencies[1], "DummyService #2"); auto itDummyDevice = std::find_if( - vecComponents.begin(), vecComponents.end(), [](const auto& sComponent) { return sComponent.ssClassName == "DummyDevice"; }); - ASSERT_NE(itDummyDevice, vecComponents.end()); - EXPECT_EQ(itDummyDevice->pathRelModule, "UnitTest_InstallPackageComposer_Component1.sdv"); - auto itDummyService2 = std::find_if(vecComponents.begin(), - vecComponents.end(), - [](const auto& sComponent) { return sComponent.ssClassName == "DummyService #2"; }); - ASSERT_NE(itDummyService2, vecComponents.end()); - EXPECT_EQ(itDummyService2->pathRelModule, "UnitTest_InstallPackageComposer_Component2.sdv"); + vecClasses.begin(), vecClasses.end(), [](const auto& sComponent) { return sComponent.ssName == "DummyDevice"; }); + ASSERT_NE(itDummyDevice, vecClasses.end()); + EXPECT_EQ(itDummyDevice->ssModulePath, "UnitTest_InstallPackageComposer_Component1.sdv"); + auto itDummyService2 = std::find_if(vecClasses.begin(), + vecClasses.end(), + [](const auto& sComponent) { return sComponent.ssName == "DummyService #2"; }); + ASSERT_NE(itDummyService2, vecClasses.end()); + EXPECT_EQ(itDummyService2->ssModulePath, "UnitTest_InstallPackageComposer_Component2.sdv"); EXPECT_TRUE(manifestWrite.Save(pathTgtPckDir)); EXPECT_TRUE(std::filesystem::exists(pathTgtPckDir / "install_manifest.toml")); diff --git a/tests/unit_tests/install_package_composer/package_version_tests.cpp b/tests/unit_tests/install_package_composer/package_version_tests.cpp index 0ef223d..85c1340 100644 --- a/tests/unit_tests/install_package_composer/package_version_tests.cpp +++ b/tests/unit_tests/install_package_composer/package_version_tests.cpp @@ -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 #include "composer_test_suite.h" #include "../../../sdv_services/core/installation_manifest.h" diff --git a/tests/unit_tests/ipc_com/CMakeLists.txt b/tests/unit_tests/ipc_com/CMakeLists.txt index fe05223..804d20c 100644 --- a/tests/unit_tests/ipc_com/CMakeLists.txt +++ b/tests/unit_tests/ipc_com/CMakeLists.txt @@ -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 (UnitTest_IPC_Communication VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/ipc_com/ipc_com.cpp b/tests/unit_tests/ipc_com/ipc_com.cpp index 331b659..950bd27 100644 --- a/tests/unit_tests/ipc_com/ipc_com.cpp +++ b/tests/unit_tests/ipc_com/ipc_com.cpp @@ -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 "../../include/gtest_custom.h" #include "../../../sdv_services/ipc_com/com_ctrl.h" #include "../../../sdv_services/ipc_com/com_channel.h" @@ -920,9 +933,9 @@ TEST(IPC_Communication_Test, Instantiate) { CCommunicationControl control; EXPECT_NO_THROW(control.Initialize("")); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(control.Shutdown()); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(IPC_Communication_Test, AssignServerEndpoint) @@ -940,9 +953,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -981,9 +994,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1038,9 +1051,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1108,9 +1121,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1178,9 +1191,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1210,9 +1223,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1247,9 +1260,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1294,9 +1307,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1338,9 +1351,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1387,9 +1400,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1436,9 +1449,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1489,9 +1502,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1536,9 +1549,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1610,9 +1623,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1662,9 +1675,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1715,9 +1728,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1761,9 +1774,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1825,9 +1838,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1900,9 +1913,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -1975,9 +1988,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2038,9 +2051,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2088,9 +2101,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2137,9 +2150,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2241,9 +2254,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2301,9 +2314,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2360,9 +2373,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2408,9 +2421,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2492,9 +2505,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2598,9 +2611,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2704,9 +2717,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2777,9 +2790,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2835,9 +2848,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2882,9 +2895,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -2956,9 +2969,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3008,9 +3021,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3061,9 +3074,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3107,9 +3120,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3172,9 +3185,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3248,9 +3261,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3323,9 +3336,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3388,9 +3401,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3440,9 +3453,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3488,9 +3501,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3591,9 +3604,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3650,9 +3663,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3709,9 +3722,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3756,9 +3769,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3839,9 +3852,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -3921,9 +3934,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4003,9 +4016,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4071,9 +4084,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4118,9 +4131,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4166,9 +4179,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4269,9 +4282,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4328,9 +4341,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4387,9 +4400,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4434,9 +4447,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4518,9 +4531,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4601,9 +4614,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4683,9 +4696,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4752,9 +4765,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4803,9 +4816,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4851,9 +4864,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -4954,9 +4967,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5013,9 +5026,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5072,9 +5085,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5119,9 +5132,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5202,9 +5215,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5284,9 +5297,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5366,9 +5379,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5434,9 +5447,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5481,9 +5494,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5529,9 +5542,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5632,9 +5645,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5691,9 +5704,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5750,9 +5763,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5797,9 +5810,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5881,9 +5894,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -5964,9 +5977,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -6046,9 +6059,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); @@ -6115,9 +6128,9 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; control.Initialize(""); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); - EXPECT_EQ(control.GetStatus(), sdv::EObjectStatus::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components LoadIPCModules(control); diff --git a/tests/unit_tests/ipc_com/ipc_com_ps.cpp b/tests/unit_tests/ipc_com/ipc_com_ps.cpp index eec8f5a..11b64ac 100644 --- a/tests/unit_tests/ipc_com/ipc_com_ps.cpp +++ b/tests/unit_tests/ipc_com/ipc_com_ps.cpp @@ -1 +1,14 @@ +/******************************************************************************** + * 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 "generated/ps/proxystub.cpp" \ No newline at end of file diff --git a/tests/unit_tests/ipc_com/main.cpp b/tests/unit_tests/ipc_com/main.cpp index a88b885..5c630d3 100644 --- a/tests/unit_tests/ipc_com/main.cpp +++ b/tests/unit_tests/ipc_com/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../../sdv_services/ipc_com/com_ctrl.cpp" diff --git a/tests/unit_tests/ipc_com/test_ifc.idl b/tests/unit_tests/ipc_com/test_ifc.idl index 3397a7a..eda9707 100644 --- a/tests/unit_tests/ipc_com/test_ifc.idl +++ b/tests/unit_tests/ipc_com/test_ifc.idl @@ -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 enum EHello diff --git a/tests/unit_tests/ipc_connect/CMakeLists.txt b/tests/unit_tests/ipc_connect/CMakeLists.txt index c0dc490..07125a3 100644 --- a/tests/unit_tests/ipc_connect/CMakeLists.txt +++ b/tests/unit_tests/ipc_connect/CMakeLists.txt @@ -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 (UnitTest_IPC_Connect VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/ipc_connect/ipc_connect.cpp b/tests/unit_tests/ipc_connect/ipc_connect.cpp index fceff00..9a7804e 100644 --- a/tests/unit_tests/ipc_connect/ipc_connect.cpp +++ b/tests/unit_tests/ipc_connect/ipc_connect.cpp @@ -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 "../../include/gtest_custom.h" #include #include @@ -29,15 +42,15 @@ ViewFilter = "Fatal" // Start listener CListener listener; - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); listener.Initialize(R"code([Listener] Type = "Local" )code"); - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); // Shutdown listener.Shutdown(); - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); appcontrol.Shutdown(); } @@ -65,16 +78,16 @@ ViewFilter = "Fatal" // Start listener CListener listener; - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); listener.Initialize(R"code([Listener] Type = "Local" Instance = 1234 )code"); - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); // Shutdown listener.Shutdown(); - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); appcontrol.Shutdown(); } @@ -102,9 +115,9 @@ ViewFilter = "Fatal" // Start client CClient client; - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); client.Initialize(""); - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); sdv::TObjectPtr ptrClient = client.Connect(R"code([Client] Type = "local" )code"); @@ -113,7 +126,7 @@ Type = "local" // Shutdown ptrClient.Clear(); client.Shutdown(); - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); appcontrol.Shutdown(); } @@ -141,9 +154,9 @@ ViewFilter = "Fatal" // Start client CClient client; - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); client.Initialize(""); - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); sdv::TObjectPtr ptrClient = client.Connect(R"code([Client] Type = "local" Instance = 1234 @@ -153,7 +166,7 @@ Instance = 1234 // Shutdown ptrClient.Clear(); client.Shutdown(); - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); appcontrol.Shutdown(); } @@ -187,17 +200,17 @@ ViewFilter = "Fatal" // Start listener CListener listener; - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); listener.Initialize(R"code([Listener] Type = "Local" )code"); - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); // Start client CClient client; - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); client.Initialize(""); - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); sdv::TObjectPtr ptrClient = client.Connect(R"code([Client] Type = "Local" )code"); @@ -218,9 +231,9 @@ Type = "Local" // Shutdown ptrClient.Clear(); client.Shutdown(); - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); listener.Shutdown(); - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); appcontrol.Shutdown(); } @@ -254,18 +267,18 @@ ViewFilter = "Fatal" // Start listener CListener listener; - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); listener.Initialize(R"code([Listener] Type = "Local" Instance = 1234 )code"); - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); // Start client CClient client; - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::initialization_pending); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); client.Initialize(""); - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); sdv::TObjectPtr ptrClient = client.Connect(R"code([Client] Type = "Local" Instance = 1234 @@ -287,8 +300,8 @@ Instance = 1234 // Shutdown ptrClient.Clear(); client.Shutdown(); - EXPECT_EQ(client.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); listener.Shutdown(); - EXPECT_EQ(listener.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); appcontrol.Shutdown(); } diff --git a/tests/unit_tests/ipc_connect/main.cpp b/tests/unit_tests/ipc_connect/main.cpp index d9a02a3..91eb0db 100644 --- a/tests/unit_tests/ipc_connect/main.cpp +++ b/tests/unit_tests/ipc_connect/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../../sdv_services/ipc_connect/listener.cpp" diff --git a/tests/unit_tests/memory_manager/CMakeLists.txt b/tests/unit_tests/memory_manager/CMakeLists.txt index bc94439..0a2205a 100644 --- a/tests/unit_tests/memory_manager/CMakeLists.txt +++ b/tests/unit_tests/memory_manager/CMakeLists.txt @@ -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(MemoryManagerTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/memory_manager/memmgr_test.cpp b/tests/unit_tests/memory_manager/memmgr_test.cpp index 114b0e8..c2aa2e4 100644 --- a/tests/unit_tests/memory_manager/memmgr_test.cpp +++ b/tests/unit_tests/memory_manager/memmgr_test.cpp @@ -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 #include #include "../../../global/process_watchdog.h" diff --git a/tests/unit_tests/module_control/CMakeLists.txt b/tests/unit_tests/module_control/CMakeLists.txt index 8fedab9..8902f26 100644 --- a/tests/unit_tests/module_control/CMakeLists.txt +++ b/tests/unit_tests/module_control/CMakeLists.txt @@ -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 (multiple EXEs) project(ModuleControlTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/module_control/main.cpp b/tests/unit_tests/module_control/main.cpp index a410299..8a8e354 100644 --- a/tests/unit_tests/module_control/main.cpp +++ b/tests/unit_tests/module_control/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "mock.h" @@ -7,9 +20,11 @@ #include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp" #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/miscellaneous.cpp" +#include "../../../sdv_services/core/toml_parser/code_snippet.cpp" #include "../../../sdv_services/core/module_control.cpp" #include "../../../sdv_services/core/module.cpp" #include "../../../sdv_services/core/app_config.cpp" +#include "../../../sdv_services/core/app_config_file.cpp" #include "../../../sdv_services/core/installation_manifest.cpp" #if defined(_WIN32) && defined(_UNICODE) diff --git a/tests/unit_tests/module_control/mock.h b/tests/unit_tests/module_control/mock.h index a49ca26..9f147fb 100644 --- a/tests/unit_tests/module_control/mock.h +++ b/tests/unit_tests/module_control/mock.h @@ -1,56 +1,123 @@ +/******************************************************************************** + * 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 MOCK_H #define MOCK_H -#define DO_NOT_INCLUDE_IN_UNIT_TEST -#include "../../../sdv_services/core/module_control.h" -#include "../../../sdv_services/core/module.h" -#include "../../../sdv_services/core/sdv_core.h" +#include +#include +#include "../../../sdv_services/core/installation_manifest.h" #include "../../../global/exec_dir_helper.h" -#include "../../../sdv_services/core/app_config.h" -class CMock +// Prevent including app_settings.h and app_control.h +#define APP_SETTINGS_H +#define APP_CONTROL_H +#define REPOSITORY_H +#define LOGGER_H +#define LOGER_CONTROL_H + +/** + * @brief CAppSettings redefined + */ +class CAppSettings : public sdv::IInterfaceAccess { public: - CMock() {} - void DestroyModuleObjects(sdv::core::TModuleID) {} + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() + + // CAppSettings mocked functions + sdv::app::EAppContext GetContextType() const { return sdv::app::EAppContext::standalone; } bool IsStandaloneApplication() { return true; } bool IsEssentialApplication() { return false; } bool IsMainApplication() { return false; } bool IsIsolatedApplication() { return false; } bool IsMaintenanceApplication() { return false; } + bool IsExternalApplication() const { return false; } bool IsConsoleSilent() { return true; } bool IsConsoleVerbose() { return false; } uint32_t GetInstanceID() { return 1234u; } - sdv::app::EAppOperationState GetOperationState() const { return sdv::app::EAppOperationState::running; } + std::filesystem::path GetRootDir() const { return GetExecDirectory(); } std::filesystem::path GetInstallDir() const { return GetExecDirectory(); } - std::filesystem::path FindInstalledModule(const std::filesystem::path&) const { return {}; } - std::optional FindInstalledComponent(const std::string&) const { return {}; } - std::string FindInstalledModuleManifest(const std::filesystem::path&) { return {}; } - sdv::core::TObjectID CreateObjectFromModule(sdv::core::TModuleID, const sdv::u8string&, const sdv::u8string&, const sdv::u8string&) { return 0; } - sdv::core::TObjectID CreateObject2(const sdv::u8string&, const sdv::u8string&, const sdv::u8string&) { return 0; } - std::string SaveConfig() { return {}; } - void ResetConfigBaseline() {} - sdv::core::TModuleID Load(const sdv::u8string&) { return 0; } - sdv::core::TModuleID ContextLoad(const std::filesystem::path&, const sdv::u8string&) { return 0; } - bool ContextUnload(sdv::core::TModuleID, bool) { return false; } - - bool m_bIsMain = false; - bool m_bIsIsolated = false; + std::vector GetSystemConfigPaths() const { return {}; } + std::filesystem::path GetUserConfigPath() const { return {}; } }; -inline CMock& GetMock() +/** + * @brief Return the application settings class. + * @return Reference to the application settings. + */ +inline CAppSettings& GetAppSettings() { - static CMock mock; - return mock; + static CAppSettings app_settings; + return app_settings; } -#define CRepository CMock -#define CAppControl CMock -#define GetRepository GetMock -#define GetAppControl GetMock -#define GetAppConfig GetMock -#define GetModuleControl GetMock +/** + * @brief CAppControl redefined + */ +class CAppControl : public sdv::IInterfaceAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() -inline std::filesystem::path GetCoreDirectory() { return "../../bin"; } + // CAppControl mocked functions + sdv::app::EAppOperationState GetOperationState() const { return sdv::app::EAppOperationState::running; } +}; + +/** + * @brief Return the application control. + * @return Reference to the application control. + */ +inline CAppControl& GetAppControl() +{ + static CAppControl app_control; + return app_control; +} + +class CAppConfigFile; + +/** + * @brief CRepository redefined + */ +class CRepository : public sdv::IInterfaceAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() + + // CRepository mocked functions + void DestroyModuleObjects(sdv::core::TModuleID /*tModuleID*/) {} + sdv::core::EConfigProcessResult StartFromConfig(const CAppConfigFile&, bool) { return sdv::core::EConfigProcessResult::failed; } + void ResetConfigBaseline() {} +}; + +/** + * @brief Return the repository. + * @return Reference to the repository. + */ +inline CRepository& GetRepository() +{ + static CRepository repository; + return repository; +} + +#include "../../../sdv_services/core/module_control.h" +#include "../../../sdv_services/core/module.h" +#include "../../../sdv_services/core/app_settings.h" +#include "../../../sdv_services/core/app_config.h" + +//inline std::filesystem::path GetCoreDirectoryMock() { return "../../bin"; } +//#define GetCoreDirectory GetCoreDirectoryMock #endif // !defined MOCK_H \ No newline at end of file diff --git a/tests/unit_tests/module_control/module_control_test.cpp b/tests/unit_tests/module_control/module_control_test.cpp index 6b57f1a..3df0066 100644 --- a/tests/unit_tests/module_control/module_control_test.cpp +++ b/tests/unit_tests/module_control/module_control_test.cpp @@ -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 #include "mock.h" #include "../../../global/exec_dir_helper.h" diff --git a/tests/unit_tests/named_mutex/CMakeLists.txt b/tests/unit_tests/named_mutex/CMakeLists.txt index 6e1e035..0776c84 100644 --- a/tests/unit_tests/named_mutex/CMakeLists.txt +++ b/tests/unit_tests/named_mutex/CMakeLists.txt @@ -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 (UnitTest_NamedMutex VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/named_mutex/named_mutex_test.cpp b/tests/unit_tests/named_mutex/named_mutex_test.cpp index 6bb8468..c1a62c2 100644 --- a/tests/unit_tests/named_mutex/named_mutex_test.cpp +++ b/tests/unit_tests/named_mutex/named_mutex_test.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../../global/ipc_named_mutex.h" @@ -127,6 +140,6 @@ TEST(NamedMutexTest, Naming) ipc::named_mutex mtx1("HELLO"); EXPECT_FALSE(mtx1.name().empty()); - ipc::named_mutex mtx2(nullptr); + ipc::named_mutex mtx2; EXPECT_FALSE(mtx2.name().empty()); } diff --git a/tests/unit_tests/parameters/CMakeLists.txt b/tests/unit_tests/parameters/CMakeLists.txt new file mode 100644 index 0000000..1f72b26 --- /dev/null +++ b/tests/unit_tests/parameters/CMakeLists.txt @@ -0,0 +1,41 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 Contributors to the Eclipse Foundation +# +# 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(ParameterTests VERSION 1.0 LANGUAGES CXX) + +# Define target +add_executable(UnitTest_Parameters "param_access.cpp" "param_info.cpp" "main.cpp" "param_labels.cpp" "param_access_chain.cpp") + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(UnitTest_Parameters GTest::GTest) + if (WIN32) + target_link_libraries(UnitTest_Parameters Ws2_32 Winmm Rpcrt4.lib) + else() + target_link_libraries(UnitTest_Parameters ${CMAKE_DL_LIBS} rt) + endif() +else() + target_link_libraries(UnitTest_Parameters GTest::GTest Rpcrt4.lib) +endif() + +# Add the test +add_test(NAME UnitTest_Parameters COMMAND UnitTest_Parameters WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + +# Execute the test +add_custom_command(TARGET UnitTest_Parameters POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_Parameters.xml + VERBATIM +) + +# The unit-test project depends on a proper compilation of sdv idl files before +add_dependencies(UnitTest_Parameters CompileCoreIDL) diff --git a/tests/unit_tests/parameters/main.cpp b/tests/unit_tests/parameters/main.cpp new file mode 100644 index 0000000..727458e --- /dev/null +++ b/tests/unit_tests/parameters/main.cpp @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 Contributors to the Eclipse Foundation + * + * 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 +#include "../../../global/localmemmgr.h" +#include "../../../global/process_watchdog.h" + +#include + +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; + + // The memory manager registers itself into the system and needs to stay in scope. + CLocalMemMgr memmgr; + + testing::InitGoogleTest(&argc, argv); + auto iRet = RUN_ALL_TESTS(); + + // Clear the label map, deallocating the labels before the memory manager gets out of scope. + sdv::internal::GetLabelMapHelper().Clear(); + + return iRet; +} diff --git a/tests/unit_tests/parameters/param_access.cpp b/tests/unit_tests/parameters/param_access.cpp new file mode 100644 index 0000000..f5ad753 --- /dev/null +++ b/tests/unit_tests/parameters/param_access.cpp @@ -0,0 +1,1702 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 Contributors to the Eclipse Foundation + * + * 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 +#include "../../../global/localmemmgr.h" +#include "../../../global/exec_dir_helper.h" +#include + +class CEmptyParameters : public sdv::CSdvParamMap +{ +public: + CEmptyParameters() + {} + + BEGIN_SDV_PARAM_MAP() + END_SDV_PARAM_MAP() +}; + +TEST(ParameterTest, EmptyParamMap) +{ + // Global information + auto vecParamMap = CEmptyParameters::GetParamMapInfoStatic(); + EXPECT_TRUE(vecParamMap.empty()); + + // Local information + CEmptyParameters param; + auto seqParams = param.GetParamPaths(); + EXPECT_TRUE(seqParams.empty()); +} + +class CSimpleParameters : public sdv::CSdvParamMap +{ +public: + CSimpleParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_i, "my_integer", 10, "int_unit", "My integer") + SDV_PARAM_ENTRY(m_d, "my_double", 1234.5, "double_unit", "My double") + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + END_SDV_PARAM_MAP() + +private: + int m_i; + double m_d; + std::string m_ss; +}; + +TEST(ParameterTest, SimpleParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CSimpleParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 3u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, SimpleParamMapObjectInfo) +{ + // Instantiation + CSimpleParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, SimpleParamMapObjectInfoIndirect) +{ + // Instantiation + CSimpleParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); +} + +TEST(ParameterTest, SimpleParamMapGetSet) +{ + // Instantiation + CSimpleParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_TRUE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // New value +} + +class CRestrictedParameters : public sdv::CSdvParamMap +{ +public: + CRestrictedParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_NUMBER_ENTRY(m_i, "my_integer", 10, >= 0, <= 20, "int_unit", "My integer") + SDV_PARAM_NUMBER_ENTRY(m_d, "my_double", 1234.5, NO_LIMIT, < 20000, "double_unit", "My double") + SDV_PARAM_STRING_ENTRY(m_ss, "my_string", "0xabcd", "^0[xX][0-9a-fA-F]+$", "string_unit", "My string") + END_SDV_PARAM_MAP() + +private: + int m_i; + double m_d; + std::string m_ss; +}; + +TEST(ParameterTest, RestrictedParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CRestrictedParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 3u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + ASSERT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_FALSE(ptrParamInfo->NumericLimitLow().first.empty()); + EXPECT_EQ(ptrParamInfo->NumericLimitLow().first, 0); + EXPECT_TRUE(ptrParamInfo->NumericLimitLow().second); + EXPECT_EQ(ptrParamInfo->NumericLimitHigh().first, 20); + EXPECT_TRUE(ptrParamInfo->NumericLimitHigh().second); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + ASSERT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_TRUE(ptrParamInfo->NumericLimitLow().first.empty()); + EXPECT_EQ(ptrParamInfo->NumericLimitHigh().first, 20000); + EXPECT_FALSE(ptrParamInfo->NumericLimitHigh().second); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + ASSERT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->StringPattern(), "^0[xX][0-9a-fA-F]+$"); +} + +TEST(ParameterTest, RestrictedParamMapObjectInfo) +{ + // Instantiation + CRestrictedParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + ASSERT_TRUE(ptrParamInfo->Numeric()); + EXPECT_FALSE(ptrParamInfo->NumericLimitLow().first.empty()); + EXPECT_EQ(ptrParamInfo->NumericLimitLow().first, 0); + EXPECT_TRUE(ptrParamInfo->NumericLimitLow().second); + EXPECT_EQ(ptrParamInfo->NumericLimitHigh().first, 20); + EXPECT_TRUE(ptrParamInfo->NumericLimitHigh().second); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + ASSERT_TRUE(ptrParamInfo->Numeric()); + EXPECT_TRUE(ptrParamInfo->NumericLimitLow().first.empty()); + EXPECT_EQ(ptrParamInfo->NumericLimitHigh().first, 20000); + EXPECT_FALSE(ptrParamInfo->NumericLimitHigh().second); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + ASSERT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->StringPattern(), "^0[xX][0-9a-fA-F]+$"); +} + +TEST(ParameterTest, RestrictedParamMapObjectInfoIndirect) +{ + // Instantiation + CRestrictedParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + ASSERT_TRUE(info.Numeric()); + EXPECT_FALSE(info.NumericLimitLow().first.empty()); + EXPECT_EQ(info.NumericLimitLow().first, 0); + EXPECT_TRUE(info.NumericLimitLow().second); + EXPECT_EQ(info.NumericLimitHigh().first, 20); + EXPECT_TRUE(info.NumericLimitHigh().second); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_TRUE(info.NumericLimitLow().first.empty()); + EXPECT_EQ(info.NumericLimitHigh().first, 20000); + EXPECT_FALSE(info.NumericLimitHigh().second); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.StringPattern(), "^0[xX][0-9a-fA-F]+$"); +} + +TEST(ParameterTest, RestrictedParamMapGetSet) +{ + // Instantiation + CRestrictedParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); // In limit + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + EXPECT_FALSE(param.SetParam(seqParams[0], 21)); // Over limit + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // Same value + EXPECT_TRUE(param.SetParam(seqParams[0], 0)); // In limit + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 0); // New value + EXPECT_FALSE(param.SetParam(seqParams[0], -1)); // Over limit + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 0); // Same value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 19999.99)); // In limit + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 19999.99); // New value + EXPECT_FALSE(param.SetParam(seqParams[1], 20000)); // Over limit + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 19999.99); // Same value + EXPECT_TRUE(param.SetParam(seqParams[1], -20000)); // No limit + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, -20000); // Same value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "0xabcd"); // Default value + EXPECT_TRUE(param.SetParam(seqParams[2], "0x1234567890abcdef")); // Hex value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "0x1234567890abcdef"); // New value + EXPECT_TRUE(param.SetParam(seqParams[2], "0XFEDCBA987654321")); // Hex value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "0XFEDCBA987654321"); // New value + EXPECT_FALSE(param.SetParam(seqParams[2], "0xabcdfg")); // Invalid value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "0XFEDCBA987654321"); // Same value + EXPECT_FALSE(param.SetParam(seqParams[2], "100")); // Invalid value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "0XFEDCBA987654321"); // Same value +} + +class CEnumParameters : public sdv::CSdvParamMap +{ +public: + // C-style enum + enum ECStyle + { + one, + two, + three + }; + + BEGIN_SDV_LABEL_MAP(ECStyle) + SDV_LABEL_ENTRY(ECStyle::one, "one") + SDV_LABEL_ENTRY(ECStyle::two, "two") + SDV_LABEL_ENTRY(ECStyle::three, "three") + END_SDV_LABEL_MAP() + + // C++-style enum + enum class ECppStyle : uint8_t + { + four, + five, + six + }; + + BEGIN_SDV_LABEL_MAP(ECppStyle) + SDV_LABEL_ENTRY(ECppStyle::four, "four") + SDV_LABEL_ENTRY(ECppStyle::five, "five") + SDV_LABEL_ENTRY(ECppStyle::six, "six") + END_SDV_LABEL_MAP() + + CEnumParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENUM_ENTRY(m_eCStyle, "C enum", ECStyle::one, "This is a C style enum variable") + SDV_PARAM_ENUM_ENTRY(m_eCppStyle, "C++ enum", ECppStyle::four, "This is a C++ style enum variable") + END_SDV_PARAM_MAP() + +private: + ECStyle m_eCStyle; + ECppStyle m_eCppStyle; +}; + +TEST(ParameterTest, EnumParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CEnumParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 2u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Enum()); + EXPECT_EQ(ptrParamInfo->Name(), "C enum"); + EXPECT_EQ(ptrParamInfo->Description(), "This is a C style enum variable"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), CEnumParameters::ECStyle::one); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Enum()); + EXPECT_EQ(ptrParamInfo->Name(), "C++ enum"); + EXPECT_EQ(ptrParamInfo->Description(), "This is a C++ style enum variable"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), CEnumParameters::ECppStyle::four); +} + +TEST(ParameterTest, EnumParamMapObjectInfo) +{ + // Instantiation + CEnumParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 2u); + EXPECT_EQ(seqParams[0], "C enum"); + EXPECT_EQ(seqParams[1], "C++ enum"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Enum()); + EXPECT_EQ(ptrParamInfo->Name(), "C enum"); + EXPECT_EQ(ptrParamInfo->Description(), "This is a C style enum variable"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), CEnumParameters::ECStyle::one); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Enum()); + EXPECT_EQ(ptrParamInfo->Name(), "C++ enum"); + EXPECT_EQ(ptrParamInfo->Description(), "This is a C++ style enum variable"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), CEnumParameters::ECppStyle::four); +} + +TEST(ParameterTest, EnumParamMapObjectInfoIndirect) +{ + // Instantiation + CEnumParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 2u); + EXPECT_EQ(seqParams[0], "C enum"); + EXPECT_EQ(seqParams[1], "C++ enum"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Enum()); + EXPECT_EQ(info.Name(), "C enum"); + EXPECT_EQ(info.Description(), "This is a C style enum variable"); + EXPECT_EQ(info.DefaultVal(), CEnumParameters::ECStyle::one); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Enum()); + EXPECT_EQ(info.Name(), "C++ enum"); + EXPECT_EQ(info.Description(), "This is a C++ style enum variable"); + EXPECT_EQ(info.DefaultVal(), CEnumParameters::ECppStyle::four); +} + +TEST(ParameterTest, EnumParamMapGetSet) +{ + // Instantiation + CEnumParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 2u); + EXPECT_EQ(seqParams[0], "C enum"); + EXPECT_EQ(seqParams[1], "C++ enum"); + + using ECStyle = CEnumParameters::ECStyle; + using ECppStyle = CEnumParameters::ECppStyle; + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, ECStyle::one); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], ECStyle::three)); // In limit + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, ECStyle::three); // New value + EXPECT_FALSE(param.SetParam(seqParams[0], 4)); // Over limit + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, ECStyle::three); // Same value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, ECppStyle::four); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], sdv::any_t(ECppStyle::six))); // In limit + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, ECppStyle::six); // New value + EXPECT_FALSE(param.SetParam(seqParams[1], 10)); // Over limit + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, ECppStyle::six); // Same value +} + +class CBitmaskParameters : public sdv::CSdvParamMap +{ +public: + // C-style enum + enum ECStyle + { + bit0 = 1, + bit1 = 2, + bit2 = 4 + }; + + BEGIN_SDV_LABEL_MAP(ECStyle) + SDV_LABEL_ENTRY(ECStyle::bit0, "bit0") + SDV_LABEL_ENTRY(ECStyle::bit1, "bit1") + SDV_LABEL_ENTRY(ECStyle::bit2, "bit2") + END_SDV_LABEL_MAP() + + // C++-style enum + enum class ECppStyle : uint8_t + { + bit5 = 8, + bit6 = 16, + bit7 = 32 + }; + + BEGIN_SDV_LABEL_MAP(ECppStyle) + SDV_LABEL_ENTRY(ECppStyle::bit5, "bit5") + SDV_LABEL_ENTRY(ECppStyle::bit6, "bit6") + SDV_LABEL_ENTRY(ECppStyle::bit7, "bit7") + END_SDV_LABEL_MAP() + + CBitmaskParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_BITMASK_ENTRY(ECStyle, m_uiCStyle, "C bitmask", 1, "This is a C style enum bitmask") + SDV_PARAM_BITMASK_ENTRY(ECppStyle, m_uiCppStyle, "C++ bitmask", 8, "This is a C++ style enum bitmask") + END_SDV_PARAM_MAP() + +private: + uint64_t m_uiCStyle; + uint8_t m_uiCppStyle; +}; + +TEST(ParameterTest, BitmaskParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CBitmaskParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 2u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Bitmask()); + EXPECT_EQ(ptrParamInfo->Name(), "C bitmask"); + EXPECT_EQ(ptrParamInfo->Description(), "This is a C style enum bitmask"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Bitmask()); + EXPECT_EQ(ptrParamInfo->Name(), "C++ bitmask"); + EXPECT_EQ(ptrParamInfo->Description(), "This is a C++ style enum bitmask"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 8); +} + +TEST(ParameterTest, BitmaskParamMapObjectInfo) +{ + // Instantiation + CBitmaskParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 2u); + EXPECT_EQ(seqParams[0], "C bitmask"); + EXPECT_EQ(seqParams[1], "C++ bitmask"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Bitmask()); + EXPECT_EQ(ptrParamInfo->Name(), "C bitmask"); + EXPECT_EQ(ptrParamInfo->Description(), "This is a C style enum bitmask"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Bitmask()); + EXPECT_EQ(ptrParamInfo->Name(), "C++ bitmask"); + EXPECT_EQ(ptrParamInfo->Description(), "This is a C++ style enum bitmask"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 8); +} + +TEST(ParameterTest, BitmaskParamMapObjectInfoIndirect) +{ + // Instantiation + CBitmaskParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 2u); + EXPECT_EQ(seqParams[0], "C bitmask"); + EXPECT_EQ(seqParams[1], "C++ bitmask"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Bitmask()); + EXPECT_EQ(info.Name(), "C bitmask"); + EXPECT_EQ(info.Description(), "This is a C style enum bitmask"); + EXPECT_EQ(info.DefaultVal(), 1); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Bitmask()); + EXPECT_EQ(info.Name(), "C++ bitmask"); + EXPECT_EQ(info.Description(), "This is a C++ style enum bitmask"); + EXPECT_EQ(info.DefaultVal(), 8); +} + +TEST(ParameterTest, BitmaskParamMapGetSet) +{ + // Instantiation + CBitmaskParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 2u); + EXPECT_EQ(seqParams[0], "C bitmask"); + EXPECT_EQ(seqParams[1], "C++ bitmask"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 1); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 5)); // In limit + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 5); // New value + EXPECT_FALSE(param.SetParam(seqParams[0], 8)); // Bits not specified + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 5); // Same value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 8); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 40)); // In limit + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 40); // New value + EXPECT_FALSE(param.SetParam(seqParams[1], 42)); // Bits not specified + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 40); // Same value +} + +class CPathParameters : public sdv::CSdvParamMap +{ +public: + CPathParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_pathOne, "1st path", std::filesystem::u8path("C:\\dir1\abc.def"), "", "The first path") + SDV_PARAM_ENTRY(m_pathTwo, "2nd path", GetExecDirectory() / GetExecFilename(), "", "The second path") + END_SDV_PARAM_MAP() + +private: + std::filesystem::path m_pathOne; + std::filesystem::path m_pathTwo; +}; + +TEST(ParameterTest, PathParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CPathParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 2u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "1st path"); + EXPECT_EQ(ptrParamInfo->Description(), "The first path"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), std::filesystem::path("C:\\dir1\abc.def").generic_u8string()); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "2nd path"); + EXPECT_EQ(ptrParamInfo->Description(), "The second path"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), GetExecDirectory() / GetExecFilename()); +} + +TEST(ParameterTest, PathParamMapObjectInfo) +{ + // Instantiation + CPathParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 2u); + EXPECT_EQ(seqParams[0], "1st path"); + EXPECT_EQ(seqParams[1], "2nd path"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "1st path"); + EXPECT_EQ(ptrParamInfo->Description(), "The first path"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), std::filesystem::path("C:\\dir1\abc.def").generic_u8string()); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "2nd path"); + EXPECT_EQ(ptrParamInfo->Description(), "The second path"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), GetExecDirectory() / GetExecFilename()); +} + +TEST(ParameterTest, PathParamMapObjectInfoIndirect) +{ + // Instantiation + CPathParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 2u); + EXPECT_EQ(seqParams[0], "1st path"); + EXPECT_EQ(seqParams[1], "2nd path"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Name(), "1st path"); + EXPECT_EQ(info.Description(), "The first path"); + EXPECT_EQ(info.DefaultVal(), std::filesystem::path("C:\\dir1\abc.def").generic_u8string()); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Name(), "2nd path"); + EXPECT_EQ(info.Description(), "The second path"); + EXPECT_EQ(info.DefaultVal(), GetExecDirectory() / GetExecFilename()); +} + +TEST(ParameterTest, PathParamMapGetSet) +{ + // Instantiation + CPathParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 2u); + EXPECT_EQ(seqParams[0], "1st path"); + EXPECT_EQ(seqParams[1], "2nd path"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, std::filesystem::path("C:\\dir1\abc.def").generic_u8string()); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], "xyz/rtw/pop.lol")); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, std::filesystem::path("xyz/rtw/pop.lol").generic_u8string()); // New value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, GetExecDirectory() / GetExecFilename()); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], GetExecDirectory())); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, GetExecDirectory()); // New value +} + +class CReadOnlyParameters : public sdv::CSdvParamMap +{ +public: + CReadOnlyParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_SET_READONLY() + SDV_PARAM_ENTRY(m_i, "my_integer", 10, "int_unit", "My integer") + SDV_PARAM_RESET_READONLY() + SDV_PARAM_ENTRY(m_d, "my_double", 1234.5, "double_unit", "My double") + SDV_PARAM_SET_READONLY() + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + SDV_PARAM_RESET_ATTRIBUTES() + SDV_PARAM_ENTRY(m_b, "my_boolean", true, "no_unit", "My boolean") + END_SDV_PARAM_MAP() + +private: + int m_i; + double m_d; + std::string m_ss; + bool m_b; +}; + +TEST(ParameterTest, ReadOnlyParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CReadOnlyParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 4u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + ASSERT_TRUE(vecParamMap[3]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_TRUE(ptrParamInfo->ReadOnly()); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + EXPECT_FALSE(ptrParamInfo->ReadOnly()); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_TRUE(ptrParamInfo->ReadOnly()); + + // Get parameter #3 global information + ptrParamInfo = vecParamMap[3]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Boolean()); + EXPECT_EQ(ptrParamInfo->Name(), "my_boolean"); + EXPECT_EQ(ptrParamInfo->Unit(), "no_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My boolean"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), true); + EXPECT_FALSE(ptrParamInfo->ReadOnly()); +} + +TEST(ParameterTest, ReadOnlyParamMapObjectInfo) +{ + // Instantiation + CReadOnlyParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 4u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + EXPECT_EQ(seqParams[3], "my_boolean"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_TRUE(ptrParamInfo->ReadOnly()); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + EXPECT_FALSE(ptrParamInfo->ReadOnly()); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_TRUE(ptrParamInfo->ReadOnly()); + + // Get parameter #3 global information + ptrParamInfo = param.FindParamObject(seqParams[3]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Boolean()); + EXPECT_EQ(ptrParamInfo->Unit(), "no_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My boolean"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), true); + EXPECT_FALSE(ptrParamInfo->ReadOnly()); +} + +TEST(ParameterTest, ReadOnlyParamMapObjectInfoIndirect) +{ + // Instantiation + CReadOnlyParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 4u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + EXPECT_EQ(seqParams[3], "my_boolean"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + EXPECT_TRUE(info.ReadOnly()); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + EXPECT_FALSE(info.ReadOnly()); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); + EXPECT_TRUE(info.ReadOnly()); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[3]); + EXPECT_TRUE(info.Boolean()); + EXPECT_EQ(info.Unit(), "no_unit"); + EXPECT_EQ(info.Description(), "My boolean"); + EXPECT_EQ(info.DefaultVal(), true); + EXPECT_FALSE(info.ReadOnly()); +} + +TEST(ParameterTest, ReadOnlyParamMapGetSet) +{ + // Instantiation + CReadOnlyParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 4u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + EXPECT_EQ(seqParams[3], "my_boolean"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_FALSE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Same value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_FALSE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Same value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[3]); + EXPECT_EQ(anyVal, true); // Default value + EXPECT_TRUE(param.SetParam(seqParams[3], false)); + anyVal = param.GetParam(seqParams[3]); + EXPECT_EQ(anyVal, false); // New value +} + +class CTemporaryParameters : public sdv::CSdvParamMap +{ +public: + CTemporaryParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_SET_TEMPORARY() + SDV_PARAM_ENTRY(m_i, "my_integer", 10, "int_unit", "My integer") + SDV_PARAM_RESET_TEMPORARY() + SDV_PARAM_ENTRY(m_d, "my_double", 1234.5, "double_unit", "My double") + SDV_PARAM_SET_TEMPORARY() + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + SDV_PARAM_RESET_ATTRIBUTES() + SDV_PARAM_ENTRY(m_b, "my_boolean", true, "no_unit", "My boolean") + END_SDV_PARAM_MAP() + +private: + int m_i; + double m_d; + std::string m_ss; + bool m_b; +}; + +TEST(ParameterTest, TemporaryParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CTemporaryParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 4u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + ASSERT_TRUE(vecParamMap[3]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_TRUE(ptrParamInfo->Temporary()); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + EXPECT_FALSE(ptrParamInfo->Temporary()); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_TRUE(ptrParamInfo->Temporary()); + + // Get parameter #3 global information + ptrParamInfo = vecParamMap[3]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Boolean()); + EXPECT_EQ(ptrParamInfo->Name(), "my_boolean"); + EXPECT_EQ(ptrParamInfo->Unit(), "no_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My boolean"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), true); + EXPECT_FALSE(ptrParamInfo->Temporary()); +} + +TEST(ParameterTest, TemporaryParamMapObjectInfo) +{ + // Instantiation + CTemporaryParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 4u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + EXPECT_EQ(seqParams[3], "my_boolean"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_TRUE(ptrParamInfo->Temporary()); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + EXPECT_FALSE(ptrParamInfo->Temporary()); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_TRUE(ptrParamInfo->Temporary()); + + // Get parameter #3 global information + ptrParamInfo = param.FindParamObject(seqParams[3]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Boolean()); + EXPECT_EQ(ptrParamInfo->Unit(), "no_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My boolean"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), true); + EXPECT_FALSE(ptrParamInfo->Temporary()); +} + +TEST(ParameterTest, TemporaryParamMapObjectInfoIndirect) +{ + // Instantiation + CTemporaryParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 4u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + EXPECT_EQ(seqParams[3], "my_boolean"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + EXPECT_TRUE(info.Temporary()); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + EXPECT_FALSE(info.Temporary()); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); + EXPECT_TRUE(info.Temporary()); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[3]); + EXPECT_TRUE(info.Boolean()); + EXPECT_EQ(info.Unit(), "no_unit"); + EXPECT_EQ(info.Description(), "My boolean"); + EXPECT_EQ(info.DefaultVal(), true); + EXPECT_FALSE(info.Temporary()); +} + +TEST(ParameterTest, TemporaryParamMapGetSet) +{ + // Instantiation + CTemporaryParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 4u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + EXPECT_EQ(seqParams[3], "my_boolean"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_TRUE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[3]); + EXPECT_EQ(anyVal, true); // Default value + EXPECT_TRUE(param.SetParam(seqParams[3], false)); + anyVal = param.GetParam(seqParams[3]); + EXPECT_EQ(anyVal, false); // New value +} + +class CLockedParameters : public sdv::CSdvParamMap +{ +public: + CLockedParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENABLE_LOCKING() + SDV_PARAM_ENTRY(m_i, "my_integer", 10, "int_unit", "My integer") + SDV_PARAM_DISABLE_LOCKING() + SDV_PARAM_ENTRY(m_d, "my_double", 1234.5, "double_unit", "My double") + SDV_PARAM_ENABLE_LOCKING() + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + SDV_PARAM_RESET_ATTRIBUTES() + SDV_PARAM_ENTRY(m_b, "my_boolean", true, "no_unit", "My boolean") + END_SDV_PARAM_MAP() + +private: + int m_i; + double m_d; + std::string m_ss; + bool m_b; +}; + +TEST(ParameterTest, LockedParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CLockedParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 4u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + ASSERT_TRUE(vecParamMap[3]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_FALSE(ptrParamInfo->Locked()); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + EXPECT_FALSE(ptrParamInfo->Locked()); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_FALSE(ptrParamInfo->Locked()); + + // Get parameter #3 global information + ptrParamInfo = vecParamMap[3]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Boolean()); + EXPECT_EQ(ptrParamInfo->Name(), "my_boolean"); + EXPECT_EQ(ptrParamInfo->Unit(), "no_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My boolean"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), true); + EXPECT_FALSE(ptrParamInfo->Locked()); +} + +TEST(ParameterTest, LockedParamMapObjectInfo) +{ + // Instantiation + CLockedParameters param; + + // Lock the parameter map + param.LockParamMap(); + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 4u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + EXPECT_EQ(seqParams[3], "my_boolean"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_TRUE(ptrParamInfo->Locked()); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + EXPECT_FALSE(ptrParamInfo->Locked()); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_TRUE(ptrParamInfo->Locked()); + + // Get parameter #3 global information + ptrParamInfo = param.FindParamObject(seqParams[3]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Boolean()); + EXPECT_EQ(ptrParamInfo->Unit(), "no_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My boolean"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), true); + EXPECT_FALSE(ptrParamInfo->Locked()); +} + +TEST(ParameterTest, LockedParamMapObjectInfoIndirect) +{ + // Instantiation + CLockedParameters param; + + // Lock the parameter map + param.LockParamMap(); + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 4u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + EXPECT_EQ(seqParams[3], "my_boolean"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + EXPECT_TRUE(info.Locked()); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + EXPECT_FALSE(info.Locked()); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); + EXPECT_TRUE(info.Locked()); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[3]); + EXPECT_TRUE(info.Boolean()); + EXPECT_EQ(info.Unit(), "no_unit"); + EXPECT_EQ(info.Description(), "My boolean"); + EXPECT_EQ(info.DefaultVal(), true); + EXPECT_FALSE(info.Locked()); +} + +TEST(ParameterTest, LockedParamMapGetSet) +{ + // Instantiation + CLockedParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 4u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + EXPECT_EQ(seqParams[3], "my_boolean"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_TRUE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[3]); + EXPECT_EQ(anyVal, true); // Default value + EXPECT_TRUE(param.SetParam(seqParams[3], false)); + anyVal = param.GetParam(seqParams[3]); + EXPECT_EQ(anyVal, false); // New value + + // Lock the parameter map + param.LockParamMap(); + + // Get/set parameter #0 value + EXPECT_FALSE(param.SetParam(seqParams[0], 30)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // Same value + + // Get/set parameter #1 value + EXPECT_TRUE(param.SetParam(seqParams[1], 678.9)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 678.9); // New value + + // Get/set parameter #2 value + EXPECT_FALSE(param.SetParam(seqParams[2], "book")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // Same value + + // Get/set parameter #2 value + EXPECT_TRUE(param.SetParam(seqParams[3], true)); + anyVal = param.GetParam(seqParams[3]); + EXPECT_EQ(anyVal, true); // New value +} + +TEST(ParameterTest, DirtyParamMapGetSet) +{ + // Instantiation + CSimpleParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get/set parameter #0 value + auto ptrParamObj = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamObj); + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_FALSE(ptrParamObj->Dirty()); + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + EXPECT_TRUE(ptrParamObj->Dirty()); + sdv::CSdvParamInfo info = param.GetParamInfo(seqParams[0]); + EXPECT_TRUE(info.Dirty()); + + // Get/set parameter #1 value + ptrParamObj = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamObj); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_FALSE(ptrParamObj->Dirty()); + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + EXPECT_TRUE(ptrParamObj->Dirty()); + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Dirty()); + + // Get/set parameter #2 value + ptrParamObj = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamObj); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_FALSE(ptrParamObj->Dirty()); + EXPECT_TRUE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // New value + EXPECT_TRUE(ptrParamObj->Dirty()); + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.Dirty()); + + // Reset dirty flag + EXPECT_TRUE(param.IsParamDirty(seqParams[0])); + EXPECT_TRUE(param.IsParamDirty(seqParams[1])); + EXPECT_TRUE(param.IsParamDirty(seqParams[2])); + param.ResetParamMapDirtyFlags(); + EXPECT_FALSE(param.IsParamDirty(seqParams[0])); + EXPECT_FALSE(param.IsParamDirty(seqParams[1])); + EXPECT_FALSE(param.IsParamDirty(seqParams[2])); +} + +class CGroupParameters : public sdv::CSdvParamMap +{ +public: + CGroupParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_GROUP("Number") + SDV_PARAM_ENTRY(m_i, "my_integer", 10, "int_unit", "My integer") + SDV_PARAM_GROUP("Number.Floating") + SDV_PARAM_ENTRY(m_d, "my_double", 1234.5, "double_unit", "My double") + SDV_PARAM_NO_GROUP() + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + END_SDV_PARAM_MAP() + +private: + int m_i; + double m_d; + std::string m_ss; +}; + +TEST(ParameterTest, GroupParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CGroupParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 3u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_EQ(ptrParamInfo->Group(), "Number"); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + EXPECT_EQ(ptrParamInfo->Group(), "Number.Floating"); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_TRUE(ptrParamInfo->Group().empty()); +} + +TEST(ParameterTest, GroupParamMapObjectInfo) +{ + // Instantiation + CGroupParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_string"); + EXPECT_EQ(seqParams[1], "Number.my_integer"); + EXPECT_EQ(seqParams[2], "Number.Floating.my_double"); + + // Get parameter #0 global information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_TRUE(ptrParamInfo->Group().empty()); + + // Get parameter #1 information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_EQ(ptrParamInfo->Group(), "Number"); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + EXPECT_EQ(ptrParamInfo->Group(), "Number.Floating"); +} + +TEST(ParameterTest, GroupParamMapObjectInfoIndirect) +{ + // Instantiation + CGroupParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_string"); + EXPECT_EQ(seqParams[1], "Number.my_integer"); + EXPECT_EQ(seqParams[2], "Number.Floating.my_double"); + + // Get parameter #0 global information + sdv::CSdvParamInfo info = param.GetParamInfo(seqParams[0]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); + EXPECT_TRUE(info.Group().empty()); + + // Get parameter #1 information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + EXPECT_EQ(info.Group(), "Number"); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + EXPECT_EQ(info.Group(), "Number.Floating"); +} diff --git a/tests/unit_tests/parameters/param_access_chain.cpp b/tests/unit_tests/parameters/param_access_chain.cpp new file mode 100644 index 0000000..92b643c --- /dev/null +++ b/tests/unit_tests/parameters/param_access_chain.cpp @@ -0,0 +1,854 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 Contributors to the Eclipse Foundation + * + * 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 +#include "../../../global/localmemmgr.h" +#include + +class CSimpleParametersBase : public sdv::CSdvParamMap +{ +public: + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_i, "my_integer", 10, "int_unit", "My integer") + SDV_PARAM_ENTRY(m_d, "my_double", 1234.5, "double_unit", "My double") + END_SDV_PARAM_MAP() + +private: + int m_i; + double m_d; +}; + +class CSimpleParameters : public CSimpleParametersBase +{ +public: + CSimpleParameters() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_CHAIN_BASE(CSimpleParametersBase) + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + END_SDV_PARAM_MAP() + +private: + std::string m_ss; +}; + +TEST(ParameterTest, ChainBaseSimpleParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CSimpleParameters::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 3u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, ChainBaseSimpleParamMapObjectInfo) +{ + // Instantiation + CSimpleParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, ChainBaseSimpleParamMapObjectInfoIndirect) +{ + // Instantiation + CSimpleParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); +} + +TEST(ParameterTest, ChainBaseSimpleParamMapGetSet) +{ + // Instantiation + CSimpleParameters param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_TRUE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // New value +} + +class CSimpleParametersVirtualBase : virtual public sdv::CSdvParamMap +{ +public: + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_i, "my_integer", 10, "int_unit", "My integer") + SDV_PARAM_ENTRY(m_d, "my_double", 1234.5, "double_unit", "My double") + END_SDV_PARAM_MAP() + +private: + int m_i; + double m_d; +}; + +class CSimpleParametersVirtual : virtual public sdv::CSdvParamMap, public CSimpleParametersVirtualBase +{ +public: + CSimpleParametersVirtual() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_CHAIN_BASE(CSimpleParametersVirtualBase) + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + END_SDV_PARAM_MAP() + +private: + std::string m_ss; +}; + +TEST(ParameterTest, VirtualChainBaseSimpleParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CSimpleParametersVirtual::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 3u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, VirtualChainBaseSimpleParamMapObjectInfo) +{ + // Instantiation + CSimpleParametersVirtual param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, VirtualChainBaseSimpleParamMapObjectInfoIndirect) +{ + // Instantiation + CSimpleParametersVirtual param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); +} + +TEST(ParameterTest, VirtualChainBaseSimpleParamMapGetSet) +{ + // Instantiation + CSimpleParametersVirtual param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_TRUE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // New value +} + +class CSimpleParametersMember : public sdv::CSdvParamMap +{ +public: + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENTRY(m_i, "my_integer", 10, "int_unit", "My integer") + SDV_PARAM_ENTRY(m_d, "my_double", 1234.5, "double_unit", "My double") + END_SDV_PARAM_MAP() + +private: + int m_i; + double m_d; +}; + +class CSimpleParametersWithMember : public sdv::CSdvParamMap +{ +public: + CSimpleParametersWithMember() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_CHAIN_MEMBER(m_member) + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + END_SDV_PARAM_MAP() + +private: + std::string m_ss; + CSimpleParametersMember m_member; +}; + +TEST(ParameterTest, MemberChainBaseSimpleParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CSimpleParametersWithMember::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 3u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, MemberChainBaseSimpleParamMapObjectInfo) +{ + // Instantiation + CSimpleParametersWithMember param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, MemberChainBaseSimpleParamMapObjectInfoIndirect) +{ + // Instantiation + CSimpleParametersWithMember param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); +} + +TEST(ParameterTest, MemberChainBaseSimpleParamMapGetSet) +{ + // Instantiation + CSimpleParametersWithMember param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_TRUE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // New value +} + +class CSimpleParametersWithMemberPointer : public sdv::CSdvParamMap +{ +public: + CSimpleParametersWithMemberPointer() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_CHAIN_MEMBER(m_pMember) + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + END_SDV_PARAM_MAP() + +private: + std::string m_ss; + CSimpleParametersMember m_member; + CSimpleParametersMember* m_pMember = &m_member; +}; + +TEST(ParameterTest, MemberPointerChainBaseSimpleParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CSimpleParametersWithMemberPointer::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 3u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, MemberPointerChainBaseSimpleParamMapObjectInfo) +{ + // Instantiation + CSimpleParametersWithMemberPointer param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, MemberPointerChainBaseSimpleParamMapObjectInfoIndirect) +{ + // Instantiation + CSimpleParametersWithMemberPointer param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); +} + +TEST(ParameterTest, MemberPointerChainBaseSimpleParamMapGetSet) +{ + // Instantiation + CSimpleParametersWithMemberPointer param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_TRUE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // New value +} + +class CSimpleParametersWithMemberSmartPointer : public sdv::CSdvParamMap +{ +public: + CSimpleParametersWithMemberSmartPointer() + { + InitParamMap(); + } + + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_CHAIN_MEMBER(m_ptrMember) + SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + END_SDV_PARAM_MAP() + +private: + std::string m_ss; + std::shared_ptr m_ptrMember = std::make_shared(); +}; + +TEST(ParameterTest, MemberSmartPointerChainBaseSimpleParamMapStaticInfo) +{ + // Get parameter names + auto vecParamMap = CSimpleParametersWithMemberSmartPointer::GetParamMapInfoStatic(); + ASSERT_EQ(vecParamMap.size(), 3u); + ASSERT_TRUE(vecParamMap[0]); + ASSERT_TRUE(vecParamMap[1]); + ASSERT_TRUE(vecParamMap[2]); + + // Get parameter #0 global information + auto ptrParamInfo = vecParamMap[0]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = vecParamMap[1]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Name(), "my_double"); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = vecParamMap[2]; + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Name(), "my_string"); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, MemberSmartPointerChainBaseSimpleParamMapObjectInfo) +{ + // Instantiation + CSimpleParametersWithMemberSmartPointer param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + auto ptrParamInfo = param.FindParamObject(seqParams[0]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My integer"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + + // Get parameter #1 global information + ptrParamInfo = param.FindParamObject(seqParams[1]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->Numeric()); + EXPECT_EQ(ptrParamInfo->Unit(), "double_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My double"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 1234.5); + + // Get parameter #2 global information + ptrParamInfo = param.FindParamObject(seqParams[2]); + ASSERT_TRUE(ptrParamInfo); + EXPECT_TRUE(ptrParamInfo->String()); + EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); + EXPECT_EQ(ptrParamInfo->Description(), "My string"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); +} + +TEST(ParameterTest, MemberSmartPointerChainBaseSimpleParamMapObjectInfoIndirect) +{ + // Instantiation + CSimpleParametersWithMemberSmartPointer param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get parameter #0 information + sdv::CSdvParamInfo info(param.GetParamInfo(seqParams[0])); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "int_unit"); + EXPECT_EQ(info.Description(), "My integer"); + EXPECT_EQ(info.DefaultVal(), 10); + + // Get parameter #1 global information + info = param.GetParamInfo(seqParams[1]); + EXPECT_TRUE(info.Numeric()); + EXPECT_EQ(info.Unit(), "double_unit"); + EXPECT_EQ(info.Description(), "My double"); + EXPECT_EQ(info.DefaultVal(), 1234.5); + + // Get parameter #2 global information + info = param.GetParamInfo(seqParams[2]); + EXPECT_TRUE(info.String()); + EXPECT_EQ(info.Unit(), "string_unit"); + EXPECT_EQ(info.Description(), "My string"); + EXPECT_EQ(info.DefaultVal(), "string_value"); +} + +TEST(ParameterTest, MemberSmartPointerChainBaseSimpleParamMapGetSet) +{ + // Instantiation + CSimpleParametersWithMemberSmartPointer param; + + // Get parameter names + auto seqParams = param.GetParamPaths(); + ASSERT_EQ(seqParams.size(), 3u); + EXPECT_EQ(seqParams[0], "my_integer"); + EXPECT_EQ(seqParams[1], "my_double"); + EXPECT_EQ(seqParams[2], "my_string"); + + // Get/set parameter #0 value + auto anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 10); // Default value + EXPECT_TRUE(param.SetParam(seqParams[0], 20)); + anyVal = param.GetParam(seqParams[0]); + EXPECT_EQ(anyVal, 20); // New value + + // Get/set parameter #1 value + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 1234.5); // Default value + EXPECT_TRUE(param.SetParam(seqParams[1], 5432.1)); + anyVal = param.GetParam(seqParams[1]); + EXPECT_EQ(anyVal, 5432.1); // New value + + // Get/set parameter #2 value + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "string_value"); // Default value + EXPECT_TRUE(param.SetParam(seqParams[2], "text_value")); + anyVal = param.GetParam(seqParams[2]); + EXPECT_EQ(anyVal, "text_value"); // New value +} diff --git a/tests/unit_tests/parameters/param_info.cpp b/tests/unit_tests/parameters/param_info.cpp new file mode 100644 index 0000000..2cdd6bf --- /dev/null +++ b/tests/unit_tests/parameters/param_info.cpp @@ -0,0 +1,189 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 Contributors to the Eclipse Foundation + * + * 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 +#include "../../../global/localmemmgr.h" +#include + +TEST(ParameterTest, AutoTypeInitializeBoolean) +{ + bool b; + EXPECT_TRUE(std::make_shared(b, "b", true, "", "", "", 0)->Boolean()); +} + +TEST(ParameterTest, AutoTypeInitializeNumeric) +{ + // Signed integer + int8_t i8; + EXPECT_TRUE(std::make_shared(i8, "i8", 1, "", "", "", 0)->Numeric()); + int16_t i16; + EXPECT_TRUE(std::make_shared(i16, "i16", 2, "", "", "", 0)->Numeric()); + int32_t i32; + EXPECT_TRUE(std::make_shared(i32, "i32", 3, "", "", "", 0)->Numeric()); + int64_t i64; + EXPECT_TRUE(std::make_shared(i64, "i64", 4, "", "", "", 0)->Numeric()); + int i; + EXPECT_TRUE(std::make_shared(i, "i", 5, "", "", "", 0)->Numeric()); + long l; + EXPECT_TRUE(std::make_shared(l, "l", 6, "", "", "", 0)->Numeric()); + long long ll; + EXPECT_TRUE(std::make_shared(ll, "ll", 7, "", "", "", 0)->Numeric()); + + // Unsigned integer + uint8_t ui8; + EXPECT_TRUE(std::make_shared(ui8, "ui8", 8, "", "", "", 0)->Numeric()); + uint16_t ui16; + EXPECT_TRUE(std::make_shared(ui16, "ui16", 9, "", "", "", 0)->Numeric()); + uint32_t ui32; + EXPECT_TRUE(std::make_shared(ui32, "ui32", 10, "", "", "", 0)->Numeric()); + uint32_t ui64; + EXPECT_TRUE(std::make_shared(ui64, "ui64", 11, "", "", "", 0)->Numeric()); + unsigned int ui; + EXPECT_TRUE(std::make_shared(ui, "ui", 12, "", "", "", 0)->Numeric()); + unsigned long ul; + EXPECT_TRUE(std::make_shared(ul, "ul", 13, "", "", "", 0)->Numeric()); + unsigned long long ull; + EXPECT_TRUE(std::make_shared(ull, "ull", 14, "", "", "", 0)->Numeric()); + + // Floating point + float f; + EXPECT_TRUE(std::make_shared(f, "f", 15.0, "", "", "", 0)->Numeric()); + double d; + EXPECT_TRUE(std::make_shared(d, "d", 16.0, "", "", "", 0)->Numeric()); + long double ld; + EXPECT_TRUE(std::make_shared(ld, "ld", 17.0, "", "", "", 0)->Numeric()); +} + +TEST(ParameterTest, AutoTypeInitializeString) +{ + // STD string object + std::string ss; + EXPECT_TRUE(std::make_shared(ss, "ss", "18", "", "", "", 0)->String()); + std::wstring ssw; + EXPECT_TRUE(std::make_shared(ssw, "ssw", L"19", "", "", "", 0)->String()); + std::u16string ss16; + EXPECT_TRUE(std::make_shared(ss16, "ss16", u"20", "", "", "", 0)->String()); + std::u32string ss32; + EXPECT_TRUE(std::make_shared(ss32, "ss32", U"21", "", "", "", 0)->String()); + + // SDV string object + sdv::string ss_sdv; + EXPECT_TRUE(std::make_shared(ss_sdv, "ss_sdv", "22", "", "", "", 0)->String()); + sdv::u8string ss8_sdv; + EXPECT_TRUE(std::make_shared(ss8_sdv, "ss8_sdv", u8"23", "", "", "", 0)->String()); + sdv::wstring ssw_sdv; + EXPECT_TRUE(std::make_shared(ssw_sdv, "ssw_sdv", L"24", "", "", "", 0)->String()); + sdv::u16string ss16_sdv; + EXPECT_TRUE(std::make_shared(ss16_sdv, "ss16_sdv", u"25", "", "", "", 0)->String()); + sdv::u32string ss32_sdv; + EXPECT_TRUE(std::make_shared(ss32_sdv, "ss32_sdv", U"26", "", "", "", 0)->String()); + + // C character array + char sz[] = ""; + EXPECT_TRUE(std::make_shared(sz, "sz", "27", "", "", "", 0)->String()); + char16_t sz16[] = u""; + EXPECT_TRUE(std::make_shared(sz16, "sz16", u"28", "", "", "", 0)->String()); + char32_t sz32[] = U""; + EXPECT_TRUE(std::make_shared(sz32, "sz32", U"29", "", "", "", 0)->String()); + wchar_t wsz[] = L""; + EXPECT_TRUE(std::make_shared(wsz, "wsz", L"30", "", "", "", 0)->String()); + + // Pointer to a string + char* psz = new char[2]; + EXPECT_TRUE(std::make_shared(psz, "psz", "31", "", "", "", 0)->String()); + delete[] psz; + char16_t* psz16 = new char16_t[2]; + EXPECT_TRUE(std::make_shared(psz16, "psz16", u"32", "", "", "", 0)->String()); + delete[] psz16; + char32_t* psz32 = new char32_t[2]; + EXPECT_TRUE(std::make_shared(psz32, "psz32", U"33", "", "", "", 0)->String()); + delete[] psz32; + wchar_t* pwsz = new wchar_t[2]; + EXPECT_TRUE(std::make_shared(pwsz, "pwsz", L"34", "", "", "", 0)->String()); + delete[] pwsz; + + // Pointer to a const string + const char* pcsz = ""; + EXPECT_TRUE(std::make_shared(pcsz, "pcsz", "35", "", "", "", 0)->String()); + const char16_t* pcsz16 = u""; + EXPECT_TRUE(std::make_shared(pcsz16, "pcsz16", u"36", "", "", "", 0)->String()); + const char32_t* pcsz32 = U""; + EXPECT_TRUE(std::make_shared(pcsz32, "pcsz32", U"37", "", "", "", 0)->String()); + const wchar_t* pcwsz = L""; + EXPECT_TRUE(std::make_shared(pcwsz, "pcwsz", L"38", "", "", "", 0)->String()); +} + +TEST(ParameterTest, AutoTypeInitializeEnum) +{ + // C-style enum + enum ECStyle {one, two, three} eC; + EXPECT_TRUE(std::make_shared(eC, "ECStyle", ECStyle::one, "", "", "", 0)->Enum()); + + // C++-style enum + enum class ECppStyle : uint8_t {one, two, three} eCpp; + EXPECT_TRUE(std::make_shared(eCpp, "ECppStyle", ECppStyle::two, "", "", "", 0)->Enum()); +} + +TEST(ParameterTest, AutoTypeReadonly) +{ + // Numeric read/write + int32_t i32RW; + EXPECT_FALSE(std::make_shared(i32RW, "i32RW", 10, "", "", "", 0)->ReadOnly()); + uint32_t ui32RW; + EXPECT_FALSE(std::make_shared(ui32RW, "ui32RW", 20, "", "", "", 0)->ReadOnly()); + double dRW; + EXPECT_FALSE(std::make_shared(dRW, "dRW", 30, "", "", "", 0)->ReadOnly()); + + // Strings read/write + // Remarks: the C-style string and the character pointer are both marked as read-only. + std::string ssRW; + EXPECT_FALSE(std::make_shared(ssRW, "ssRW", "40", "", "", "", 0)->ReadOnly()); + sdv::string ss_sdvRW; + EXPECT_FALSE(std::make_shared(ss_sdvRW, "ss_sdvRW", "50", "", "", "", 0)->ReadOnly()); + + // Enums read/write + enum ECStyle {one, two, three}; + enum class ECppStyle : uint8_t {one, two, three}; + ECStyle eCStyleRW; + EXPECT_FALSE(std::make_shared(eCStyleRW , "eCStyleRW", ECStyle ::one, "", "", "", 0)->ReadOnly()); + ECppStyle eCppStyleRW; + EXPECT_FALSE(std::make_shared(eCppStyleRW, "eCppStyleRW", ECppStyle::two, "", "", "", 0)->ReadOnly()); + + // Numeric read-only + const int32_t i32R = 0; + EXPECT_TRUE(std::make_shared(i32R, "i32R", 60, "", "", "", 0)->ReadOnly()); + const uint32_t ui32R = 0; + EXPECT_TRUE(std::make_shared(ui32R, "ui32R", 70, "", "", "", 0)->ReadOnly()); + const double dR = 0.0; + EXPECT_TRUE(std::make_shared(dR, "dR", 80, "", "", "", 0)->ReadOnly()); + + // Strings read/write + const std::string ssR; + EXPECT_TRUE(std::make_shared(ssR, "ssR", "90", "", "", "", 0)->ReadOnly()); + const sdv::string ss_sdvR; + EXPECT_TRUE(std::make_shared(ss_sdvR, "ss_sdvR", "100", "", "", "", 0)->ReadOnly()); + char szR[] = ""; + EXPECT_TRUE(std::make_shared(szR, "szR", "101", "", "", "", 0)->ReadOnly()); + char* pszR = new char[2]; + EXPECT_TRUE(std::make_shared(pszR, "pszR", "102", "", "", "", 0)->ReadOnly()); + delete[] pszR; + const char* pcszR = ""; + EXPECT_TRUE(std::make_shared(pcszR, "pcszR", "103", "", "", "", 0)->ReadOnly()); + + // Enums read/write + const ECStyle eCStyleR = ECStyle::one; + EXPECT_TRUE(std::make_shared(eCStyleR, "eCStyleR", ECStyle::one, "", "", "", 0)->ReadOnly()); + const ECppStyle eCppStyleR = ECppStyle::one; + EXPECT_TRUE(std::make_shared(eCppStyleR, "eCppStyleR", ECppStyle::one, "", "", "", 0)->ReadOnly()); +} + diff --git a/tests/unit_tests/parameters/param_labels.cpp b/tests/unit_tests/parameters/param_labels.cpp new file mode 100644 index 0000000..c414e25 --- /dev/null +++ b/tests/unit_tests/parameters/param_labels.cpp @@ -0,0 +1,233 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 Contributors to the Eclipse Foundation + * + * 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 +#include "../../../global/localmemmgr.h" +#include + +enum class EEnumWithoutLabelMap +{ + one, + two, + three +}; + +enum class EEnumWithEmptyLabelMap +{ + four, + five, + six +}; + +BEGIN_SDV_LABEL_MAP(EEnumWithEmptyLabelMap) +END_SDV_LABEL_MAP() + +enum class EEnumWithLabelMap +{ + seven, + eight, + nine +}; + +BEGIN_SDV_LABEL_MAP(EEnumWithLabelMap) + SDV_LABEL_ENTRY(EEnumWithLabelMap::seven, "seven") + SDV_LABEL_ENTRY(EEnumWithLabelMap::eight, "eight") + SDV_LABEL_ENTRY(EEnumWithLabelMap::nine, "nine") +END_SDV_LABEL_MAP() + +enum class EEnumWithLabelMapBehindFunction +{ + ten, + eleven, + twelve +}; + +sdv::sequence GetLabelMapForEnumBeforMapDefinition() +{ + return sdv::internal::GetLabelMapHelper().GetLabelMap(); +} + +BEGIN_SDV_LABEL_MAP(EEnumWithLabelMapBehindFunction) + SDV_LABEL_ENTRY(EEnumWithLabelMapBehindFunction::ten, "ten") + SDV_LABEL_ENTRY(EEnumWithLabelMapBehindFunction::eleven, "eleven") + SDV_LABEL_ENTRY(EEnumWithLabelMapBehindFunction::twelve, "twelve") +END_SDV_LABEL_MAP() + +namespace LabelMapTest +{ + enum class EEnumInNamespaceWithLabelMap + { + thirteen, + fourteen, + fifteen + }; + + BEGIN_SDV_LABEL_MAP(EEnumInNamespaceWithLabelMap) + SDV_LABEL_ENTRY(EEnumInNamespaceWithLabelMap::thirteen, "thirteen") + SDV_LABEL_ENTRY(EEnumInNamespaceWithLabelMap::fourteen, "fourteen") + SDV_LABEL_ENTRY(EEnumInNamespaceWithLabelMap::fifteen, "fifteen") + END_SDV_LABEL_MAP() + + enum class EEnumInNamespaceWithLabelMapBehindFunction + { + sixteen, + seventeen, + eighteen + }; + + sdv::sequence GetLabelMapForEnumBeforMapDefinition() + { + return sdv::internal::GetLabelMapHelper().GetLabelMap(); + } + + BEGIN_SDV_LABEL_MAP(EEnumInNamespaceWithLabelMapBehindFunction) + SDV_LABEL_ENTRY(EEnumInNamespaceWithLabelMapBehindFunction::sixteen, "sixteen") + SDV_LABEL_ENTRY(EEnumInNamespaceWithLabelMapBehindFunction::seventeen, "seventeen") + SDV_LABEL_ENTRY(EEnumInNamespaceWithLabelMapBehindFunction::eighteen, "eighteen") + END_SDV_LABEL_MAP() +} // namespace LabelMapTest + +class CLabelMapTest +{ +public: + enum class EEnumInClassWithLabelMap + { + nineteen, + twenty, + twenty_one + }; + + BEGIN_SDV_LABEL_MAP(EEnumInClassWithLabelMap) + SDV_LABEL_ENTRY(EEnumInClassWithLabelMap::nineteen, "nineteen") + SDV_LABEL_ENTRY(EEnumInClassWithLabelMap::twenty, "twenty") + SDV_LABEL_ENTRY(EEnumInClassWithLabelMap::twenty_one, "twenty_one") + END_SDV_LABEL_MAP() + + enum class EEnumInClassWithLabelMapBehindFunction + { + twenty_two, + twenty_three, + twenty_four + }; + + static sdv::sequence GetLabelMapForEnumBeforMapDefinition() + { + return sdv::internal::GetLabelMapHelper().GetLabelMap(); + } + + BEGIN_SDV_LABEL_MAP(EEnumInClassWithLabelMapBehindFunction) + SDV_LABEL_ENTRY(EEnumInClassWithLabelMapBehindFunction::twenty_two, "twenty_two") + SDV_LABEL_ENTRY(EEnumInClassWithLabelMapBehindFunction::twenty_three, "twenty_three") + SDV_LABEL_ENTRY(EEnumInClassWithLabelMapBehindFunction::twenty_four, "twenty_four") + END_SDV_LABEL_MAP() + + enum class EEnumInClassWithLabelMapOutsideClass + { + twenty_five, + twenty_six, + twenty_seven + }; + + enum class EEnumInClassWithLabelMapOutsideClassBehindFunction + { + twenty_eight, + twenty_nine, + thirty + }; +}; + +BEGIN_SDV_LABEL_MAP(CLabelMapTest::EEnumInClassWithLabelMapOutsideClass) + SDV_LABEL_ENTRY(CLabelMapTest::EEnumInClassWithLabelMapOutsideClass::twenty_five, "twenty_five") + SDV_LABEL_ENTRY(CLabelMapTest::EEnumInClassWithLabelMapOutsideClass::twenty_six, "twenty_six") + SDV_LABEL_ENTRY(CLabelMapTest::EEnumInClassWithLabelMapOutsideClass::twenty_seven, "twenty_seven") +END_SDV_LABEL_MAP() + +sdv::sequence GetLabelMapForEnumBeforMapDefinitionOutsideClass() +{ + return sdv::internal::GetLabelMapHelper().GetLabelMap(); +} + +BEGIN_SDV_LABEL_MAP(CLabelMapTest::EEnumInClassWithLabelMapOutsideClassBehindFunction) + SDV_LABEL_ENTRY(CLabelMapTest::EEnumInClassWithLabelMapOutsideClassBehindFunction::twenty_eight, "twenty_eight") + SDV_LABEL_ENTRY(CLabelMapTest::EEnumInClassWithLabelMapOutsideClassBehindFunction::twenty_nine, "twenty_nine") + SDV_LABEL_ENTRY(CLabelMapTest::EEnumInClassWithLabelMapOutsideClassBehindFunction::thirty, "thirty") +END_SDV_LABEL_MAP() + +TEST(ParameterTest, LabelMap) +{ + // Enum without label map + auto seqLabelMap = sdv::internal::GetLabelMapHelper().GetLabelMap(); + EXPECT_TRUE(seqLabelMap.empty()); + + // Enum with empty label map + seqLabelMap = sdv::internal::GetLabelMapHelper().GetLabelMap(); + EXPECT_TRUE(seqLabelMap.empty()); + + // Enum with label map + seqLabelMap = sdv::internal::GetLabelMapHelper().GetLabelMap(); + ASSERT_EQ(seqLabelMap.size(), 3u); + EXPECT_EQ(seqLabelMap[0].anyValue, static_cast(EEnumWithLabelMap::seven)); + EXPECT_EQ(seqLabelMap[1].anyValue, static_cast(EEnumWithLabelMap::eight)); + EXPECT_EQ(seqLabelMap[2].anyValue, static_cast(EEnumWithLabelMap::nine)); + + // Enum with label map with access function defined before label map + seqLabelMap = GetLabelMapForEnumBeforMapDefinition(); + ASSERT_EQ(seqLabelMap.size(), 3u); + EXPECT_EQ(seqLabelMap[0].anyValue, static_cast(EEnumWithLabelMapBehindFunction::ten)); + EXPECT_EQ(seqLabelMap[1].anyValue, static_cast(EEnumWithLabelMapBehindFunction::eleven)); + EXPECT_EQ(seqLabelMap[2].anyValue, static_cast(EEnumWithLabelMapBehindFunction::twelve)); + + // Enum in namespace with label map + seqLabelMap = sdv::internal::GetLabelMapHelper().GetLabelMap(); + ASSERT_EQ(seqLabelMap.size(), 3u); + EXPECT_EQ(seqLabelMap[0].anyValue, static_cast(LabelMapTest::EEnumInNamespaceWithLabelMap::thirteen)); + EXPECT_EQ(seqLabelMap[1].anyValue, static_cast(LabelMapTest::EEnumInNamespaceWithLabelMap::fourteen)); + EXPECT_EQ(seqLabelMap[2].anyValue, static_cast(LabelMapTest::EEnumInNamespaceWithLabelMap::fifteen)); + + // Enum in namespace with label map with access function defined before label map + seqLabelMap = LabelMapTest::GetLabelMapForEnumBeforMapDefinition(); + ASSERT_EQ(seqLabelMap.size(), 3u); + EXPECT_EQ(seqLabelMap[0].anyValue, static_cast(LabelMapTest::EEnumInNamespaceWithLabelMapBehindFunction::sixteen)); + EXPECT_EQ(seqLabelMap[1].anyValue, static_cast(LabelMapTest::EEnumInNamespaceWithLabelMapBehindFunction::seventeen)); + EXPECT_EQ(seqLabelMap[2].anyValue, static_cast(LabelMapTest::EEnumInNamespaceWithLabelMapBehindFunction::eighteen)); + + // Enum in class with label map + seqLabelMap = sdv::internal::GetLabelMapHelper().GetLabelMap(); + ASSERT_EQ(seqLabelMap.size(), 3u); + EXPECT_EQ(seqLabelMap[0].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMap::nineteen)); + EXPECT_EQ(seqLabelMap[1].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMap::twenty)); + EXPECT_EQ(seqLabelMap[2].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMap::twenty_one)); + + // Enum in class with label map with access function defined before label map + seqLabelMap = CLabelMapTest::GetLabelMapForEnumBeforMapDefinition(); + ASSERT_EQ(seqLabelMap.size(), 3u); + EXPECT_EQ(seqLabelMap[0].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMapBehindFunction::twenty_two)); + EXPECT_EQ(seqLabelMap[1].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMapBehindFunction::twenty_three)); + EXPECT_EQ(seqLabelMap[2].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMapBehindFunction::twenty_four)); + + // Enum in class with label map outside class + seqLabelMap = sdv::internal::GetLabelMapHelper().GetLabelMap(); + ASSERT_EQ(seqLabelMap.size(), 3u); + EXPECT_EQ(seqLabelMap[0].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMapOutsideClass::twenty_five)); + EXPECT_EQ(seqLabelMap[1].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMapOutsideClass::twenty_six)); + EXPECT_EQ(seqLabelMap[2].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMapOutsideClass::twenty_seven)); + + // Enum in class with label map outside class behind access function + seqLabelMap = GetLabelMapForEnumBeforMapDefinitionOutsideClass(); + ASSERT_EQ(seqLabelMap.size(), 3u); + EXPECT_EQ( + seqLabelMap[0].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMapOutsideClassBehindFunction::twenty_eight)); + EXPECT_EQ( + seqLabelMap[1].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMapOutsideClassBehindFunction::twenty_nine)); + EXPECT_EQ(seqLabelMap[2].anyValue, static_cast(CLabelMapTest::EEnumInClassWithLabelMapOutsideClassBehindFunction::thirty)); +}; diff --git a/tests/unit_tests/path_match/CMakeLists.txt b/tests/unit_tests/path_match/CMakeLists.txt index 7973401..d696c65 100644 --- a/tests/unit_tests/path_match/CMakeLists.txt +++ b/tests/unit_tests/path_match/CMakeLists.txt @@ -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(PathMatchTestTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/path_match/path_match_test_suite.cpp b/tests/unit_tests/path_match/path_match_test_suite.cpp index 7dd6dbc..38a390d 100644 --- a/tests/unit_tests/path_match/path_match_test_suite.cpp +++ b/tests/unit_tests/path_match/path_match_test_suite.cpp @@ -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 "path_match_test_suite.h" #include "../../../global/exec_dir_helper.h" #include "../../../global/process_watchdog.h" diff --git a/tests/unit_tests/path_match/path_match_test_suite.h b/tests/unit_tests/path_match/path_match_test_suite.h index f82379f..63c9e92 100644 --- a/tests/unit_tests/path_match/path_match_test_suite.h +++ b/tests/unit_tests/path_match/path_match_test_suite.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 COMPOSER_TEST_SUITE_H #define COMPOSER_TEST_SUITE_H diff --git a/tests/unit_tests/path_match/regex_match.cpp b/tests/unit_tests/path_match/regex_match.cpp index cc6cdbc..8a249d3 100644 --- a/tests/unit_tests/path_match/regex_match.cpp +++ b/tests/unit_tests/path_match/regex_match.cpp @@ -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 "path_match_test_suite.h" #include "../../../global/path_match.h" #include "../../../global/exec_dir_helper.h" diff --git a/tests/unit_tests/path_match/wildcard_match.cpp b/tests/unit_tests/path_match/wildcard_match.cpp index 2f95801..0b00f4d 100644 --- a/tests/unit_tests/path_match/wildcard_match.cpp +++ b/tests/unit_tests/path_match/wildcard_match.cpp @@ -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 "path_match_test_suite.h" #include "../../../global/path_match.h" #include "../../../global/exec_dir_helper.h" diff --git a/tests/unit_tests/process_control/CMakeLists.txt b/tests/unit_tests/process_control/CMakeLists.txt index 0880b8f..290d462 100644 --- a/tests/unit_tests/process_control/CMakeLists.txt +++ b/tests/unit_tests/process_control/CMakeLists.txt @@ -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 (multiple EXEs) project(ProcessControlUnitTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/process_control/main.cpp b/tests/unit_tests/process_control/main.cpp index 5769563..3cdcc8c 100644 --- a/tests/unit_tests/process_control/main.cpp +++ b/tests/unit_tests/process_control/main.cpp @@ -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 "gtest/gtest.h" #include "../../../global/process_watchdog.h" diff --git a/tests/unit_tests/process_control/process_control_app.cpp b/tests/unit_tests/process_control/process_control_app.cpp index 27b5b41..2fa1f08 100644 --- a/tests/unit_tests/process_control/process_control_app.cpp +++ b/tests/unit_tests/process_control/process_control_app.cpp @@ -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 #include #include diff --git a/tests/unit_tests/process_control/process_control_ifc.h b/tests/unit_tests/process_control/process_control_ifc.h index b1cc953..7f81dd6 100644 --- a/tests/unit_tests/process_control/process_control_ifc.h +++ b/tests/unit_tests/process_control/process_control_ifc.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 PROCESS_CONTROL_IFC_H #define PROCESS_CONTROL_IFC_H diff --git a/tests/unit_tests/process_control/process_control_tests.cpp b/tests/unit_tests/process_control/process_control_tests.cpp index 87e5c81..7ffb029 100644 --- a/tests/unit_tests/process_control/process_control_tests.cpp +++ b/tests/unit_tests/process_control/process_control_tests.cpp @@ -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 "../../include/gtest_custom.h" #include #include diff --git a/tests/unit_tests/repository/CMakeLists.txt b/tests/unit_tests/repository/CMakeLists.txt index 60619e2..452ce77 100644 --- a/tests/unit_tests/repository/CMakeLists.txt +++ b/tests/unit_tests/repository/CMakeLists.txt @@ -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(RepositoryTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/repository/IComponent.idl b/tests/unit_tests/repository/IComponent.idl index 8bd047a..66cdae2 100644 --- a/tests/unit_tests/repository/IComponent.idl +++ b/tests/unit_tests/repository/IComponent.idl @@ -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 /** diff --git a/tests/unit_tests/repository/main.cpp b/tests/unit_tests/repository/main.cpp index dba1448..acb1378 100644 --- a/tests/unit_tests/repository/main.cpp +++ b/tests/unit_tests/repository/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "mock.h" @@ -7,12 +20,14 @@ #include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp" #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/miscellaneous.cpp" +#include "../../../sdv_services/core/toml_parser/code_snippet.cpp" #include "../../../sdv_services/core/module_control.cpp" #include "../../../sdv_services/core/module.cpp" #include "../../../sdv_services/core/repository.cpp" #include "../../../sdv_services/core/iso_monitor.cpp" #include "../../../sdv_services/core/object_lifetime_control.cpp" #include "../../../sdv_services/core/app_config.cpp" +#include "../../../sdv_services/core/app_config_file.cpp" #include "../../../sdv_services/core/installation_manifest.cpp" #if defined(_WIN32) && defined(_UNICODE) diff --git a/tests/unit_tests/repository/mock.h b/tests/unit_tests/repository/mock.h index f8fe550..ed08560 100644 --- a/tests/unit_tests/repository/mock.h +++ b/tests/unit_tests/repository/mock.h @@ -1,41 +1,109 @@ +/******************************************************************************** + * 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 MOCK_H #define MOCK_H #include -#define DO_NOT_INCLUDE_IN_UNIT_TEST -#include "../../../sdv_services/core/app_control.h" -#include "../../../sdv_services/core/sdv_core.h" +#include +#include "../../../global/exec_dir_helper.h" +#include "../../../sdv_services/core/installation_manifest.h" -class CMock +// Prevent including app_settings.h and app_control.h +#define APP_SETTINGS_H +#define APP_CONTROL_H +#define LOGGER_H +#define LOGGER_CONTROL_H + +/** + * @brief CAppSettings redefined + */ +class CAppSettings : public sdv::IInterfaceAccess { public: - //void DestroyModuleObjects(sdv::core::TModuleID) {} + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() + + // CAppSettings mocked functions + sdv::app::EAppContext GetContextType() const { return sdv::app::EAppContext::standalone; } bool IsStandaloneApplication() { return true; } bool IsEssentialApplication() { return false; } bool IsMainApplication() { return false; } bool IsIsolatedApplication() { return false; } bool IsMaintenanceApplication() { return false; } - bool IsExternalApplication() { return false; } + bool IsExternalApplication() const { return false; } bool IsConsoleSilent() { return true; } bool IsConsoleVerbose() { return false; } uint32_t GetInstanceID() { return 1000u; } - void RequestShutdown() {} - sdv::app::EAppOperationState GetOperationState() const { return sdv::app::EAppOperationState::running; } - std::filesystem::path GetInstallDir() const { return std::filesystem::path(); } - std::filesystem::path FindInstalledModule(const std::filesystem::path&) const { return {}; } - std::optional FindInstalledComponent(const std::string&) const { return {}; } - std::string FindInstalledModuleManifest(const std::filesystem::path&) { return {}; } + std::filesystem::path GetRootDir() const { return GetExecDirectory(); } + std::filesystem::path GetInstallDir() const { return GetExecDirectory(); } + std::vector GetSystemConfigPaths() const { return {}; } + std::filesystem::path GetUserConfigPath() const { return {}; } }; -inline CMock& GetMock() +/** + * @brief Return the application settings class. + * @return Reference to the application settings. + */ +inline CAppSettings& GetAppSettings() { - static CMock mock; - return mock; + static CAppSettings app_settings; + return app_settings; } -#define CAppControl CMock -#define GetAppControl GetMock -#define GetAppConfig GetMock +/** + * @brief CAppControl redefined + */ +class CAppControl : public sdv::IInterfaceAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() + + // CAppControl mocked functions + void RequestShutdown() {} + sdv::app::EAppOperationState GetOperationState() const { return sdv::app::EAppOperationState::running; } +}; + +/** + * @brief Return the application control. + * @return Reference to the application control. + */ +inline CAppControl& GetAppControl() +{ + static CAppControl app_control; + return app_control; +} + +/** + * @brief CLoggerControl redefined + */ +class CLoggerControl : public sdv::IInterfaceAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + END_SDV_INTERFACE_MAP() +}; + +/** + * @brief Return the logger control. + * @return Reference to the logger control. + */ +inline CLoggerControl& GetLoggerControl() +{ + static CLoggerControl logger_control; + return logger_control; +} #include "../../../sdv_services/core/toml_parser/parser_toml.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" @@ -43,7 +111,8 @@ inline CMock& GetMock() #include "../../../sdv_services/core/repository.h" #include "../../../sdv_services/core/app_config.h" -inline std::filesystem::path GetCoreDirectory() { return "../../bin"; } +//inline std::filesystem::path GetCoreDirectoryMock() { return "../../bin"; } +//#define GetCoreDirectory GetCoreDirectoryMock class CHelper { @@ -69,5 +138,7 @@ public: inline CModuleControl& GetModuleControl() { return CHelper::GetModuleControl(); } inline CRepository& GetRepository() { return CHelper::GetRepository(); } +#define GetModuleControl GetModuleControl +#define GetRepository GetRepository #endif // !defined MOCK_H \ No newline at end of file diff --git a/tests/unit_tests/repository/repository_test.cpp b/tests/unit_tests/repository/repository_test.cpp index ad4f5af..c383aef 100644 --- a/tests/unit_tests/repository/repository_test.cpp +++ b/tests/unit_tests/repository/repository_test.cpp @@ -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 #include #include "mock.h" @@ -16,47 +29,52 @@ public: SDV_INTERFACE_ENTRY(sdv::IObjectControl) END_SDV_INTERFACE_MAP() - virtual void Initialize([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual void Initialize([[maybe_unused]] const sdv::u8string& ssObjectConfig) override { FAIL() << "Error: Initialize should not be called by Repo Service!"; - //m_eObjectStatus = sdv::EObjectStatus::initialization_failure; + //m_eObjectState = sdv::EObjectState::initialization_failure; } - virtual sdv::EObjectStatus GetStatus() const + virtual sdv::EObjectState GetObjectState() const override { - return m_eObjectStatus; + return m_eObjectState; } /** * @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) + virtual void SetOperationMode(sdv::EOperationMode eMode) override { switch (eMode) { case sdv::EOperationMode::configuring: - if (m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized) - m_eObjectStatus = sdv::EObjectStatus::configuring; + if (m_eObjectState == sdv::EObjectState::running || m_eObjectState == sdv::EObjectState::initialized) + m_eObjectState = sdv::EObjectState::configuring; break; case sdv::EOperationMode::running: - if (m_eObjectStatus == sdv::EObjectStatus::configuring || m_eObjectStatus == sdv::EObjectStatus::initialized) - m_eObjectStatus = sdv::EObjectStatus::running; + if (m_eObjectState == sdv::EObjectState::configuring || m_eObjectState == sdv::EObjectState::initialized) + m_eObjectState = sdv::EObjectState::running; break; default: break; } } - virtual void Shutdown() + virtual sdv::u8string GetObjectConfig() const override { - m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress; + return {}; + } + + virtual void Shutdown() override + { + m_eObjectState = sdv::EObjectState::shutdown_in_progress; FAIL() << "Error: Shutdown should not be called by Repo Service!"; - //m_eObjectStatus = sdv::EObjectStatus::destruction_pending; + //m_eObjectState = sdv::EObjectState::destruction_pending; } - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; + sdv::EObjectState m_eObjectState = sdv::EObjectState::initialization_pending; }; TEST(RepositoryTest, LoadNonexistentModule) diff --git a/tests/unit_tests/repository/test_component.cpp b/tests/unit_tests/repository/test_component.cpp index 38d957d..0f6908e 100644 --- a/tests/unit_tests/repository/test_component.cpp +++ b/tests/unit_tests/repository/test_component.cpp @@ -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 #include #include @@ -7,8 +20,7 @@ #include "generated/IComponent.h" #include "../../../global/tracefifo/trace_fifo.cpp" -class CTestLockService - : public sdv::CSdvObject, public ITestLock +class CTestLockService : public sdv::CSdvObject, public ITestLock { public: @@ -16,7 +28,7 @@ public: SDV_INTERFACE_ENTRY(ITestLock) END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("TestLockService") void Lock() override @@ -38,79 +50,26 @@ DEFINE_SDV_OBJECT(CTestLockService) /** * @brief Example component testing IObjectControl */ - class CTestObjectControl - : public sdv::CSdvObject - , public sdv::IObjectControl +class CTestObjectControl : public sdv::CSdvObject { public: - - ~CTestObjectControl() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("Example_Object") /** - * @brief Initialization method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; + virtual void OnShutdown() override + {} }; DEFINE_SDV_OBJECT(CTestObjectControl) @@ -118,72 +77,27 @@ DEFINE_SDV_OBJECT(CTestObjectControl) /** * @brief Example component testing IObjectControl */ -class CTestObjectControlFail : public sdv::CSdvObject, public sdv::IObjectControl +class CTestObjectControlFail : public sdv::CSdvObject { public: - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) - END_SDV_INTERFACE_MAP() - - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("TestObject_IObjectControlFail") /** - * @brief Initialization method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - m_eObjectStatus = sdv::EObjectStatus::initialization_failure; + return false; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; + virtual void OnShutdown() override + {} }; DEFINE_SDV_OBJECT(CTestObjectControlFail) diff --git a/tests/unit_tests/scheduler/CMakeLists.txt b/tests/unit_tests/scheduler/CMakeLists.txt index 76d0df9..dd0110f 100644 --- a/tests/unit_tests/scheduler/CMakeLists.txt +++ b/tests/unit_tests/scheduler/CMakeLists.txt @@ -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(SchedulerTests VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/scheduler/scheduler_test.cpp b/tests/unit_tests/scheduler/scheduler_test.cpp index 64d77ef..f5bfd1f 100644 --- a/tests/unit_tests/scheduler/scheduler_test.cpp +++ b/tests/unit_tests/scheduler/scheduler_test.cpp @@ -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 #include #include diff --git a/tests/unit_tests/sdv_control/CMakeLists.txt b/tests/unit_tests/sdv_control/CMakeLists.txt index 698ccbb..9c2f9d4 100644 --- a/tests/unit_tests/sdv_control/CMakeLists.txt +++ b/tests/unit_tests/sdv_control/CMakeLists.txt @@ -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 (UnitTest_SDV_Control VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/sdv_control/includes.h b/tests/unit_tests/sdv_control/includes.h index 9339218..6682f15 100644 --- a/tests/unit_tests/sdv_control/includes.h +++ b/tests/unit_tests/sdv_control/includes.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 INCLUDES_H #define INCLUDES_H diff --git a/tests/unit_tests/sdv_control/install_update_uninstall.cpp b/tests/unit_tests/sdv_control/install_update_uninstall.cpp index cd7a779..8bc397b 100644 --- a/tests/unit_tests/sdv_control/install_update_uninstall.cpp +++ b/tests/unit_tests/sdv_control/install_update_uninstall.cpp @@ -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 "includes.h" #include "../../../sdv_executables/sdv_control/list_elements.h" diff --git a/tests/unit_tests/sdv_control/list_info.cpp b/tests/unit_tests/sdv_control/list_info.cpp index 94ccda1..949f691 100644 --- a/tests/unit_tests/sdv_control/list_info.cpp +++ b/tests/unit_tests/sdv_control/list_info.cpp @@ -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 "includes.h" #include "../../../sdv_executables/sdv_control/list_elements.h" diff --git a/tests/unit_tests/sdv_control/main.cpp b/tests/unit_tests/sdv_control/main.cpp index 2e462dc..c193285 100644 --- a/tests/unit_tests/sdv_control/main.cpp +++ b/tests/unit_tests/sdv_control/main.cpp @@ -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 "includes.h" #include "../../../global/process_watchdog.h" #include "../../../global/cmdlnparser/cmdlnparser.cpp" diff --git a/tests/unit_tests/sdv_control/preinstalled_test_service.cpp b/tests/unit_tests/sdv_control/preinstalled_test_service.cpp index ae751c4..865af4a 100644 --- a/tests/unit_tests/sdv_control/preinstalled_test_service.cpp +++ b/tests/unit_tests/sdv_control/preinstalled_test_service.cpp @@ -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 #include #include "generated/test_component.h" @@ -5,89 +18,33 @@ /** * @brief Basic complex test component #1 */ -class CPreInstalledComplexService : public sdv::CSdvObject, public sdv::IObjectControl, public IHello +class CPreInstalledComplexService : public sdv::CSdvObject, public IHello { public: - /** - * @brief Constructor - */ - ~CPreInstalledComplexService() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::complex_service) DECLARE_OBJECT_CLASS_NAME("SDVControl_Test_PreInstalled_ComplexService") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @param[in] ssObjectConfig Optional configuration string. - */ - virtual void Initialize([[maybe_unused]] const sdv::u8string& ssObjectConfig) + * @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 bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object - */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. + */ + virtual void OnShutdown() override + {} /** * @brief Say hello. Overload of IHello::SayHello. @@ -97,10 +54,6 @@ public: { return "Hello from pre-installed complex service"; } - - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status }; DEFINE_SDV_OBJECT(CPreInstalledComplexService) diff --git a/tests/unit_tests/sdv_control/start_stop_service.cpp b/tests/unit_tests/sdv_control/start_stop_service.cpp index 2e62e0a..60ddd30 100644 --- a/tests/unit_tests/sdv_control/start_stop_service.cpp +++ b/tests/unit_tests/sdv_control/start_stop_service.cpp @@ -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 "includes.h" #include "../../../sdv_executables/sdv_control/list_elements.h" #include "../../../sdv_executables/sdv_control/start_stop_service.h" diff --git a/tests/unit_tests/sdv_control/startup_shutdown.cpp b/tests/unit_tests/sdv_control/startup_shutdown.cpp index a62b283..e7367ac 100644 --- a/tests/unit_tests/sdv_control/startup_shutdown.cpp +++ b/tests/unit_tests/sdv_control/startup_shutdown.cpp @@ -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 "includes.h" #include "../../../sdv_executables/sdv_control/startup_shutdown.h" diff --git a/tests/unit_tests/sdv_control/test_app.cpp b/tests/unit_tests/sdv_control/test_app.cpp index e69de29..53ff22d 100644 --- a/tests/unit_tests/sdv_control/test_app.cpp +++ b/tests/unit_tests/sdv_control/test_app.cpp @@ -0,0 +1,12 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ diff --git a/tests/unit_tests/sdv_control/test_basic_service.cpp b/tests/unit_tests/sdv_control/test_basic_service.cpp index de26623..7b31d2f 100644 --- a/tests/unit_tests/sdv_control/test_basic_service.cpp +++ b/tests/unit_tests/sdv_control/test_basic_service.cpp @@ -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 #include #include "generated/test_component.h" @@ -5,89 +18,33 @@ /** * @brief Basic service test component */ -class CTestBasicService : public sdv::CSdvObject, public sdv::IObjectControl, public IHello +class CTestBasicService : public sdv::CSdvObject, public IHello { public: - /** - * @brief Constructor - */ - ~CTestBasicService() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::BasicService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::basic_service) DECLARE_OBJECT_CLASS_NAME("SDVControl_Test_BasicService") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } + virtual void OnShutdown() override + {} /** * @brief Say hello. Overload of IHello::SayHello. @@ -97,10 +54,6 @@ public: { return "Hello from basic service"; } - - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status }; DEFINE_SDV_OBJECT(CTestBasicService) diff --git a/tests/unit_tests/sdv_control/test_complex_service1.cpp b/tests/unit_tests/sdv_control/test_complex_service1.cpp index 4a4e81a..ffe7f25 100644 --- a/tests/unit_tests/sdv_control/test_complex_service1.cpp +++ b/tests/unit_tests/sdv_control/test_complex_service1.cpp @@ -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 #include #include "generated/test_component.h" @@ -5,89 +18,33 @@ /** * @brief Basic complex test component #1 */ -class CTestComplexService1 : public sdv::CSdvObject, public sdv::IObjectControl, public IHello +class CTestComplexService1 : public sdv::CSdvObject, public IHello { public: - /** - * @brief Constructor - */ - ~CTestComplexService1() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::complex_service) DECLARE_OBJECT_CLASS_NAME("SDVControl_Test_ComplexService1") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } + virtual void OnShutdown() override + {} /** * @brief Say hello. Overload of IHello::SayHello. @@ -97,10 +54,6 @@ public: { return "Hello from complex service #1"; } - - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status }; DEFINE_SDV_OBJECT(CTestComplexService1) diff --git a/tests/unit_tests/sdv_control/test_complex_service2.cpp b/tests/unit_tests/sdv_control/test_complex_service2.cpp index 0f15dc2..e159831 100644 --- a/tests/unit_tests/sdv_control/test_complex_service2.cpp +++ b/tests/unit_tests/sdv_control/test_complex_service2.cpp @@ -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 #include #include "generated/test_component.h" @@ -5,89 +18,33 @@ /** * @brief Complex service test component #2 */ -class CTestComplexService2 : public sdv::CSdvObject, public sdv::IObjectControl, public IHello +class CTestComplexService2 : public sdv::CSdvObject, public IHello { public: - /** - * @brief Constructor - */ - ~CTestComplexService2() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::ComplexService) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::complex_service) DECLARE_OBJECT_CLASS_NAME("SDVControl_Test_ComplexService2") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } + virtual void OnShutdown() override + {} /** * @brief Say hello. Overload of IHello::SayHello. @@ -97,10 +54,6 @@ public: { return "Hello from complex service #2"; } - - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status }; DEFINE_SDV_OBJECT(CTestComplexService2) diff --git a/tests/unit_tests/sdv_control/test_component.idl b/tests/unit_tests/sdv_control/test_component.idl index ef3ed06..f63d5e1 100644 --- a/tests/unit_tests/sdv_control/test_component.idl +++ b/tests/unit_tests/sdv_control/test_component.idl @@ -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 #include diff --git a/tests/unit_tests/sdv_control/test_component_ps.cpp b/tests/unit_tests/sdv_control/test_component_ps.cpp index eec8f5a..11b64ac 100644 --- a/tests/unit_tests/sdv_control/test_component_ps.cpp +++ b/tests/unit_tests/sdv_control/test_component_ps.cpp @@ -1 +1,14 @@ +/******************************************************************************** + * 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 "generated/ps/proxystub.cpp" \ No newline at end of file diff --git a/tests/unit_tests/sdv_control/test_device.cpp b/tests/unit_tests/sdv_control/test_device.cpp index e1e0547..bb8a4bf 100644 --- a/tests/unit_tests/sdv_control/test_device.cpp +++ b/tests/unit_tests/sdv_control/test_device.cpp @@ -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 #include #include "generated/test_component.h" @@ -5,89 +18,33 @@ /** * @brief Device test component */ -class CTestDevice : public sdv::CSdvObject, public sdv::IObjectControl, public IHello +class CTestDevice : public sdv::CSdvObject, public IHello { public: - /** - * @brief Constructor - */ - ~CTestDevice() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) DECLARE_OBJECT_CLASS_NAME("SDVControl_Test_Device") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } + virtual void OnShutdown() override + {} /** * @brief Say hello. Overload of IHello::SayHello. @@ -97,10 +54,6 @@ public: { return "Hello from device"; } - - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status }; DEFINE_SDV_OBJECT(CTestDevice) diff --git a/tests/unit_tests/sdv_control/test_system_service.cpp b/tests/unit_tests/sdv_control/test_system_service.cpp index c2d9427..bc96f45 100644 --- a/tests/unit_tests/sdv_control/test_system_service.cpp +++ b/tests/unit_tests/sdv_control/test_system_service.cpp @@ -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 #include #include "generated/test_component.h" @@ -5,89 +18,33 @@ /** * @brief System service test component */ -class CTestSystemService : public sdv::CSdvObject, public sdv::IObjectControl, public IHello +class CTestSystemService : public sdv::CSdvObject, public IHello { public: - /** - * @brief Constructor - */ - ~CTestSystemService() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(IHello) 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("SDVControl_Test_SystemService") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } + virtual void OnShutdown() override + {} /** * @brief Say hello. Overload of IHello::SayHello. @@ -97,10 +54,6 @@ public: { return "Hello from system service"; } - - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status }; DEFINE_SDV_OBJECT(CTestSystemService) diff --git a/tests/unit_tests/sdv_control/test_utility1.cpp b/tests/unit_tests/sdv_control/test_utility1.cpp index c5b09ce..41ee9f6 100644 --- a/tests/unit_tests/sdv_control/test_utility1.cpp +++ b/tests/unit_tests/sdv_control/test_utility1.cpp @@ -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 #include #include "generated/test_component.h" @@ -5,89 +18,33 @@ /** * @brief Utility test component #1 */ -class CTestUtility1 : public sdv::CSdvObject, public sdv::IObjectControl, public IHello +class CTestUtility1 : public sdv::CSdvObject, public IHello { public: - /** - * @brief Constructor - */ - ~CTestUtility1() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Utility) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::utility) DECLARE_OBJECT_CLASS_NAME("SDVControl_Test_Utility1") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } + virtual void OnShutdown() override + {} /** * @brief Say hello. Overload of IHello::SayHello. @@ -97,10 +54,6 @@ public: { return "Hello from utility #1"; } - - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status }; DEFINE_SDV_OBJECT(CTestUtility1) diff --git a/tests/unit_tests/sdv_control/test_utility2.cpp b/tests/unit_tests/sdv_control/test_utility2.cpp index d4251f0..e173cec 100644 --- a/tests/unit_tests/sdv_control/test_utility2.cpp +++ b/tests/unit_tests/sdv_control/test_utility2.cpp @@ -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 #include #include "generated/test_component.h" @@ -5,89 +18,33 @@ /** * @brief Utility test component #2 */ -class CTestUtility2 : public sdv::CSdvObject, public sdv::IObjectControl, public IHello +class CTestUtility2 : public sdv::CSdvObject, public IHello { public: - /** - * @brief Constructor - */ - ~CTestUtility2() - { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::destruction_pending); - } - // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(sdv::IObjectControl) SDV_INTERFACE_ENTRY(IHello) END_SDV_INTERFACE_MAP() // Object declarations - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::Utility) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::utility) DECLARE_OBJECT_CLASS_NAME("SDVControl_Test_Utility2") /** - * @brief Initialize method. On success, a subsequent call to GetStatus returns EObjectStatus::running - * @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([[maybe_unused]] const sdv::u8string& ssObjectConfig) + virtual bool OnInitialize() override { - EXPECT_EQ(m_eObjectStatus, sdv::EObjectStatus::initialization_pending); - - m_eObjectStatus = sdv::EObjectStatus::initialized; + return true; } /** - * @brief Gets the current status of the object - * @return EObjectStatus The current status of the object + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. */ - virtual sdv::EObjectStatus GetStatus() const - { - return m_eObjectStatus; - } - - /** - * @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) - { - 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; - } - } - - /** - * @brief Shutdown method called before the object is destroyed. - * @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 - */ - virtual void Shutdown() - { - EXPECT_TRUE(m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized || - m_eObjectStatus == sdv::EObjectStatus::configuring); - if (m_eObjectStatus != sdv::EObjectStatus::running && m_eObjectStatus != sdv::EObjectStatus::initialized - && m_eObjectStatus != sdv::EObjectStatus::configuring) - std::cout << "Object status = " << static_cast(m_eObjectStatus) << " (expected initialized=" << - static_cast(sdv::EObjectStatus::initialized) << " or configuring=" << - static_cast(sdv::EObjectStatus::configuring) << " or running=" << - static_cast(sdv::EObjectStatus::running) << ")." << std::endl; - - m_eObjectStatus = sdv::EObjectStatus::destruction_pending; - } + virtual void OnShutdown() override + {} /** * @brief Say hello. Overload of IHello::SayHello. @@ -97,10 +54,6 @@ public: { return "Hello from utility #2"; } - - -private: - sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status }; DEFINE_SDV_OBJECT(CTestUtility2) diff --git a/tests/unit_tests/sdv_macro_test/CMakeLists.txt b/tests/unit_tests/sdv_macro_test/CMakeLists.txt index 99f89ea..9cc8cb5 100644 --- a/tests/unit_tests/sdv_macro_test/CMakeLists.txt +++ b/tests/unit_tests/sdv_macro_test/CMakeLists.txt @@ -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 (UnitTest_SDV_Macro_Test VERSION 1.0 LANGUAGES CXX) diff --git a/tests/unit_tests/sdv_macro_test/sdv_macro_test.cpp b/tests/unit_tests/sdv_macro_test/sdv_macro_test.cpp index d1094a5..1135cda 100644 --- a/tests/unit_tests/sdv_macro_test/sdv_macro_test.cpp +++ b/tests/unit_tests/sdv_macro_test/sdv_macro_test.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../include/sdv_test_macro.h" diff --git a/tests/unit_tests/shared_mem/CMakeLists.txt b/tests/unit_tests/shared_mem/CMakeLists.txt index faece76..db778eb 100644 --- a/tests/unit_tests/shared_mem/CMakeLists.txt +++ b/tests/unit_tests/shared_mem/CMakeLists.txt @@ -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 (multiple EXEs) project(CommunicationTests VERSION 1.0 LANGUAGES CXX) @@ -138,9 +151,9 @@ add_custom_command(TARGET UnitTest_SharedMemLargeDataTests POST_BUILD ) # Build dependencies -add_dependencies(UnitTest_SharedMemTests_App_Repeater core_services) -add_dependencies(UnitTest_SharedMemTests_App_Connect core_services) -add_dependencies(UnitTest_InprocMemTests core_services) +add_dependencies(UnitTest_SharedMemTests_App_Repeater dependency_sdv_components) +add_dependencies(UnitTest_SharedMemTests_App_Connect dependency_sdv_components) +add_dependencies(UnitTest_InprocMemTests dependency_sdv_components) add_dependencies(UnitTest_SharedMemBufferTests UnitTest_SharedMemTests_App_Repeater) add_dependencies(UnitTest_SharedMemConnectTests UnitTest_SharedMemTests_App_Connect) add_dependencies(UnitTest_SharedMemLargeDataTests UnitTest_SharedMemTests_App_Connect) diff --git a/tests/unit_tests/shared_mem/app_connect.cpp b/tests/unit_tests/shared_mem/app_connect.cpp index c92bfdc..12e6187 100644 --- a/tests/unit_tests/shared_mem/app_connect.cpp +++ b/tests/unit_tests/shared_mem/app_connect.cpp @@ -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 #include #include "../../../global/base64.h" @@ -310,9 +323,9 @@ extern "C" int main(int argc, char* argv[]) // Create an control management channel (if required). CSharedMemChannelMgnt mgntControlMgntChannel; mgntControlMgntChannel.Initialize(""); - if (mgntControlMgntChannel.GetStatus() != sdv::EObjectStatus::initialized) return -11; + if (mgntControlMgntChannel.GetObjectState() != sdv::EObjectState::initialized) return -11; mgntControlMgntChannel.SetOperationMode(sdv::EOperationMode::running); - if (mgntControlMgntChannel.GetStatus() != sdv::EObjectStatus::running) return -11; + if (mgntControlMgntChannel.GetObjectState() != sdv::EObjectState::running) return -11; // Open the control channel endpoint sdv::TObjectPtr ptrControlConnection; @@ -337,9 +350,9 @@ extern "C" int main(int argc, char* argv[]) // Create the data management channel. CSharedMemChannelMgnt mgntDataMgntChannel; mgntDataMgntChannel.Initialize(""); - if (mgntDataMgntChannel.GetStatus() != sdv::EObjectStatus::initialized) return -1; + if (mgntDataMgntChannel.GetObjectState() != sdv::EObjectState::initialized) return -1; mgntDataMgntChannel.SetOperationMode(sdv::EOperationMode::running); - if (mgntDataMgntChannel.GetStatus() != sdv::EObjectStatus::running) return -1; + if (mgntDataMgntChannel.GetObjectState() != sdv::EObjectState::running) return -1; // If this is a server, create a data endpoint and communicate this endpoint over the control channel. // If not, open the data endpoint. @@ -420,11 +433,11 @@ Size = 1024000 if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStatusEventCallback(uiDataEventCookie); ptrDataConnection.Clear(); mgntDataMgntChannel.Shutdown(); - if (mgntDataMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -6; + if (mgntDataMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -6; if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStatusEventCallback(uiControlEventCookie); ptrControlConnection.Clear(); mgntControlMgntChannel.Shutdown(); - if (mgntControlMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -16; + if (mgntControlMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -16; TRACE("Shutdown of app ", bServer ? "server" : "client", " connect process..."); diff --git a/tests/unit_tests/shared_mem/app_repeater.cpp b/tests/unit_tests/shared_mem/app_repeater.cpp index ef84225..4245b7c 100644 --- a/tests/unit_tests/shared_mem/app_repeater.cpp +++ b/tests/unit_tests/shared_mem/app_repeater.cpp @@ -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 + ********************************************************************************/ + // App process providing repeating functionality #include diff --git a/tests/unit_tests/shared_mem/in_process_mem_buffer_tests.cpp b/tests/unit_tests/shared_mem/in_process_mem_buffer_tests.cpp index d051994..abc282b 100644 --- a/tests/unit_tests/shared_mem/in_process_mem_buffer_tests.cpp +++ b/tests/unit_tests/shared_mem/in_process_mem_buffer_tests.cpp @@ -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 "gtest/gtest.h" #include "../../../sdv_services/ipc_shared_mem/in_process_mem_buffer.h" #include @@ -146,9 +159,6 @@ TEST(InProcessMemoryBufferTest, TriggerTestRxTx) std::unique_lock lockSender(mtxSenderStart); lockSender.unlock(); cvSenderStart.notify_all(); - std::unique_lock lockReceiver(mtxReceiverStart); - cvReceiverStart.wait(lockReceiver); - lockReceiver.unlock(); while (!bShutdown) { bool bResult = sender.WaitForFreeSpace(200); @@ -159,11 +169,11 @@ TEST(InProcessMemoryBufferTest, TriggerTestRxTx) }; std::unique_lock lockStartSender(mtxSenderStart); + std::unique_lock lockStartReceiver(mtxReceiverStart); std::thread threadSender(fnWaitForTriggerSender); + std::thread threadReceiver(fnWaitForTriggerReceiver); cvSenderStart.wait(lockStartSender); lockStartSender.unlock(); - std::unique_lock lockStartReceiver(mtxReceiverStart); - std::thread threadReceiver(fnWaitForTriggerReceiver); cvReceiverStart.wait(lockStartReceiver); lockStartReceiver.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Needed for the threads to enter their loop. diff --git a/tests/unit_tests/shared_mem/main.cpp b/tests/unit_tests/shared_mem/main.cpp index e391824..6bbd7a2 100644 --- a/tests/unit_tests/shared_mem/main.cpp +++ b/tests/unit_tests/shared_mem/main.cpp @@ -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 "gtest/gtest.h" #include "../../../global/process_watchdog.h" #define TIME_TRACKING diff --git a/tests/unit_tests/shared_mem/pattern_gen.cpp b/tests/unit_tests/shared_mem/pattern_gen.cpp index 8f79d64..7a9aa0a 100644 --- a/tests/unit_tests/shared_mem/pattern_gen.cpp +++ b/tests/unit_tests/shared_mem/pattern_gen.cpp @@ -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 #include #include diff --git a/tests/unit_tests/shared_mem/pattern_gen.h b/tests/unit_tests/shared_mem/pattern_gen.h index 9208d09..43d1acd 100644 --- a/tests/unit_tests/shared_mem/pattern_gen.h +++ b/tests/unit_tests/shared_mem/pattern_gen.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 PATTERN_GEN_H #define PATTERN_GEN_H diff --git a/tests/unit_tests/shared_mem/shared_mem_buffer_tests.cpp b/tests/unit_tests/shared_mem/shared_mem_buffer_tests.cpp index 05335e0..5d698f3 100644 --- a/tests/unit_tests/shared_mem/shared_mem_buffer_tests.cpp +++ b/tests/unit_tests/shared_mem/shared_mem_buffer_tests.cpp @@ -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 #include #include @@ -199,7 +212,6 @@ TEST(SharedMemoryBufferTest, TriggerTestRxTx) std::thread threadReceiver(fnWaitForTriggerReceiver); cvSenderStart.wait(lockStartSender); lockStartSender.unlock(); - //CHECKPOINT(); cvReceiverStart.wait(lockStartReceiver); lockStartReceiver.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Needed for the threads to enter their loop. @@ -211,7 +223,7 @@ TEST(SharedMemoryBufferTest, TriggerTestRxTx) } // Let the buffer finish its sending. - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); bShutdown = true; threadSender.join(); diff --git a/tests/unit_tests/shared_mem/shared_mem_connect.cpp b/tests/unit_tests/shared_mem/shared_mem_connect.cpp index 3a5e87c..3e6ea57 100644 --- a/tests/unit_tests/shared_mem/shared_mem_connect.cpp +++ b/tests/unit_tests/shared_mem/shared_mem_connect.cpp @@ -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 "gtest/gtest.h" #include "../../../sdv_services/ipc_shared_mem/channel_mgnt.h" #include "../../../sdv_services/ipc_shared_mem/connection.h" @@ -192,11 +205,11 @@ TEST(SharedMemChannelService, Instantiate) CSharedMemChannelMgnt mgnt; EXPECT_NO_THROW(mgnt.Initialize("")); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgnt.Shutdown()); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::destruction_pending); appcontrol.Shutdown(); } @@ -209,11 +222,11 @@ TEST(SharedMemChannelService, ChannelConfigString) CSharedMemChannelMgnt mgnt; EXPECT_NO_THROW(mgnt.Initialize("")); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgnt.Shutdown()); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, CreateRandomEndpoint) @@ -225,14 +238,14 @@ TEST(SharedMemChannelService, CreateRandomEndpoint) // Create an endpoint. EXPECT_NO_THROW(mgnt.Initialize("")); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgnt.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); if (sChannelEndpoint.pConnection) sdv::TObjectPtr(sChannelEndpoint.pConnection); EXPECT_NO_THROW(mgnt.Shutdown()); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, CreateExplicitEndpoint) @@ -244,7 +257,7 @@ TEST(SharedMemChannelService, CreateExplicitEndpoint) // Create an endpoint. EXPECT_NO_THROW(mgnt.Initialize("")); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgnt.CreateEndpoint(R"code([IpcChannel] Name = "CHANNEL_1234" Size = 10240 @@ -254,7 +267,7 @@ Size = 10240 if (sChannelEndpoint.pConnection) sdv::TObjectPtr(sChannelEndpoint.pConnection); EXPECT_NO_THROW(mgnt.Shutdown()); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, GetRandomEndpointAccess) @@ -266,9 +279,9 @@ TEST(SharedMemChannelService, GetRandomEndpointAccess) // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgntClient.Initialize("")); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -281,8 +294,8 @@ TEST(SharedMemChannelService, GetRandomEndpointAccess) EXPECT_NO_THROW(mgntServer.Shutdown()); EXPECT_NO_THROW(mgntClient.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, GetExplicitEndpointAccess) @@ -294,9 +307,9 @@ TEST(SharedMemChannelService, GetExplicitEndpointAccess) // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgntClient.Initialize("")); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code([IpcChannel] Name = "CHANNEL_1234" Size = 10240 @@ -312,8 +325,8 @@ Size = 10240 EXPECT_NO_THROW(mgntServer.Shutdown()); EXPECT_NO_THROW(mgntClient.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, WaitForConnection) @@ -325,7 +338,7 @@ TEST(SharedMemChannelService, WaitForConnection) // Create an endpoint. EXPECT_NO_THROW(mgnt.Initialize("")); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgnt.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -360,7 +373,7 @@ TEST(SharedMemChannelService, WaitForConnection) EXPECT_NO_THROW(mgnt.Shutdown()); - EXPECT_EQ(mgnt.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, AsyncConnect) @@ -372,9 +385,9 @@ TEST(SharedMemChannelService, AsyncConnect) // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgntClient.Initialize("")); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -456,8 +469,8 @@ TEST(SharedMemChannelService, AsyncConnect) EXPECT_NO_THROW(mgntServer.Shutdown()); EXPECT_NO_THROW(mgntClient.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, EstablishConnectionEvents) @@ -469,9 +482,9 @@ TEST(SharedMemChannelService, EstablishConnectionEvents) // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgntClient.Initialize("")); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -572,8 +585,8 @@ TEST(SharedMemChannelService, EstablishConnectionEvents) EXPECT_NO_THROW(mgntServer.Shutdown()); EXPECT_NO_THROW(mgntClient.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, EstablishReconnect) @@ -585,11 +598,11 @@ TEST(SharedMemChannelService, EstablishReconnect) // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgntClient1.Initialize("")); - EXPECT_EQ(mgntClient1.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient1.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgntClient2.Initialize("")); - EXPECT_EQ(mgntClient2.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -656,7 +669,7 @@ TEST(SharedMemChannelService, EstablishReconnect) EXPECT_NO_THROW(ptrServerConnection.Clear()); EXPECT_NO_THROW(mgntServer.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, EstablishReconnectEvents) @@ -668,11 +681,11 @@ TEST(SharedMemChannelService, EstablishReconnectEvents) // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgntClient1.Initialize("")); - EXPECT_EQ(mgntClient1.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient1.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgntClient2.Initialize("")); - EXPECT_EQ(mgntClient2.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -760,7 +773,7 @@ TEST(SharedMemChannelService, EstablishReconnectEvents) EXPECT_NO_THROW(ptrServerConnection.Clear()); EXPECT_NO_THROW(mgntServer.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, AppEstablishConnection) @@ -774,7 +787,7 @@ Mode = "Essential")code")); // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -838,7 +851,7 @@ Mode = "Essential")code")); EXPECT_NO_THROW(mgntServer.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); // Wait for process termination pProcessLifetime->WaitForTerminate(tProcessID, 0xffffffff); @@ -855,7 +868,7 @@ Mode = "Essential")code")); // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -918,7 +931,7 @@ Mode = "Essential")code")); EXPECT_NO_THROW(mgntServer.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, AppForcedShutdown_Watchdog) @@ -932,7 +945,7 @@ Mode = "Essential")code")); // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -997,7 +1010,7 @@ Mode = "Essential")code")); EXPECT_NO_THROW(mgntServer.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); // Wait for process termination pProcessLifetime->WaitForTerminate(tProcessID, 0xffffffff); @@ -1018,7 +1031,7 @@ Mode = "Essential")code")); // Create the first control endpoint. CSharedMemChannelMgnt mgntControl1; EXPECT_NO_THROW(mgntControl1.Initialize("")); - EXPECT_EQ(mgntControl1.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntControl1.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint1 = mgntControl1.CreateEndpoint(""); EXPECT_NE(sEndpoint1.pConnection, nullptr); EXPECT_FALSE(sEndpoint1.ssConnectString.empty()); @@ -1054,7 +1067,7 @@ Mode = "Essential")code")); // Create the second control endpoint. CSharedMemChannelMgnt mgntControl2; EXPECT_NO_THROW(mgntControl2.Initialize("")); - EXPECT_EQ(mgntControl2.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntControl2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint2 = mgntControl2.CreateEndpoint(""); EXPECT_NE(sEndpoint2.pConnection, nullptr); EXPECT_FALSE(sEndpoint2.ssConnectString.empty()); @@ -1101,7 +1114,7 @@ Mode = "Essential")code")); // Create the first control endpoint. CSharedMemChannelMgnt mgntControl1; EXPECT_NO_THROW(mgntControl1.Initialize("")); - EXPECT_EQ(mgntControl1.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntControl1.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint1 = mgntControl1.CreateEndpoint(""); EXPECT_NE(sEndpoint1.pConnection, nullptr); EXPECT_FALSE(sEndpoint1.ssConnectString.empty()); @@ -1137,7 +1150,7 @@ Mode = "Essential")code")); // Create the second control endpoint. CSharedMemChannelMgnt mgntControl2; EXPECT_NO_THROW(mgntControl2.Initialize("")); - EXPECT_EQ(mgntControl2.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntControl2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint2 = mgntControl2.CreateEndpoint(""); EXPECT_NE(sEndpoint2.pConnection, nullptr); EXPECT_FALSE(sEndpoint2.ssConnectString.empty()); @@ -1182,7 +1195,7 @@ Mode = "Essential")code")); // Create the first control endpoint. CSharedMemChannelMgnt mgntControl1; EXPECT_NO_THROW(mgntControl1.Initialize("")); - EXPECT_EQ(mgntControl1.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntControl1.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint1 = mgntControl1.CreateEndpoint(""); EXPECT_NE(sEndpoint1.pConnection, nullptr); EXPECT_FALSE(sEndpoint1.ssConnectString.empty()); @@ -1218,7 +1231,7 @@ Mode = "Essential")code")); // Create the second control endpoint. CSharedMemChannelMgnt mgntControl2; EXPECT_NO_THROW(mgntControl2.Initialize("")); - EXPECT_EQ(mgntControl2.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntControl2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint2 = mgntControl2.CreateEndpoint(""); EXPECT_NE(sEndpoint2.pConnection, nullptr); EXPECT_FALSE(sEndpoint2.ssConnectString.empty()); diff --git a/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp b/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp index 84cdcbf..45393e6 100644 --- a/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp +++ b/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp @@ -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 "gtest/gtest.h" #define TIME_TRACKING #include "../../../sdv_services/ipc_shared_mem/channel_mgnt.h" @@ -268,13 +281,13 @@ Report = "Silent" // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); EXPECT_NO_THROW(mgntClient.Initialize("service = \"client\"")); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); mgntClient.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::running); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( [IpcChannel] Size = 1024000 @@ -350,8 +363,8 @@ Size = 1024000 EXPECT_NO_THROW(mgntClient.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, CommunicateMultiLargeBlock) @@ -364,13 +377,13 @@ TEST(SharedMemChannelService, CommunicateMultiLargeBlock) // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); EXPECT_NO_THROW(mgntClient.Initialize("service = \"client\"")); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); mgntClient.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::running); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( [IpcChannel] Size = 1024000 @@ -478,8 +491,8 @@ Size = 1024000 EXPECT_NO_THROW(mgntClient.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, CommunicateFragmentedLargeBlock) @@ -492,13 +505,13 @@ TEST(SharedMemChannelService, CommunicateFragmentedLargeBlock) // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); EXPECT_NO_THROW(mgntClient.Initialize("service = \"client\"")); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); mgntClient.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::running); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( [IpcChannel] Size = 1024000 @@ -594,8 +607,8 @@ Size = 1024000 EXPECT_NO_THROW(mgntClient.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); - EXPECT_EQ(mgntClient.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); + EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST(SharedMemChannelService, AppCommunicateOneLargeBlock) @@ -611,9 +624,9 @@ Mode="Essential")code")); // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( [IpcChannel] Size = 1024000 @@ -676,7 +689,7 @@ Size = 1024000 EXPECT_NO_THROW(mgntServer.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); // Wait for process termination pProcessLifetime->WaitForTerminate(tProcessID, 0xffffffff); @@ -695,9 +708,9 @@ Mode="Essential")code")); // Create an endpoint. EXPECT_NO_THROW(mgntServer.Initialize("")); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::running); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( [IpcChannel] Size = 1024000 @@ -788,7 +801,7 @@ Size = 1024000 EXPECT_NO_THROW(mgntServer.Shutdown()); - EXPECT_EQ(mgntServer.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::destruction_pending); // Wait for process termination pProcessLifetime->WaitForTerminate(tProcessID, 0xffffffff); diff --git a/tests/unit_tests/smart_ifc/CMakeLists.txt b/tests/unit_tests/smart_ifc/CMakeLists.txt index fb53d3c..7c90b72 100644 --- a/tests/unit_tests/smart_ifc/CMakeLists.txt +++ b/tests/unit_tests/smart_ifc/CMakeLists.txt @@ -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 (UnitTest_Smart_Interface VERSION 1.0 LANGUAGES CXX) @@ -35,4 +48,4 @@ add_custom_command(TARGET UnitTest_Smart_Interface POST_BUILD ) # Build dependencies -add_dependencies(UnitTest_Smart_Interface CompileCoreIDL) +add_dependencies(UnitTest_Smart_Interface dependency_sdv_components) diff --git a/tests/unit_tests/smart_ifc/ifc_map.cpp b/tests/unit_tests/smart_ifc/ifc_map.cpp index a43d1c0..4a4840c 100644 --- a/tests/unit_tests/smart_ifc/ifc_map.cpp +++ b/tests/unit_tests/smart_ifc/ifc_map.cpp @@ -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 "../../include/gtest_custom.h" #include "generated/smart_ifc.h" #include diff --git a/tests/unit_tests/smart_ifc/main.cpp b/tests/unit_tests/smart_ifc/main.cpp index d934a84..e988681 100644 --- a/tests/unit_tests/smart_ifc/main.cpp +++ b/tests/unit_tests/smart_ifc/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../../global/localmemmgr.h" diff --git a/tests/unit_tests/smart_ifc/smart_ifc.cpp b/tests/unit_tests/smart_ifc/smart_ifc.cpp index 185edaf..53a5ef0 100644 --- a/tests/unit_tests/smart_ifc/smart_ifc.cpp +++ b/tests/unit_tests/smart_ifc/smart_ifc.cpp @@ -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 "../../include/gtest_custom.h" #include "generated/smart_ifc.h" #include diff --git a/tests/unit_tests/smart_ifc/smart_ifc.idl b/tests/unit_tests/smart_ifc/smart_ifc.idl index a4ad19d..bb9e5b8 100644 --- a/tests/unit_tests/smart_ifc/smart_ifc.idl +++ b/tests/unit_tests/smart_ifc/smart_ifc.idl @@ -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 /** diff --git a/tests/unit_tests/socket_can_com_tests/CMakeLists.txt b/tests/unit_tests/socket_can_com_tests/CMakeLists.txt index 0116ca9..808e68e 100644 --- a/tests/unit_tests/socket_can_com_tests/CMakeLists.txt +++ b/tests/unit_tests/socket_can_com_tests/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Only valid for Linux if(UNIX) diff --git a/tests/unit_tests/socket_can_com_tests/config/test_can_socket.toml b/tests/unit_tests/socket_can_com_tests/config/test_can_socket.toml index aa6cabf..50f15e7 100644 --- a/tests/unit_tests/socket_can_com_tests/config/test_can_socket.toml +++ b/tests/unit_tests/socket_can_com_tests/config/test_can_socket.toml @@ -4,4 +4,5 @@ Version = 100 [[Component]] Path = "can_com_sockets.sdv" Class = "CAN_Com_Sockets" +[Component.Parameters] canSockets=["vcan3"] diff --git a/tests/unit_tests/socket_can_com_tests/include/can_com_test_helper.h b/tests/unit_tests/socket_can_com_tests/include/can_com_test_helper.h index f15ce86..34fab7e 100644 --- a/tests/unit_tests/socket_can_com_tests/include/can_com_test_helper.h +++ b/tests/unit_tests/socket_can_com_tests/include/can_com_test_helper.h @@ -1,3 +1,13 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + #ifndef CAN_TEST_HELPER_H #define CAN_TEST_SOCKET_H diff --git a/tests/unit_tests/socket_can_com_tests/src/can_com_test_socket.cpp b/tests/unit_tests/socket_can_com_tests/src/can_com_test_socket.cpp index da866e7..66dde82 100644 --- a/tests/unit_tests/socket_can_com_tests/src/can_com_test_socket.cpp +++ b/tests/unit_tests/socket_can_com_tests/src/can_com_test_socket.cpp @@ -1,3 +1,13 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + #include "../../../../global/process_watchdog.h" #include "../include/can_com_test_helper.h" #include @@ -117,9 +127,9 @@ public: return CCANSockets::GetInterfaces(); } - sdv::EObjectStatus GetTestStatus() const + sdv::EObjectState GetTestStatus() const { - return CCANSockets::GetStatus(); + return CCANSockets::GetObjectState(); } uint64_t GetMessagesSent() const @@ -190,7 +200,7 @@ bool InitializeAppControl(sdv::app::CAppControl* appcontrol, const std::string& void InitializeCanComObject(CTestCANSocket& canComObj, const std::string config, MockCANReceiver& mockRcv) { ASSERT_NO_THROW(canComObj.Initialize(config.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.SetOperationMode(sdv::EOperationMode::configuring)); ASSERT_NO_THROW(canComObj.RegisterReceiver(&mockRcv)); EXPECT_NO_THROW(canComObj.SetOperationMode(sdv::EOperationMode::running)); @@ -221,12 +231,12 @@ TEST_F(CANSocketTest, ValidConfigString) sdv::u8string ssObjectConfig = R"(canSockets = "vcan0")"; // vcan0 interface must exist CTestCANSocket canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); - ASSERT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized); + ASSERT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.Send(testMsg, 0)); ASSERT_NO_THROW(canComObj.Shutdown()); - ASSERT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::destruction_pending); + ASSERT_EQ(canComObj.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST_F(CANSocketTest, InvalidConfigString) @@ -237,10 +247,10 @@ TEST_F(CANSocketTest, InvalidConfigString) sdv::u8string ssObjectConfig = R"(canSockets = "vcan08")"; CTestCANSocket canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialization_failure); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialization_failure); ASSERT_NO_THROW(canComObj.Shutdown()); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST_F(CANSocketTest, ValidConfigArray) @@ -253,12 +263,12 @@ TEST_F(CANSocketTest, ValidConfigArray) sdv::u8string ssObjectConfig = R"(canSockets = ["vcan0", "vcan1"])"; CTestCANSocket canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.Send(testMsg, 0)); EXPECT_NO_THROW(canComObj.Shutdown()); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST_F(CANSocketTest, InvalidConfigArray) @@ -269,10 +279,10 @@ TEST_F(CANSocketTest, InvalidConfigArray) sdv::u8string ssObjectConfig = R"(canSockets = ["vcan08", "vcan09"])"; CTestCANSocket canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialization_failure); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialization_failure); ASSERT_NO_THROW(canComObj.Shutdown()); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST_F(CANSocketTest, ValidConfigArrayButUnknownElement) @@ -285,12 +295,12 @@ TEST_F(CANSocketTest, ValidConfigArrayButUnknownElement) sdv::u8string ssObjectConfig = R"(canSockets = ["vcan0", "vcan08"])"; CTestCANSocket canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.Send(testMsg, 0)); ASSERT_NO_THROW(canComObj.Shutdown()); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST_F(CANSocketTest, InvalidConfigIdentifier) @@ -301,10 +311,10 @@ TEST_F(CANSocketTest, InvalidConfigIdentifier) sdv::u8string ssObjectConfig = R"(invalidCanSockets = ["vcan0", "vcan1"])"; // Invalid config identifier CTestCANSocket canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialization_failure); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialization_failure); ASSERT_NO_THROW(canComObj.Shutdown()); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::destruction_pending); } TEST_F(CANSocketTest, ReceiveTestDifferentDataSizes) @@ -695,7 +705,7 @@ TEST_F(CANSocketTest, ManualSendAndReceiveTestOfMulitpleSockets) sdv::u8string ssConfig = R"(canSockets = ["vcan0", "vcan3", "vcan8", "vcan1", "vcan9", "vcan4", "vcan2"])"; CTestCANSocket canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssConfig.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); // Register a receiver. MockCANReceiver mockRcv; @@ -741,7 +751,7 @@ TEST_F(CANSocketTest, ManualSendAndReceiveTestOfMulitpleSockets) EXPECT_NO_THROW(canComObj.UnregisterReceiver(&mockRcv)); ASSERT_NO_THROW(canComObj.Shutdown()); // Shutdown the object after the test - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::destruction_pending); } // Test similar to ManualSendAndReceiveTestOfMulitpleSockets @@ -765,7 +775,7 @@ TEST_F(CANSocketTest, ManualSendAndReceiveTestWithDifferentDataSizes) sdv::u8string ssConfig = R"(canSockets = ["vcan0", "vcan3", "vcan8", "vcan1", "vcan9", "vcan4", "vcan2"])"; CTestCANSocket canComObj; ASSERT_NO_THROW(canComObj.Initialize(ssConfig.c_str())); - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::initialized); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); // Register a receiver. MockCANReceiver mockRcv; @@ -811,7 +821,7 @@ TEST_F(CANSocketTest, ManualSendAndReceiveTestWithDifferentDataSizes) EXPECT_NO_THROW(canComObj.UnregisterReceiver(&mockRcv)); ASSERT_NO_THROW(canComObj.Shutdown()); // Shutdown the object after the test - EXPECT_EQ(canComObj.GetStatus(), sdv::EObjectStatus::destruction_pending); + EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::destruction_pending); } extern "C" int main(int argc, char* argv[]) diff --git a/tests/unit_tests/toml_parser/CMakeLists.txt b/tests/unit_tests/toml_parser/CMakeLists.txt index 385f331..656665f 100644 --- a/tests/unit_tests/toml_parser/CMakeLists.txt +++ b/tests/unit_tests/toml_parser/CMakeLists.txt @@ -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 +#******************************************************************************* + # Character Reader executable add_executable(UnitTest_TOMLParser "character_reader_tests.cpp" @@ -5,9 +19,13 @@ add_executable(UnitTest_TOMLParser "parser_tests.cpp" "main.cpp" "generate_toml_tests.cpp" - "content_modifications.cpp" + "generate_toml_delete_node.cpp" "statement_boundary_detection.cpp" - "miscellaneous_tests.cpp") + "miscellaneous_tests.cpp" + "generate_toml_with_transfer.cpp" + "generate_toml_switch_inline.cpp" + "generate_toml_getset_comment.cpp" + "generate_toml_insert_node.cpp" "generate_toml_miscellaneous.cpp" "generate_toml_combine_reduce.cpp") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_link_libraries(UnitTest_TOMLParser GTest::GTest ${CMAKE_THREAD_LIBS_INIT}) if (WIN32) @@ -29,7 +47,7 @@ add_custom_command(TARGET UnitTest_TOMLParser POST_BUILD ) # Build dependencies -add_dependencies(UnitTest_TOMLParser core_services) +add_dependencies(UnitTest_TOMLParser dependency_sdv_components) diff --git a/tests/unit_tests/toml_parser/character_reader_tests.cpp b/tests/unit_tests/toml_parser/character_reader_tests.cpp index b38cf36..a5791c3 100644 --- a/tests/unit_tests/toml_parser/character_reader_tests.cpp +++ b/tests/unit_tests/toml_parser/character_reader_tests.cpp @@ -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 #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.h" #include "../../../sdv_services/core/toml_parser/exception.h" diff --git a/tests/unit_tests/toml_parser/generate_toml_combine_reduce.cpp b/tests/unit_tests/toml_parser/generate_toml_combine_reduce.cpp new file mode 100644 index 0000000..cfa13e4 --- /dev/null +++ b/tests/unit_tests/toml_parser/generate_toml_combine_reduce.cpp @@ -0,0 +1,515 @@ +/******************************************************************************** + * 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 + +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" + +TEST(GenerateTOML, CombineRoot) +{ + toml_parser::CParser parser1(R"toml( +val1 = 10 +val2 = "20")toml"); + toml_parser::CParser parser2(R"toml( +val3 = 30.0 +val1 = 10)toml"); + + EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code( +val1 = 10 +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineRootComments) +{ + toml_parser::CParser parser1(R"toml( +val1 = 10 # This is value 1 +val2 = "20" # This is value 2)toml"); + toml_parser::CParser parser2(R"toml( +val3 = 30.0 # This is value 3 +val1 = 10 # This is again value 1)toml"); + + EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code( +val1 = 10 # This is value 1 +val2 = "20" # This is value 2 +val3 = 30.0 # This is value 3)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineRootwithTable) +{ + toml_parser::CParser parser1(R"toml( +val1 = 10 +val2 = "20")toml"); + toml_parser::CParser parser2(R"toml([MyTable] +val3 = 30.0 +val1 = 10)toml"); + + auto ptrTable = parser2.Root().Direct("MyTable"); + ASSERT_TRUE(ptrTable); + EXPECT_TRUE(parser1.Root().Combine(ptrTable->Cast())); + + std::string ssCombinedTOML = R"code( +val1 = 10 +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineTablewithRoot) +{ + toml_parser::CParser parser1(R"toml([MyTable] +val1 = 10 +val2 = "20")toml"); + toml_parser::CParser parser2(R"toml( +val3 = 30.0 +val1 = 10)toml"); + + auto ptrNode = parser1.Root().Direct("MyTable"); + ASSERT_TRUE(ptrNode); + auto ptrTable = ptrNode->Cast(); + ASSERT_TRUE(ptrTable); + EXPECT_TRUE(ptrTable->Combine(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code([MyTable] +val1 = 10 +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineTablewithDifferentTable) +{ + toml_parser::CParser parser1(R"toml([MyTable1] +val1 = 10 +val2 = "20")toml"); + toml_parser::CParser parser2(R"toml([MyTable2] +val3 = 30.0 +val1 = 10)toml"); + + auto ptrNode1 = parser1.Root().Direct("MyTable1"); + ASSERT_TRUE(ptrNode1); + auto ptrTable1 = ptrNode1->Cast(); + ASSERT_TRUE(ptrTable1); + auto ptrNode2 = parser2.Root().Direct("MyTable2"); + ASSERT_TRUE(ptrNode2); + auto ptrTable2 = ptrNode2->Cast(); + ASSERT_TRUE(ptrTable2); + EXPECT_TRUE(ptrTable1->Combine(ptrTable2)); + + std::string ssCombinedTOML = R"code([MyTable1] +val1 = 10 +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineTablewithIdenticalTable) +{ + toml_parser::CParser parser1(R"toml([MyTable] +val1 = 10 +val2 = "20")toml"); + toml_parser::CParser parser2(R"toml([MyTable] +val3 = 30.0 +val1 = 10)toml"); + + EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code([MyTable] +val1 = 10 +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineInlineTablewithDifferentStandardTable) +{ + toml_parser::CParser parser1(R"toml(MyTable1 = {val1 = 10, val2 = "20"})toml"); + toml_parser::CParser parser2(R"toml([MyTable2] +val3 = 30.0 +val1 = 10)toml"); + + auto ptrNode1 = parser1.Root().Direct("MyTable1"); + ASSERT_TRUE(ptrNode1); + auto ptrTable1 = ptrNode1->Cast(); + ASSERT_TRUE(ptrTable1); + auto ptrNode2 = parser2.Root().Direct("MyTable2"); + ASSERT_TRUE(ptrNode2); + auto ptrTable2 = ptrNode2->Cast(); + ASSERT_TRUE(ptrTable2); + EXPECT_TRUE(ptrTable1->Combine(ptrTable2)); + + std::string ssCombinedTOML = R"code(MyTable1 = {val1 = 10, val2 = "20", val3 = 30.0})code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineInlineTablewithIdenticalStandardTable) +{ + toml_parser::CParser parser1(R"toml(MyTable = {val1 = 10, val2 = "20"})toml"); + toml_parser::CParser parser2(R"toml([MyTable] +val3 = 30.0 +val1 = 10)toml"); + + EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code(MyTable = {val1 = 10, val2 = "20", val3 = 30.0})code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineStandardTablewithDifferentInlineTable) +{ + toml_parser::CParser parser1(R"toml([MyTable1] +val1 = 10 +val2 = "20")toml"); + toml_parser::CParser parser2(R"toml(MyTable2 = {val3 = 30.0, val1 = 10})toml"); + + auto ptrNode1 = parser1.Root().Direct("MyTable1"); + ASSERT_TRUE(ptrNode1); + auto ptrTable1 = ptrNode1->Cast(); + ASSERT_TRUE(ptrTable1); + auto ptrNode2 = parser2.Root().Direct("MyTable2"); + ASSERT_TRUE(ptrNode2); + auto ptrTable2 = ptrNode2->Cast(); + ASSERT_TRUE(ptrTable2); + EXPECT_TRUE(ptrTable1->Combine(ptrTable2)); + + std::string ssCombinedTOML = R"code([MyTable1] +val1 = 10 +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineStandardTablewithIdenticalInlineTable) +{ + toml_parser::CParser parser1(R"toml([MyTable] +val1 = 10 +val2 = "20")toml"); + toml_parser::CParser parser2(R"toml(MyTable = {val3 = 30.0, val1 = 10})toml"); + + EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code([MyTable] +val1 = 10 +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineInlineTablewithDifferentInlineTable) +{ + toml_parser::CParser parser1(R"toml(MyTable1 = {val1 = 10, val2 = "20"})toml"); + toml_parser::CParser parser2(R"toml(MyTable2 = {val3 = 30.0, val1 = 10})toml"); + + auto ptrNode1 = parser1.Root().Direct("MyTable1"); + ASSERT_TRUE(ptrNode1); + auto ptrTable1 = ptrNode1->Cast(); + ASSERT_TRUE(ptrTable1); + auto ptrNode2 = parser2.Root().Direct("MyTable2"); + ASSERT_TRUE(ptrNode2); + auto ptrTable2 = ptrNode2->Cast(); + ASSERT_TRUE(ptrTable2); + EXPECT_TRUE(ptrTable1->Combine(ptrTable2)); + + std::string ssCombinedTOML = R"code(MyTable1 = {val1 = 10, val2 = "20", val3 = 30.0})code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineInlineTablewithIdenticalInlineTable) +{ + toml_parser::CParser parser1(R"toml(MyTable = {val1 = 10, val2 = "20"})toml"); + toml_parser::CParser parser2(R"toml(MyTable = {val3 = 30.0, val1 = 10})toml"); + + EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code(MyTable = {val1 = 10, val2 = "20", val3 = 30.0})code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineDifferentArrays) +{ + toml_parser::CParser parser1(R"toml( +val1 = [10, 20] +val2 = ["30", "40"])toml"); + toml_parser::CParser parser2(R"toml( +val3 = [50.0, 60.0] +val1 = [70, 80])toml"); + + EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code( +val1 = [70, 80] +val2 = ["30", "40"] +val3 = [50.0, 60.0])code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, CombineDifferentTableArrays) +{ + toml_parser::CParser parser1(R"toml([[table_array1]] +val1 = [10, 20] +[[table_array1]] +val2 = ["30", "40"])toml"); + toml_parser::CParser parser2(R"toml( +[[table_array2]] +val3 = [50.0, 60.0] +[[table_array2]] +val1 = [70, 80])toml"); + + EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code([[table_array1]] +val1 = [10, 20] +[[table_array1]] +val2 = ["30", "40"] +[[table_array2]] +val3 = [50.0, 60.0] +[[table_array2]] +val1 = [70, 80])code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, ReduceRoot) +{ + toml_parser::CParser parser1(R"toml( +val1 = 10 +val2 = "20" +val3 = 30.0)toml"); + toml_parser::CParser parser2(R"toml( +val3 = 35.0 +val1 = 10)toml"); + + EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code( +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, ReduceRootComments) +{ + toml_parser::CParser parser1(R"toml( +val1 = 10 # This is value 1 +val2 = "20" # This is value 2 +val3 = 30.0 # This is value 3)toml"); + toml_parser::CParser parser2(R"toml( +val3 = 35.0 # This also is value 3 +val1 = 10 # This also is value 1)toml"); + + EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code( +val2 = "20" # This is value 2 +val3 = 30.0 # This is value 3)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, ReduceTable) +{ + toml_parser::CParser parser1(R"toml([my_table] +val1 = 10 +val2 = "20" +val3 = 30.0)toml"); + toml_parser::CParser parser2(R"toml([my_table] +val3 = 35.0 +val1 = 10)toml"); + + EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code([my_table] +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, ReduceStandardTableWithInlineTable) +{ + toml_parser::CParser parser1(R"toml([my_table] +val1 = 10 +val2 = "20" +val3 = 30.0)toml"); + toml_parser::CParser parser2(R"toml(my_table = {val3 = 35.0, val1 = 10})toml"); + + EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code([my_table] +val2 = "20" +val3 = 30.0)code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, ReduceInlineTableWithStandardTable) +{ + toml_parser::CParser parser1(R"toml(my_table = {val1 = 10, val2 = "20", val3 = 30.0})toml"); + toml_parser::CParser parser2(R"toml([my_table] +val3 = 35.0 +val1 = 10)toml"); + + EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code(my_table = { val2 = "20", val3 = 30.0})code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, ReduceInlineTable) +{ + toml_parser::CParser parser1(R"toml(my_table = {val1 = 10, val2 = "20", val3 = 30.0})toml"); + toml_parser::CParser parser2(R"toml(my_table = {val3 = 35.0, val1 = 10})toml"); + + EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"code(my_table = { val2 = "20", val3 = 30.0})code"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, ReduceDifferentArrays) +{ + toml_parser::CParser parser1(R"code( +val1 = [10, 20] +val2 = ["30", "40"] +val3 = [50.0, 60.0])code"); + toml_parser::CParser parser2(R"toml( +val3 = [50.0, 60.0] +val1 = [70, 80])toml"); + + EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"toml( +val1 = [10, 20] +val2 = ["30", "40"] +)toml"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(GenerateTOML, ReduceDifferentTableArrays) +{ + toml_parser::CParser parser1(R"code([[table_array1]] +val1 = [10, 20] +[[table_array1]] +val2 = ["30", "40"] +[[table_array2]] +val3 = [50.0, 60.0] +[[table_array2]] +val1 = [70, 80])code"); + toml_parser::CParser parser2(R"toml( +[[table_array1]] +val2 = ["30", "40"] +[[table_array2]] +val3 = [50.0, 60.0] +[[table_array2]] +val1 = [70] +)toml"); + + EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); + + std::string ssCombinedTOML = R"toml([[table_array1]] +val1 = [10, 20] +[[table_array2]] +val1 = [70, 80])toml"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} diff --git a/tests/unit_tests/toml_parser/generate_toml_delete_node.cpp b/tests/unit_tests/toml_parser/generate_toml_delete_node.cpp new file mode 100644 index 0000000..8edccec --- /dev/null +++ b/tests/unit_tests/toml_parser/generate_toml_delete_node.cpp @@ -0,0 +1,648 @@ +/******************************************************************************** + * 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 + +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" + +// Delete nodes (for all types of nodes, between all types of nodes) +// - beginning +// - middle +// - end +// - Using deleted node info -- error +// - Last node (empty after that) +// - Smart delete (comments/whitespace around) + +// Insert nodes (for all types of nodes, between all types of nodes) +// Before and after: +// - beginning +// - middle +// - end +// - Being the first item in a TOML file +// - Inserted and straight away deleted +// - Inserted with false/deleted reference --error +// - Inserted values before (okay) and behind (error) tables +// - Inserted duplicate value -- error +// - Smart insert (comments/whitespace around) + +// Shift nodes (for all types of nodes, between root, tables and arrays) + +/* + * @brief Delete a key from the TOML string. + * @param[in] rssTOMLInput Reference to the TOML string. + * @param[in] rssKey Reference to the key to delete. + * @param[in] rssOuput Reference to the expected ouput. + * @return Returns 'true' on success. + */ +bool TestDelete(const std::string& rssTOMLInput, const std::string& rssKey, const std::string& rssOutput) +{ + toml_parser::CParser parser; + bool bRes = true; + EXPECT_NO_THROW(bRes = parser.Process(rssTOMLInput)); + EXPECT_TRUE(bRes); + if (!bRes) return bRes; + auto ptrNode = parser.Root().Direct(rssKey); + EXPECT_TRUE(ptrNode); + if (!ptrNode) return false; + EXPECT_TRUE(bRes = ptrNode->DeleteNode()); + if (!bRes) return bRes; + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, rssOutput); + if (ssTOML != rssOutput) return false; + return true; +}; + +TEST(TOMLDeleteNode, DeleteValues) +{ + // Delete a key from the begin (whitespace reduced) + EXPECT_TRUE(TestDelete(R"toml(key = 10 # value key +bare_key = "value" # value bare_key +bare-key = false # value bare-key)toml", + "key", + R"toml(bare_key = "value" # value bare_key +bare-key = false # value bare-key)toml")); + + // Delete a key from the begin + EXPECT_TRUE(TestDelete(R"toml( +key = 10 # value key +bare_key = "value" # value bare_key +bare-key = false # value bare-key +)toml", + "key", + R"toml( +bare_key = "value" # value bare_key +bare-key = false # value bare-key +)toml")); + + // Delete a key from the middle + EXPECT_TRUE(TestDelete(R"toml( +key = 10 # value key +bare_key = "value" # value bare_key +bare-key = false # value bare-key +)toml", + "bare_key", + R"toml( +key = 10 # value key +bare-key = false # value bare-key +)toml")); + + // Delete a key from the end + EXPECT_TRUE(TestDelete(R"toml( +key = 10 # value key +bare_key = "value" # value bare_key +bare-key = false # value bare-key +)toml", + "bare-key", + R"toml( +key = 10 # value key +bare_key = "value" # value bare_key +)toml")); +} + +TEST(TOMLDeleteNode, DeleteInlineTableValues) +{ + // Delete key from the inline table + EXPECT_TRUE(TestDelete(R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc"} +)toml", + "1234.y", + R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, z = 2, str = "abc"} +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc"} +)toml", + "1234.x", + R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = { y = 1, z = 2, str = "abc"} +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc"} +)toml", + "1234.str", + R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2} +)toml")); + +} + +TEST(TOMLDeleteNode, DeleteInlineSubTableValues) +{ + // Delete key from the inline sub-table + EXPECT_TRUE(TestDelete(R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} +)toml", + "1234.tbl.b", + R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, c=3}} +)toml")); + +} + +TEST(TOMLDeleteNode, DeleteInlineTables) +{ + // Delete table + EXPECT_TRUE(TestDelete(R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} +)toml", + "1234.tbl", + R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc"} +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} +)toml", + "1234", + R"toml( +key = 10 +bare_key = "value" +bare-key = false +)toml")); +} + +TEST(TOMLDeleteNode, DeleteTableValues) +{ + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +)toml", + "my_table.key", + R"toml( +[my_table] +bare_key = "value" +bare-key = false +)toml")); + + // Delete a key from the middle + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +)toml", + "my_table.bare_key", + R"toml( +[my_table] +key = 10 +bare-key = false +)toml")); + + // Delete a key from the end + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +)toml", + "my_table.bare-key", + R"toml( +[my_table] +key = 10 +bare_key = "value" +)toml")); + +} + +TEST(TOMLDeleteNode, DeleteInlineTableValueInTable) +{ + // Delete key from the inline table in a table + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc"} +)toml", + "my_table.1234.y", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, z = 2, str = "abc"} +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc"} +)toml", + "my_table.1234.x", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = { y = 1, z = 2, str = "abc"} +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc"} +)toml", + "my_table.1234.str", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2} +)toml")); + +} + +TEST(TOMLDeleteNode, DeleteInlineSubTableValueInTable) +{ + // Delete key from the inline sub-table + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} +)toml", + "my_table.1234.tbl.b", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, c=3}} +)toml")); + +} + +TEST(TOMLDeleteNode, DeleteInlineTableInTable) +{ + // Delete table + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} +)toml", + "my_table.1234.tbl", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc"} +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} +)toml", + "my_table.1234", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +)toml")); + +} + +TEST(TOMLDeleteNode, DeleteValuesInChildTable) +{ + // Delete key from the child-table + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +[my_table.1234] +x = 0 +y = 1 +z = 2 +str = "abc" +)toml", + "my_table.1234.y", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +[my_table.1234] +x = 0 +z = 2 +str = "abc" +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +[my_table.1234] +x = 0 +y = 1 +z = 2 +str = "abc" +)toml", + "my_table.1234.x", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +[my_table.1234] +y = 1 +z = 2 +str = "abc" +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +[my_table.1234] +x = 0 +y = 1 +z = 2 +str = "abc" +)toml", + "my_table.1234.str", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +[my_table.1234] +x = 0 +y = 1 +z = 2 +)toml")); + +} + +TEST(TOMLDeleteNode, DeleteChildTableInTable) +{ + // Delete table + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +[my_table.1234] +x = 0 +y = 1 +z = 2 +str = "abc" +[my_table.1234.tbl] +a =1 +b=2 +c=3 +)toml", + "my_table.1234.tbl", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +[my_table.1234] +x = 0 +y = 1 +z = 2 +str = "abc" +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +[my_table.1234] +x = 0 +y = 1 +z = 2 +str = "abc" +[my_table.1234.tbl] +a =1 +b=2 +c=3 +)toml", + "my_table.1234", + R"toml( +[my_table] +key = 10 +bare_key = "value" +bare-key = false +)toml")); +} + +TEST(TOMLDeleteNode, DeleteArrayValues) +{ + // Delete array values + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "key[0]", + R"toml( +key = [ 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "key[1]", + R"toml( +key = [10, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "key[2]", + R"toml( +key = [10, 20] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml")); + +} + +TEST(TOMLDeleteNode, DeleteArrayValuesWithSucceedingComma) +{ + // Delete array values with succeeding comma + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "bare_key[0]", + R"toml( +key = [10, 20, 30] +bare_key = [ "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "bare_key[1]", + R"toml( +key = [10, 20, 30] +bare_key = ["value1", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "bare_key[2]", + R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml")); + +} + +TEST(TOMLDeleteNode, DeleteValuesWithComments) +{ + // Delete array values with comments + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "bare-key[0]", + R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [ 2020, # value 1 + ] +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "bare-key[0].a", + R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{ b = true}, # value 0 + 2020, # value 1 + ] +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "bare-key[0].b", + R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false}, # value 0 + 2020, # value 1 + ] +)toml")); + EXPECT_TRUE(TestDelete(R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + 2020, # value 1 + ] +)toml", + "bare-key[1]", + R"toml( +key = [10, 20, 30] +bare_key = ["value1", "value2", 3030, ] +bare-key = [{a = false, b = true}, # value 0 + ] +)toml")); +} \ No newline at end of file diff --git a/tests/unit_tests/toml_parser/generate_toml_getset_comment.cpp b/tests/unit_tests/toml_parser/generate_toml_getset_comment.cpp new file mode 100644 index 0000000..8597a17 --- /dev/null +++ b/tests/unit_tests/toml_parser/generate_toml_getset_comment.cpp @@ -0,0 +1,328 @@ +/******************************************************************************** + * 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 + +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" + +TEST(GenerateTOML, GetCommentRoot) +{ + toml_parser::CParser parser; + + std::string ssTOML = R"code(# This is a comment)code"; + + EXPECT_NO_THROW(parser.Process(ssTOML)); + + std::string ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_before); + EXPECT_TRUE(ssComment.empty()); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_behind); + EXPECT_TRUE(ssComment.empty()); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); + EXPECT_TRUE(ssComment.empty()); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); + EXPECT_EQ(ssComment, "This is a comment"); +} + +TEST(GenerateTOML, GetMultiLineCommentRoot) +{ + toml_parser::CParser parser; + + std::string ssTOML = R"code( +# This is a comment that stretches +# more than one line. + +# And here's another comment of more +# than one line. +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOML)); + + std::string ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_before); + EXPECT_TRUE(ssComment.empty()); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_behind); + EXPECT_TRUE(ssComment.empty()); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); + EXPECT_TRUE(ssComment.empty()); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); + EXPECT_EQ(ssComment, R"code(This is a comment that stretches more than one line. +And here's another comment of more than one line.)code"); +} + +TEST(GenerateTOML, SetCommentRoot) +{ + toml_parser::CParser parser; + + // This will test the SetComment function and overwriting the existing comment. + + std::string ssTOMLInput = R"code(# This is a double line comment +# This is line two of the double line comment)code"; + std::string ssTOMLOutput = R"code(# This is comment way before + +# This is comment before +# This is comment way behind +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::comment_before , "This is comment before"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::comment_behind , "This is comment behind"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before , "This is comment way before"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind , "This is comment way behind"); + + std::string ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_before); + EXPECT_EQ(ssComment, "This is comment before"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_behind); + EXPECT_EQ(ssComment, "This is comment behind"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); + EXPECT_EQ(ssComment, "This is comment way before"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); + EXPECT_EQ(ssComment, "This is comment way behind"); + + std::string ssGenerated = parser.Root().GenerateTOML(); + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, SetMultiLineCommentRoot) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# This is a double line comment +# This is line two of the double line comment)code"; + std::string ssTOMLOutput = R"code(# This is comment way before + +# And surprise, also a second line + +# This is comment before +# +# With a second line +# This is comment way behind + +# And final, also a second line +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::comment_before, R"(This is comment before +With a second line)"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::comment_behind, R"(This is comment behind +Also with a second line)"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before, R"(This is comment way before +And surprise, also a second line)"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind, R"(This is comment way behind +And final, also a second line)"); + + std::string ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_before); + EXPECT_EQ(ssComment, R"(This is comment before +With a second line)"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_behind); + EXPECT_EQ(ssComment, R"(This is comment behind +Also with a second line)"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); + EXPECT_EQ(ssComment, R"(This is comment way before +And surprise, also a second line)"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); + EXPECT_EQ(ssComment, R"(This is comment way behind +And final, also a second line)"); + + std::string ssGenerated = parser.Root().GenerateTOML(); + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, RemoveCommentRoot) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# This is a double line comment +# This is line two of the double line comment)code"; + std::string ssTOMLOutput = R"code()code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind, std::string()); + + std::string ssGenerated = parser.Root().GenerateTOML(); + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, GetCommentRootValue) +{ + toml_parser::CParser parser; + + std::string ssTOML = R"code(# This is a separate comment + +# This is a comment before the value +value = "this is the value text" # Comment following the value + +# This is also a separate comment)code"; + + EXPECT_NO_THROW(parser.Process(ssTOML)); + sdv::TInterfaceAccessPtr ptrValue = parser.Root().GetNodeDirect("value"); + ASSERT_TRUE(ptrValue); + sdv::toml::INodeInfo* pComment = ptrValue.GetInterface(); + ASSERT_NE(pComment, nullptr); + + std::string ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::comment_before); + EXPECT_EQ(ssComment, "This is a comment before the value"); + ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::comment_behind); + EXPECT_EQ(ssComment, "Comment following the value"); + ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); + EXPECT_EQ(ssComment, "This is a separate comment"); + ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); + EXPECT_EQ(ssComment, "This is also a separate comment"); +} + +TEST(GenerateTOML, GetMultiLineCommentRootValue) +{ + toml_parser::CParser parser; + + std::string ssTOML = R"code( + + +# This is a separate comment with several line-breaks before. +# Followed by this text on the same line. + +# Note: there was a space after the empty comment line. +# And another separate comment on a next line. + +# This is a comment before the value. +# And another comment before the value at the same line. +# +# Note: there was a space after the empty comment line. +# But that should not influence the comment lines. +value = "this is the value text" # Comment following the value. + # More comment following the value. + # This becomes one line. + # + # But this is a separate line. + +# This is also a separate comment. +# Followed by this text on the same line. + +# And another text on a separate line.)code"; + + EXPECT_NO_THROW(parser.Process(ssTOML)); + sdv::TInterfaceAccessPtr ptrValue = parser.Root().GetNodeDirect("value"); + ASSERT_TRUE(ptrValue); + sdv::toml::INodeInfo* pComment = ptrValue.GetInterface(); + ASSERT_NE(pComment, nullptr); + + std::string ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::comment_before); + EXPECT_EQ(ssComment, R"code(This is a comment before the value. And another comment before the value at the same line. +Note: there was a space after the empty comment line. But that should not influence the comment lines.)code"); + ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::comment_behind); + EXPECT_EQ(ssComment, R"code(Comment following the value. More comment following the value. This becomes one line. +But this is a separate line.)code"); + ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); + EXPECT_EQ(ssComment, R"code(This is a separate comment with several line-breaks before. Followed by this text on the same line. +Note: there was a space after the empty comment line. And another separate comment on a next line.)code"); + ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); + EXPECT_EQ(ssComment, R"code(This is also a separate comment. Followed by this text on the same line. +And another text on a separate line.)code"); +} + +TEST(GenerateTOML, SetCommentRootValue) +{ + toml_parser::CParser parser; + + // This will test the SetComment function and overwriting the existing comment. + + std::string ssTOMLInput = R"code(# This is a double line comment +# This is line two of the double line comment)code"; + std::string ssTOMLOutput = R"code(# This is comment #3 + +# This is comment #1 +# This is comment #4 +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::comment_before , "This is comment #1"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::comment_behind , "This is comment #2"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before , "This is comment #3"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind , "This is comment #4"); + + std::string ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_before); + EXPECT_EQ(ssComment, "This is comment #1"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_behind); + EXPECT_EQ(ssComment, "This is comment #2"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); + EXPECT_EQ(ssComment, "This is comment #3"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); + EXPECT_EQ(ssComment, "This is comment #4"); + + std::string ssGenerated = parser.Root().GenerateTOML(); + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, SetMultiLineCommentRootValue) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# This is a double line comment +# This is line two of the double line comment)code"; + std::string ssTOMLOutput = R"code(# This is comment #3 + +# And surprise, also a second line + +# This is comment #1 +# +# With a second line +# This is comment #4 + +# And final, also a second line +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::comment_before, R"(This is comment #1 +With a second line)"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::comment_behind, R"(This is comment #2 +Also with a second line)"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before, R"(This is comment #3 +And surprise, also a second line)"); + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind, R"(This is comment #4 +And final, also a second line)"); + + std::string ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_before); + EXPECT_EQ(ssComment, R"(This is comment #1 +With a second line)"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::comment_behind); + EXPECT_EQ(ssComment, R"(This is comment #2 +Also with a second line)"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); + EXPECT_EQ(ssComment, R"(This is comment #3 +And surprise, also a second line)"); + ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); + EXPECT_EQ(ssComment, R"(This is comment #4 +And final, also a second line)"); + + std::string ssGenerated = parser.Root().GenerateTOML(); + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, RemoveCommentRootValue) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# This is a double line comment +# This is line two of the double line comment)code"; + std::string ssTOMLOutput = R"code()code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + parser.Root().SetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind, std::string()); + + std::string ssGenerated = parser.Root().GenerateTOML(); + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} diff --git a/tests/unit_tests/toml_parser/generate_toml_insert_node.cpp b/tests/unit_tests/toml_parser/generate_toml_insert_node.cpp new file mode 100644 index 0000000..b817392 --- /dev/null +++ b/tests/unit_tests/toml_parser/generate_toml_insert_node.cpp @@ -0,0 +1,1068 @@ +/******************************************************************************** + * 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 + +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" + +// Test TODO: +// - Inserted and straight away deleted +// - Inserted with false/deleted reference --error +// - Inserted values before (okay) and behind (error) tables +// - Inserted duplicate value -- error +// - Smart insert (comments/whitespace around) +// Insert as TOML, but only partly correct. + +TEST(TOMLInsertNode, TestInsertValuesRoot) +{ + toml_parser::CParser parser; + EXPECT_NE(parser.Root().InsertValue(sdv::toml::npos, "value_int", 10), nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_int = 10)toml"); + EXPECT_NE(parser.Root().InsertValue(sdv::toml::npos, "value_str", u8"abc"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_int = 10 +value_str = "abc")toml"); + EXPECT_NE(parser.Root().InsertValue(0, "value_float", 123.456), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_float = 123.456 +value_int = 10 +value_str = "abc")toml"); +} + +TEST(TOMLInsertNode, TestInsertTableRoot) +{ + // Insert a standard table + toml_parser::CParser parser; + EXPECT_NE(parser.Root().InsertTable(sdv::toml::npos, "table1", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([table1])toml"); + + // Insert an inline table before + EXPECT_NE(parser.Root().InsertTable(0, "table2", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table2 = {} +[table1])toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_NE(parser.Root().InsertTable(sdv::toml::npos, "table3", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table2 = {} +table3 = {} +[table1])toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_NE(parser.Root().InsertTable(0, "table4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table2 = {} +table3 = {} +[table4] +[table1])toml"); +} + +TEST(TOMLInsertNode, TestInsertArrayRoot) +{ + toml_parser::CParser parser; + EXPECT_NE(parser.Root().InsertArray(sdv::toml::npos, "value_array1"), nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_array1 = [])toml"); + EXPECT_NE(parser.Root().InsertArray(sdv::toml::npos, "value_array2"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_array1 = [] +value_array2 = [])toml"); + EXPECT_NE(parser.Root().InsertArray(0, "value_array3"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_array3 = [] +value_array1 = [] +value_array2 = [])toml"); +} + +TEST(TOMLInsertNode, TestInsertTableArrayRoot) +{ + // Insert a standard table array + toml_parser::CParser parser; + EXPECT_NE(parser.Root().InsertTableArray(sdv::toml::npos, "table_array1", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array1]])toml"); + + // Insert an inline table array before + EXPECT_NE(parser.Root().InsertTableArray(0, "table_array2", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] +[[table_array1]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_NE(parser.Root().InsertTableArray(sdv::toml::npos, "table_array3", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] +table_array3 = [{}] +[[table_array1]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_NE(parser.Root().InsertTableArray(0, "table_array4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] +table_array3 = [{}] +[[table_array4]] +[[table_array1]])toml"); + + // Add an additional table array entry for table array #4 + EXPECT_NE(parser.Root().InsertTableArray(sdv::toml::npos, "table_array4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] +table_array3 = [{}] +[[table_array4]] +[[table_array1]] +[[table_array4]])toml"); +} + +TEST(TOMLInsertNode, TestInsertValueInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pStandardTable, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert the values into the table + EXPECT_NE(pStandardTable->InsertValue(sdv::toml::npos, "value_int", 10), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_int = 10)toml"); + EXPECT_NE(pStandardTable->InsertValue(sdv::toml::npos, "value_str", u8"abc"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_int = 10 +value_str = "abc")toml"); + EXPECT_NE(pStandardTable->InsertValue(0, "value_float", 123.456), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_float = 123.456 +value_int = 10 +value_str = "abc")toml"); +} + +TEST(TOMLInsertNode, TestInsertTableInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pStandardTable, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert a standard table + EXPECT_NE(pStandardTable->InsertTable(sdv::toml::npos, "table1", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table.table1])toml"); + + // Insert an inline table before + EXPECT_NE(pStandardTable->InsertTable(0, "table2", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table2 = {} +[standard_table.table1])toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_NE(pStandardTable->InsertTable(sdv::toml::npos, "table3", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table2 = {} +table3 = {} +[standard_table.table1])toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_NE(pStandardTable->InsertTable(0, "table4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table2 = {} +table3 = {} +[standard_table.table4] +[standard_table.table1])toml"); +} + +TEST(TOMLInsertNode, TestInsertArrayInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pStandardTable, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert arrays + EXPECT_NE(pStandardTable->InsertArray(sdv::toml::npos, "value_array1"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_array1 = [])toml"); + EXPECT_NE(pStandardTable->InsertArray(sdv::toml::npos, "value_array2"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_array1 = [] +value_array2 = [])toml"); + EXPECT_NE(pStandardTable->InsertArray(0, "value_array3"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_array3 = [] +value_array1 = [] +value_array2 = [])toml"); +} + +TEST(TOMLInsertNode, TestInsertTableArrayInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pStandardTable, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert standard table array + EXPECT_NE(pStandardTable->InsertTableArray(sdv::toml::npos, "table_array1", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[standard_table.table_array1]])toml"); + + // Insert an inline table array before + EXPECT_NE(pStandardTable->InsertTableArray(0, "table_array2", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +[[standard_table.table_array1]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_NE(pStandardTable->InsertTableArray(sdv::toml::npos, "table_array3", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +table_array3 = [{}] +[[standard_table.table_array1]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_NE(pStandardTable->InsertTableArray(0, "table_array4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +table_array3 = [{}] +[[standard_table.table_array4]] +[[standard_table.table_array1]])toml"); + + // Add an additional table array entry for table array #4 + EXPECT_NE(pStandardTable->InsertTableArray(sdv::toml::npos, "table_array4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +table_array3 = [{}] +[[standard_table.table_array4]] +[[standard_table.table_array1]] +[[standard_table.table_array4]])toml"); +} + +TEST(TOMLInsertNode, TestInsertValueInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pInlineTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + sdv::toml::npos, "inline_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). + GetInterface(); + ASSERT_NE(pInlineTable, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert the values into the table + EXPECT_NE(pInlineTable->InsertValue(sdv::toml::npos, "value_int", 10), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_int = 10})toml"); + EXPECT_NE(pInlineTable->InsertValue(sdv::toml::npos, "value_str", u8"abc"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_int = 10, value_str = "abc"})toml"); + EXPECT_NE(pInlineTable->InsertValue(0, "value_float", 123.456), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_float = 123.456, value_int = 10, value_str = "abc"})toml"); +} + +TEST(TOMLInsertNode, TestInsertTableInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pInlineTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + sdv::toml::npos, "inline_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). + GetInterface(); + ASSERT_NE(pInlineTable, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert a standard table + EXPECT_NE(pInlineTable->InsertTable(sdv::toml::npos, "table1", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table1 = {}})toml"); + + // Insert an inline table before + EXPECT_NE(pInlineTable->InsertTable(0, "table2", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table2 = {}, table1 = {}})toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_NE(pInlineTable->InsertTable(sdv::toml::npos, "table3", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table2 = {}, table1 = {}, table3 = {}})toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_NE(pInlineTable->InsertTable(0, "table4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table4 = {}, table2 = {}, table1 = {}, table3 = {}})toml"); +} + +TEST(TOMLInsertNode, TestInsertArrayInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pInlineTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + sdv::toml::npos, "inline_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). + GetInterface(); + ASSERT_NE(pInlineTable, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert arrays + EXPECT_NE(pInlineTable->InsertArray(sdv::toml::npos, "value_array1"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array1 = []})toml"); + EXPECT_NE(pInlineTable->InsertArray(sdv::toml::npos, "value_array2"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array1 = [], value_array2 = []})toml"); + EXPECT_NE(pInlineTable->InsertArray(0, "value_array3"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array3 = [], value_array1 = [], value_array2 = []})toml"); +} + +TEST(TOMLInsertNode, TestInsertTableArrayInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pInlineTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + sdv::toml::npos, "inline_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). + GetInterface(); + ASSERT_NE(pInlineTable, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert standard table array + EXPECT_NE(pInlineTable->InsertTableArray(sdv::toml::npos, "table_array1", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array1 = [{}]})toml"); + + // Insert an inline table array before + EXPECT_NE(pInlineTable->InsertTableArray(0, "table_array2", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array2 = [{}], table_array1 = [{}]})toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_NE(pInlineTable->InsertTableArray(sdv::toml::npos, "table_array3", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_NE(pInlineTable->InsertTableArray(0, "table_array4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array4 = [{}], table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); + + // Add an additional table array entry for table array #4 + EXPECT_NE(pInlineTable->InsertTableArray(sdv::toml::npos, "table_array4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array4 = [{}, {}], table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); +} + +TEST(TOMLInsertNode, TestInsertValueInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pInlineArray = sdv::TInterfaceAccessPtr(parser.Root().InsertArray( + sdv::toml::npos, "inline_array")).GetInterface(); + ASSERT_NE(pInlineArray, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert the values into the table (with or without name) + EXPECT_NE(pInlineArray->InsertValue(sdv::toml::npos, "", 10), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [10])toml"); + EXPECT_NE(pInlineArray->InsertValue(sdv::toml::npos, "", u8"abc"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [10, "abc"])toml"); + EXPECT_NE(pInlineArray->InsertValue(0, "value_float", 123.456), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [123.456, 10, "abc"])toml"); +} + +TEST(TOMLInsertNode, TestInsertTableInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pInlineArray = sdv::TInterfaceAccessPtr(parser.Root().InsertArray( + sdv::toml::npos, "inline_array")).GetInterface(); + ASSERT_NE(pInlineArray, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert a standard table + sdv::toml::INodeCollectionInsert* pTableInsert = sdv::TInterfaceAccessPtr( + pInlineArray->InsertTable(sdv::toml::npos, "", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pTableInsert, nullptr); + pTableInsert->InsertValue(0, "a", 10); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{a = 10}])toml"); + + // Insert an inline table before + pTableInsert = sdv::TInterfaceAccessPtr( + pInlineArray->InsertTable(0, "", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). + GetInterface(); + ASSERT_NE(pTableInsert, nullptr); + pTableInsert->InsertValue(0, "b", 20); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{b = 20}, {a = 10}])toml"); + + // Insert an inline table behind + pTableInsert = sdv::TInterfaceAccessPtr( + pInlineArray->InsertTable(sdv::toml::npos, "", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). + GetInterface(); + ASSERT_NE(pTableInsert, nullptr); + pTableInsert->InsertValue(0, "c", 30); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{b = 20}, {a = 10}, {c = 30}])toml"); + + // Insert a standard table in front + pTableInsert = sdv::TInterfaceAccessPtr( + pInlineArray->InsertTable(0, "", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pTableInsert, nullptr); + pTableInsert->InsertValue(0, "d", 40); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{d = 40}, {b = 20}, {a = 10}, {c = 30}])toml"); +} + +TEST(TOMLInsertNode, TestInsertArrayInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pInlineArray = sdv::TInterfaceAccessPtr(parser.Root().InsertArray( + sdv::toml::npos, "inline_array")).GetInterface(); + ASSERT_NE(pInlineArray, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert arrays + sdv::toml::INodeCollectionInsert* pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertArray(sdv::toml::npos, "")). + GetInterface(); + ASSERT_NE(pArrayInsert, nullptr); + pArrayInsert->InsertValue(0, "", 10); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[10]])toml"); + pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertArray(sdv::toml::npos, "value_array2")). + GetInterface(); + ASSERT_NE(pArrayInsert, nullptr); + pArrayInsert->InsertValue(0, "", 20); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[10], [20]])toml"); + pArrayInsert = + sdv::TInterfaceAccessPtr(pInlineArray->InsertArray(0, "value_array3")). + GetInterface(); + ASSERT_NE(pArrayInsert, nullptr); + pArrayInsert->InsertValue(0, "", 30); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[30], [10], [20]])toml"); +} + +TEST(TOMLInsertNode, TestInsertTableArrayInArray) +{ + // Insert an array + toml_parser::CParser parser; + auto* pArray = parser.Root().InsertArray(sdv::toml::npos, "inline_array"); + ASSERT_NE(pArray, nullptr); + sdv::toml::INodeCollectionInsert* pInlineArray = + sdv::TInterfaceAccessPtr(pArray).GetInterface(); + ASSERT_NE(pInlineArray, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert standard table array + sdv::toml::INodeCollectionInsert* pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertTableArray(sdv::toml::npos, + "table_array1", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pArrayInsert, nullptr); + pArrayInsert->InsertValue(0, "a", 10); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{a = 10}]])toml"); + + // Insert an inline table array before + pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertTableArray(0, "table_array2", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)).GetInterface(); + ASSERT_NE(pArrayInsert, nullptr); + pArrayInsert->InsertValue(0, "b", 20); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{b = 20}], [{a = 10}]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertTableArray(sdv::toml::npos, "table_array3", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)).GetInterface(); + ASSERT_NE(pArrayInsert, nullptr); + pArrayInsert->InsertValue(0, "c", 30); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{b = 20}], [{a = 10}], [{c = 30}]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertTableArray(0, "table_array4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)).GetInterface(); + ASSERT_NE(pArrayInsert, nullptr); + pArrayInsert->InsertValue(0, "d", 40); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{d = 40}], [{b = 20}], [{a = 10}], [{c = 30}]])toml"); + + // Add an additional table array entry for table array #4 + sdv::toml::INodeCollection* pCollection = sdv::TInterfaceAccessPtr(pArray).GetInterface(); + ASSERT_NE(pCollection, nullptr); + sdv::toml::INodeCollectionInsert* pFirstNode = + sdv::TInterfaceAccessPtr(pCollection->GetNode(0)).GetInterface(); + ASSERT_NE(pFirstNode, nullptr); + sdv::toml::INodeCollectionInsert* pTableInsert = sdv::TInterfaceAccessPtr(pFirstNode->InsertTable(sdv::toml::npos, + "table_array4", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pTableInsert, nullptr); + pTableInsert->InsertValue(0, "e", 50); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{d = 40}, {e = 50}], [{b = 20}], [{a = 10}], [{c = 30}]])toml"); +} + +TEST(TOMLInsertNode, TestInsertValueInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pTableArray = sdv::TInterfaceAccessPtr(parser.Root().InsertTableArray( + sdv::toml::npos, "table_array", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pTableArray, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert the values into the table + EXPECT_NE(pTableArray->InsertValue(sdv::toml::npos, "value_int", 10), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_int = 10)toml"); + EXPECT_NE(pTableArray->InsertValue(sdv::toml::npos, "value_str", u8"abc"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_int = 10 +value_str = "abc")toml"); + EXPECT_NE(pTableArray->InsertValue(0, "value_float", 123.456), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_float = 123.456 +value_int = 10 +value_str = "abc")toml"); +} + +TEST(TOMLInsertNode, TestInsertTableInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pTableArray = sdv::TInterfaceAccessPtr(parser.Root().InsertTableArray( + sdv::toml::npos, "table_array", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pTableArray, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert a standard table + EXPECT_NE(pTableArray->InsertTable(sdv::toml::npos, "table1", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +[table_array.table1])toml"); + + // Insert an inline table before + EXPECT_NE(pTableArray->InsertTable(0, "table2", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table2 = {} +[table_array.table1])toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_NE(pTableArray->InsertTable(sdv::toml::npos, "table3", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table2 = {} +table3 = {} +[table_array.table1])toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_NE(pTableArray->InsertTable(0, "table4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table2 = {} +table3 = {} +[table_array.table4] +[table_array.table1])toml"); +} + +TEST(TOMLInsertNode, TestInsertArrayInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pTableArray = sdv::TInterfaceAccessPtr(parser.Root().InsertTableArray( + sdv::toml::npos, "table_array", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pTableArray, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert arrays + EXPECT_NE(pTableArray->InsertArray(sdv::toml::npos, "value_array1"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_array1 = [])toml"); + EXPECT_NE(pTableArray->InsertArray(sdv::toml::npos, "value_array2"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_array1 = [] +value_array2 = [])toml"); + EXPECT_NE(pTableArray->InsertArray(0, "value_array3"), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_array3 = [] +value_array1 = [] +value_array2 = [])toml"); +} + +TEST(TOMLInsertNode, TestInsertTableArrayInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::INodeCollectionInsert* pTableArray = sdv::TInterfaceAccessPtr(parser.Root().InsertTableArray( + sdv::toml::npos, "table_array", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + GetInterface(); + ASSERT_NE(pTableArray, nullptr); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert standard table array + EXPECT_NE(pTableArray->InsertTableArray(sdv::toml::npos, "table_array1", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +[[table_array.table_array1]])toml"); + + // Insert an inline table array before + EXPECT_NE(pTableArray->InsertTableArray(0, "table_array2", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +[[table_array.table_array1]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_NE(pTableArray->InsertTableArray(sdv::toml::npos, "table_array3", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +table_array3 = [{}] +[[table_array.table_array1]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_NE(pTableArray->InsertTableArray(0, "table_array4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +table_array3 = [{}] +[[table_array.table_array4]] +[[table_array.table_array1]])toml"); + + // Add an additional table array entry for table array #4 + EXPECT_NE(pTableArray->InsertTableArray(sdv::toml::npos, "table_array4", + sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +table_array3 = [{}] +[[table_array.table_array4]] +[[table_array.table_array1]] +[[table_array.table_array4]])toml"); +} + +TEST(TOMLInsertNode, TestInsertValuesRootAsTOML) +{ + toml_parser::CParser parser; + EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(# This is the first value +value_int = 10)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is the first value +value_int = 10)toml"); + EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(value_str = "abc" # This is the second value + +# Comment in between + +# And the third value +value_float = 123.456 + +# Some +# Final +# Words :-) +)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is the first value +value_int = 10 +value_str = "abc" # This is the second value + +# Comment in between + +# And the third value +value_float = 123.456 + +# Some +# Final +# Words :-) +)toml"); +} + +TEST(TOMLInsertNode, TestInsertTableRootAsTOML) +{ + // Insert a standard table + toml_parser::CParser parser; + EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml([table1] # This is table 1 +a = 10 +b = 20.30)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([table1] # This is table 1 +a = 10 +b = 20.30)toml"); + + // Insert an inline table before + EXPECT_EQ(parser.Root().InsertTOML(0, R"toml(# This is table 2 +table2 = {c = 40, d = "50"})toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is table 2 +table2 = {c = 40, d = "50"} +[table1] # This is table 1 +a = 10 +b = 20.30)toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(table3 = {e = '60'} # This is table 3 +)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is table 2 +table2 = {c = 40, d = "50"} +table3 = {e = '60'} # This is table 3 +[table1] # This is table 1 +a = 10 +b = 20.30)toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_EQ(parser.Root().InsertTOML(0, R"toml( + +# And this is table 4 +[table4] +f = 70 +g = 80.90)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is table 2 +table2 = {c = 40, d = "50"} +table3 = {e = '60'} # This is table 3 + + +# And this is table 4 +[table4] +f = 70 +g = 80.90 +[table1] # This is table 1 +a = 10 +b = 20.30)toml"); +} + +TEST(TOMLInsertNode, TestInsertArrayRootAsTOML) +{ + toml_parser::CParser parser; + EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(value_array1 = [10, 20, 30] # This is array 1)toml", true), + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_array1 = [10, 20, 30] # This is array 1)toml"); + EXPECT_EQ(parser.Root().InsertTOML(0, R"toml(# This is array 2 +value_array2 = [ + 40, # This is an integer value + 50.60, # This is a float value + "70", # This is a string value + ])toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is array 2 +value_array2 = [ + 40, # This is an integer value + 50.60, # This is a float value + "70", # This is a string value + ] +value_array1 = [10, 20, 30] # This is array 1)toml"); + EXPECT_EQ(parser.Root().InsertTOML(1, R"toml(# And finally array 3 +value_array3 = [])toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is array 2 +value_array2 = [ + 40, # This is an integer value + 50.60, # This is a float value + "70", # This is a string value + ] +# And finally array 3 +value_array3 = [] +value_array1 = [10, 20, 30] # This is array 1)toml"); +} + +TEST(TOMLInsertNode, TestInsertTableArrayRootAsTOML) +{ + // Insert a standard table array + toml_parser::CParser parser; + EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml([[table_array1]] +a = 10 +b = 20)toml", true), + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array1]] +a = 10 +b = 20)toml"); + + // Insert an inline table array before + EXPECT_EQ(parser.Root().InsertTOML(0, R"toml(table_array2 = [{c = 30}, {d = 40}])toml", true), + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +[[table_array1]] +a = 10 +b = 20)toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(table_array3 = +[ + { e = 50 } +])toml", true), + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = +[ + { e = 50 } +] +[[table_array1]] +a = 10 +b = 20)toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_EQ(parser.Root().InsertTOML(0, R"toml([[table_array4]] +f = 60 +g = 70)toml", true), + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = +[ + { e = 50 } +] +[[table_array4]] +f = 60 +g = 70 +[[table_array1]] +a = 10 +b = 20)toml"); + + // Add an additional table array entry for table array #4 (even provided as inline, must be added as standard). + EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(table_array4 = [{h = 80}])toml", true), + sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = +[ + { e = 50 } +] +[[table_array4]] +f = 60 +g = 70 +[[table_array1]] +a = 10 +b = 20 +[[table_array4]] +h = 80)toml"); + + // Add an additional table array entry for table array #4 (even provided as inline, must be added as standard). + EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml([[table_array3]] +i = 90 +[[table_array3]] +j = 100 +)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + EXPECT_EQ(parser.Root().InsertTOML(0, R"toml([[table_array3]] +k = 110)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = +[{k = 110}, + { e = 50 }, +{i = 90}, {j = 100}] +[[table_array4]] +f = 60 +g = 70 +[[table_array1]] +a = 10 +b = 20 +[[table_array4]] +h = 80)toml"); + + parser.Root().AutomaticFormat(); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = [{k = 110}, {e = 50}, {i = 90}, {j = 100}] +[[table_array4]] +f = 60 +g = 70 +[[table_array1]] +a = 10 +b = 20 +[[table_array4]] +h = 80)toml"); +} + +TEST(TOMLInsertNode, DISABLED_TestInsertValueInStandardTableAsTOML) +{ + //// Insert a standard table + //toml_parser::CParser parser; + //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + // sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + // GetInterface(); + //ASSERT_NE(pStandardTable, nullptr); + //std::string ssTOML = parser.GenerateTOML(); + //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); +} + +TEST(TOMLInsertNode, DISABLED_TestInsertTableInStandardTableAsTOML) +{ + //// Insert a standard table + //toml_parser::CParser parser; + //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + // sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + // GetInterface(); + //ASSERT_NE(pStandardTable, nullptr); + //std::string ssTOML = parser.GenerateTOML(); + //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); +} + +TEST(TOMLInsertNode, DISABLED_TestInsertArrayInStandardTableAsTOML) +{ + //// Insert a standard table + //toml_parser::CParser parser; + //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + // sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + // GetInterface(); + //ASSERT_NE(pStandardTable, nullptr); + //std::string ssTOML = parser.GenerateTOML(); + //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); +} + +TEST(TOMLInsertNode, DISABLED_TestInsertTableArrayInStandardTableAsTOML) +{ + //// Insert a standard table + //toml_parser::CParser parser; + //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( + // sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). + // GetInterface(); + //ASSERT_NE(pStandardTable, nullptr); + //std::string ssTOML = parser.GenerateTOML(); + //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); +} + +TEST(TOMLInsertNode, DISABLED_TestInsertValueInInlineTableAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertTableInInlineTableAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertArrayInInlineTableAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertTableArrayInInlineTableAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertValueInArrayAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertTableInArrayAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertArrayInArrayAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertTableArrayInArrayAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertValueInTableArrayAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertTableInTableArrayAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertArrayInTableArrayAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertTableArrayInTableArrayAsTOML) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertMixed) +{} + +TEST(TOMLInsertNode, DISABLED_TestInsertMixedWithDelete) +{} diff --git a/tests/unit_tests/toml_parser/generate_toml_miscellaneous.cpp b/tests/unit_tests/toml_parser/generate_toml_miscellaneous.cpp new file mode 100644 index 0000000..f3b56fc --- /dev/null +++ b/tests/unit_tests/toml_parser/generate_toml_miscellaneous.cpp @@ -0,0 +1,21 @@ +/******************************************************************************** + * 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 + +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" + +// Test TODO: +// Shift nodes up and down within one container +// Remove formatting from node \ No newline at end of file diff --git a/tests/unit_tests/toml_parser/generate_toml_switch_inline.cpp b/tests/unit_tests/toml_parser/generate_toml_switch_inline.cpp new file mode 100644 index 0000000..8f9344e --- /dev/null +++ b/tests/unit_tests/toml_parser/generate_toml_switch_inline.cpp @@ -0,0 +1,50 @@ +/******************************************************************************** + * 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 + +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" + +// Test TODO: +// Switch to inline and vice versa + +TEST(GenerateTOML, SwitchTableToInline) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code([table] +key1 = "some string" +key2 = 123)code"; + std::string ssTOMLOutput = R"code(table = {key1 = "some string", key2 = 123})code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML()); + EXPECT_EQ(ssGenerated, ssTOMLInput); + + sdv::TInterfaceAccessPtr ptrTable = parser.Root().GetNodeDirect("table"); + EXPECT_TRUE(ptrTable); + sdv::toml::INodeInfo* pInfo = ptrTable.GetInterface(); + sdv::toml::INodeCollectionConvert* pConvert = ptrTable.GetInterface(); + ASSERT_NE(pInfo, nullptr); + ASSERT_NE(pConvert, nullptr); + EXPECT_FALSE(pInfo->IsInline()); + EXPECT_TRUE(pConvert->CanMakeInline()); + EXPECT_TRUE(pConvert->MakeInline()); + EXPECT_TRUE(pInfo->IsInline()); + + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML()); + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} diff --git a/tests/unit_tests/toml_parser/generate_toml_tests.cpp b/tests/unit_tests/toml_parser/generate_toml_tests.cpp index 11ec269..55b59d3 100644 --- a/tests/unit_tests/toml_parser/generate_toml_tests.cpp +++ b/tests/unit_tests/toml_parser/generate_toml_tests.cpp @@ -1,4 +1,17 @@ -#include +/******************************************************************************** + * 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 #include "../../../sdv_services/core/toml_parser/parser_toml.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" @@ -41,7 +54,6 @@ TEST(GenerateTOML, NodeCommentWithSpaces) key = "value" # This is a comment at the end of a line )code"; - parser.Process(ssTOML); EXPECT_NO_THROW(parser.Process(ssTOML)); std::string ssGenerated; @@ -70,6 +82,23 @@ another = "# This is not a comment" EXPECT_EQ(ssGenerated, ssTOML); } +TEST(GenerateTOML, ArrayWhitespace) +{ + toml_parser::CParser parser; + + std::string ssTOML = R"code( + array = [ 1, 2, 3, + 4, 5, 6 ] +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOML)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssTOML); +} + TEST(GenerateTOML, ArrayComment) { toml_parser::CParser parser; @@ -93,24 +122,86 @@ array = [ 1, # Value #1 EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferNodeComment) +TEST(GenerateTOML, ArrayCommentWithSpace) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# This is a full-line comment -key = "value" # This is a comment at the end of a line -another = "# This is not a comment")code"; - std::string ssTOMLOutput = R"code([tree.branch] -# This is a full-line comment -key = "value" # This is a comment at the end of a line -another = "# This is not a comment")code"; + std::string ssTOML = R"code( - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); +# unattached comment + +# Pre-array +array = [ 1, # Value #1 + +# unattached comment + + 2, # Value #2 + +# unattached comment + + 3, # Value #3 + +# unattached comment + + 4, # Value #4 + +# unattached comment + + 5, # Value #5 + +# unattached comment + + 6, # Value #6 + +# unattached comment + + ] # Post-array + +# unattached comment + +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOML)); std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML()); - EXPECT_EQ(ssGenerated, ssTOMLOutput); + EXPECT_EQ(ssGenerated, ssTOML); +} + +TEST(GenerateTOML, InlineTableWhitespace) +{ + toml_parser::CParser parser; + + // Note: line-breaks within an inline table are not allowed. + std::string ssTOML = R"code( + table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOML)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssTOML); +} + +TEST(GenerateTOML, InlineTableComment) +{ + toml_parser::CParser parser; + + // Note: line-breaks within an inline table are not allowed. + std::string ssTOML = R"code( +# Pre-table + table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } # Post-table +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOML)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssTOML); } TEST(GenerateTOML, Keys) @@ -130,28 +221,6 @@ bare-key = "value" EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferKeys) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(key = "value" -bare_key = "value" -bare-key = "value" -1234 = "value")code"; - std::string ssTOMLOutput = R"code([tree.branch] -key = "value" -bare_key = "value" -bare-key = "value" -1234 = "value")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, QuotedKeys) { toml_parser::CParser parser; @@ -170,30 +239,6 @@ TEST(GenerateTOML, QuotedKeys) EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferQuotedKeys) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = u8R"code("127.0.0.1" = "value" -"character encoding" = "value" -"ʎǝʞ" = "value" -'key2' = "value" -'quoted "value"' = "value")code"; - std::string ssTOMLOutput = u8R"code([tree.branch] -"127.0.0.1" = "value" -"character encoding" = "value" -"ʎǝʞ" = "value" -'key2' = "value" -'quoted "value"' = "value")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, BlankKeys) { std::string ssTOML1 = R"code("" = "blank" # VALID but discouraged)code"; @@ -210,27 +255,6 @@ TEST(GenerateTOML, BlankKeys) EXPECT_EQ(ssGenerated2, ssTOML2); } -TEST(GenerateTOML, TransferBlankKeys) -{ - std::string ssTOMLInput1 = R"code("" = "blank" # VALID but discouraged)code"; - std::string ssTOMLInput2 = R"code('' = 'blank' # VALID but discouraged)code"; - std::string ssTOMLOutput1 = R"code([tree.branch] -"" = "blank" # VALID but discouraged)code"; - std::string ssTOMLOutput2 = R"code([tree.branch] -'' = 'blank' # VALID but discouraged)code"; - - toml_parser::CParser parser1, parser2; - EXPECT_NO_THROW(parser1.Process(ssTOMLInput1)); - EXPECT_NO_THROW(parser2.Process(ssTOMLInput2)); - - std::string ssGenerated1, ssGenerated2; - EXPECT_NO_THROW(ssGenerated1 = parser1.GenerateTOML("tree.branch")); - EXPECT_NO_THROW(ssGenerated2 = parser2.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated1, ssTOMLOutput1); - EXPECT_EQ(ssGenerated2, ssTOMLOutput2); -} - TEST(GenerateTOML, DottedKeys) { toml_parser::CParser parser; @@ -248,28 +272,6 @@ site."google.com" = true)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferDottedKeys) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(name = "Orange" -physical.color = "orange" -physical.shape = "round" -site."google.com" = true)code"; - std::string ssTOMLOutput = R"code([tree.branch] -name = "Orange" -physical.color = "orange" -physical.shape = "round" -site."google.com" = true)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, WhitespaceKeys) { toml_parser::CParser parser; @@ -286,26 +288,6 @@ fruit . flavor = "banana" # same as fruit.flavor)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferWhitespaceKeys) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(fruit.name = "banana" # this is best practice -fruit. color = "yellow" # same as fruit.color -fruit . flavor = "banana" # same as fruit.flavor)code"; - std::string ssTOMLOutput = R"code([tree.branch] -fruit.name = "banana" # this is best practice -fruit. color = "yellow" # same as fruit.color -fruit . flavor = "banana" # same as fruit.flavor)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, OutOfOrderKeys) { toml_parser::CParser parser; @@ -327,36 +309,6 @@ orange.color = "orange")code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferOutOfOrderKeys) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(apple.type = "fruit" -orange.type = "fruit" - -apple.skin = "thin" -orange.skin = "thick" - -apple.color = "red" -orange.color = "orange")code"; - std::string ssTOMLOutput = R"code([tree.branch] -apple.type = "fruit" -orange.type = "fruit" - -apple.skin = "thin" -orange.skin = "thick" - -apple.color = "red" -orange.color = "orange")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, FloatLookingAlikeKeys) { toml_parser::CParser parser; @@ -371,22 +323,6 @@ TEST(GenerateTOML, FloatLookingAlikeKeys) EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferFloatLookingAlikeKeys) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(3.14159 = "pi")code"; - std::string ssTOMLOutput = R"code([tree.branch] -3.14159 = "pi")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, BasicStrings) { toml_parser::CParser parser; @@ -401,22 +337,6 @@ TEST(GenerateTOML, BasicStrings) EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferBasicStrings) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")code"; - std::string ssTOMLOutput = R"code([tree.branch] -str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, MultiLineStrings) { toml_parser::CParser parser; @@ -433,26 +353,6 @@ Violets are blue""")code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferMultiLineStrings) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(str1 = """ -Roses are red -Violets are blue""")code"; - std::string ssTOMLOutput = R"code([tree.branch] -str1 = """ -Roses are red -Violets are blue""")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, LongMultiLineStrings) { toml_parser::CParser parser; @@ -480,48 +380,6 @@ str3 = """\ EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferLongMultiLineStrings) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(str1 = "The quick brown fox jumps over the lazy dog." - -str2 = """ -The quick brown \ - - - fox jumps over \ - the lazy dog.""" - -str3 = """\ - The quick brown \ - fox jumps over \ - the lazy dog.\ - """)code"; - std::string ssTOMLOutput = R"code([tree.branch] -str1 = "The quick brown fox jumps over the lazy dog." - -str2 = """ -The quick brown \ - - - fox jumps over \ - the lazy dog.""" - -str3 = """\ - The quick brown \ - fox jumps over \ - the lazy dog.\ - """)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, QuotingStrings) { toml_parser::CParser parser; @@ -542,34 +400,6 @@ str7 = """"This," she said, "is just a pointless statement."""")code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferQuotingStrings) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(str4 = """Here are two quotation marks: "". Simple enough.""" -# str5 = """Here are three quotation marks: """.""" # INVALID -str5 = """Here are three quotation marks: ""\".""" -str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" - -# "This," she said, "is just a pointless statement." -str7 = """"This," she said, "is just a pointless statement."""")code"; - std::string ssTOMLOutput = R"code([tree.branch] -str4 = """Here are two quotation marks: "". Simple enough.""" -# str5 = """Here are three quotation marks: """.""" # INVALID -str5 = """Here are three quotation marks: ""\".""" -str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" - -# "This," she said, "is just a pointless statement." -str7 = """"This," she said, "is just a pointless statement."""")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, LiteralStrings) { toml_parser::CParser parser; @@ -587,28 +417,6 @@ regex = '<\i\c*\s*>')code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferLiteralStrings) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(winpath = 'C:\Users\nodejs\templates' -winpath2 = '\\ServerX\admin$\system32\' -quoted = 'Tom "Dubs" Preston-Werner' -regex = '<\i\c*\s*>')code"; - std::string ssTOMLOutput = R"code([tree.branch] -winpath = 'C:\Users\nodejs\templates' -winpath2 = '\\ServerX\admin$\system32\' -quoted = 'Tom "Dubs" Preston-Werner' -regex = '<\i\c*\s*>')code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, MultiLineLiteralStrings) { toml_parser::CParser parser; @@ -629,34 +437,6 @@ trimmed in raw strings. EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferMultiLineLiteralStrings) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(regex2 = '''I [dw]on't need \d{2} apples''' -lines = ''' -The first newline is -trimmed in raw strings. - All other whitespace - is preserved. -''')code"; - std::string ssTOMLOutput = R"code([tree.branch] -regex2 = '''I [dw]on't need \d{2} apples''' -lines = ''' -The first newline is -trimmed in raw strings. - All other whitespace - is preserved. -''')code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, QuotedLiteralStrings) { toml_parser::CParser parser; @@ -677,34 +457,6 @@ str = ''''That,' she said, 'is still pointless.'''')code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferQuotedLiteralStrings) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(quot15 = '''Here are fifteen quotation marks: """""""""""""""''' - -# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID -apos15 = "Here are fifteen apostrophes: '''''''''''''''" - -# 'That,' she said, 'is still pointless.' -str = ''''That,' she said, 'is still pointless.'''')code"; - std::string ssTOMLOutput = R"code([tree.branch] -quot15 = '''Here are fifteen quotation marks: """""""""""""""''' - -# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID -apos15 = "Here are fifteen apostrophes: '''''''''''''''" - -# 'That,' she said, 'is still pointless.' -str = ''''That,' she said, 'is still pointless.'''')code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, Integers) { toml_parser::CParser parser; @@ -722,28 +474,6 @@ int4 = -17)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferIntegers) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(int1 = +99 -int2 = 42 -int3 = 0 -int4 = -17)code"; - std::string ssTOMLOutput = R"code([tree.branch] -int1 = +99 -int2 = 42 -int3 = 0 -int4 = -17)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, ReadibleIntegers) { toml_parser::CParser parser; @@ -761,28 +491,6 @@ int8 = 1_2_3_4_5 # VALID but discouraged)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferReadibleIntegers) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(int5 = 1_000 -int6 = 5_349_221 -int7 = 53_49_221 # Indian number system grouping -int8 = 1_2_3_4_5 # VALID but discouraged)code"; - std::string ssTOMLOutput = R"code([tree.branch] -int5 = 1_000 -int6 = 5_349_221 -int7 = 53_49_221 # Indian number system grouping -int8 = 1_2_3_4_5 # VALID but discouraged)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, OtherBaseIntegers) { toml_parser::CParser parser; @@ -807,42 +515,6 @@ bin1 = 0b11010110)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferOtherBaseIntegers) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(# hexadecimal with prefix `0x` -hex1 = 0xDEADBEEF -hex2 = 0xdeadbeef -hex3 = 0xdead_beef - -# octal with prefix `0o` -oct1 = 0o01234567 -oct2 = 0o755 # useful for Unix file permissions - -# binary with prefix `0b` -bin1 = 0b11010110)code"; - std::string ssTOMLOutput = R"code([tree.branch] -# hexadecimal with prefix `0x` -hex1 = 0xDEADBEEF -hex2 = 0xdeadbeef -hex3 = 0xdead_beef - -# octal with prefix `0o` -oct1 = 0o01234567 -oct2 = 0o755 # useful for Unix file permissions - -# binary with prefix `0b` -bin1 = 0b11010110)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, FloatingPoints) { toml_parser::CParser parser; @@ -868,44 +540,6 @@ flt7 = 6.626e-34)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferFloatingPoints) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(# fractional -flt1 = +1.0 -flt2 = 3.1415 -flt3 = -0.01 - -# exponent -flt4 = 5e+22 -flt5 = 1e06 -flt6 = -2E-2 - -# both -flt7 = 6.626e-34)code"; - std::string ssTOMLOutput = R"code([tree.branch] -# fractional -flt1 = +1.0 -flt2 = 3.1415 -flt3 = -0.01 - -# exponent -flt4 = 5e+22 -flt5 = 1e06 -flt6 = -2E-2 - -# both -flt7 = 6.626e-34)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, ReadibleFloatingPoints) { toml_parser::CParser parser; @@ -920,22 +554,6 @@ TEST(GenerateTOML, ReadibleFloatingPoints) EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferReadibleFloatingPoints) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(flt8 = 224_617.445_991_228)code"; - std::string ssTOMLOutput = R"code([tree.branch] -flt8 = 224_617.445_991_228)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, SpecialFloatingPoints) { toml_parser::CParser parser; @@ -958,38 +576,6 @@ sf6 = -nan # valid, actual encoding is implementation-specific)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferSpecialFloatingPoints) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(# infinity -sf1 = inf # positive infinity -sf2 = +inf # positive infinity -sf3 = -inf # negative infinity - -# not a number -sf4 = nan # actual sNaN/qNaN encoding is implementation-specific -sf5 = +nan # same as `nan` -sf6 = -nan # valid, actual encoding is implementation-specific)code"; - std::string ssTOMLOutput = R"code([tree.branch] -# infinity -sf1 = inf # positive infinity -sf2 = +inf # positive infinity -sf3 = -inf # negative infinity - -# not a number -sf4 = nan # actual sNaN/qNaN encoding is implementation-specific -sf5 = +nan # same as `nan` -sf6 = -nan # valid, actual encoding is implementation-specific)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, Booleans) { toml_parser::CParser parser; @@ -1005,24 +591,6 @@ bool2 = false)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferBooleans) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(bool1 = true -bool2 = false)code"; - std::string ssTOMLOutput = R"code([tree.branch] -bool1 = true -bool2 = false)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, DISABLED_OffsetDateTimes) { toml_parser::CParser parser; @@ -1039,26 +607,6 @@ odt3 = 1979-05-27T00:32:00.999999-07:00)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, DISABLED_TransferOffsetDateTimes) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(odt1 = 1979-05-27T07:32:00Z -odt2 = 1979-05-27T00:32:00-07:00 -odt3 = 1979-05-27T00:32:00.999999-07:00)code"; - std::string ssTOMLOutput = R"code([tree.branch] -odt1 = 1979-05-27T07:32:00Z -odt2 = 1979-05-27T00:32:00-07:00 -odt3 = 1979-05-27T00:32:00.999999-07:00)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, DISABLED_ReadibleOffsetDateTimes) { toml_parser::CParser parser; @@ -1073,22 +621,6 @@ TEST(GenerateTOML, DISABLED_ReadibleOffsetDateTimes) EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, DISABLED_TransferReadibleOffsetDateTimes) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(odt4 = 1979-05-27 07:32:00Z)code"; - std::string ssTOMLOutput = R"code([tree.branch] -odt4 = 1979-05-27 07:32:00Z)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, DISABLED_LocalDateTimes) { toml_parser::CParser parser; @@ -1104,24 +636,6 @@ ldt2 = 1979-05-27T00:32:00.999999)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, DISABLED_TransferLocalDateTimes) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(ldt1 = 1979-05-27T07:32:00 -ldt2 = 1979-05-27T00:32:00.999999)code"; - std::string ssTOMLOutput = R"code([tree.branch] -ldt1 = 1979-05-27T07:32:00 -ldt2 = 1979-05-27T00:32:00.999999)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, DISABLED_LocalDates) { toml_parser::CParser parser; @@ -1136,22 +650,6 @@ TEST(GenerateTOML, DISABLED_LocalDates) EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, DISABLED_TransferLocalDates) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(ld1 = 1979-05-27)code"; - std::string ssTOMLOutput = R"code([tree.branch] -ld1 = 1979-05-27)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, DISABLED_LocalTimes) { toml_parser::CParser parser; @@ -1167,24 +665,6 @@ lt2 = 00:32:00.999999)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, DISABLED_TransferLocalTimes) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(lt1 = 07:32:00 -lt2 = 00:32:00.999999)code"; - std::string ssTOMLOutput = R"code([tree.branch] -lt1 = 07:32:00 -lt2 = 00:32:00.999999)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, Arrays) { toml_parser::CParser parser; @@ -1199,12 +679,8 @@ string_array = [ "all", 'strings', """are the same""", '''type''' ] numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] contributors = [ - "Foo Bar ", - { - name = "Baz Qux", - email = "bazqux@example.com", - url = "https://example.com/bazqux" - } + "Foo Bar ", + { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } ])code"; parser.Process(ssTOML); @@ -1216,44 +692,6 @@ contributors = EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferArrays) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(integers = [ 1, 2, 3 ] -colors = [ "red", "yellow", "green" ] -nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] -nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] -string_array = [ "all", 'strings', """are the same""", '''type''' ] - -# Mixed-type arrays are allowed -numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] -contributors = [ - "Foo Bar ", - { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } -])code"; - std::string ssTOMLOutput = R"code([tree.branch] -integers = [ 1, 2, 3 ] -colors = [ "red", "yellow", "green" ] -nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] -nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] -string_array = [ "all", 'strings', """are the same""", '''type''' ] - -# Mixed-type arrays are allowed -numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] -contributors = [ - "Foo Bar ", - { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } -])code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, MultiLineArrays) { toml_parser::CParser parser; @@ -1275,36 +713,6 @@ integers3 = [ EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferMultiLineArrays) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(integers2 = [ - 1, 2, 3 -] - -integers3 = [ - 1, - 2, # this is ok -])code"; - std::string ssTOMLOutput = R"code([tree.branch] -integers2 = [ - 1, 2, 3 -] - -integers3 = [ - 1, - 2, # this is ok -])code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, Tables) { toml_parser::CParser parser; @@ -1327,37 +735,6 @@ key2 = 456)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferTables) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code([table] - -[table-1] -key1 = "some string" -key2 = 123 - -[table-2] -key1 = "another string" -key2 = 456)code"; - std::string ssTOMLOutput = R"code([tree.branch.table] - -[tree.branch.table-1] -key1 = "some string" -key2 = 123 - -[tree.branch.table-2] -key1 = "another string" -key2 = 456)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, QuotedKeyTables) { toml_parser::CParser parser; @@ -1373,23 +750,6 @@ type.name = "pug")code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferQuotedKeyTables) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code([dog."tater.man"] -type.name = "pug")code"; - std::string ssTOMLOutput = R"code([tree.branch.dog."tater.man"] -type.name = "pug")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, WhitespaceKeyTables) { toml_parser::CParser parser; @@ -1411,35 +771,6 @@ a = 1)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferWhitespaceKeyTables) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = u8R"code([a.b.c] # this is best practice -x = 1 -[ d.e.f ] # same as [d.e.f] -y = 1 -[ g . h . i ] # same as [g.h.i] -z = 1 -[ j . "Ęž" . 'l' ] # same as [j."Ęž".'l'] -a = 1)code"; - std::string ssTOMLOutput = u8R"code([tree.branch.a.b.c] # this is best practice -x = 1 -[tree.branch. d.e.f ] # same as [d.e.f] -y = 1 -[tree.branch. g . h . i ] # same as [g.h.i] -z = 1 -[tree.branch. j . "Ęž" . 'l' ] # same as [j."Ęž".'l'] -a = 1)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, MixedOrderTables) { toml_parser::CParser parser; @@ -1460,33 +791,6 @@ aa = 11)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferMixedOrderTables) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(# VALID BUT DISCOURAGED -[fruit.apple] -a = 1 -[animal] -b = 2 -[fruit.orange] -aa = 11)code"; - std::string ssTOMLOutput = R"code(# VALID BUT DISCOURAGED -[tree.branch.fruit.apple] -a = 1 -[tree.branch.animal] -b = 2 -[tree.branch.fruit.orange] -aa = 11)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, MixedValueAndTables) { toml_parser::CParser parser; @@ -1508,36 +812,6 @@ member_since = 1999)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferMixedValueAndTables) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(# Top-level table begins. -name = "Fido" -breed = "pug" - -# Top-level table ends. -[owner] -name = "Regina Dogman" -member_since = 1999)code"; - std::string ssTOMLOutput = R"code([tree.branch] -# Top-level table begins. -name = "Fido" -breed = "pug" - -# Top-level table ends. -[tree.branch.owner] -name = "Regina Dogman" -member_since = 1999)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, AutomaticTables) { toml_parser::CParser parser; @@ -1557,29 +831,6 @@ apple.taste.sweet = true)code"; EXPECT_EQ(ssGenerated, ssTOMLOutput2); } -TEST(GenerateTOML, TransferAutomaticTables) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(fruit.apple.color = "red" -fruit.apple.taste.sweet = true)code"; - std::string ssTOMLOutput = R"code([tree.branch] -fruit.apple.color = "red" -fruit.apple.taste.sweet = true)code"; - std::string ssTOMLOutput2 = R"code([tree.branch] -apple.color = "red" -apple.taste.sweet = true)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - EXPECT_EQ(ssGenerated, ssTOMLOutput); - - EXPECT_NO_THROW(ssGenerated = parser.Root().Direct("fruit")->GenerateTOML(toml_parser::CGenContext("tree.branch"))); - EXPECT_EQ(ssGenerated, ssTOMLOutput2); -} - TEST(GenerateTOML, MixedAutomaticTables) { toml_parser::CParser parser; @@ -1602,37 +853,6 @@ smooth = true)code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferMixedAutomaticTables) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code([fruit] -apple.color = "red" -apple.taste.sweet = true - -# [fruit.apple] # INVALID -# [fruit.apple.taste] # INVALID - -[fruit.apple.texture] # you can add sub-tables -smooth = true)code"; - std::string ssTOMLOutput = R"code([tree.branch.fruit] -apple.color = "red" -apple.taste.sweet = true - -# [fruit.apple] # INVALID -# [fruit.apple.taste] # INVALID - -[tree.branch.fruit.apple.texture] # you can add sub-tables -smooth = true)code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, InlineTables) { toml_parser::CParser parser; @@ -1649,25 +869,6 @@ animal = { type.name = "pug" })code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferInlineTables) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(name = { first = "Tom", last = "Preston-Werner" } -point = { x = 1, y = 2 } -animal = { type.name = "pug" })code"; - std::string ssTOMLOutput = R"code([tree.branch] -name = { first = "Tom", last = "Preston-Werner" } -point = { x = 1, y = 2 } -animal = { type.name = "pug" })code"; - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, EmbeddedInlineTables) { toml_parser::CParser parser; @@ -1684,26 +885,6 @@ TEST(GenerateTOML, EmbeddedInlineTables) EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferEmbeddedInlineTables) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(test=[{ first = "Tom", last = "Preston-Werner" }, -{ x = 1, y = 2 }, -{ type.name = "pug" }])code"; - std::string ssTOMLOutput = R"code([tree.branch] -test=[{ first = "Tom", last = "Preston-Werner" }, -{ x = 1, y = 2 }, -{ type.name = "pug" }])code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, TableArrays) { toml_parser::CParser parser; @@ -1728,41 +909,6 @@ color = "gray")code"; EXPECT_EQ(ssGenerated, ssTOML); } -TEST(GenerateTOML, TransferTableArrays) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code([[products]] -name = "Hammer" -sku = 738594937 - -[[products]] # empty table within the array - -[[products]] -name = "Nail" -sku = 284758393 - -color = "gray")code"; - std::string ssTOMLOutput = R"code([[tree.branch.products]] -name = "Hammer" -sku = 738594937 - -[[tree.branch.products]] # empty table within the array - -[[tree.branch.products]] -name = "Nail" -sku = 284758393 - -color = "gray")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, MixedTableAndTableArrays) { toml_parser::CParser parser; @@ -1786,28 +932,27 @@ name = "banana" [[fruits.varieties]] name = "plantain")code"; - std::string ssTOMLFruits1Physical = R"code( -[physical] # subtable + std::string ssTOMLFruits1Physical = R"code([physical] # subtable color = "red" shape = "round" + )code"; - std::string ssTOMLFruits1Varieties = R"code( -[[varieties]] # nested array of tables + std::string ssTOMLFruits1Varieties = R"code([[varieties]] # nested array of tables name = "red delicious" [[varieties]] name = "granny smith" + )code"; - std::string ssTOMLFruits1Variety1 = R"code( -[variety] # nested array of tables + std::string ssTOMLFruits1Variety1 = R"code([variety] # nested array of tables name = "red delicious" + )code"; - std::string ssTOMLFruits1Variety2 = R"code( -[variety] + std::string ssTOMLFruits1Variety2 = R"code([variety] name = "granny smith" + )code"; - std::string ssTOMLFruits2Variety1 = R"code( -[variety] + std::string ssTOMLFruits2Variety1 = R"code([variety] name = "plantain")code"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -1849,57 +994,6 @@ name = "plantain")code"; } -TEST(GenerateTOML, TransferMixedTableAndTableArrays) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code([[fruits]] -name = "apple" - -[fruits.physical] # subtable -color = "red" -shape = "round" - -[[fruits.varieties]] # nested array of tables -name = "red delicious" - -[[fruits.varieties]] -name = "granny smith" - - -[[fruits]] -name = "banana" - -[[fruits.varieties]] -name = "plantain")code"; - std::string ssTOMLOutput = R"code([[tree.branch.fruits]] -name = "apple" - -[tree.branch.fruits.physical] # subtable -color = "red" -shape = "round" - -[[tree.branch.fruits.varieties]] # nested array of tables -name = "red delicious" - -[[tree.branch.fruits.varieties]] -name = "granny smith" - - -[[tree.branch.fruits]] -name = "banana" - -[[tree.branch.fruits.varieties]] -name = "plantain")code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} - TEST(GenerateTOML, InlineTableArrays) { toml_parser::CParser parser; @@ -1915,22 +1009,3 @@ TEST(GenerateTOML, InlineTableArrays) EXPECT_EQ(ssGenerated, ssTOML); } - -TEST(GenerateTOML, TransferInlineTableArrays) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code(points = [ { x = 1, y = 2, z = 3 }, - { x = 7, y = 8, z = 9 }, - { x = 2, y = 4, z = 8 } ])code"; - std::string ssTOMLOutput = R"code([tree.branch] -points = [ { x = 1, y = 2, z = 3 }, - { x = 7, y = 8, z = 9 }, - { x = 2, y = 4, z = 8 } ])code"; - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); - - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} diff --git a/tests/unit_tests/toml_parser/generate_toml_with_transfer.cpp b/tests/unit_tests/toml_parser/generate_toml_with_transfer.cpp new file mode 100644 index 0000000..74ccb0f --- /dev/null +++ b/tests/unit_tests/toml_parser/generate_toml_with_transfer.cpp @@ -0,0 +1,1279 @@ +/******************************************************************************** + * 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 +#include "../../../sdv_services/core/toml_parser/parser_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" + +TEST(GenerateTOML, TransferNodeComment) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# This is a full-line comment +key = "value" # This is a comment at the end of a line +another = "# This is not a comment")code"; + std::string ssTOMLOutput = R"code([tree.branch] +# This is a full-line comment +key = "value" # This is a comment at the end of a line +another = "# This is not a comment")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferNodeCommentWithSpaces) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code( + # This is a full-line comment + key = "value" # This is a comment at the end of a line +)code"; + std::string ssTOMLOutput = R"code([tree.branch] + + # This is a full-line comment + key = "value" # This is a comment at the end of a line +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferUnattachedComment) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# Comment not belonging to node + +# This is a full-line comment +key = "value" # This is a comment at the end of a line +another = "# This is not a comment" + +# Comment not belonging to node)code"; + std::string ssTOMLOutput = R"code([tree.branch] +# Comment not belonging to node + +# This is a full-line comment +key = "value" # This is a comment at the end of a line +another = "# This is not a comment" + +# Comment not belonging to node)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferArrayWhitespace) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code( + array = [ 1, 2, 3, + 4, 5, 6 ] +)code"; + std::string ssTOMLOutput = R"code([tree.branch] + + array = [ 1, 2, 3, + 4, 5, 6 ] +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferArrayComment) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code( +# Pre-array +array = [ 1, # Value #1 + 2, # Value #2 + 3, # Value #3 + 4, # Value #4 + 5, # Value #5 + 6, # Value #6 + ] # Post-array +)code"; + std::string ssTOMLOutput = R"code([tree.branch] + +# Pre-array +array = [ 1, # Value #1 + 2, # Value #2 + 3, # Value #3 + 4, # Value #4 + 5, # Value #5 + 6, # Value #6 + ] # Post-array +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferArrayCommentWithSpace) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code( + +# unattached comment + +# Pre-array +array = [ 1, # Value #1 + +# unattached comment + + 2, # Value #2 + +# unattached comment + + 3, # Value #3 + +# unattached comment + + 4, # Value #4 + +# unattached comment + + 5, # Value #5 + +# unattached comment + + 6, # Value #6 + +# unattached comment + + ] # Post-array + +# unattached comment + +)code"; + std::string ssTOMLOutput = R"code([tree.branch] + + +# unattached comment + +# Pre-array +array = [ 1, # Value #1 + +# unattached comment + + 2, # Value #2 + +# unattached comment + + 3, # Value #3 + +# unattached comment + + 4, # Value #4 + +# unattached comment + + 5, # Value #5 + +# unattached comment + + 6, # Value #6 + +# unattached comment + + ] # Post-array + +# unattached comment + +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferInlineTableWhitespace) +{ + toml_parser::CParser parser; + + // NOTE: Line-breaks within inline tables are not allowed. + std::string ssTOMLInput = R"code( + table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } +)code"; + std::string ssTOMLOutput = R"code([tree.branch] + + table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferInlineTableComment) +{ + toml_parser::CParser parser; + + // NOTE: Line-breaks within inline tables are not allowed. + std::string ssTOMLInput = R"code( +# Pre-table + table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } # Post-table +)code"; + std::string ssTOMLOutput = R"code([tree.branch] + +# Pre-table + table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } # Post-table +)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferKeys) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(key = "value" +bare_key = "value" +bare-key = "value" +1234 = "value")code"; + std::string ssTOMLOutput = R"code([tree.branch] +key = "value" +bare_key = "value" +bare-key = "value" +1234 = "value")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferQuotedKeys) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = u8R"code("127.0.0.1" = "value" +"character encoding" = "value" +"ʎǝʞ" = "value" +'key2' = "value" +'quoted "value"' = "value")code"; + std::string ssTOMLOutput = u8R"code([tree.branch] +"127.0.0.1" = "value" +"character encoding" = "value" +"ʎǝʞ" = "value" +'key2' = "value" +'quoted "value"' = "value")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferBlankKeys) +{ + std::string ssTOMLInput1 = R"code("" = "blank" # VALID but discouraged)code"; + std::string ssTOMLInput2 = R"code('' = 'blank' # VALID but discouraged)code"; + std::string ssTOMLOutput1 = R"code([tree.branch] +"" = "blank" # VALID but discouraged)code"; + std::string ssTOMLOutput2 = R"code([tree.branch] +'' = 'blank' # VALID but discouraged)code"; + + toml_parser::CParser parser1, parser2; + EXPECT_NO_THROW(parser1.Process(ssTOMLInput1)); + EXPECT_NO_THROW(parser2.Process(ssTOMLInput2)); + + std::string ssGenerated1, ssGenerated2; + EXPECT_NO_THROW(ssGenerated1 = parser1.GenerateTOML("tree.branch")); + EXPECT_NO_THROW(ssGenerated2 = parser2.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated1, ssTOMLOutput1); + EXPECT_EQ(ssGenerated2, ssTOMLOutput2); +} + +TEST(GenerateTOML, TransferDottedKeys) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(name = "Orange" +physical.color = "orange" +physical.shape = "round" +site."google.com" = true)code"; + std::string ssTOMLOutput = R"code([tree.branch] +name = "Orange" +physical.color = "orange" +physical.shape = "round" +site."google.com" = true)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferWhitespaceKeys) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(fruit.name = "banana" # this is best practice +fruit. color = "yellow" # same as fruit.color +fruit . flavor = "banana" # same as fruit.flavor)code"; + std::string ssTOMLOutput = R"code([tree.branch] +fruit.name = "banana" # this is best practice +fruit. color = "yellow" # same as fruit.color +fruit . flavor = "banana" # same as fruit.flavor)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferOutOfOrderKeys) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(apple.type = "fruit" +orange.type = "fruit" + +apple.skin = "thin" +orange.skin = "thick" + +apple.color = "red" +orange.color = "orange")code"; + std::string ssTOMLOutput = R"code([tree.branch] +apple.type = "fruit" +orange.type = "fruit" + +apple.skin = "thin" +orange.skin = "thick" + +apple.color = "red" +orange.color = "orange")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferFloatLookingAlikeKeys) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(3.14159 = "pi")code"; + std::string ssTOMLOutput = R"code([tree.branch] +3.14159 = "pi")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferBasicStrings) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")code"; + std::string ssTOMLOutput = R"code([tree.branch] +str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferMultiLineStrings) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(str1 = """ +Roses are red +Violets are blue""")code"; + std::string ssTOMLOutput = R"code([tree.branch] +str1 = """ +Roses are red +Violets are blue""")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferLongMultiLineStrings) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(str1 = "The quick brown fox jumps over the lazy dog." + +str2 = """ +The quick brown \ + + + fox jumps over \ + the lazy dog.""" + +str3 = """\ + The quick brown \ + fox jumps over \ + the lazy dog.\ + """)code"; + std::string ssTOMLOutput = R"code([tree.branch] +str1 = "The quick brown fox jumps over the lazy dog." + +str2 = """ +The quick brown \ + + + fox jumps over \ + the lazy dog.""" + +str3 = """\ + The quick brown \ + fox jumps over \ + the lazy dog.\ + """)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferQuotingStrings) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(str4 = """Here are two quotation marks: "". Simple enough.""" +# str5 = """Here are three quotation marks: """.""" # INVALID +str5 = """Here are three quotation marks: ""\".""" +str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" + +# "This," she said, "is just a pointless statement." +str7 = """"This," she said, "is just a pointless statement."""")code"; + std::string ssTOMLOutput = R"code([tree.branch] +str4 = """Here are two quotation marks: "". Simple enough.""" +# str5 = """Here are three quotation marks: """.""" # INVALID +str5 = """Here are three quotation marks: ""\".""" +str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" + +# "This," she said, "is just a pointless statement." +str7 = """"This," she said, "is just a pointless statement."""")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferLiteralStrings) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(winpath = 'C:\Users\nodejs\templates' +winpath2 = '\\ServerX\admin$\system32\' +quoted = 'Tom "Dubs" Preston-Werner' +regex = '<\i\c*\s*>')code"; + std::string ssTOMLOutput = R"code([tree.branch] +winpath = 'C:\Users\nodejs\templates' +winpath2 = '\\ServerX\admin$\system32\' +quoted = 'Tom "Dubs" Preston-Werner' +regex = '<\i\c*\s*>')code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferMultiLineLiteralStrings) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(regex2 = '''I [dw]on't need \d{2} apples''' +lines = ''' +The first newline is +trimmed in raw strings. + All other whitespace + is preserved. +''')code"; + std::string ssTOMLOutput = R"code([tree.branch] +regex2 = '''I [dw]on't need \d{2} apples''' +lines = ''' +The first newline is +trimmed in raw strings. + All other whitespace + is preserved. +''')code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferQuotedLiteralStrings) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(quot15 = '''Here are fifteen quotation marks: """""""""""""""''' + +# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID +apos15 = "Here are fifteen apostrophes: '''''''''''''''" + +# 'That,' she said, 'is still pointless.' +str = ''''That,' she said, 'is still pointless.'''')code"; + std::string ssTOMLOutput = R"code([tree.branch] +quot15 = '''Here are fifteen quotation marks: """""""""""""""''' + +# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID +apos15 = "Here are fifteen apostrophes: '''''''''''''''" + +# 'That,' she said, 'is still pointless.' +str = ''''That,' she said, 'is still pointless.'''')code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferIntegers) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(int1 = +99 +int2 = 42 +int3 = 0 +int4 = -17)code"; + std::string ssTOMLOutput = R"code([tree.branch] +int1 = +99 +int2 = 42 +int3 = 0 +int4 = -17)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferReadibleIntegers) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(int5 = 1_000 +int6 = 5_349_221 +int7 = 53_49_221 # Indian number system grouping +int8 = 1_2_3_4_5 # VALID but discouraged)code"; + std::string ssTOMLOutput = R"code([tree.branch] +int5 = 1_000 +int6 = 5_349_221 +int7 = 53_49_221 # Indian number system grouping +int8 = 1_2_3_4_5 # VALID but discouraged)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferOtherBaseIntegers) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# hexadecimal with prefix `0x` +hex1 = 0xDEADBEEF +hex2 = 0xdeadbeef +hex3 = 0xdead_beef + +# octal with prefix `0o` +oct1 = 0o01234567 +oct2 = 0o755 # useful for Unix file permissions + +# binary with prefix `0b` +bin1 = 0b11010110)code"; + std::string ssTOMLOutput = R"code([tree.branch] +# hexadecimal with prefix `0x` +hex1 = 0xDEADBEEF +hex2 = 0xdeadbeef +hex3 = 0xdead_beef + +# octal with prefix `0o` +oct1 = 0o01234567 +oct2 = 0o755 # useful for Unix file permissions + +# binary with prefix `0b` +bin1 = 0b11010110)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferFloatingPoints) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# fractional +flt1 = +1.0 +flt2 = 3.1415 +flt3 = -0.01 + +# exponent +flt4 = 5e+22 +flt5 = 1e06 +flt6 = -2E-2 + +# both +flt7 = 6.626e-34)code"; + std::string ssTOMLOutput = R"code([tree.branch] +# fractional +flt1 = +1.0 +flt2 = 3.1415 +flt3 = -0.01 + +# exponent +flt4 = 5e+22 +flt5 = 1e06 +flt6 = -2E-2 + +# both +flt7 = 6.626e-34)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferReadibleFloatingPoints) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(flt8 = 224_617.445_991_228)code"; + std::string ssTOMLOutput = R"code([tree.branch] +flt8 = 224_617.445_991_228)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferSpecialFloatingPoints) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# infinity +sf1 = inf # positive infinity +sf2 = +inf # positive infinity +sf3 = -inf # negative infinity + +# not a number +sf4 = nan # actual sNaN/qNaN encoding is implementation-specific +sf5 = +nan # same as `nan` +sf6 = -nan # valid, actual encoding is implementation-specific)code"; + std::string ssTOMLOutput = R"code([tree.branch] +# infinity +sf1 = inf # positive infinity +sf2 = +inf # positive infinity +sf3 = -inf # negative infinity + +# not a number +sf4 = nan # actual sNaN/qNaN encoding is implementation-specific +sf5 = +nan # same as `nan` +sf6 = -nan # valid, actual encoding is implementation-specific)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferBooleans) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(bool1 = true +bool2 = false)code"; + std::string ssTOMLOutput = R"code([tree.branch] +bool1 = true +bool2 = false)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, DISABLED_TransferOffsetDateTimes) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(odt1 = 1979-05-27T07:32:00Z +odt2 = 1979-05-27T00:32:00-07:00 +odt3 = 1979-05-27T00:32:00.999999-07:00)code"; + std::string ssTOMLOutput = R"code([tree.branch] +odt1 = 1979-05-27T07:32:00Z +odt2 = 1979-05-27T00:32:00-07:00 +odt3 = 1979-05-27T00:32:00.999999-07:00)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, DISABLED_TransferReadibleOffsetDateTimes) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(odt4 = 1979-05-27 07:32:00Z)code"; + std::string ssTOMLOutput = R"code([tree.branch] +odt4 = 1979-05-27 07:32:00Z)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, DISABLED_TransferLocalDateTimes) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(ldt1 = 1979-05-27T07:32:00 +ldt2 = 1979-05-27T00:32:00.999999)code"; + std::string ssTOMLOutput = R"code([tree.branch] +ldt1 = 1979-05-27T07:32:00 +ldt2 = 1979-05-27T00:32:00.999999)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, DISABLED_TransferLocalDates) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(ld1 = 1979-05-27)code"; + std::string ssTOMLOutput = R"code([tree.branch] +ld1 = 1979-05-27)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, DISABLED_TransferLocalTimes) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(lt1 = 07:32:00 +lt2 = 00:32:00.999999)code"; + std::string ssTOMLOutput = R"code([tree.branch] +lt1 = 07:32:00 +lt2 = 00:32:00.999999)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferArrays) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(integers = [ 1, 2, 3 ] +colors = [ "red", "yellow", "green" ] +nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] +nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] +string_array = [ "all", 'strings', """are the same""", '''type''' ] + +# Mixed-type arrays are allowed +numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] +contributors = [ + "Foo Bar ", + { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } +])code"; + std::string ssTOMLOutput = R"code([tree.branch] +integers = [ 1, 2, 3 ] +colors = [ "red", "yellow", "green" ] +nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] +nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] +string_array = [ "all", 'strings', """are the same""", '''type''' ] + +# Mixed-type arrays are allowed +numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] +contributors = [ + "Foo Bar ", + { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } +])code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferMultiLineArrays) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(integers2 = [ + 1, 2, 3 +] + +integers3 = [ + 1, + 2, # this is ok +])code"; + std::string ssTOMLOutput = R"code([tree.branch] +integers2 = [ + 1, 2, 3 +] + +integers3 = [ + 1, + 2, # this is ok +])code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferTables) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code([table] + +[table-1] +key1 = "some string" +key2 = 123 + +[table-2] +key1 = "another string" +key2 = 456)code"; + std::string ssTOMLOutput = R"code([tree.branch.table] + +[tree.branch.table-1] +key1 = "some string" +key2 = 123 + +[tree.branch.table-2] +key1 = "another string" +key2 = 456)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferQuotedKeyTables) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code([dog."tater.man"] +type.name = "pug")code"; + std::string ssTOMLOutput = R"code([tree.branch.dog."tater.man"] +type.name = "pug")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferWhitespaceKeyTables) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = u8R"code([a.b.c] # this is best practice +x = 1 +[ d.e.f ] # same as [d.e.f] +y = 1 +[ g . h . i ] # same as [g.h.i] +z = 1 +[ j . "Ęž" . 'l' ] # same as [j."Ęž".'l'] +a = 1)code"; + std::string ssTOMLOutput = u8R"code([tree.branch.a.b.c] # this is best practice +x = 1 +[tree.branch. d.e.f ] # same as [d.e.f] +y = 1 +[tree.branch. g . h . i ] # same as [g.h.i] +z = 1 +[tree.branch. j . "Ęž" . 'l' ] # same as [j."Ęž".'l'] +a = 1)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferMixedOrderTables) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# VALID BUT DISCOURAGED +[fruit.apple] +a = 1 +[animal] +b = 2 +[fruit.orange] +aa = 11)code"; + std::string ssTOMLOutput = R"code(# VALID BUT DISCOURAGED +[tree.branch.fruit.apple] +a = 1 +[tree.branch.animal] +b = 2 +[tree.branch.fruit.orange] +aa = 11)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferMixedValueAndTables) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(# Top-level table begins. +name = "Fido" +breed = "pug" + +# Top-level table ends. +[owner] +name = "Regina Dogman" +member_since = 1999)code"; + std::string ssTOMLOutput = R"code([tree.branch] +# Top-level table begins. +name = "Fido" +breed = "pug" + +# Top-level table ends. +[tree.branch.owner] +name = "Regina Dogman" +member_since = 1999)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferAutomaticTables) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(fruit.apple.color = "red" +fruit.apple.taste.sweet = true)code"; + std::string ssTOMLOutput = R"code([tree.branch] +fruit.apple.color = "red" +fruit.apple.taste.sweet = true)code"; + std::string ssTOMLOutput2 = R"code([tree.branch] +apple.color = "red" +apple.taste.sweet = true)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + EXPECT_EQ(ssGenerated, ssTOMLOutput); + + EXPECT_NO_THROW(ssGenerated = parser.Root().Direct("fruit")->GenerateTOML(toml_parser::CGenContext("tree.branch"))); + EXPECT_EQ(ssGenerated, ssTOMLOutput2); +} + +TEST(GenerateTOML, TransferMixedAutomaticTables) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code([fruit] +apple.color = "red" +apple.taste.sweet = true + +# [fruit.apple] # INVALID +# [fruit.apple.taste] # INVALID + +[fruit.apple.texture] # you can add sub-tables +smooth = true)code"; + std::string ssTOMLOutput = R"code([tree.branch.fruit] +apple.color = "red" +apple.taste.sweet = true + +# [fruit.apple] # INVALID +# [fruit.apple.taste] # INVALID + +[tree.branch.fruit.apple.texture] # you can add sub-tables +smooth = true)code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferInlineTables) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(name = { first = "Tom", last = "Preston-Werner" } +point = { x = 1, y = 2 } +animal = { type.name = "pug" })code"; + std::string ssTOMLOutput = R"code([tree.branch] +name = { first = "Tom", last = "Preston-Werner" } +point = { x = 1, y = 2 } +animal = { type.name = "pug" })code"; + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferEmbeddedInlineTables) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(test=[{ first = "Tom", last = "Preston-Werner" }, +{ x = 1, y = 2 }, +{ type.name = "pug" }])code"; + std::string ssTOMLOutput = R"code([tree.branch] +test=[{ first = "Tom", last = "Preston-Werner" }, +{ x = 1, y = 2 }, +{ type.name = "pug" }])code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferTableArrays) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code([[products]] +name = "Hammer" +sku = 738594937 + +[[products]] # empty table within the array + +[[products]] +name = "Nail" +sku = 284758393 + +color = "gray")code"; + std::string ssTOMLOutput = R"code([[tree.branch.products]] +name = "Hammer" +sku = 738594937 + +[[tree.branch.products]] # empty table within the array + +[[tree.branch.products]] +name = "Nail" +sku = 284758393 + +color = "gray")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferMixedTableAndTableArrays) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code([[fruits]] +name = "apple" + +[fruits.physical] # subtable +color = "red" +shape = "round" + +[[fruits.varieties]] # nested array of tables +name = "red delicious" + +[[fruits.varieties]] +name = "granny smith" + + +[[fruits]] +name = "banana" + +[[fruits.varieties]] +name = "plantain")code"; + std::string ssTOMLOutput = R"code([[tree.branch.fruits]] +name = "apple" + +[tree.branch.fruits.physical] # subtable +color = "red" +shape = "round" + +[[tree.branch.fruits.varieties]] # nested array of tables +name = "red delicious" + +[[tree.branch.fruits.varieties]] +name = "granny smith" + + +[[tree.branch.fruits]] +name = "banana" + +[[tree.branch.fruits.varieties]] +name = "plantain")code"; + + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} + +TEST(GenerateTOML, TransferInlineTableArrays) +{ + toml_parser::CParser parser; + + std::string ssTOMLInput = R"code(points = [ { x = 1, y = 2, z = 3 }, + { x = 7, y = 8, z = 9 }, + { x = 2, y = 4, z = 8 } ])code"; + std::string ssTOMLOutput = R"code([tree.branch] +points = [ { x = 1, y = 2, z = 3 }, + { x = 7, y = 8, z = 9 }, + { x = 2, y = 4, z = 8 } ])code"; + EXPECT_NO_THROW(parser.Process(ssTOMLInput)); + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML("tree.branch")); + + EXPECT_EQ(ssGenerated, ssTOMLOutput); +} diff --git a/tests/unit_tests/toml_parser/lexer_tests.cpp b/tests/unit_tests/toml_parser/lexer_tests.cpp index f10fb13..1d5c85d 100644 --- a/tests/unit_tests/toml_parser/lexer_tests.cpp +++ b/tests/unit_tests/toml_parser/lexer_tests.cpp @@ -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 #include #include diff --git a/tests/unit_tests/toml_parser/main.cpp b/tests/unit_tests/toml_parser/main.cpp index 5a353a9..91e99c3 100644 --- a/tests/unit_tests/toml_parser/main.cpp +++ b/tests/unit_tests/toml_parser/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" @@ -6,6 +20,7 @@ #include "../../../sdv_services/core/toml_parser/parser_toml.cpp" #include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp" #include "../../../sdv_services/core/toml_parser/miscellaneous.cpp" +#include "../../../sdv_services/core/toml_parser/code_snippet.cpp" #if defined(_WIN32) && defined(_UNICODE) extern "C" int wmain(int argc, wchar_t* argv[]) diff --git a/tests/unit_tests/toml_parser/miscellaneous_tests.cpp b/tests/unit_tests/toml_parser/miscellaneous_tests.cpp index 8b1fb86..e1d85c8 100644 --- a/tests/unit_tests/toml_parser/miscellaneous_tests.cpp +++ b/tests/unit_tests/toml_parser/miscellaneous_tests.cpp @@ -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 #include #include diff --git a/tests/unit_tests/toml_parser/parser_tests.cpp b/tests/unit_tests/toml_parser/parser_tests.cpp index 818c3a8..3e20625 100644 --- a/tests/unit_tests/toml_parser/parser_tests.cpp +++ b/tests/unit_tests/toml_parser/parser_tests.cpp @@ -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 #include "../../../sdv_services/core/toml_parser/parser_toml.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" @@ -59,7 +73,7 @@ TEST(RecognizeTypes, Key_Value) auto value_name = parser.Root().Direct("name"); EXPECT_EQ(value_name->GetType(), sdv::toml::ENodeType::node_string); - EXPECT_EQ(value_name->GetValue(), sdv::any_t("Hammer")); + EXPECT_EQ(value_name->GetValue(), "Hammer"); EXPECT_EQ(value_name->GetIndex(), 0u); auto value_id = parser.Root().Direct("id"); @@ -427,6 +441,37 @@ TEST(NestedContent, InlineTable) EXPECT_EQ(static_cast(table3_e_b->GetValue()), "E"); } +TEST(NestedContent, InlineTableBreakLine) +{ + // The following are not allowed + EXPECT_THROW(toml_parser::CParser(R"code( +table = { a = 1, b = 2, + c = 3, d = 4 } +)code"), sdv::toml::XTOMLParseException); + EXPECT_THROW(toml_parser::CParser(R"code( +table = { a = 1, b = 2 + ,c = 3, d = 4 } +)code"), sdv::toml::XTOMLParseException); + + // Line breaks are allowed when part of an array or have multi-line strings + EXPECT_NO_THROW(toml_parser::CParser(R"code( +array = [{ a = 1, b = 2}, + {c = 3, d = 4}] +)code")); + EXPECT_NO_THROW(toml_parser::CParser(R"code( +table = { a = 1, b = [2, 3, + 4, 5], c = 6, d = 7} +)code")); + EXPECT_NO_THROW(toml_parser::CParser(R"code( +table = { x = "abc", y = """def- +ghi""", z = "jkl" } +)code")); + EXPECT_NO_THROW(toml_parser::CParser(R"code( +table = { x = 'abc', y = '''def- +ghi''', z = 'jkl' } +)code")); +} + TEST(SpecialCases, Keys) { using namespace std::string_literals; diff --git a/tests/unit_tests/toml_parser/statement_boundary_detection.cpp b/tests/unit_tests/toml_parser/statement_boundary_detection.cpp index 4f26910..873a30e 100644 --- a/tests/unit_tests/toml_parser/statement_boundary_detection.cpp +++ b/tests/unit_tests/toml_parser/statement_boundary_detection.cpp @@ -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 #include #include @@ -9,7 +22,7 @@ std::string DumpTokenList(toml_parser::CLexer& rLexer) { auto eNavMode = rLexer.NavigationMode(); rLexer.NavigationMode(toml_parser::CLexer::ENavigationMode::do_not_skip_anything); - rLexer.Reset(); + rLexer.ResetCursor(); std::stringstream sstream; while (!rLexer.IsEnd()) { @@ -51,7 +64,7 @@ std::string DumpTokenList(toml_parser::CLexer& rLexer) } } - rLexer.Reset(); + rLexer.ResetCursor(); rLexer.NavigationMode(eNavMode); return sstream.str(); } @@ -365,7 +378,6 @@ std::string CategoryText(toml_parser::ETokenCategory eCategory) } } - /** * @brief Find routine, extend the line and check for smart boundaries * @param[in] rlexer Reference to the lexer to search for the tokens. The position will be reset and consumed until the @@ -378,7 +390,7 @@ bool FindAndExtendToken(toml_parser::CLexer& rlexer, const std::string& rssKey, const std::vector& rvecCategories) { // Find the key - rlexer.Reset(); + rlexer.ResetCursor(); while (!rlexer.IsEnd()) { const toml_parser::CToken& rToken = rlexer.Consume(); @@ -386,31 +398,47 @@ bool FindAndExtendToken(toml_parser::CLexer& rlexer, const std::string& rssKey, { // Determine the range boundaries and create the range auto prBoundaries = StatementBoundaries(rToken); - EXPECT_TRUE(prBoundaries.first); - EXPECT_TRUE(prBoundaries.second); if (!prBoundaries.first || !prBoundaries.second) + { + std::cout << "Invalid statent boundaries..." << std::endl; return false; + } toml_parser::CNodeTokenRange range(toml_parser::CTokenRange(prBoundaries.first, prBoundaries.second.Next())); // Auto extend the boundaries rlexer.SmartExtendNodeRange(range); - EXPECT_TRUE(range.ExtendedNode().Begin()); + if (!range.ExtendedNode().Begin()) + { + std::cout << "Invalid extended node..." << std::endl; + return false; + } std::reference_wrapper refToken(range.ExtendedNode().Begin()); size_t nIndex = 0; auto itCategory = rvecCategories.begin(); do { if (nIndex == rvecCategories.size()) - std::cout << "Find " << rssKey << " Index #" << nIndex << ": Received " << CategoryText(refToken.get().Category()) << std::endl; - EXPECT_LT(nIndex, rvecCategories.size()); + { + std::cout << "Find " << rssKey << " Index #" << nIndex << ": Received " + << CategoryText(refToken.get().Category()) << std::endl; + return false; + } if (itCategory == rvecCategories.end()) return false; if (*itCategory != refToken.get().Category()) - std::cout << "Find " << rssKey << " Index #" << nIndex << ": Expected " << CategoryText(*itCategory) << - ", received " << CategoryText(refToken.get().Category()) << std::endl; - EXPECT_EQ(*itCategory, refToken.get().Category()); + { + std::cout << "Find " << rssKey << " Index #" << nIndex << ": Expected " << CategoryText(*itCategory) + << ", received " << CategoryText(refToken.get().Category()) << std::endl; + return false; + } nIndex++; itCategory++; } while ((refToken = refToken.get().Next()).get() && refToken.get() != range.ExtendedNode().End()); + if (itCategory != rvecCategories.end()) + { + std::cout << "More items in the category vector than in the extended range: itCategory: " + << CategoryText(*itCategory) << std::endl; + return false; + } return true; // successful } } @@ -466,7 +494,6 @@ token_a = 10 toml_parser::ETokenCategory::token_syntax_assignment, toml_parser::ETokenCategory::token_whitespace, toml_parser::ETokenCategory::token_integer, - toml_parser::ETokenCategory::token_syntax_new_line, toml_parser::ETokenCategory::token_syntax_new_line})); } @@ -511,7 +538,6 @@ token_d = "def" toml_parser::ETokenCategory::token_whitespace, toml_parser::ETokenCategory::token_float, toml_parser::ETokenCategory::token_syntax_new_line, - toml_parser::ETokenCategory::token_syntax_new_line, toml_parser::ETokenCategory::token_syntax_new_line})); } @@ -610,6 +636,71 @@ token_f = "ghi" # after toml_parser::ETokenCategory::token_syntax_new_line})); } +TEST(TOMLLexerStatementBoundaryTests, AssignmentWithFollowingComment2) +{ + std::string ssTOML = R"code( + + +# This is a separate comment with several line-breaks before. +# Followed by this text on the same line. + +# Note: there was a space after the empty comment line. +# And another separate comment on a next line. + +# This is a comment before the value. +# And another comment before the value at the same line. +# +# Note: there was a space after the empty comment line. +# But that should not influence the comment lines. +value = "this is the value text" # Comment following the value. + # More comment following the value. + # This becomes one line. + # + # But this is a separate line. + +# This is also a separate comment. +# Followed by this text on the same line. + +# And another text on a separate line.)code"; + + // Process the code + toml_parser::CLexer lexerCode(ssTOML); + + // Assignment. Extended boundary includes following comments, whitespace and newlines. + EXPECT_TRUE(FindAndExtendToken(lexerCode, "value", + {toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_key, + toml_parser::ETokenCategory::token_whitespace, + toml_parser::ETokenCategory::token_syntax_assignment, + toml_parser::ETokenCategory::token_whitespace, + toml_parser::ETokenCategory::token_string, + toml_parser::ETokenCategory::token_whitespace, + toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_whitespace, + toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_whitespace, + toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_whitespace, + toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_whitespace, + toml_parser::ETokenCategory::token_comment, + toml_parser::ETokenCategory::token_syntax_new_line, + toml_parser::ETokenCategory::token_syntax_new_line})); +} + TEST(TOMLLexerStatementBoundaryTests, TableAssignmentWithDedicatedComment) { std::string ssCode = R"code( @@ -962,7 +1053,7 @@ TEST(TOMLLexerStatementBoundaryTests, IndentedTableWithoutComments) toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, IndenteTableWithAndWithoutComments) +TEST(TOMLLexerStatementBoundaryTests, IndentedTableWithAndWithoutComments) { std::string ssCode = R"code( [token_s] @@ -985,6 +1076,7 @@ token_u.token_aa.token_bb = 10 # table token_u has a table token_aa which ha toml_parser::ETokenCategory::token_syntax_table_open, toml_parser::ETokenCategory::token_key, toml_parser::ETokenCategory::token_syntax_table_close, + toml_parser::ETokenCategory::token_syntax_new_line, toml_parser::ETokenCategory::token_syntax_new_line})); } @@ -1095,8 +1187,7 @@ token_u.token_aa.token_cc = { dd = { ee = 10, ff = 11 }, # this is the commen toml_parser::ETokenCategory::token_syntax_assignment, toml_parser::ETokenCategory::token_whitespace, toml_parser::ETokenCategory::token_integer, - toml_parser::ETokenCategory::token_syntax_comma, - toml_parser::ETokenCategory::token_whitespace})); + toml_parser::ETokenCategory::token_syntax_comma})); EXPECT_TRUE(FindAndExtendToken(lexerCode, "ii", {toml_parser::ETokenCategory::token_whitespace, toml_parser::ETokenCategory::token_key, diff --git a/tests/unit_tests/trace_fifo/CMakeLists.txt b/tests/unit_tests/trace_fifo/CMakeLists.txt index 844eb09..e828d28 100644 --- a/tests/unit_tests/trace_fifo/CMakeLists.txt +++ b/tests/unit_tests/trace_fifo/CMakeLists.txt @@ -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(UnitTest_TraceFifo VERSION 1.0 LANGUAGES CXX) @@ -29,4 +42,4 @@ add_custom_command(TARGET UnitTest_TraceFifo POST_BUILD ) # Build dependencies -add_dependencies(UnitTest_TraceFifo core_services) +add_dependencies(UnitTest_TraceFifo dependency_sdv_components) diff --git a/tests/unit_tests/trace_fifo/fifo_test.cpp b/tests/unit_tests/trace_fifo/fifo_test.cpp index 241e3ed..034b1c3 100644 --- a/tests/unit_tests/trace_fifo/fifo_test.cpp +++ b/tests/unit_tests/trace_fifo/fifo_test.cpp @@ -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 #include "../../../global/localmemmgr.h" #include "../../../global/tracefifo/trace_fifo.h" diff --git a/tests/unit_tests/trace_fifo/main.cpp b/tests/unit_tests/trace_fifo/main.cpp index 27e4589..0677c6e 100644 --- a/tests/unit_tests/trace_fifo/main.cpp +++ b/tests/unit_tests/trace_fifo/main.cpp @@ -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 #include "../../../global/process_watchdog.h" #include "../../../global/localmemmgr.h" diff --git a/tests/unit_tests/unix_sockets/CMakeLists.txt b/tests/unit_tests/unix_sockets/CMakeLists.txt new file mode 100644 index 0000000..b022ab8 --- /dev/null +++ b/tests/unit_tests/unix_sockets/CMakeLists.txt @@ -0,0 +1,49 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + +cmake_minimum_required(VERSION 3.20) + +project(UnixSocketCommunicationTests LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Executable: GTest for UDS connect +add_executable(UnitTest_UnixSocketConnectTests + uds_connect_tests.cpp +) + +target_include_directories(UnitTest_UnixSocketConnectTests PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(UnitTest_UnixSocketConnectTests + GTest::GTest ${CMAKE_THREAD_LIBS_INIT} stdc++fs ${CMAKE_DL_LIBS} rt + ) +else() + target_link_libraries(UnitTest_UnixSocketConnectTests + GTest::GTest Ws2_32 Winmm Rpcrt4.lib + ) +endif() + +add_test(NAME UnitTest_UnixSocketConnectTests + COMMAND UnitTest_UnixSocketConnectTests) + +add_dependencies(UnitTest_UnixSocketConnectTests dependency_sdv_components) + +if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) +add_custom_command(TARGET UnitTest_UnixSocketConnectTests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake + "$" + --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixSocketConnectTests.xml + VERBATIM +) +endif() \ No newline at end of file diff --git a/tests/unit_tests/unix_sockets/uds_connect_tests.cpp b/tests/unit_tests/unix_sockets/uds_connect_tests.cpp new file mode 100644 index 0000000..5738d94 --- /dev/null +++ b/tests/unit_tests/unix_sockets/uds_connect_tests.cpp @@ -0,0 +1,1373 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + +#if defined __unix__ + +#include "gtest/gtest.h" + +#include +#include + +#include "../../../sdv_services/uds_unix_sockets/channel_mgnt.cpp" +#include "../../../sdv_services/uds_unix_sockets/connection.cpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +class CUDSConnectReceiver : + public sdv::IInterfaceAccess, + public sdv::ipc::IDataReceiveCallback, + public sdv::ipc::IConnectEventCallback +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + END_SDV_INTERFACE_MAP() + + // don't test data path yet + void ReceiveData(sdv::sequence>& /*seqData*/) override {} + + void SetStatus(sdv::ipc::EConnectStatus s) override { + { + std::lock_guard lk(m_mtx); + m_status = s; + } + m_cv.notify_all(); + } + + bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + + if (m_status == expected) + return true; + + auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); + + while (m_status != expected) + { + if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout) + return false; + } + return true; + } + + sdv::ipc::EConnectStatus GetStatus() const { + std::lock_guard lk(m_mtx); + return m_status; + } + +private: + sdv::ipc::EConnectStatus m_status { sdv::ipc::EConnectStatus::uninitialized }; + mutable std::mutex m_mtx; + std::condition_variable m_cv; +}; + +// A data-aware receiver that captures received data chunks and exposes synchronization helpers. +class CUDSDataReceiver : + public sdv::IInterfaceAccess, + public sdv::ipc::IDataReceiveCallback, + public sdv::ipc::IConnectEventCallback +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + END_SDV_INTERFACE_MAP() + + void ReceiveData(sdv::sequence>& seqData) override { + { + std::lock_guard lk(m_mtx); + m_lastData = seqData; // copy/move depending on sequence semantics + m_received = true; + } + m_cv.notify_all(); + } + + void SetStatus(sdv::ipc::EConnectStatus s) override { + { + std::lock_guard lk(m_mtx); + m_status = s; + } + m_cv.notify_all(); + } + + bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + + if (m_status == expected) + return true; + + auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); + + while (m_status != expected) + { + if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout) + return false; + } + + return true; + } + + bool WaitForData(uint32_t ms = 2000) { + std::unique_lock lk(m_mtx); + return m_cv.wait_for(lk, std::chrono::milliseconds(ms), + [&]{ return m_received; }); + } + + sdv::sequence> GetLastData() const { + std::lock_guard lk(m_mtx); + return m_lastData; + } + +private: + mutable std::mutex m_mtx; + std::condition_variable m_cv; + sdv::ipc::EConnectStatus m_status{ sdv::ipc::EConnectStatus::uninitialized }; + sdv::sequence> m_lastData; + bool m_received{ false }; +}; + + +// A receiver that intentionally throws from SetStatus(...) to test callback-safety. +class CUDSThrowingReceiver : + public sdv::IInterfaceAccess, + public sdv::ipc::IDataReceiveCallback, + public sdv::ipc::IConnectEventCallback +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + END_SDV_INTERFACE_MAP() + + void ReceiveData(sdv::sequence>& /*seq*/) override {} + + void SetStatus(sdv::ipc::EConnectStatus s) override + { + // Store the last status and then throw to simulate misbehaving user code. + { + std::lock_guard lk(m_mtx); + m_last = s; + } + m_cv.notify_all(); + throw std::runtime_error("Intentional user callback failure"); + } + + bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + + if (m_last == expected) + return true; + + auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); + + while (m_last != expected) + { + if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout) + return false; + } + + return true; + } + +private: + std::mutex m_mtx; + std::condition_variable m_cv; + sdv::ipc::EConnectStatus m_last{ sdv::ipc::EConnectStatus::uninitialized }; +}; + + +// Small helper: convert server connectString to client connectString +static std::string MakeClientCS(std::string cs) +{ + const std::string from = "role=server"; + const std::string to = "role=client"; + auto pos = cs.find(from); + if (pos != std::string::npos) + cs.replace(pos, from.size(), to); + return cs; +} + + +static std::string ExtractPathFromCS(const std::string& cs) +{ + // search "path=" in connect-string + const std::string key = "path="; + auto p = cs.find(key); + if (p == std::string::npos) return {}; + auto start = p + key.size(); + auto end = cs.find(';', start); + if (end == std::string::npos) end = cs.size(); + return cs.substr(start, end - start); +} + +static std::string MakeRandomSuffix() +{ + std::mt19937_64 rng{std::random_device{}()}; + std::uniform_int_distribution dist; + std::ostringstream oss; + oss << std::hex << dist(rng); + return oss.str(); +} + + +TEST(UnixSocketIPC, Instantiate) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup("")); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); + + EXPECT_NO_THROW(mgr.Shutdown()); + + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); + + appcontrol.Shutdown(); +} + +TEST(UnixSocketIPC, ChannelConfigString) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup("")); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); + + EXPECT_NO_THROW(mgr.Shutdown()); + + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); +} + +TEST(UnixSocketIPC, CreateRandomEndpoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup("")); + + CUnixDomainSocketsChannelMgnt mgr; + + // Create an endpoint. + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); + sdv::ipc::SChannelEndpoint sChannelEndpoint = mgr.CreateEndpoint(""); + EXPECT_NE(sChannelEndpoint.pConnection, nullptr); + EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); + if (sChannelEndpoint.pConnection) sdv::TObjectPtr(sChannelEndpoint.pConnection); + EXPECT_NO_THROW(mgr.Shutdown()); + + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); +} + +// BASIC TEST: CreateEndpoint -> Access(server/client) -> AsyncConnect -> Wait -> Disconnect +TEST(UnixSocketIPC, BasicConnectDisconnect) +{ + // Start SDV framework + + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + // Create and initialize UDS manager + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // Create a Unix socket endpoint + auto ep = mgr.CreateEndpoint(""); + ASSERT_FALSE(ep.ssConnectString.empty()); + + std::string serverCS = ep.ssConnectString; + std::string clientCS = MakeClientCS(serverCS); + + // SERVER SIDE + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + + sdv::ipc::IConnect* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + // CLIENT SIDE (thread) + std::atomic clientResult{0}; + std::atomic allowClientDisconnect{false}; + std::thread clientThread([&]{ + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + if (!clientObj) { clientResult = 1; return; } + auto* clientConn = clientObj.GetInterface(); + if (!clientConn) { clientResult = 2; return; } + + CUDSConnectReceiver cRcvr; + if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; } + + if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; } + + if (clientConn->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult = 5; return; } + + // Wait for the server to be conected before disconecting the client + while (!allowClientDisconnect.load()) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + clientConn->Disconnect(); + clientResult = 0; + }); + + // SERVER must also report connected + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + + // Allow client to dissconect now, because the Server is connected + allowClientDisconnect = true; + clientThread.join(); + EXPECT_EQ(clientResult.load(), 0); + + //DISCONNECT both + serverConn->Disconnect(); + EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + + // Shutdown Manager / Framework + EXPECT_NO_THROW(mgr.Shutdown()); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); + + std::cout << "Shutdown Manager ok " << std::endl; + app.Shutdown(); + +} + +TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) +{ + // Start SDV + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + //UDS manager + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + //Endpoint -> same path for both sessions + auto ep = mgr.CreateEndpoint(""); + ASSERT_FALSE(ep.ssConnectString.empty()); + + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + // SESSION 1 + // SERVER + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + sdv::ipc::IConnect* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + // CLIENT (thread) + std::atomic clientResult{0}; + std::atomic allowClientDisconnect{false}; + + std::thread clientThread([&]{ + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + if (!clientObj) { clientResult = 1; return; } + auto* clientConn = clientObj.GetInterface(); + if (!clientConn) { clientResult = 2; return; } + + CUDSConnectReceiver cRcvr; + if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; } + + if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; } + if (clientConn->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult = 5; return; } + + //waits for confirmation before disconect + while (!allowClientDisconnect.load()) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + clientConn->Disconnect(); + clientResult = 0; + }); + + // Server has to be connected (same timeout with client?) + EXPECT_TRUE(serverConn->WaitForConnection(5000)) << "Server didn't reach 'connected' in time"; + EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + + // Allows Client to disconnect and waits for finishing + allowClientDisconnect = true; + clientThread.join(); + EXPECT_EQ(clientResult.load(), 0); + + // Disconnect server + serverConn->Disconnect(); + EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + + // SESSION 2 + // SERVER + sdv::TObjectPtr serverObj2 = mgr.Access(serverCS); + ASSERT_TRUE(serverObj2); + sdv::ipc::IConnect* serverConn2 = serverObj2.GetInterface(); + ASSERT_NE(serverConn2, nullptr); + + CUDSConnectReceiver sRcvr2; + ASSERT_TRUE(serverConn2->AsyncConnect(&sRcvr2)); + + // CLIENT (thread) + std::atomic clientResult2{0}; + std::atomic allowClientDisconnect2{false}; + + std::thread clientThread2([&]{ + sdv::TObjectPtr clientObj2 = mgr.Access(clientCS); + if (!clientObj2) { clientResult2 = 1; return; } + auto* clientConn2 = clientObj2.GetInterface(); + if (!clientConn2) { clientResult2 = 2; return; } + + CUDSConnectReceiver cRcvr2; + if (!clientConn2->AsyncConnect(&cRcvr2)) { clientResult2 = 3; return; } + + if (!clientConn2->WaitForConnection(5000)) { clientResult2 = 4; return; } + if (clientConn2->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult2 = 5; return; } + + //waits for confirmation before disconect + while (!allowClientDisconnect2.load()) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + clientConn2->Disconnect(); + clientResult2 = 0; + }); + + // Server has to be connected + // if unlink(path) from session 1 worked, bind/listen/accept works again + EXPECT_TRUE(serverConn2->WaitForConnection(5000)) << "Server didn't reach 'connected' in time (session 2)"; + EXPECT_EQ(serverConn2->GetStatus(), sdv::ipc::EConnectStatus::connected); + + // Allows Client to disconnect and waits for finishing + allowClientDisconnect2 = true; + clientThread2.join(); + EXPECT_EQ(clientResult2.load(), 0); + + // Disconnect server + serverConn2->Disconnect(); + EXPECT_EQ(serverConn2->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + + //Shutdown manager/framework + EXPECT_NO_THROW(mgr.Shutdown()); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); + app.Shutdown(); +} + +//Manager: Initialize -> configuring -> running -> Shutdown +TEST(UnixSocketIPC, OperationModeTransitions) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); + + // configuring and then running + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::configuring)); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::configuring); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // Shutdown + EXPECT_NO_THROW(mgr.Shutdown()); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); + app.Shutdown(); +} + + +// Endpoint from config TOML + long path(clamping) + success connecting +TEST(UnixSocketIPC, CreateEndpoint_WithConfigAndPathClamping) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // long path, deliberat > 108 (sun_path) — it will be cut in CreateEndpoint + std::string longName(160, 'A'); + std::string longPath = std::string("/tmp/sdv/") + longName + "_" + MakeRandomSuffix() + ".sock"; + + std::ostringstream cfg; + cfg << "[IpcChannel]\n"; + cfg << "Name = \"ignored_" << MakeRandomSuffix() << "\"\n"; + cfg << "Path = \"" << longPath << "\"\n"; + + auto ep = mgr.CreateEndpoint(cfg.str()); + ASSERT_FALSE(ep.ssConnectString.empty()); + + // Checking if endpoint is server type and has an ok path + std::string serverCS = ep.ssConnectString; + std::string clientCS = MakeClientCS(serverCS); + auto clampedPath = ExtractPathFromCS(serverCS); + ASSERT_FALSE(clampedPath.empty()); + EXPECT_LT(clampedPath.size(), sizeof(sockaddr_un::sun_path)); // doar verificare generica + + // Connecting + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* clientConn = clientObj.GetInterface(); + ASSERT_NE(clientConn, nullptr); + CUDSConnectReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_TRUE(clientConn->WaitForConnection(5000)); + EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + + // Cleanup + clientConn->Disconnect(); + serverConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//Access() with default paths on both ends +TEST(UnixSocketIPC, Access_DefaultPath_ServerClientConnect) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // Without "path=", both sides use the default MakeUserRuntimeDir()+"/UDS_auto.sock" + const std::string serverCS = "proto=uds;role=server;"; + const std::string clientCS = "proto=uds;role=client;"; + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* clientConn = clientObj.GetInterface(); + ASSERT_NE(clientConn, nullptr); + CUDSConnectReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_TRUE(clientConn->WaitForConnection(5000)); + EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + + clientConn->Disconnect(); + serverConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//WaitForConnection(INFINITE): client delays by 1s, server waits indefinitely +TEST(UnixSocketIPC, WaitForConnection_InfiniteWait_SlowClient) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + ASSERT_FALSE(ep.ssConnectString.empty()); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + std::thread delayedClient([&]{ + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + auto* clientConn = clientObj.GetInterface(); + CUDSConnectReceiver cRcvr; + clientConn->AsyncConnect(&cRcvr); + clientConn->WaitForConnection(5000); + clientConn->Disconnect(); + }); + + // INFINITE wait (0xFFFFFFFFu) + EXPECT_TRUE(serverConn->WaitForConnection(0xFFFFFFFFu)); + EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + + // Cleanup + delayedClient.join(); + serverConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//WaitForConnection(0): immediate check, before and after connection +TEST(UnixSocketIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + ASSERT_FALSE(ep.ssConnectString.empty()); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + auto* serverConn = serverObj.GetInterface(); + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + // before client: immediate check must be false + EXPECT_FALSE(serverConn->WaitForConnection(0)); + + // start client + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + auto* clientConn = clientObj.GetInterface(); + CUDSConnectReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + // afterward the immediate check may still fail (race). Use normal wait for determinism. + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_TRUE(clientConn->WaitForConnection(5000)); + + clientConn->Disconnect(); + serverConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//Only the client starts -> timeout & connection_error +TEST(UnixSocketIPC, ClientTimeout_NoServer) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // Unique path ensuring nothing is listening + const std::string path = std::string("/tmp/sdv/timeout_") + MakeRandomSuffix() + ".sock"; + const std::string clientCS = std::string("proto=uds;role=client;path=") + path + ";"; + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* clientConn = clientObj.GetInterface(); + ASSERT_NE(clientConn, nullptr); + CUDSConnectReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + // client loop is ~2s; wait less and check it's still not connected + EXPECT_FALSE(clientConn->WaitForConnection(1500)); + // after another ~1s it should be in connection_error + std::this_thread::sleep_for(std::chrono::milliseconds(800)); + EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connection_error); + + clientConn->Disconnect(); // cleanup (joins threads) + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//Server disconnecting -> client transitions to 'disconnected' +TEST(UnixSocketIPC, ServerDisconnectPropagatesToClient) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + auto* serverConn = serverObj.GetInterface(); + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + auto* clientConn = clientObj.GetInterface(); + CUDSConnectReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + ASSERT_TRUE(serverConn->WaitForConnection(5000)); + ASSERT_TRUE(clientConn->WaitForConnection(5000)); + + // Break the server side and give the client receiver thread time to see EOF + serverConn->Disconnect(); + + // Deterministic wait for client-side transition to 'disconnected' + EXPECT_TRUE(cRcvr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, /*ms*/ 3000)) << "Client did not observe 'disconnected' after server closed the socket."; + + EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + + clientConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//Reconnecting on the same server object +TEST(UnixSocketIPC, ReconnectOnSameServerInstance) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + auto* serverConn = serverObj.GetInterface(); + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + // First session + { + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + auto* clientConn = clientObj.GetInterface(); + CUDSConnectReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + ASSERT_TRUE(serverConn->WaitForConnection(5000)); + ASSERT_TRUE(clientConn->WaitForConnection(5000)); + clientConn->Disconnect(); + serverConn->Disconnect(); + EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + } + + // Second session on the same serverConn object + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + { + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + auto* clientConn = clientObj.GetInterface(); + CUDSConnectReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + ASSERT_TRUE(serverConn->WaitForConnection(5000)); + ASSERT_TRUE(clientConn->WaitForConnection(5000)); + clientConn->Disconnect(); + } + serverConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//Simple payload "hello" test +TEST(UnixSocketIPC, DataPath_SimpleHello) +{ + // Framework + Manager + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // Endpoint + auto ep = mgr.CreateEndpoint(""); + ASSERT_FALSE(ep.ssConnectString.empty()); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + // Server + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + CUDSDataReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + // Client + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* clientConn = clientObj.GetInterface(); + + //sdv::ipc::IDataSend* clientConn = clientObj.GetInterface(); + ASSERT_NE(clientConn, nullptr); + CUDSDataReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + // Wait for both to be connected + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_TRUE(clientConn->WaitForConnection(5000)); + + // Build "hello" payload + sdv::pointer p; + p.resize(5); + std::memcpy(reinterpret_cast(p.get()), "hello", 5); + sdv::sequence> seq; + seq.push_back(p); + + // Send from client -> receive on server + auto* pSend = dynamic_cast(clientConn); + ASSERT_NE(pSend, nullptr); + EXPECT_TRUE(pSend->SendData(seq)); + + // Wait deterministically for server-side data callback + EXPECT_TRUE(sRcvr.WaitForData(3000)); + + auto recv = sRcvr.GetLastData(); + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), 5u); + EXPECT_EQ(std::memcmp(recv[0].get(), "hello", 5), 0); + + // Cleanup + clientConn->Disconnect(); + serverConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//Multi‑chunk payload (2 chunks) +TEST(UnixSocketIPC, DataPath_MultiChunk_TwoBuffers) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + ASSERT_FALSE(ep.ssConnectString.empty()); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + auto* serverConn = serverObj.GetInterface(); + CUDSDataReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + auto* clientConn = clientObj.GetInterface(); + CUDSDataReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_TRUE(clientConn->WaitForConnection(5000)); + + // Two buffers: "sdv" and "framewrok" + sdv::pointer p1, p2; + p1.resize(3); + std::memcpy(reinterpret_cast(p1.get()), "sdv", 3); + p2.resize(9); + std::memcpy(reinterpret_cast(p2.get()), "framework", 9); + + sdv::sequence> seq; + seq.push_back(p1); + seq.push_back(p2); + + // Send from client -> receive on server + auto* pSend = dynamic_cast(clientConn); + ASSERT_NE(pSend, nullptr); + EXPECT_TRUE(pSend->SendData(seq)); + + EXPECT_TRUE(sRcvr.WaitForData(3000)); + + auto recv = sRcvr.GetLastData(); + ASSERT_EQ(recv.size(), 2u); + EXPECT_EQ(recv[0].size(), 3u); + EXPECT_EQ(recv[1].size(), 9u); + EXPECT_EQ(std::memcmp(recv[0].get(), "sdv", 3), 0); + EXPECT_EQ(std::memcmp(recv[1].get(), "framework", 9), 0); + + clientConn->Disconnect(); + serverConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//Large payload -> fragmented receive (end‑to‑end reassembly) +TEST(UnixSocketIPC, DataPath_LargePayload_Fragmentation_Reassembly) +{ + // Framework + Manager + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // Endpoint + auto ep = mgr.CreateEndpoint(""); + ASSERT_FALSE(ep.ssConnectString.empty()); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + // Server + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + CUDSDataReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + // Client + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* clientConn = clientObj.GetInterface(); + ASSERT_NE(clientConn, nullptr); + CUDSDataReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + // Wait until both connected + ASSERT_TRUE(serverConn->WaitForConnection(5000)); + ASSERT_TRUE(clientConn->WaitForConnection(5000)); + + // Build a large payload (e.g., 256 KiB) to force fragmentation in SendData(...) + const size_t totalBytes = 256 * 1024; + sdv::pointer big; + big.resize(totalBytes); + + // Fill with a deterministic pattern for verification + for (size_t i = 0; i < totalBytes; ++i) + { + big.get()[i] = static_cast(i & 0xFF); + } + + sdv::sequence> seq; + seq.push_back(big); + + auto* pSend = dynamic_cast(clientConn); + ASSERT_NE(pSend, nullptr); + EXPECT_TRUE(pSend->SendData(seq)); // SendData will fragment as needed + + // Wait deterministically for server-side ReceiveData(...) + ASSERT_TRUE(sRcvr.WaitForData(5000)); + auto recv = sRcvr.GetLastData(); + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), totalBytes); + EXPECT_EQ(std::memcmp(recv[0].get(), big.get(), totalBytes), 0); + + clientConn->Disconnect(); + serverConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//Zero‑length chunks mixed with non‑zero chunks +TEST(UnixSocketIPC, DataPath_ZeroLengthChunks_ArePreserved) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + auto* serverConn = serverObj.GetInterface(); + CUDSDataReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + auto* clientConn = clientObj.GetInterface(); + CUDSDataReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + ASSERT_TRUE(serverConn->WaitForConnection(5000)); + ASSERT_TRUE(clientConn->WaitForConnection(5000)); + + // Build sequence: [0][5][""] [8] + sdv::pointer p0, p5, p0b, p8; + p0.resize(0); + p5.resize(5); + std::memcpy(p5.get(), "world", 5); + p0b.resize(0); + p8.resize(8); + std::memcpy(p8.get(), "fragment", 8); + + sdv::sequence> seq; + seq.push_back(p0); + seq.push_back(p5); + seq.push_back(p0b); + seq.push_back(p8); + + auto* pSend = dynamic_cast(clientConn); + ASSERT_NE(pSend, nullptr); + EXPECT_TRUE(pSend->SendData(seq)); // table includes zero sizes; receiver must see them + + ASSERT_TRUE(sRcvr.WaitForData(3000)); + auto recv = sRcvr.GetLastData(); + ASSERT_EQ(recv.size(), 4u); + EXPECT_EQ(recv[0].size(), 0u); + EXPECT_EQ(recv[1].size(), 5u); + EXPECT_EQ(recv[2].size(), 0u); + EXPECT_EQ(recv[3].size(), 8u); + EXPECT_EQ(std::memcmp(recv[1].get(), "world", 5), 0); + EXPECT_EQ(std::memcmp(recv[3].get(), "fragment", 8), 0); + + clientConn->Disconnect(); + serverConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); app.Shutdown(); +} + +//Peer closes mid‑transfer -> SendData fails and client observes disconnected +TEST(UnixSocketIPC, PeerCloseMidTransfer_ClientSeesDisconnected_AndSendMayFailOrSucceed) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + CUnixDomainSocketsChannelMgnt mgr; + + ASSERT_NO_THROW(mgr.Initialize("")); + ASSERT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + auto* serverConn = serverObj.GetInterface(); + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + auto* clientConn = clientObj.GetInterface(); + CUDSConnectReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + ASSERT_TRUE(serverConn->WaitForConnection(5000)); + ASSERT_TRUE(clientConn->WaitForConnection(5000)); + + // Large buffer + sdv::pointer big; + big.resize(512 * 1024); + memset(big.get(), 0xAA, big.size()); + + sdv::sequence> seq; + seq.push_back(big); + + auto* pSend = dynamic_cast(clientConn); + ASSERT_NE(pSend, nullptr); + + std::atomic sendResult{true}; + std::thread t([&]{ + sendResult.store(pSend->SendData(seq)); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + serverConn->Disconnect(); + + t.join(); + + // We no longer EXPECT false: + // SendData may return true OR false depending on kernel timing. + // Just log the value for debugging: + std::cout << "[Debug] SendData result = " << sendResult.load() << std::endl; + + // But client MUST observe disconnected: + EXPECT_TRUE(cRcvr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000)); + + clientConn->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//Client cancels connect (no server) -> clean stop of worker threads +TEST(UnixSocketIPC, ClientCancelConnect_NoServer_CleansUpPromptly) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // Unique path with no server listening + const std::string path = std::string("/tmp/sdv/cancel_") + MakeRandomSuffix() + ".sock"; + const std::string clientCS = std::string("proto=uds;role=client;path=") + path + ";"; + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* clientConn = clientObj.GetInterface(); + ASSERT_NE(clientConn, nullptr); + CUDSConnectReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + // Cancel before the built-in retry loop completes. + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + clientConn->Disconnect(); + + // Immediately after disconnect, status should be 'disconnected' (no hangs). + EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + + EXPECT_NO_THROW(mgr.Shutdown()); app.Shutdown(); +} + +//Server starts, then immediately disconnects (no client yet) +TEST(UnixSocketIPC, ServerStartThenImmediateDisconnect_NoClient) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + const std::string serverCS = ep.ssConnectString; + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + CUDSConnectReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + // The server may be still listening; ensure we can disconnect cleanly + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + serverConn->Disconnect(); + EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + + +//Callback throws in SetStatus -> transport threads keep running +TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + CUnixDomainSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + CUDSThrowingReceiver sRcvr; ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* clientConn = clientObj.GetInterface(); + ASSERT_NE(clientConn, nullptr); + CUDSThrowingReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + // Despite exceptions thrown inside SetStatus, the transport should still reach connected. + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_TRUE(clientConn->WaitForConnection(5000)); + + // And it should still propagate a clean disconnect (even with throwing callbacks). + clientConn->Disconnect(); + serverConn->Disconnect(); + + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//RegisterStatusEventCallback: multiple listeners receive status updates +TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + ASSERT_NO_THROW(mgr.Initialize("")); + ASSERT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // --- Setup server endpoint --- + auto ep = mgr.CreateEndpoint(""); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + auto* server = serverObj.GetInterface(); + ASSERT_NE(server, nullptr); + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + auto* client = clientObj.GetInterface(); + ASSERT_NE(client, nullptr); + + // --- Callback receiver 1 --- + CUDSConnectReceiver recv1; + uint64_t cookie1 = server->RegisterStatusEventCallback(&recv1); + EXPECT_NE(cookie1, 0u); + + // --- Callback receiver 2 --- + CUDSConnectReceiver recv2; + uint64_t cookie2 = server->RegisterStatusEventCallback(&recv2); + EXPECT_NE(cookie2, 0u); + EXPECT_NE(cookie1, cookie2); + + // --- Start connections --- + ASSERT_TRUE(server->AsyncConnect(&recv1)); + ASSERT_TRUE(client->AsyncConnect(&recv2)); + + ASSERT_TRUE(server->WaitForConnection(5000)); + ASSERT_TRUE(client->WaitForConnection(5000)); + + // Both receivers should have received 'connected' + EXPECT_TRUE(recv1.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)); + EXPECT_TRUE(recv2.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)); + + // --- Disconnect --- + client->Disconnect(); + + EXPECT_TRUE(recv1.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)); + EXPECT_TRUE(recv2.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)); + + server->Disconnect(); + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +//UnregisterStatusEventCallback: removed listener stops receiving events +TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving) +{ + // Framework + Manager + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + ASSERT_NO_THROW(mgr.Initialize("")); + ASSERT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // Endpoint + auto ep = mgr.CreateEndpoint(""); + const std::string serverCS = ep.ssConnectString; + const std::string clientCS = MakeClientCS(serverCS); + + // Server + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* server = serverObj.GetInterface(); + ASSERT_NE(server, nullptr); + + // Client + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* client = clientObj.GetInterface(); + ASSERT_NE(client, nullptr); + + // --------- Two distinct receivers ---------- + // recvReg: used ONLY for the status-callback registry + // recvConn: used ONLY for AsyncConnect (m_pReceiver / m_pEvent path) + CUDSConnectReceiver recvReg; + CUDSConnectReceiver recvConn; + + // Register recvReg as a status listener (registry path) + uint64_t cookie = server->RegisterStatusEventCallback(&recvReg); + ASSERT_NE(cookie, 0u) << "Cookie must be non-zero"; + + // Start connections (server uses recvReg only for registry; recvConn is the IConnect/IDataReceive side) + ASSERT_TRUE(server->AsyncConnect(&recvConn)); + ASSERT_TRUE(client->AsyncConnect(&recvConn)); + + // Wait until both sides are connected + ASSERT_TRUE(server->WaitForConnection(5000)); + ASSERT_TRUE(client->WaitForConnection(5000)); + + // Both the connection receiver (recvConn) and the registry listener (recvReg) should observe 'connected' + EXPECT_TRUE(recvConn.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)) << "Connection receiver didn't see 'connected'."; + EXPECT_TRUE(recvReg.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)) << "Registry listener didn't see 'connected'."; + + // --------- Unregister the registry listener ---------- + server->UnregisterStatusEventCallback(cookie); + + // Trigger a disconnect on client to force status transitions + client->Disconnect(); + + // The connection receiver (recvConn) still sees disconnected (normal path) + EXPECT_TRUE(recvConn.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)) << "Connection receiver didn't see 'disconnected'."; + + // The registry listener (recvReg) MUST NOT receive 'disconnected' anymore + // (wait a short, deterministic interval) + EXPECT_FALSE(recvReg.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 300)) << "Registry listener received 'disconnected' after UnregisterStatusEventCallback()."; + + // Server cleanup + server->Disconnect(); + + // Manager / framework cleanup + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +#endif // defined __unix__ \ No newline at end of file diff --git a/tests/unit_tests/win_sockets/CMakeLists.txt b/tests/unit_tests/win_sockets/CMakeLists.txt new file mode 100644 index 0000000..9d64222 --- /dev/null +++ b/tests/unit_tests/win_sockets/CMakeLists.txt @@ -0,0 +1,53 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + +cmake_minimum_required(VERSION 3.20) + +project(WinSocketCommunicationTests LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Executable: GTest for UDS connect +add_executable(UnitTest_WinSocketConnectTests + win_connect_tests.cpp +) + +target_include_directories(UnitTest_WinSocketConnectTests + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/export + ${PROJECT_SOURCE_DIR}/export/support + ${PROJECT_SOURCE_DIR}/export/interfaces + ${PROJECT_SOURCE_DIR}/sdv_services +) + + target_link_libraries(UnitTest_WinSocketConnectTests + PRIVATE + GTest::GTest + # Sockets & timing + Ws2_32 + Winmm + Rpcrt4 + ) + +add_test(NAME UnitTest_WinSocketConnectTests + COMMAND UnitTest_WinSocketConnectTests) + +add_dependencies(UnitTest_WinSocketConnectTests dependency_sdv_components) + +if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) +add_custom_command(TARGET UnitTest_WinSocketConnectTests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake + "$" + --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinSocketConnectTests.xml + VERBATIM +) +endif() \ No newline at end of file diff --git a/tests/unit_tests/win_sockets/win_connect_tests.cpp b/tests/unit_tests/win_sockets/win_connect_tests.cpp new file mode 100644 index 0000000..e6b2446 --- /dev/null +++ b/tests/unit_tests/win_sockets/win_connect_tests.cpp @@ -0,0 +1,983 @@ +/******************************************************************************** +* 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 +********************************************************************************/ + +#if defined(_WIN32) + +#include "gtest/gtest.h" + +#include +#include + +#include "../../../sdv_services/uds_win_sockets/channel_mgnt.cpp" +#include "../../../sdv_services/uds_win_sockets/connection.cpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Helper namespace +namespace test_utils { + +/** + * @brief Ensure the directory for the given full path exists + */ +inline void EnsureParentDir(const std::string& fullPath) +{ + auto pos = fullPath.find_last_of("\\/"); + if (pos == std::string::npos) + return; + + std::string dir = fullPath.substr(0, pos); + CreateDirectoryA(dir.c_str(), nullptr); +} + +/** + * @brief Expand environment variables such as %LOCALAPPDATA% + */ +inline std::string Expand(const std::string& in) +{ + if (in.find('%') == std::string::npos) + return in; + + char buf[4096] = {}; + DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, sizeof(buf)); + return (n > 0 && n < sizeof(buf)) ? std::string(buf) : in; +} + +/** + * @brief Build a short Win32 path for UDS sockets + */ +inline std::string MakeShortUdsPath(const char* name) +{ + std::string base = R"(%LOCALAPPDATA%\sdv\)"; + base = Expand(base); + EnsureParentDir(base); + return base + name; +} + +/** + * @brief Generate a random hex suffix for unique paths + */ +inline std::string RandomHex() +{ + std::mt19937_64 rng{ std::random_device{}() }; + std::uniform_int_distribution dist; + std::ostringstream oss; + oss << std::hex << dist(rng); + return oss.str(); +} + +/** + * @brief Unique absolute path (keeps tests independent) + */ +inline std::string UniqueUds(const char* prefix) +{ + return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str()); +} + +/** + * @brief Wait until a server connection worker has reached "armed" state + */ +inline void SpinUntilServerArmed(sdv::ipc::IConnect* server, uint32_t maxWaitMs = 300) +{ + using namespace std::chrono; + auto deadline = steady_clock::now() + milliseconds(maxWaitMs); + + while (server->GetStatus() == sdv::ipc::EConnectStatus::uninitialized && + steady_clock::now() < deadline) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +/** + * @brief Small sleep helper for teardown time + */ +inline void SleepTiny(uint32_t ms = 20) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +} + +} // namespace test_utils + +// Unified test receiver (status + data) +class CTestReceiver : + public sdv::IInterfaceAccess, + public sdv::ipc::IConnectEventCallback, + public sdv::ipc::IDataReceiveCallback +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + END_SDV_INTERFACE_MAP() + + void SetStatus(sdv::ipc::EConnectStatus s) override + { + { + std::lock_guard lk(m_mtx); + m_status = s; + } + m_cv.notify_all(); + } + + void ReceiveData(sdv::sequence>& seq) override + { + { + std::lock_guard lk(m_mtx); + m_data = seq; + m_hasData = true; + } + m_cv.notify_all(); + } + + bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + return m_cv.wait_for(lk, std::chrono::milliseconds(ms), + [&]{ return m_status == expected; }); + } + + bool WaitForData(uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + return m_cv.wait_for(lk, std::chrono::milliseconds(ms), + [&]{ return m_hasData; }); + } + + sdv::sequence> Data() + { + std::lock_guard lk(m_mtx); + return m_data; + } + +private: + mutable std::mutex m_mtx; + std::condition_variable m_cv; + + sdv::ipc::EConnectStatus m_status{ sdv::ipc::EConnectStatus::uninitialized }; + sdv::sequence> m_data; + bool m_hasData{false}; +}; + + +// Helper for creating server + client on a given UDS connect-string +struct ServerClient +{ + sdv::TObjectPtr serverObj; + sdv::ipc::IConnect* server = nullptr; + sdv::TObjectPtr clientObj; + sdv::ipc::IConnect* client = nullptr; +}; + +static ServerClient CreatePair(CSocketsChannelMgnt& mgr, const std::string& cs) +{ + ServerClient out; + // Server + out.serverObj = mgr.Access(cs); + out.server = out.serverObj ? out.serverObj.GetInterface() : nullptr; + + // Client + out.clientObj = mgr.Access(cs); + out.client = out.clientObj ? out.clientObj.GetInterface() : nullptr; + + return out; +} + +// TESTS START HERE +using namespace test_utils; + +// Instantiate manager +TEST(WindowsAFUnixIPC, Instantiate) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + + CSocketsChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); + EXPECT_NO_THROW(mgr.Shutdown()); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); + + app.Shutdown(); +} + +// Basic connect/disconnect (server + client) +TEST(WindowsAFUnixIPC, BasicConnectDisconnect) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string path = UniqueUds("basic"); + const std::string cs = std::string("proto=uds;path=") + path + ";"; + + // Create endpoint + auto ep = mgr.CreateEndpoint(cs); + ASSERT_FALSE(ep.ssConnectString.empty()); + + // Create server + client objects + auto pair = CreatePair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + + // Server side connect + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + // Client side connect + pair.client->AsyncConnect(&cr); + + EXPECT_TRUE(pair.server->WaitForConnection(5000)); + EXPECT_TRUE(pair.client->WaitForConnection(5000)); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Simple data path: client -> server sends "hello" +TEST(WindowsAFUnixIPC, DataPath_SimpleHello) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = std::string("proto=uds;path=") + UniqueUds("hello") + ";"; + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreatePair(mgr, ep.ssConnectString); + + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + // Build hello payload + sdv::pointer p; + p.resize(5); + std::memcpy(p.get(), "hello", 5); + + sdv::sequence> seq; + seq.push_back(p); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + + EXPECT_TRUE(sender->SendData(seq)); + EXPECT_TRUE(sr.WaitForData(3000)); + + auto recv = sr.Data(); + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), 5u); + EXPECT_EQ(std::memcmp(recv[0].get(), "hello", 5), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Server disconnect propagates to client +TEST(WindowsAFUnixIPC, ServerDisconnectPropagates) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = std::string("proto=uds;path=") + UniqueUds("disc") + ";"; + auto ep = mgr.CreateEndpoint(cs); + + auto pair = CreatePair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + pair.server->Disconnect(); + + EXPECT_TRUE(cr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000)); + + pair.client->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// DataPath – multi-chunk payload +TEST(WindowsAFUnixIPC, DataPath_MultiChunk) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = std::string("proto=uds;path=") + UniqueUds("mc") + ";"; + + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreatePair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + // Build multichunk + sdv::pointer p1, p2; + p1.resize(3); std::memcpy(p1.get(), "sdv", 3); + p2.resize(9); std::memcpy(p2.get(), "framework", 9); + + sdv::sequence> seq; + seq.push_back(p1); + seq.push_back(p2); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + EXPECT_TRUE(sender->SendData(seq)); + + ASSERT_TRUE(sr.WaitForData(3000)); + auto recv = sr.Data(); + + ASSERT_EQ(recv.size(), 2u); + EXPECT_EQ(std::memcmp(recv[0].get(), "sdv", 3), 0); + EXPECT_EQ(std::memcmp(recv[1].get(), "framework", 9), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Large payload fragmentation + reassembly +TEST(WindowsAFUnixIPC, DataPath_LargePayloadFragmentation) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = std::string("proto=uds;path=") + UniqueUds("big") + ";"; + + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreatePair(mgr, ep.ssConnectString); + + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + const size_t N = 256 * 1024; + sdv::pointer payload; + payload.resize(N); + for (size_t i = 0; i < N; ++i) + payload.get()[i] = uint8_t(i & 0xFF); + + sdv::sequence> seq; + seq.push_back(payload); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + + EXPECT_TRUE(sender->SendData(seq)); + ASSERT_TRUE(sr.WaitForData(5000)); + + auto recv = sr.Data(); + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), N); + EXPECT_EQ(std::memcmp(recv[0].get(), payload.get(), N), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Zero-length chunks preserved +TEST(WindowsAFUnixIPC, DataPath_ZeroLengthChunks) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = std::string("proto=uds;path=") + UniqueUds("zlen") + ";"; + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreatePair(mgr, ep.ssConnectString); + + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + sdv::pointer p0a, p5, p0b, p8; + p0a.resize(0); + p5.resize(5); std::memcpy(p5.get(), "world",5); + p0b.resize(0); + p8.resize(8); std::memcpy(p8.get(), "fragment",8); + + sdv::sequence> seq; + seq.push_back(p0a); + seq.push_back(p5); + seq.push_back(p0b); + seq.push_back(p8); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + EXPECT_TRUE(sender->SendData(seq)); + + ASSERT_TRUE(sr.WaitForData(3000)); + auto recv = sr.Data(); + + ASSERT_EQ(recv.size(), 4u); + EXPECT_EQ(recv[0].size(), 0u); + EXPECT_EQ(recv[1].size(), 5u); + EXPECT_EQ(recv[2].size(), 0u); + EXPECT_EQ(recv[3].size(), 8u); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Operation mode transitions +TEST(WindowsAFUnixIPC, OperationModeTransitions) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); + mgr.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::configuring); + mgr.SetOperationMode(sdv::EOperationMode::running); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + mgr.Shutdown(); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); + + app.Shutdown(); +} + +// Reconnect using the same UDS path (two consecutive sessions) +TEST(WindowsAFUnixIPC, ReconnectAfterDisconnect_SamePath) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string udsPath = MakeShortUdsPath(("vapi_win_reconn_" + RandomHex() + ".sock").c_str()); + + const std::string cs = std::string("proto=uds;path=") + udsPath + ";"; + + // ----- Session 1 ----- + { + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreatePair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + std::atomic clientRes{0}; + + std::thread ct([&]{ + if (!pair.clientObj) { clientRes = 1; return; } + auto* c = pair.client; + if (!c) { clientRes = 2; return; } + c->AsyncConnect(&cr); + if (!c->WaitForConnection(5000)) { clientRes = 3; return; } + c->Disconnect(); + clientRes = 0; + }); + + EXPECT_TRUE(pair.server->WaitForConnection(5000)); + ct.join(); + EXPECT_EQ(clientRes.load(), 0); + + pair.server->Disconnect(); + } + + SleepTiny(20); + + // ----- Session 2 ----- + { + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreatePair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + std::atomic clientRes{0}; + + std::thread ct([&]{ + if (!pair.clientObj) { clientRes = 1; return; } + auto* c = pair.client; + if (!c) { clientRes = 2; return; } + c->AsyncConnect(&cr); + if (!c->WaitForConnection(5000)) { clientRes = 3; return; } + c->Disconnect(); + clientRes = 0; + }); + + EXPECT_TRUE(pair.server->WaitForConnection(5000)); + ct.join(); + EXPECT_EQ(clientRes.load(), 0); + + pair.server->Disconnect(); + } + + mgr.Shutdown(); + app.Shutdown(); +} + +// WaitForConnection(INFINITE) with a delayed client +TEST(WindowsAFUnixIPC, WaitForConnection_InfiniteWait_SlowClient) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = std::string("proto=uds;path=") + MakeShortUdsPath(("vapi_win_slow_" + RandomHex() + ".sock").c_str()) + ";"; + auto ep = mgr.CreateEndpoint(cs); + + sdv::TObjectPtr sObj = mgr.Access(ep.ssConnectString); + auto* server = sObj.GetInterface(); + ASSERT_NE(server, nullptr); + + CTestReceiver sr; + server->AsyncConnect(&sr); + SpinUntilServerArmed(server); + + std::thread delayedClient([&]{ + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + sdv::TObjectPtr cObj = mgr.Access(ep.ssConnectString); + auto* client = cObj.GetInterface(); + CTestReceiver cr; + client->AsyncConnect(&cr); + client->WaitForConnection(5000); + client->Disconnect(); + }); + + EXPECT_TRUE(server->WaitForConnection(6000)); + delayedClient.join(); + server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// WaitForConnection(0) before and after establishing the connection +TEST(WindowsAFUnixIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = + std::string("proto=uds;path=") + + MakeShortUdsPath(("vapi_win_zero_" + RandomHex() + ".sock").c_str()) + + ";"; + + auto ep = mgr.CreateEndpoint(cs); + sdv::TObjectPtr sObj = mgr.Access(ep.ssConnectString); + auto* server = sObj.GetInterface(); + + ASSERT_NE(server, nullptr); + + CTestReceiver sr; + server->AsyncConnect(&sr); + SpinUntilServerArmed(server); + + EXPECT_FALSE(server->WaitForConnection(0)); + + sdv::TObjectPtr cObj = mgr.Access(ep.ssConnectString); + auto* client = cObj.GetInterface(); + + CTestReceiver cr; + client->AsyncConnect(&cr); + + EXPECT_TRUE(server->WaitForConnection(5000)); + EXPECT_TRUE(client->WaitForConnection(5000)); + + client->Disconnect(); + server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Client timeout when NO server exists on that path +TEST(WindowsAFUnixIPC, ClientTimeout_NoServer) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string raw = MakeShortUdsPath(("vapi_win_nosrv_" + RandomHex() + ".sock").c_str()); + const std::string cs = std::string("proto=uds;path=") + raw + ";"; + + sdv::TObjectPtr cObj = mgr.Access(cs); + + if (!cObj) + { + SUCCEED() << "Access(proto=uds;path=..., no-server) may return nullptr immediately on Windows."; + mgr.Shutdown(); + app.Shutdown(); + return; + } + + auto* client = cObj.GetInterface(); + ASSERT_NE(client, nullptr); + + CTestReceiver cr; + client->AsyncConnect(&cr); + + EXPECT_FALSE(client->WaitForConnection(1500)); + std::this_thread::sleep_for(std::chrono::milliseconds(800)); + + EXPECT_EQ(client->GetStatus(), sdv::ipc::EConnectStatus::connection_error); + + client->Disconnect(); + mgr.Shutdown(); + app.Shutdown(); +} + +// Server closes the connection during a large transfer → client sees disconnected +TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = + std::string("proto=uds;path=") + + MakeShortUdsPath(("vapi_win_midclose_" + RandomHex() + ".sock").c_str()) + + ";"; + + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreatePair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + pair.client->AsyncConnect(&cr); + + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + sdv::pointer buf; + buf.resize(512 * 1024); + std::memset(buf.get(), 0xAA, buf.size()); + + sdv::sequence> seq; + seq.push_back(buf); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + + std::atomic sendOk{true}; + std::thread t([&]{ + sendOk.store(sender->SendData(seq)); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + pair.server->Disconnect(); + + t.join(); + + EXPECT_TRUE(cr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000)); + + pair.client->Disconnect(); + mgr.Shutdown(); + app.Shutdown(); +} + +// Client cancels connect attempt when no server exists → must clean up promptly +TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string raw = MakeShortUdsPath(("vapi_win_cancel_" + RandomHex() + ".sock").c_str()); + const std::string cs = std::string("proto=uds;path=") + raw + ";"; + sdv::TObjectPtr cObj = mgr.Access(cs); + + if (!cObj) + { + SUCCEED() << "Immediate nullptr for no-server case is valid Windows behavior."; + mgr.Shutdown(); + app.Shutdown(); + return; + } + + auto* client = cObj.GetInterface(); + ASSERT_NE(client, nullptr); + + CTestReceiver cr; + client->AsyncConnect(&cr); + + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + client->Disconnect(); + + EXPECT_EQ(client->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Server starts and immediately disconnects (no client) +TEST(WindowsAFUnixIPC, ServerStartThenImmediateDisconnect_NoClient) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = + std::string("proto=uds;path=") + + MakeShortUdsPath(("vapi_win_srvonly_" + RandomHex() + ".sock").c_str()) + + ";"; + + auto ep = mgr.CreateEndpoint(cs); + sdv::TObjectPtr sObj = mgr.Access(ep.ssConnectString); + auto* server = sObj.GetInterface(); + ASSERT_NE(server, nullptr); + + CTestReceiver sr; + server->AsyncConnect(&sr); + SpinUntilServerArmed(server); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + server->Disconnect(); + EXPECT_EQ(server->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + + mgr.Shutdown(); + app.Shutdown(); +} + +// UnregisterStatusEventCallback: ensure single-listener semantics +TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = + std::string("proto=uds;path=") + + MakeShortUdsPath(("vapi_win_cb_" + RandomHex() + ".sock").c_str()) + + ";"; + + auto ep = mgr.CreateEndpoint(cs); + + sdv::TObjectPtr sObj = mgr.Access(ep.ssConnectString); + auto* server = sObj.GetInterface(); + ASSERT_NE(server, nullptr); + + sdv::TObjectPtr cObj = mgr.Access(ep.ssConnectString); + auto* client = cObj.GetInterface(); + ASSERT_NE(client, nullptr); + + CTestReceiver regListener; + const uint64_t cookie = server->RegisterStatusEventCallback(®Listener); + ASSERT_NE(cookie, 0u); + + CTestReceiver mainRecv; + + server->AsyncConnect(&mainRecv); + client->AsyncConnect(&mainRecv); + SpinUntilServerArmed(server); + + EXPECT_TRUE(server->WaitForConnection(5000)); + EXPECT_TRUE(client->WaitForConnection(5000)); + + EXPECT_TRUE(mainRecv.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)); + EXPECT_FALSE(regListener.WaitForStatus(sdv::ipc::EConnectStatus::connected, 300)) << "The registry listener should NOT receive events while main receiver is active."; + + server->UnregisterStatusEventCallback(cookie); + + client->Disconnect(); + EXPECT_TRUE(mainRecv.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1500)); + + EXPECT_FALSE(regListener.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 300)); + + server->Disconnect(); + mgr.Shutdown(); + app.Shutdown(); +} + +// CreateEndpoint with very long path → must be normalized to basename +TEST(WindowsAFUnixIPC, CreateEndpoint_LongInputPath_Normalized) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + std::string longName(160, 'A'); + + const std::string rawPath = "C:\\Users\\" + longName + "\\AppData\\Local\\sdv\\vapi_win_long_" + RandomHex() + ".sock"; + const std::string cs = std::string("proto=uds;path=") + rawPath + ";"; + + auto ep = mgr.CreateEndpoint(cs); + + ASSERT_FALSE(ep.ssConnectString.empty()); + + const std::string publishedCS = ep.ssConnectString; + EXPECT_NE(publishedCS.find("path=vapi_win_long_"), std::string::npos); + + sdv::TObjectPtr sObj = mgr.Access(publishedCS); + auto* server = sObj.GetInterface(); + ASSERT_NE(server, nullptr); + + CTestReceiver sr; + server->AsyncConnect(&sr); + SpinUntilServerArmed(server); + + sdv::TObjectPtr cObj = mgr.Access(publishedCS); + auto* client = cObj.GetInterface(); + ASSERT_NE(client, nullptr); + + CTestReceiver cr; + client->AsyncConnect(&cr); + + EXPECT_TRUE(server->WaitForConnection(5000)); + EXPECT_TRUE(client->WaitForConnection(5000)); + + client->Disconnect(); + server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +#endif // _WIN32 \ No newline at end of file diff --git a/tmp_project/CMakeLists.txt b/tmp_project/CMakeLists.txt index c885572..76ab09e 100644 --- a/tmp_project/CMakeLists.txt +++ b/tmp_project/CMakeLists.txt @@ -1,3 +1,13 @@ +#******************************************************************************* +# 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 +#******************************************************************************* + # Enforce CMake version 3.20 cmake_minimum_required (VERSION 3.20) cmake_policy (VERSION 3.20)