From e6aa1d32ade881b63f843f848b26f7e72aa63403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20B=C3=B6rschig?= Date: Thu, 7 May 2026 11:28:00 +0200 Subject: [PATCH] refactor LifecycleManagement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marius Börschig --- AGENTS.md | 113 +++ .../services/orchestration/CMakeLists.txt | 9 +- .../orchestration/ILifecycleStates.hpp | 77 -- .../orchestration/LifecycleManagement.cpp | 225 +---- .../orchestration/LifecycleManagement.hpp | 97 +-- .../orchestration/LifecycleService.cpp | 4 +- .../services/orchestration/LifecycleState.hpp | 111 +++ .../orchestration/LifecycleStateMachine.hpp | 635 ++++++++++++++ .../orchestration/LifecycleStates.cpp | 823 ------------------ .../orchestration/LifecycleStates.hpp | 301 ------- .../Test_LifecycleStateMachine.cpp | 185 ++++ 11 files changed, 1117 insertions(+), 1463 deletions(-) create mode 100644 AGENTS.md delete mode 100644 SilKit/source/services/orchestration/ILifecycleStates.hpp create mode 100644 SilKit/source/services/orchestration/LifecycleState.hpp create mode 100644 SilKit/source/services/orchestration/LifecycleStateMachine.hpp delete mode 100644 SilKit/source/services/orchestration/LifecycleStates.cpp delete mode 100644 SilKit/source/services/orchestration/LifecycleStates.hpp create mode 100644 SilKit/source/services/orchestration/Test_LifecycleStateMachine.cpp diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..3143e4dfa --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,113 @@ +# Repo Context Hints + +## Snapshot + +SIL Kit is a C++ library for connecting Software-in-the-Loop environments. + +- Build system: `CMake` +- Main language: `C++` +- Start here for overview: `README.rst` +- Start here for architecture and platform support: `docs/for-developers/developers.rst` + +## Important Directories + +- `SilKit/`: library headers, implementation, packaging, tests +- `Utilities/`: runtime tools like `sil-kit-registry`, `sil-kit-system-controller`, `sil-kit-monitor` +- `Demos/`: example applications +- `docs/`: Sphinx/Doxygen docs and code samples +- `cmake/`: shared CMake modules +- `.github/workflows/`: CI behavior +- `ThirdParty/`: vendored dependencies, usually not the place to edit + +## Library Layout + +Inside `SilKit/` the most relevant locations are: + +- `include/`: public API +- `source/core/`: runtime internals, participant, transport, requests +- `source/services/`: CAN, Ethernet, FlexRay, LIN, PubSub, RPC, logging, metrics, orchestration +- `source/tracing/`: tracing and replay +- `source/experimental/`: experimental APIs and network simulation +- `IntegrationTests/`: integration coverage +- `cmake/SilKitTest.cmake`: test registration helpers + +The library is built from many object libraries under `SilKit/source/` and exported as `SilKit::SilKit`. + +## Build Defaults + +Root `CMakeLists.txt` defaults: + +- `SILKIT_BUILD_TESTS=ON` +- `SILKIT_BUILD_UTILITIES=ON` +- `SILKIT_BUILD_DEMOS=ON` +- `SILKIT_BUILD_DASHBOARD=ON` +- `SILKIT_BUILD_DOCS=OFF` +- `SILKIT_BUILD_STATIC=OFF` + +Useful presets from `CMakePresets.json`: + +- `debug` +- `release` +- `relwithdebinfo` +- `x86-debug` +- `x86-release` +- `distrib` + +Common commands: + +```sh +cmake --preset debug +cmake --build --preset debug +ctest --preset debug --output-on-failure +``` + +Windows CI uses Ninja and MSVC toolset compatibility matters. + +## Tests + +- Tests use `CTest` + `GoogleTest` +- Test executables are declared in `SilKit/CMakeLists.txt` +- Test suite registration happens in `SilKit/cmake/SilKitTest.cmake` +- Test source files commonly follow `Test_*.cpp` + +When adding tests, prefer the existing `add_silkit_test_to_executable(...)` flow. + +## Docs And Runtime + +- Docs are built from `docs/` with Doxygen + Sphinx when `SILKIT_BUILD_DOCS=ON` +- Many demos and samples expect `sil-kit-registry` +- Coordinated simulation examples also expect `sil-kit-system-controller` + +## Guardrails + +- Ignore `_build/`, `_install/`, and `.vs/` as generated or local state +- Avoid editing `ThirdParty/` unless the task is explicitly about dependencies +- This repo expects submodules: `git submodule update --init --recursive` +- External pull requests are currently not accepted; see `CONTRIBUTING.md` + +## Good Entry Points + +- Build behavior: `CMakeLists.txt`, `CMakePresets.json`, `.github/workflows/build-win.yml` +- Library structure: `SilKit/CMakeLists.txt`, `SilKit/source/CMakeLists.txt` +- Examples: `Demos/`, `docs/code-samples/` + +## Editing Heuristics + +- Prefer small, localized changes +- If public API changes, inspect both `SilKit/include/` and matching code in `SilKit/source/` +- If CMake changes, inspect both root and module-local `CMakeLists.txt` +- If tests change, consider both unit and integration coverage +- Keep demos and docs aligned with actual runtime expectations + + +## Code Style +- Files under `SilKit/include/` are public API and should keep the nested `SilKit::...` namespaces. +- Files under `SilKit/source/` are internal implementation and should use the flat `VSilKit::...` namespace. +- Some existing internal code does not follow this everywhere yet; when touching code in `SilKit/source/`, prefer moving new or refactored implementation toward `VSilKit` without adding unrelated churn. +- Use modern C++17 for now. +- Write C++17 in a way that keeps a future migration to C++20 straightforward. +- Prefer composition over inheritance. +- Prefer templates and explicit composition for reusable internal logic and test seams. +- Prefer `std::unique_ptr` and `std::shared_ptr` for owned dynamic memory. +- Prefer the C++ standard library wherever possible over custom utilities. +- Prefer simple, explicit designs over framework-like abstractions or heavy metaprogramming. \ No newline at end of file diff --git a/SilKit/source/services/orchestration/CMakeLists.txt b/SilKit/source/services/orchestration/CMakeLists.txt index c9f0b0a5b..e79033688 100644 --- a/SilKit/source/services/orchestration/CMakeLists.txt +++ b/SilKit/source/services/orchestration/CMakeLists.txt @@ -19,11 +19,10 @@ target_link_libraries(I_SilKit_Services_Orchestration add_library(O_SilKit_Services_Orchestration OBJECT - ILifecycleStates.hpp + LifecycleState.hpp + LifecycleStateMachine.hpp LifecycleManagement.hpp LifecycleManagement.cpp - LifecycleStates.hpp - LifecycleStates.cpp LifecycleService.hpp LifecycleService.cpp @@ -63,6 +62,10 @@ add_silkit_test_to_executable(SilKitUnitTests SOURCES Test_LifecycleService.cpp LIBS S_SilKitImpl I_SilKit_Core_Mock_Participant ) +add_silkit_test_to_executable(SilKitUnitTests + SOURCES Test_LifecycleStateMachine.cpp + LIBS S_SilKitImpl +) add_silkit_test_to_executable(SilKitUnitTests SOURCES Test_SystemController.cpp LIBS S_SilKitImpl I_SilKit_Core_Mock_Participant diff --git a/SilKit/source/services/orchestration/ILifecycleStates.hpp b/SilKit/source/services/orchestration/ILifecycleStates.hpp deleted file mode 100644 index 66a69712d..000000000 --- a/SilKit/source/services/orchestration/ILifecycleStates.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Vector Informatik GmbH -// -// SPDX-License-Identifier: MIT - -#pragma once -#include - -namespace SilKit { -namespace Services { -namespace Orchestration { - -enum class CallbackResult -{ - Error, - Completed, - Deferred -}; - -class ILifecycleState -{ -public: - virtual ~ILifecycleState() = default; - - // Advance from Invalid to ServicesCreated; Fail otherwise. - virtual void Initialize(std::string reason) = 0; - - // Autonomous: Directly... - // Coordinated: Wait for SystemState::ServicesCreated, then... - // ... advance to CommunicationInitializingState after receiving replies from all other participants. - virtual void ServicesCreated(std::string reason) = 0; - - // Advance to CommunicationInitialized after all pending service subscriptions are completed. - virtual void CommunicationInitializing(std::string reason) = 0; - - // Autonomous: Directly... - // Coordinated: Wait for SystemState::CommunicationInitialized, then... - // ... trigger CommunicationReadyHandler; If completed, advance to ReadyToRun. - virtual void CommunicationInitialized(std::string reason) = 0; - - // Deferred CommunicationReadyHandler completion, advance to ReadyToRun - virtual void CompleteCommunicationReadyHandler(std::string reason) = 0; - - // Autonomous: Directly... - // Coordinated: Wait for SystemState::ReadyToRun, then... - // ... trigger StartingHandler for participants without active timeSync; If completed, advance to ReadyToRun. - virtual void ReadyToRun(std::string reason) = 0; - - virtual void RunSimulation(std::string reason) = 0; - - virtual void PauseSimulation(std::string reason) = 0; - - virtual void ContinueSimulation(std::string reason) = 0; - - // Autonomous: Directly... - // Coordinated: Wait for SystemState::Stopping, then... - // ... trigger StopHandler; If completed, advance to Stopped. Then, - // advance to ShuttingDown after receiving replies from all other participants. - virtual void StopSimulation(std::string reason) = 0; - - // Trigger the ShutdownHandler; If completed, advance to ShutdownState. - virtual void ShutdownParticipant(std::string reason) = 0; - - virtual void AbortSimulation(std::string reason) = 0; - virtual void ResolveAbortSimulation(std::string reason) = 0; - - virtual void Error(std::string reason) = 0; - - virtual auto toString() -> std::string = 0; - virtual auto GetParticipantState() -> ParticipantState = 0; - - // Currently not implemented - virtual void RestartParticipant(std::string reason) = 0; -}; - -} // namespace Orchestration -} // namespace Services -} // namespace SilKit diff --git a/SilKit/source/services/orchestration/LifecycleManagement.cpp b/SilKit/source/services/orchestration/LifecycleManagement.cpp index 89c73e2eb..5c68410ef 100644 --- a/SilKit/source/services/orchestration/LifecycleManagement.cpp +++ b/SilKit/source/services/orchestration/LifecycleManagement.cpp @@ -6,8 +6,8 @@ #include "LifecycleManagement.hpp" #include "LifecycleService.hpp" -#include "LifecycleStates.hpp" #include "TimeSyncService.hpp" +#include "procs/IParticipantReplies.hpp" namespace SilKit { namespace Services { @@ -18,58 +18,44 @@ LifecycleManagement::LifecycleManagement(Core::IParticipantInternal* participant : _participant{participant} , _lifecycleService(parentService) , _logger(logger) + , _stateMachine{*this} { - _invalidState = std::make_shared(this); - _servicesCreatedState = std::make_shared(this); - _communicationInitializingState = std::make_shared(this); - _communicationInitializedState = std::make_shared(this); - _readyToRunState = std::make_shared(this); - _runningState = std::make_shared(this); - _pausedState = std::make_shared(this); - _stoppingState = std::make_shared(this); - _stoppedState = std::make_shared(this); - _shuttingDownState = std::make_shared(this); - _shutDownState = std::make_shared(this); - _errorState = std::make_shared(this); - _abortingState = std::make_shared(this); - - _currentState = _invalidState.get(); } // ILifecycleManagement void LifecycleManagement::Initialize(std::string reason) { - _currentState->Initialize(std::move(reason)); + _stateMachine.Initialize(std::move(reason)); } void LifecycleManagement::ServicesCreated(std::string reason) { - _currentState->ServicesCreated(std::move(reason)); + _stateMachine.ServicesCreated(std::move(reason)); } void LifecycleManagement::CommunicationInitialized(std::string reason) { - _currentState->CommunicationInitialized(std::move(reason)); + _stateMachine.CommunicationInitialized(std::move(reason)); } void LifecycleManagement::CompleteCommunicationReadyHandler(std::string reason) { - _currentState->CompleteCommunicationReadyHandler(std::move(reason)); + _stateMachine.CompleteCommunicationReadyHandler(std::move(reason)); } void LifecycleManagement::ReadyToRun(std::string reason) { - _currentState->ReadyToRun(std::move(reason)); + _stateMachine.ReadyToRun(std::move(reason)); } void LifecycleManagement::Restart(std::string reason) { - _currentState->RestartParticipant(std::move(reason)); + _stateMachine.Restart(std::move(reason)); } void LifecycleManagement::Shutdown(std::string reason) { - _currentState->ShutdownParticipant(std::move(reason)); + _stateMachine.Shutdown(std::move(reason)); } void LifecycleManagement::NotifyShutdownInConnection() @@ -79,51 +65,33 @@ void LifecycleManagement::NotifyShutdownInConnection() void LifecycleManagement::Pause(std::string reason) { - _currentState->PauseSimulation(std::move(reason)); + _stateMachine.Pause(std::move(reason)); } void LifecycleManagement::Continue(std::string reason) { - _currentState->ContinueSimulation(std::move(reason)); + _stateMachine.Continue(std::move(reason)); } void LifecycleManagement::Stop(std::string reason) { - _currentState->StopSimulation(std::move(reason)); -} - -void LifecycleManagement::ResolveAbortSimulation(std::string reason) -{ - _currentState->ResolveAbortSimulation(reason); - ShutdownAfterAbort(std::move(reason)); -} - -void LifecycleManagement::RestartAfterStop(std::string reason) -{ - // for now, the participant will always shut down after stopping - _lifecycleService->Restart(std::move(reason)); -} - -void LifecycleManagement::ShutdownAfterAbort(std::string reason) -{ - // for now, the participant will always shut down after stopping - Shutdown(std::move(reason)); + _stateMachine.Stop(std::move(reason)); } void LifecycleManagement::StartAutonomous(std::string reason) { - _currentState->ServicesCreated(reason); + _stateMachine.StartAutonomous(std::move(reason)); } void LifecycleManagement::Error(std::string reason) { - _currentState->Error(std::move(reason)); + _stateMachine.Error(std::move(reason)); } void LifecycleManagement::AbortSimulation(std::string reason) { - _currentState->AbortSimulation(std::move(reason)); - if (_currentState == GetErrorState()) + _stateMachine.AbortSimulation(std::move(reason)); + if (_stateMachine.CurrentState() == LifecycleState::Error) { GetLogger()->Warn("AbortSimulation caused a transition to an error state"); } @@ -131,7 +99,7 @@ void LifecycleManagement::AbortSimulation(std::string reason) void LifecycleManagement::CommunicationInitializing(std::string reason) { - _currentState->CommunicationInitializing(std::move(reason)); + _stateMachine.CommunicationInitializing(std::move(reason)); } // Callback handling @@ -206,171 +174,72 @@ bool LifecycleManagement::HandleShutdown() } } -bool LifecycleManagement::HandleAbort() -{ - if (!_lastBeforeAbortingState) - { - throw SilKit::StateError("Abort handler was about to be triggered without knowing which state was active " - "before abort was called."); - } - - try - { - _lifecycleService->TriggerAbortHandler(_lastBeforeAbortingState->GetParticipantState()); - return true; - } - catch (const std::exception& e) - { - std::stringstream ss; - ss << "Detected exception in callback:\n" << e.what(); - _logger->Warn(ss.str()); - return false; - } -} - -void LifecycleManagement::StartTime() -{ - (dynamic_cast(_lifecycleService->GetTimeSyncService()))->StartTime(); -} - -void LifecycleManagement::StopTime() -{ - (dynamic_cast(_lifecycleService->GetTimeSyncService()))->StopTime(); -} - void LifecycleManagement::AddAsyncSubscriptionsCompletionHandler(std::function handler) { _lifecycleService->AddAsyncSubscriptionsCompletionHandler(std::move(handler)); } -void LifecycleManagement::SetState(ILifecycleState* newState, std::string reason) -{ - UpdateLifecycleState(newState); - UpdateParticipantState(std::move(reason)); -} - -void LifecycleManagement::SetStateAndForwardIntent(ILifecycleState* newState, - void (ILifecycleState::*intent)(std::string), std::string reason) -{ - UpdateLifecycleState(newState); - // NB: UpdateParticipantState can alter _currentState if the ParticipantState change causes a SystemState change. - // This addressed by NOPs in the new state for the original intent. - UpdateParticipantState(reason); - (_currentState->*intent)(std::move(reason)); -} - -void LifecycleManagement::UpdateLifecycleState(ILifecycleState* newState) -{ - std::unique_lock lock{_mutex}; - - if (newState == GetAbortingState()) - { - _lastBeforeAbortingState = _currentState; - } - _currentState = newState; -} - -void LifecycleManagement::UpdateParticipantState(std::string reason) +auto LifecycleManagement::CurrentState() const -> LifecycleState { - _lifecycleService->ChangeParticipantState(_currentState->GetParticipantState(), std::move(reason)); + return _stateMachine.CurrentState(); } -ILifecycleState* LifecycleManagement::GetCurrentState() -{ - std::unique_lock lock{_mutex}; - return _currentState; -} - -ILifecycleState* LifecycleManagement::GetInvalidState() -{ - return _invalidState.get(); -} - -ILifecycleState* LifecycleManagement::GetOperationalState() -{ - return _operationalState.get(); -} - -ILifecycleState* LifecycleManagement::GetErrorState() -{ - return _errorState.get(); -} - -ILifecycleState* LifecycleManagement::GetAbortingState() -{ - return _abortingState.get(); -} - -ILifecycleState* LifecycleManagement::GetServicesCreatedState() -{ - return _servicesCreatedState.get(); -} - -ILifecycleState* LifecycleManagement::GetCommunicationInitializingState() -{ - return _communicationInitializingState.get(); -} - -ILifecycleState* LifecycleManagement::GetCommunicationInitializedState() +OperationMode LifecycleManagement::GetOperationMode() const { - return _communicationInitializedState.get(); + return _lifecycleService->GetOperationMode(); } -ILifecycleState* LifecycleManagement::GetReadyToRunState() +auto LifecycleManagement::IsTimeSyncActive() const -> bool { - return _readyToRunState.get(); + return _lifecycleService->IsTimeSyncActive(); } -ILifecycleState* LifecycleManagement::GetRunningState() +void LifecycleManagement::ChangeParticipantState(ParticipantState newState, std::string reason) { - return _runningState.get(); + _lifecycleService->ChangeParticipantState(newState, std::move(reason)); } -ILifecycleState* LifecycleManagement::GetPausedState() +auto LifecycleManagement::HandleAbort(ParticipantState lastState) -> bool { - return _pausedState.get(); + try + { + _lifecycleService->TriggerAbortHandler(lastState); + return true; + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "Detected exception in callback:\n" << e.what(); + _logger->Warn(ss.str()); + return false; + } } -ILifecycleState* LifecycleManagement::GetStoppingState() +void LifecycleManagement::StartTime() { - return _stoppingState.get(); + (dynamic_cast(_lifecycleService->GetTimeSyncService()))->StartTime(); } -ILifecycleState* LifecycleManagement::GetStoppedState() +void LifecycleManagement::StopTime() { - return _stoppedState.get(); + (dynamic_cast(_lifecycleService->GetTimeSyncService()))->StopTime(); } -ILifecycleState* LifecycleManagement::GetShuttingDownState() +void LifecycleManagement::CallAfterAllParticipantsReplied(std::function handler) { - return _shuttingDownState.get(); + _participant->GetParticipantRepliesProcedure()->CallAfterAllParticipantsReplied(std::move(handler)); } -ILifecycleState* LifecycleManagement::GetShutdownState() +void LifecycleManagement::SetFinalStatePromise() { - return _shutDownState.get(); + _lifecycleService->SetFinalStatePromise(); } -Services::Logging::ILogger* LifecycleManagement::GetLogger() +auto LifecycleManagement::GetLogger() -> Services::Logging::ILogger* { return _logger; } -LifecycleService* LifecycleManagement::GetService() -{ - return _lifecycleService; -} - -OperationMode LifecycleManagement::GetOperationMode() const -{ - return _lifecycleService->GetOperationMode(); -} - -Core::IParticipantInternal* LifecycleManagement::GetParticipant() -{ - return _participant; -} - } // namespace Orchestration } // namespace Services } // namespace SilKit diff --git a/SilKit/source/services/orchestration/LifecycleManagement.hpp b/SilKit/source/services/orchestration/LifecycleManagement.hpp index d5f2a2800..d366b095e 100644 --- a/SilKit/source/services/orchestration/LifecycleManagement.hpp +++ b/SilKit/source/services/orchestration/LifecycleManagement.hpp @@ -4,14 +4,13 @@ #pragma once -#include -#include -#include +#include #include "silkit/services/logging/ILogger.hpp" #include "OrchestrationDatatypes.hpp" -#include "ILifecycleStates.hpp" +#include "LifecycleState.hpp" +#include "LifecycleStateMachine.hpp" #include "IParticipantInternal.hpp" namespace SilKit { @@ -49,90 +48,30 @@ class LifecycleManagement // Autonomous lifecycle state initialization void StartAutonomous(std::string reason); - // Check capabilites, potentially start the wall clock coupling thread and send the initial NextSimTask + auto CurrentState() const -> LifecycleState; + + // Machine effects + OperationMode GetOperationMode() const; + auto IsTimeSyncActive() const -> bool; + void ChangeParticipantState(ParticipantState newState, std::string reason); + auto HandleCommunicationReady() -> CallbackResult; + auto HandleStarting() -> bool; + auto HandleStop() -> bool; + auto HandleShutdown() -> bool; + auto HandleAbort(ParticipantState lastState) -> bool; void StartTime(); - // Potentially stop the wall clock coupling thread void StopTime(); - - // Callback handling - CallbackResult HandleCommunicationReady(); - bool HandleStarting(); - bool HandleStop(); - bool HandleShutdown(); - bool HandleAbort(); - - // Wait for pending subscriptions before advancing from CommunicationInitializing to CommunicationInitialized void AddAsyncSubscriptionsCompletionHandler(std::function handler); - - // Abort handling - void ResolveAbortSimulation(std::string reason); - - // Actions after Stop - void RestartAfterStop(std::string reason); - void ShutdownAfterAbort(std::string reason); - - // Ignore peer disconnects after stop + void CallAfterAllParticipantsReplied(std::function handler); void NotifyShutdownInConnection(); - - // State setter - void SetState(ILifecycleState* newState, std::string message); - - // Set state and trigger an action on the new state. - void SetStateAndForwardIntent(ILifecycleState* nextState, void (ILifecycleState::*intent)(std::string), - std::string reason); - - // State getter - ILifecycleState* GetCurrentState(); - ILifecycleState* GetInvalidState(); - ILifecycleState* GetOperationalState(); - ILifecycleState* GetErrorState(); - ILifecycleState* GetAbortingState(); - ILifecycleState* GetServicesCreatedState(); - ILifecycleState* GetCommunicationInitializingState(); - ILifecycleState* GetCommunicationInitializedState(); - ILifecycleState* GetReadyToRunState(); - ILifecycleState* GetRunningState(); - ILifecycleState* GetPausedState(); - ILifecycleState* GetStoppingState(); - ILifecycleState* GetStoppedState(); - ILifecycleState* GetShuttingDownState(); - ILifecycleState* GetShutdownState(); - - // Property getters - OperationMode GetOperationMode() const; - - // Interface getters - Logging::ILogger* GetLogger(); - LifecycleService* GetService(); - Core::IParticipantInternal* GetParticipant(); - -private: - void UpdateLifecycleState(ILifecycleState* newState); - void UpdateParticipantState(std::string reason); + void SetFinalStatePromise(); + auto GetLogger() -> Logging::ILogger*; Core::IParticipantInternal* _participant{nullptr}; - - std::shared_ptr _invalidState; - std::shared_ptr _operationalState; - std::shared_ptr _errorState; - std::shared_ptr _abortingState; - std::shared_ptr _servicesCreatedState; - std::shared_ptr _communicationInitializingState; - std::shared_ptr _communicationInitializedState; - std::shared_ptr _readyToRunState; - std::shared_ptr _runningState; - std::shared_ptr _pausedState; - std::shared_ptr _stoppingState; - std::shared_ptr _stoppedState; - std::shared_ptr _shuttingDownState; - std::shared_ptr _shutDownState; - - ILifecycleState* _currentState; - ILifecycleState* _lastBeforeAbortingState{nullptr}; LifecycleService* _lifecycleService; Services::Logging::ILogger* _logger; - std::recursive_mutex _mutex; + LifecycleStateMachine _stateMachine; }; } // namespace Orchestration diff --git a/SilKit/source/services/orchestration/LifecycleService.cpp b/SilKit/source/services/orchestration/LifecycleService.cpp index e946dc344..ac267acf8 100644 --- a/SilKit/source/services/orchestration/LifecycleService.cpp +++ b/SilKit/source/services/orchestration/LifecycleService.cpp @@ -513,8 +513,8 @@ void LifecycleService::NewSystemState(SystemState systemState) case SystemState::Stopping: _logger->Info("Simulation is stopping"); // Only allow external stop signal if we are actually running or paused - if (_lifecycleManager.GetCurrentState() == _lifecycleManager.GetRunningState() - || _lifecycleManager.GetCurrentState() == _lifecycleManager.GetPausedState()) + if (_lifecycleManager.CurrentState() == LifecycleState::Running + || _lifecycleManager.CurrentState() == LifecycleState::Paused) { _lifecycleManager.Stop(ss.str()); } diff --git a/SilKit/source/services/orchestration/LifecycleState.hpp b/SilKit/source/services/orchestration/LifecycleState.hpp new file mode 100644 index 000000000..ec51a9b75 --- /dev/null +++ b/SilKit/source/services/orchestration/LifecycleState.hpp @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2024 Vector Informatik GmbH +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "silkit/services/orchestration/OrchestrationDatatypes.hpp" + +namespace SilKit { +namespace Services { +namespace Orchestration { + +enum class CallbackResult +{ + Error, + Completed, + Deferred +}; + +enum class LifecycleState +{ + Invalid, + ServicesCreated, + CommunicationInitializing, + CommunicationInitialized, + ReadyToRun, + Running, + Paused, + Stopping, + Stopped, + ShuttingDown, + Shutdown, + Aborting, + Error, +}; + +inline auto ToParticipantState(LifecycleState state) -> ParticipantState +{ + switch (state) + { + case LifecycleState::Invalid: + return ParticipantState::Invalid; + case LifecycleState::ServicesCreated: + return ParticipantState::ServicesCreated; + case LifecycleState::CommunicationInitializing: + return ParticipantState::CommunicationInitializing; + case LifecycleState::CommunicationInitialized: + return ParticipantState::CommunicationInitialized; + case LifecycleState::ReadyToRun: + return ParticipantState::ReadyToRun; + case LifecycleState::Running: + return ParticipantState::Running; + case LifecycleState::Paused: + return ParticipantState::Paused; + case LifecycleState::Stopping: + return ParticipantState::Stopping; + case LifecycleState::Stopped: + return ParticipantState::Stopped; + case LifecycleState::ShuttingDown: + return ParticipantState::ShuttingDown; + case LifecycleState::Shutdown: + return ParticipantState::Shutdown; + case LifecycleState::Aborting: + return ParticipantState::Aborting; + case LifecycleState::Error: + return ParticipantState::Error; + } + + return ParticipantState::Invalid; +} + +inline auto to_string(LifecycleState state) -> std::string +{ + switch (state) + { + case LifecycleState::Invalid: + return "Invalid"; + case LifecycleState::ServicesCreated: + return "ServicesCreated"; + case LifecycleState::CommunicationInitializing: + return "CommunicationInitializing"; + case LifecycleState::CommunicationInitialized: + return "CommunicationInitialized"; + case LifecycleState::ReadyToRun: + return "ReadyToRun"; + case LifecycleState::Running: + return "Running"; + case LifecycleState::Paused: + return "Paused"; + case LifecycleState::Stopping: + return "Stopping"; + case LifecycleState::Stopped: + return "Stopped"; + case LifecycleState::ShuttingDown: + return "ShuttingDown"; + case LifecycleState::Shutdown: + return "Shutdown"; + case LifecycleState::Aborting: + return "Aborting"; + case LifecycleState::Error: + return "Error"; + } + + return "Invalid"; +} + +} // namespace Orchestration +} // namespace Services +} // namespace SilKit diff --git a/SilKit/source/services/orchestration/LifecycleStateMachine.hpp b/SilKit/source/services/orchestration/LifecycleStateMachine.hpp new file mode 100644 index 000000000..add8bb91f --- /dev/null +++ b/SilKit/source/services/orchestration/LifecycleStateMachine.hpp @@ -0,0 +1,635 @@ +// SPDX-FileCopyrightText: 2024 Vector Informatik GmbH +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include "LifecycleState.hpp" +#include "silkit/participant/exception.hpp" + +namespace SilKit { +namespace Services { +namespace Orchestration { + +template +class LifecycleStateMachine +{ +public: + explicit LifecycleStateMachine(Effects& effects) + : _effects{effects} + { + } + + auto CurrentState() const -> LifecycleState + { + std::unique_lock lock{_mutex}; + return _state; + } + + void Initialize(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleInitialize, std::move(reason)); + } + + void ServicesCreated(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleServicesCreated, std::move(reason)); + } + + void CommunicationInitializing(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleCommunicationInitializing, std::move(reason)); + } + + void CommunicationInitialized(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleCommunicationInitialized, std::move(reason)); + } + + void CompleteCommunicationReadyHandler(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleCompleteCommunicationReadyHandler, std::move(reason)); + } + + void ReadyToRun(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleReadyToRun, std::move(reason)); + } + + void RunSimulation(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleRunSimulation, std::move(reason)); + } + + void Pause(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandlePause, std::move(reason)); + } + + void Continue(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleContinue, std::move(reason)); + } + + void Stop(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleStop, std::move(reason)); + } + + void Shutdown(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleShutdown, std::move(reason)); + } + + void AbortSimulation(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleAbortSimulation, std::move(reason)); + } + + void ResolveAbortSimulation(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleResolveAbortSimulation, std::move(reason)); + } + + void Error(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleError, std::move(reason)); + } + + void Restart(std::string reason) + { + Dispatch(&LifecycleStateMachine::HandleRestart, std::move(reason)); + } + + void StartAutonomous(std::string reason) + { + ServicesCreated(std::move(reason)); + } + +private: + using Handler = void (LifecycleStateMachine::*)(std::string); + +private: + void Dispatch(Handler handler, std::string reason) + { + std::unique_lock lock{_mutex}; + (this->*handler)(std::move(reason)); + } + + void TransitionTo(LifecycleState newState, std::string reason) + { + UpdateState(newState); + PublishCurrentState(std::move(reason)); + } + + void TransitionToAndForward(LifecycleState newState, Handler handler, std::string reason) + { + UpdateState(newState); + PublishCurrentState(reason); + (this->*handler)(std::move(reason)); + } + + void UpdateState(LifecycleState newState) + { + if (newState == LifecycleState::Aborting) + { + _lastBeforeAbortingState = _state; + } + + _state = newState; + } + + void PublishCurrentState(std::string reason) + { + _effects.ChangeParticipantState(ToParticipantState(_state), std::move(reason)); + } + + void InvalidStateTransition(const char* transitionName, bool triggerErrorState, std::string originalReason) + { + std::stringstream ss; + ss << "Detected invalid state transition.\n" + << "Current state: " << to_string(_state) << "\n" + << "Requested transition: " << transitionName << "\n" + << "Original reason: " << originalReason; + + if (triggerErrorState) + { + HandleError(ss.str()); + } + else + { + _effects.GetLogger()->Warn(ss.str()); + } + } + + void ProcessAbortCommandInCallback() + { + auto reason = fmt::format("Received SystemCommand::AbortSimulation during {} callback", to_string(_state)); + ProcessAbortCommand(std::move(reason)); + } + + void ProcessAbortCommand(std::string reason) + { + _commReadyAbortRequested = false; + _startingAbortRequested = false; + _stoppingAbortRequested = false; + TransitionToAndForward(LifecycleState::Aborting, &LifecycleStateMachine::HandleResolveAbortSimulation, + std::move(reason)); + } + + void HandleInitialize(std::string reason) + { + switch (_state) + { + case LifecycleState::Invalid: + TransitionTo(LifecycleState::ServicesCreated, std::move(reason)); + return; + case LifecycleState::Shutdown: + case LifecycleState::Aborting: + case LifecycleState::Error: + return; + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + + void HandleServicesCreated(std::string reason) + { + switch (_state) + { + case LifecycleState::ServicesCreated: + _effects.CallAfterAllParticipantsReplied([this, reason]() mutable { + std::unique_lock lock{_mutex}; + TransitionToAndForward(LifecycleState::CommunicationInitializing, + &LifecycleStateMachine::HandleServicesCreated, std::move(reason)); + }); + return; + case LifecycleState::CommunicationInitializing: + HandleCommunicationInitializing(std::move(reason)); + return; + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + + void HandleCommunicationInitializing(std::string reason) + { + switch (_state) + { + case LifecycleState::CommunicationInitializing: + _effects.AddAsyncSubscriptionsCompletionHandler([this, reason]() mutable { + std::unique_lock lock{_mutex}; + TransitionToAndForward(LifecycleState::CommunicationInitialized, + &LifecycleStateMachine::HandleCommunicationInitializing, std::move(reason)); + }); + return; + case LifecycleState::CommunicationInitialized: + if (_effects.GetOperationMode() == OperationMode::Autonomous) + { + HandleCommunicationInitialized("CommunicationInitialized for autonomous participant."); + } + return; + case LifecycleState::ReadyToRun: + case LifecycleState::Running: + return; + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + + void HandleCommunicationInitialized(std::string reason) + { + switch (_state) + { + case LifecycleState::CommunicationInitialized: + _commReadyHandlerExecuting = true; + switch (_effects.HandleCommunicationReady()) + { + case CallbackResult::Error: + if (_commReadyAbortRequested) + { + ProcessAbortCommandInCallback(); + } + else + { + TransitionTo(LifecycleState::Error, "Exception during CommunicationReadyHandle execution."); + } + break; + case CallbackResult::Completed: + if (_commReadyAbortRequested) + { + ProcessAbortCommandInCallback(); + } + else + { + HandleCompleteCommunicationReadyHandler(std::move(reason)); + } + break; + case CallbackResult::Deferred: + _effects.GetLogger()->Debug("Deferred CommunicationReady callback."); + break; + } + _commReadyHandlerExecuting = false; + return; + case LifecycleState::ReadyToRun: + if (_effects.GetOperationMode() == OperationMode::Autonomous) + { + HandleReadyToRun("ReadyToRun for autonomous participant."); + } + return; + case LifecycleState::Shutdown: + return; + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + + void HandleCompleteCommunicationReadyHandler(std::string reason) + { + switch (_state) + { + case LifecycleState::CommunicationInitialized: + TransitionToAndForward(LifecycleState::ReadyToRun, &LifecycleStateMachine::HandleCommunicationInitialized, + std::move(reason)); + return; + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + + void HandleReadyToRun(std::string reason) + { + switch (_state) + { + case LifecycleState::ReadyToRun: + if (!_effects.IsTimeSyncActive()) + { + _startingHandlerExecuting = true; + const auto success = _effects.HandleStarting(); + if (success) + { + if (_startingAbortRequested) + { + ProcessAbortCommandInCallback(); + } + else + { + TransitionTo(LifecycleState::Running, "Finished StartingHandler execution."); + } + } + else + { + if (_startingAbortRequested) + { + ProcessAbortCommandInCallback(); + } + else + { + TransitionTo(LifecycleState::Error, "Exception during StartingHandler execution."); + } + } + _startingHandlerExecuting = false; + } + else + { + if (_startingAbortRequested) + { + ProcessAbortCommandInCallback(); + } + else + { + TransitionToAndForward(LifecycleState::Running, &LifecycleStateMachine::HandleReadyToRun, + std::move(reason)); + } + } + return; + case LifecycleState::Running: + _effects.StartTime(); + return; + case LifecycleState::Shutdown: + return; + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + + void HandleRunSimulation(std::string reason) + { + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + } + + void HandlePause(std::string reason) + { + switch (_state) + { + case LifecycleState::Running: + TransitionTo(LifecycleState::Paused, std::move(reason)); + return; + case LifecycleState::Paused: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + + void HandleContinue(std::string reason) + { + switch (_state) + { + case LifecycleState::Running: + InvalidStateTransition(__FUNCTION__, false, std::move(reason)); + return; + case LifecycleState::Paused: + TransitionTo(LifecycleState::Running, std::move(reason)); + return; + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + + void HandleStop(std::string reason) + { + switch (_state) + { + case LifecycleState::Running: + _effects.StopTime(); + TransitionToAndForward(LifecycleState::Stopping, &LifecycleStateMachine::HandleStop, std::move(reason)); + return; + case LifecycleState::Paused: + TransitionToAndForward(LifecycleState::Stopping, &LifecycleStateMachine::HandleStop, std::move(reason)); + return; + case LifecycleState::Stopping: + if (_effects.HandleStop()) + { + if (_stoppingAbortRequested) + { + ProcessAbortCommandInCallback(); + } + else + { + TransitionToAndForward(LifecycleState::Stopped, &LifecycleStateMachine::HandleStop, std::move(reason)); + } + } + else + { + if (_stoppingAbortRequested) + { + ProcessAbortCommandInCallback(); + } + else + { + TransitionTo(LifecycleState::Error, "Exception during StopHandler execution."); + } + } + return; + case LifecycleState::Stopped: + _effects.CallAfterAllParticipantsReplied([this, reason]() mutable { + std::unique_lock lock{_mutex}; + HandleShutdown(std::move(reason)); + }); + return; + case LifecycleState::ShuttingDown: + case LifecycleState::Shutdown: + return; + case LifecycleState::Error: + HandleShutdown(std::move(reason)); + return; + default: + InvalidStateTransition(__FUNCTION__, false, std::move(reason)); + return; + } + } + + void HandleShutdown(std::string reason) + { + switch (_state) + { + case LifecycleState::Stopped: + case LifecycleState::Error: + TransitionToAndForward(LifecycleState::ShuttingDown, &LifecycleStateMachine::HandleShutdown, + std::move(reason)); + return; + case LifecycleState::ShuttingDown: + if (!_effects.HandleShutdown()) + { + _effects.GetLogger()->Warn( + "ShutdownHandler threw an exception. This is ignored. The participant will now shut down."); + } + TransitionToAndForward(LifecycleState::Shutdown, &LifecycleStateMachine::HandleShutdown, std::move(reason)); + return; + case LifecycleState::Aborting: + TransitionToAndForward(LifecycleState::Shutdown, &LifecycleStateMachine::HandleShutdown, std::move(reason)); + return; + case LifecycleState::Shutdown: + _effects.CallAfterAllParticipantsReplied([this, reason]() mutable { + std::unique_lock lock{_mutex}; + if (_state != LifecycleState::Error) + { + _effects.NotifyShutdownInConnection(); + _effects.SetFinalStatePromise(); + } + else + { + _effects.GetLogger()->Warn( + fmt::format("lifecycle failed to shut down correctly - original shutdown reason was '{}'.", + reason)); + } + }); + return; + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + + void HandleAbortSimulation(std::string reason) + { + switch (_state) + { + case LifecycleState::Invalid: + case LifecycleState::ServicesCreated: + case LifecycleState::CommunicationInitializing: + case LifecycleState::Stopped: + case LifecycleState::Error: + HandleResolveAbortSimulation(std::move(reason)); + return; + case LifecycleState::CommunicationInitialized: + if (_commReadyHandlerExecuting) + { + _commReadyAbortRequested = true; + } + else + { + HandleResolveAbortSimulation("Received SystemCommand::AbortSimulation."); + } + return; + case LifecycleState::ReadyToRun: + if (_startingHandlerExecuting) + { + _startingAbortRequested = true; + } + else + { + HandleResolveAbortSimulation(std::move(reason)); + } + return; + case LifecycleState::Running: + _effects.StopTime(); + HandleResolveAbortSimulation(std::move(reason)); + return; + case LifecycleState::Paused: + HandleResolveAbortSimulation("Received abort simulation."); + return; + case LifecycleState::Stopping: + _stoppingAbortRequested = true; + return; + case LifecycleState::ShuttingDown: + HandleResolveAbortSimulation(std::string{}); + return; + case LifecycleState::Shutdown: + HandleResolveAbortSimulation(std::string{}); + return; + case LifecycleState::Aborting: + return; + } + } + + void HandleResolveAbortSimulation(std::string reason) + { + switch (_state) + { + case LifecycleState::Invalid: + case LifecycleState::ServicesCreated: + case LifecycleState::CommunicationInitializing: + case LifecycleState::CommunicationInitialized: + case LifecycleState::ReadyToRun: + case LifecycleState::Running: + case LifecycleState::Paused: + case LifecycleState::Stopping: + case LifecycleState::Stopped: + case LifecycleState::Error: + ProcessAbortCommand(std::move(reason)); + return; + case LifecycleState::ShuttingDown: + _effects.GetLogger()->Info("Received abort signal while shutting down - ignoring abort."); + return; + case LifecycleState::Shutdown: + _effects.GetLogger()->Info("Received abort signal after shutdown - ignoring abort."); + return; + case LifecycleState::Aborting: + if (_effects.HandleAbort(ToParticipantState(_lastBeforeAbortingState))) + { + // NOP + } + else + { + _effects.GetLogger()->Warn( + "ShutdownHandler threw an exception. This is ignored. The participant will now shut down."); + } + HandleShutdown(std::move(reason)); + return; + } + } + + void HandleError(std::string reason) + { + switch (_state) + { + case LifecycleState::Shutdown: + return; + case LifecycleState::Error: + _effects.GetLogger()->Warn("Received error transition within error state. Original reason: " + + std::move(reason)); + return; + default: + TransitionTo(LifecycleState::Error, std::move(reason)); + return; + } + } + + void HandleRestart(std::string reason) + { + switch (_state) + { + case LifecycleState::Stopped: + case LifecycleState::Error: + throw SilKitError("Restart is currently not supported."); + default: + InvalidStateTransition(__FUNCTION__, true, std::move(reason)); + return; + } + } + +private: + Effects& _effects; + mutable std::recursive_mutex _mutex; + LifecycleState _state{LifecycleState::Invalid}; + LifecycleState _lastBeforeAbortingState{LifecycleState::Invalid}; + bool _commReadyHandlerExecuting{false}; + bool _commReadyAbortRequested{false}; + bool _startingHandlerExecuting{false}; + bool _startingAbortRequested{false}; + bool _stoppingAbortRequested{false}; +}; + +} // namespace Orchestration +} // namespace Services +} // namespace SilKit diff --git a/SilKit/source/services/orchestration/LifecycleStates.cpp b/SilKit/source/services/orchestration/LifecycleStates.cpp deleted file mode 100644 index c4c4543af..000000000 --- a/SilKit/source/services/orchestration/LifecycleStates.cpp +++ /dev/null @@ -1,823 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Vector Informatik GmbH -// -// SPDX-License-Identifier: MIT - -#include - -#include "LoggerMessage.hpp" -#include "LifecycleStates.hpp" -#include "LifecycleService.hpp" -#include "LifecycleManagement.hpp" -#include "IRequestReplyService.hpp" -#include "procs/IParticipantReplies.hpp" - -namespace SilKit { -namespace Services { -namespace Orchestration { - -// ------------------------------------ -// State -// ------------------------------------ - -void State::Initialize(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::ServicesCreated(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::CommunicationInitializing(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::CommunicationInitialized(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::CompleteCommunicationReadyHandler(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::ReadyToRun(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::RunSimulation(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::PauseSimulation(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::ContinueSimulation(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::StopSimulation(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, false, std::move(reason)); -} - -void State::RestartParticipant(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::ShutdownParticipant(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void State::Error(std::string reason) -{ - _lifecycleManager->SetState(_lifecycleManager->GetErrorState(), std::move(reason)); -} - -void State::InvalidStateTransition(std::string transitionName, bool triggerErrorState, std::string originalReason) -{ - auto currentState = toString(); - std::stringstream ss; - ss << "Detected invalid state transition.\n" - << "Current state: " << currentState << "\n" - << "Requested transition: " << transitionName << "\n" - << "Original reason: " << originalReason; - - if (triggerErrorState) - { - _lifecycleManager->Error(ss.str()); - } - else - { - _lifecycleManager->GetLogger()->Warn(ss.str()); - } -} - -bool State::IsAnyOf(SystemState state, std::initializer_list stateList) -{ - return std::any_of(begin(stateList), end(stateList), [=](auto candidate) { return candidate == state; }); -} - -void State::ProcessAbortCommandInCallback() -{ - auto reason = fmt::format("Received SystemCommand::AbortSimulation during {} callback", toString()); - ProcessAbortCommand(std::move(reason)); -} - -void State::ProcessAbortCommand(std::string reason) -{ - _abortRequested = false; - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetAbortingState(), - &ILifecycleState::ResolveAbortSimulation, std::move(reason)); -} - -// ------------------------------------ -// InvalidState -// ------------------------------------ - -void InvalidState::Initialize(std::string reason) -{ - _lifecycleManager->SetState(_lifecycleManager->GetServicesCreatedState(), std::move(reason)); -} - -void InvalidState::AbortSimulation(std::string reason) -{ - ResolveAbortSimulation(std::move(reason)); -} - -void InvalidState::ResolveAbortSimulation(std::string reason) -{ - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetAbortingState(), - &ILifecycleState::ResolveAbortSimulation, std::move(reason)); -} - -std::string InvalidState::toString() -{ - return "Invalid"; -} - -auto InvalidState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::Invalid; -} - -// ------------------------------------ -// ServicesCreatedState -// ------------------------------------ - -void ServicesCreatedState::ServicesCreated(std::string reason) -{ - // ServicesCreated will only advance to CommunicationInitializing after receiving replies from all participants. - // This guarantees that the ServiceDiscoveryEvents sent before all have arrived and internal pubsub/rpc controllers - // have been created. Then, the state machine will only advance to CommunicationInitialized after all pending - // subscriptions have been received. - _lifecycleManager->GetParticipant()->GetParticipantRepliesProcedure()->CallAfterAllParticipantsReplied( - [reason, this]() { - // If done, move forward to CommunicationInitializingState and call ServicesCreated - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetCommunicationInitializingState(), - &ILifecycleState::ServicesCreated, reason); - }); -} - -void ServicesCreatedState::AbortSimulation(std::string reason) -{ - ResolveAbortSimulation(std::move(reason)); -} - -void ServicesCreatedState::ResolveAbortSimulation(std::string reason) -{ - // Skip stopping as the simulation was not running yet - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetAbortingState(), - &ILifecycleState::ResolveAbortSimulation, std::move(reason)); -} - -auto ServicesCreatedState::toString() -> std::string -{ - return "ServicesCreated"; -} - -auto ServicesCreatedState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::ServicesCreated; -} - -// ------------------------------------ -// CommunicationInitializingState -// ------------------------------------ - -void CommunicationInitializingState::ServicesCreated(std::string reason) -{ - _lifecycleManager->CommunicationInitializing(std::move(reason)); -} - -void CommunicationInitializingState::CommunicationInitializing(std::string reason) -{ - // Delay the next state until pending subscriptions of controllers are completed. - // Applies to all controllers that are included in the trait UseAsyncRegistration(). - _lifecycleManager->AddAsyncSubscriptionsCompletionHandler([reason, this]() { - // If done, move forward to CommunicationInitializedState and call CommunicationInitializing - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetCommunicationInitializedState(), - &ILifecycleState::CommunicationInitializing, reason); - }); -} - -void CommunicationInitializingState::AbortSimulation(std::string reason) -{ - ResolveAbortSimulation(std::move(reason)); -} - -void CommunicationInitializingState::ResolveAbortSimulation(std::string reason) -{ - // Skip stopping as the simulation was not running yet - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetAbortingState(), - &ILifecycleState::ResolveAbortSimulation, std::move(reason)); -} - -auto CommunicationInitializingState::toString() -> std::string -{ - return "CommunicationInitializing"; -} - -auto CommunicationInitializingState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::CommunicationInitializing; -} - -// ------------------------------------ -// CommunicationInitializedState -// ------------------------------------ - -void CommunicationInitializedState::CommunicationInitializing(std::string /*reason*/) -{ - // Autonomous advance, coordinated wait for SystemState Change - if (_lifecycleManager->GetOperationMode() == OperationMode::Autonomous) - { - // Trigger the CommunicationReadyHandler (which can be completed asynchronously) - _lifecycleManager->CommunicationInitialized("CommunicationInitialized for autonomous participant."); - } -} - -void CommunicationInitializedState::CommunicationInitialized(std::string reason) -{ - _handlerExecuting = true; - auto callbackResult = _lifecycleManager->HandleCommunicationReady(); - switch (callbackResult) - { - case SilKit::Services::Orchestration::CallbackResult::Error: - if (_abortRequested) - { - ProcessAbortCommandInCallback(); - } - else - { - // Switch to error state if handle triggers error - _lifecycleManager->SetState(_lifecycleManager->GetErrorState(), - "Exception during CommunicationReadyHandle execution."); - } - break; - case SilKit::Services::Orchestration::CallbackResult::Completed: - if (_abortRequested) - { - ProcessAbortCommandInCallback(); - } - else - { - CompleteCommunicationReadyHandler(std::move(reason)); - } - break; - case SilKit::Services::Orchestration::CallbackResult::Deferred: - _lifecycleManager->GetLogger()->Debug("Deferred CommunicationReady callback."); - break; - default: - break; - } - _handlerExecuting = false; -} - -void CommunicationInitializedState::CompleteCommunicationReadyHandler(std::string reason) -{ - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetReadyToRunState(), - &ILifecycleState::CommunicationInitialized, reason); -} - - -void CommunicationInitializedState::AbortSimulation(std::string /*reason*/) -{ - if (_handlerExecuting) - { - _abortRequested = true; - } - else - { - // if we are still waiting for a system state update and receive an abort command, execute immediately. - ResolveAbortSimulation("Received SystemCommand::AbortSimulation."); - } -} - -void CommunicationInitializedState::ResolveAbortSimulation(std::string reason) -{ - ProcessAbortCommand(std::move(reason)); -} - -auto CommunicationInitializedState::toString() -> std::string -{ - return "CommunicationInitialized"; -} - -auto CommunicationInitializedState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::CommunicationInitialized; -} - -// ------------------------------------ -// ReadyToRunState -// ------------------------------------ - -void ReadyToRunState::CommunicationInitializing(std::string /*reason*/) -{ - // NOP for possible concurrent SystemState transition -} - -void ReadyToRunState::CommunicationInitialized(std::string /*reason*/) -{ - if (_lifecycleManager->GetOperationMode() == OperationMode::Autonomous) - { - _lifecycleManager->ReadyToRun("ReadyToRun for autonomous participant."); - } -} - -void ReadyToRunState::ReadyToRun(std::string reason) -{ - if (!_lifecycleManager->GetService()->IsTimeSyncActive()) - { - std::stringstream ss; - ss << "Participant is about to start running and virtual time synchronization is inactive"; - - _handlerExecuting = true; - auto success = _lifecycleManager->HandleStarting(); - if (success) - { - if (_abortRequested) - { - ProcessAbortCommandInCallback(); - } - else - { - _lifecycleManager->SetState(_lifecycleManager->GetRunningState(), - "Finished StartingHandler execution."); - } - } - else - { - if (_abortRequested) - { - ProcessAbortCommandInCallback(); - } - else - { - // Switch to error state if handle triggers error - _lifecycleManager->SetState(_lifecycleManager->GetErrorState(), - "Exception during StartingHandler execution."); - } - } - _handlerExecuting = false; - } - else - { - if (_abortRequested) - { - ProcessAbortCommandInCallback(); - } - else - { - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetRunningState(), - &ILifecycleState::ReadyToRun, std::move(reason)); - } - } -} - -void ReadyToRunState::AbortSimulation(std::string reason) -{ - if (_handlerExecuting) - { - _abortRequested = true; - } - else - { - // if we are still waiting for a system state update and receive an abort command, execute immediately. - ResolveAbortSimulation(std::move(reason)); - } -} - -void ReadyToRunState::ResolveAbortSimulation(std::string reason) -{ - // Skip stopping as the simulation was not running yet - ProcessAbortCommand(std::move(reason)); -} - -std::string ReadyToRunState::toString() -{ - return "ReadyToRun"; -} - -auto ReadyToRunState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::ReadyToRun; -} - -// ------------------------------------ -// RunningState -// ------------------------------------ - -void RunningState::CommunicationInitializing(std::string /*reason*/) -{ - // NOP for possible concurrent SystemState transition -} - -void RunningState::CommunicationInitialized(std::string /*reason*/) -{ - // NOP for possible concurrent SystemState transition -} - -void RunningState::ReadyToRun(std::string /*reason*/) -{ - _lifecycleManager->StartTime(); -} - -void RunningState::PauseSimulation(std::string reason) -{ - _lifecycleManager->SetState(_lifecycleManager->GetPausedState(), std::move(reason)); -} - -void RunningState::ContinueSimulation(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, false, std::move(reason)); -} - -void RunningState::StopSimulation(std::string reason) -{ - _lifecycleManager->StopTime(); - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetStoppingState(), &ILifecycleState::StopSimulation, - std::move(reason)); -} - -void RunningState::AbortSimulation(std::string reason) -{ - _lifecycleManager->StopTime(); - // TODO handle abort during executeSimStep - // For now, just abort and hope for the best... - ResolveAbortSimulation(std::move(reason)); -} - -void RunningState::ResolveAbortSimulation(std::string reason) -{ - ProcessAbortCommand(std::move(reason)); -} - -auto RunningState::toString() -> std::string -{ - return "Running"; -} - -auto RunningState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::Running; -} - -// ------------------------------------ -// PausedState -// ------------------------------------ - -void PausedState::PauseSimulation(std::string reason) -{ - InvalidStateTransition(__FUNCTION__, true, std::move(reason)); -} - -void PausedState::ContinueSimulation(std::string reason) -{ - _lifecycleManager->SetState(_lifecycleManager->GetRunningState(), std::move(reason)); -} - -void PausedState::StopSimulation(std::string reason) -{ - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetStoppingState(), &ILifecycleState::StopSimulation, - std::move(reason)); -} - -void PausedState::AbortSimulation(std::string /*reason*/) -{ - // TODO handle abort during executeSimStep - // For now, just abort and hope for the best... - ResolveAbortSimulation("Received abort simulation."); -} - -void PausedState::ResolveAbortSimulation(std::string reason) -{ - ProcessAbortCommand(std::move(reason)); -} - -auto PausedState::toString() -> std::string -{ - return "Paused"; -} - -auto PausedState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::Paused; -} - -// ------------------------------------ -// StoppingState -// ------------------------------------ - -void StoppingState::StopSimulation(std::string reason) -{ - auto success = _lifecycleManager->HandleStop(); - if (success) - { - if (_abortRequested) - { - ProcessAbortCommandInCallback(); - } - else - { - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetStoppedState(), - &ILifecycleState::StopSimulation, std::move(reason)); - } - } - else - { - if (_abortRequested) - { - ProcessAbortCommandInCallback(); - } - else - { - // Switch to error state if handle triggers error - _lifecycleManager->SetState(_lifecycleManager->GetErrorState(), "Exception during StopHandler execution."); - } - } -} - -void StoppingState::AbortSimulation(std::string /*reason*/) -{ - _abortRequested = true; -} - -void StoppingState::ResolveAbortSimulation(std::string reason) -{ - ProcessAbortCommand(std::move(reason)); -} - -auto StoppingState::toString() -> std::string -{ - return "Stopping"; -} - -auto StoppingState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::Stopping; -} - -// ------------------------------------ -// StoppedState -// ------------------------------------ - -void StoppedState::StopSimulation(std::string reason) -{ - // StoppedState will only advance to ShuttingDown after receiving replies from all participants. - // This guarantees that the ParticipantState::Stopping has arrived and other participants will - // evaluate the correct SystemState and stop themselves. - _lifecycleManager->GetParticipant()->GetParticipantRepliesProcedure()->CallAfterAllParticipantsReplied( - [this, reason]() { _lifecycleManager->Shutdown(reason); }); -} - -void StoppedState::RestartParticipant(std::string /*reason*/) -{ - throw SilKitError("Restart is currently not supported."); -} - -void StoppedState::ShutdownParticipant(std::string reason) -{ - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetShuttingDownState(), - &ILifecycleState::ShutdownParticipant, std::move(reason)); -} - -void StoppedState::AbortSimulation(std::string reason) -{ - ResolveAbortSimulation(std::move(reason)); -} - -void StoppedState::ResolveAbortSimulation(std::string reason) -{ - ProcessAbortCommand(std::move(reason)); -} - -auto StoppedState::toString() -> std::string -{ - return "Stopped"; -} - -auto StoppedState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::Stopped; -} - -// ------------------------------------ -// ShuttingDownState -// ------------------------------------ - -void ShuttingDownState::StopSimulation(std::string /*reason*/) -{ - // Ignore Stop() in ShuttingDownState -} - -void ShuttingDownState::ShutdownParticipant(std::string reason) -{ - auto success = _lifecycleManager->HandleShutdown(); - if (!success) - { - _lifecycleManager->GetLogger()->Warn( - "ShutdownHandler threw an exception. This is ignored. The participant will now shut down."); - } - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetShutdownState(), - &ILifecycleState::ShutdownParticipant, std::move(reason)); -} - -void ShuttingDownState::AbortSimulation(std::string /*reason*/) -{ - ResolveAbortSimulation(std::string()); -} - -void ShuttingDownState::ResolveAbortSimulation(std::string /*reason*/) -{ - _lifecycleManager->GetLogger()->Info("Received abort signal while shutting down - ignoring abort."); -} - -auto ShuttingDownState::toString() -> std::string -{ - return "ShuttingDown"; -} - -auto ShuttingDownState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::ShuttingDown; -} - -// ------------------------------------ -// ShutdownState -// ------------------------------------ - -void ShutdownState::Initialize(std::string /*reason*/) -{ - // Ignore due to possible invalid transition between InvalidWorkflowConfig->Error->Abort->Shutdown and StartLifecycle->Initialize -} - -void ShutdownState::ReadyToRun(std::string /*reason*/) -{ - // Ignore due to possible race between SystemState update and AbortSimulation -} - -void ShutdownState::CommunicationInitialized(std::string /*reason*/) -{ - // Ignore due to possible race between SystemState update and AbortSimulation -} - -void ShutdownState::StopSimulation(std::string /*reason*/) -{ - // Ignore Stop() in ShutdownState -} - -void ShutdownState::Error(std::string /*reason*/) -{ - // Ignore Error() in ShutdownState -} - -void ShutdownState::ShutdownParticipant(std::string reason) -{ - _lifecycleManager->GetParticipant()->GetParticipantRepliesProcedure()->CallAfterAllParticipantsReplied( - [this, reason]() { - bool success = _lifecycleManager->GetCurrentState() != _lifecycleManager->GetErrorState(); - if (success) - { - _lifecycleManager->NotifyShutdownInConnection(); - _lifecycleManager->GetService()->SetFinalStatePromise(); - } - else - { - Logging::Warn(_lifecycleManager->GetLogger(), - "lifecycle failed to shut down correctly - original shutdown reason was '{}'.", - std::move(reason)); - } - }); -} - -void ShutdownState::AbortSimulation(std::string /*reason*/) -{ - ResolveAbortSimulation(std::string()); -} - -void ShutdownState::ResolveAbortSimulation(std::string /*reason*/) -{ - _lifecycleManager->GetLogger()->Info("Received abort signal after shutdown - ignoring abort."); -} - -auto ShutdownState::toString() -> std::string -{ - return "Shutdown"; -} - -auto ShutdownState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::Shutdown; -} - -// ------------------------------------ -// AbortingState -// ------------------------------------ - -void AbortingState::Initialize(std::string /*reason*/) -{ - // Ignore due to possible invalid transition between InvalidWorkflowConfig->Error->Abort->Shutdown and StartLifecycle->Initialize -} - -void AbortingState::ShutdownParticipant(std::string reason) -{ - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetShutdownState(), - &ILifecycleState::ShutdownParticipant, std::move(reason)); -} - -void AbortingState::AbortSimulation(std::string /*reason*/) -{ - // NOP: Ignore AbortSimulation() in AbortingState -} - -void AbortingState::ResolveAbortSimulation(std::string reason) -{ - auto success = _lifecycleManager->HandleAbort(); - if (success) - { - // NOP - } - else - { - std::string msg = "ShutdownHandler threw an exception. This is ignored. The participant will now shut down."; - _lifecycleManager->GetLogger()->Warn(msg); - } - _lifecycleManager->ShutdownAfterAbort(std::move(reason)); -} - -auto AbortingState::toString() -> std::string -{ - return "Aborting"; -} - -auto AbortingState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::Aborting; -} - -// ------------------------------------ -// ErrorState -// ------------------------------------ - -void ErrorState::Initialize(std::string /*reason*/) -{ - // Ignore due to possible invalid transition between InvalidWorkflowConfig->Error->Abort->Shutdown and StartLifecycle->Initialize -} - -void ErrorState::RestartParticipant(std::string /*reason*/) -{ - throw SilKitError("Restart feature is currently not supported."); -} - -void ErrorState::StopSimulation(std::string reason) -{ - _lifecycleManager->Shutdown(std::move(reason)); -} - -void ErrorState::ShutdownParticipant(std::string reason) -{ - _lifecycleManager->SetStateAndForwardIntent(_lifecycleManager->GetShuttingDownState(), - &ILifecycleState::ShutdownParticipant, std::move(reason)); -} - -void ErrorState::AbortSimulation(std::string reason) -{ - ResolveAbortSimulation(std::move(reason)); -} - -void ErrorState::ResolveAbortSimulation(std::string reason) -{ - ProcessAbortCommand(std::move(reason)); -} - -void ErrorState::Error(std::string reason) -{ - _lifecycleManager->GetLogger()->Warn("Received error transition within error state. Original reason: " - + std::move(reason)); -} - -auto ErrorState::toString() -> std::string -{ - return "Error"; -} - -auto ErrorState::GetParticipantState() -> ParticipantState -{ - return ParticipantState::Error; -} - -} // namespace Orchestration -} // namespace Services -} // namespace SilKit diff --git a/SilKit/source/services/orchestration/LifecycleStates.hpp b/SilKit/source/services/orchestration/LifecycleStates.hpp deleted file mode 100644 index 3b12bf031..000000000 --- a/SilKit/source/services/orchestration/LifecycleStates.hpp +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Vector Informatik GmbH -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -#include "silkit/services/orchestration/OrchestrationDatatypes.hpp" - -#include "ILifecycleStates.hpp" -#include "LifecycleManagement.hpp" - -namespace SilKit { -namespace Services { -namespace Orchestration { - -class State : public ILifecycleState -{ -public: - State(LifecycleManagement* lifecycleManager) - : _lifecycleManager(lifecycleManager) - { - } - -public: - virtual ~State() = default; - - // See method description in ILifecycleState - - virtual void Initialize(std::string reason) override; - virtual void ServicesCreated(std::string reason) override; - virtual void CommunicationInitializing(std::string reason) override; - virtual void CommunicationInitialized(std::string reason) override; - virtual void CompleteCommunicationReadyHandler(std::string reason) override; - virtual void ReadyToRun(std::string reason) override; - virtual void RunSimulation(std::string reason) override; - virtual void PauseSimulation(std::string reason) override; - virtual void ContinueSimulation(std::string reason) override; - virtual void StopSimulation(std::string reason) override; - virtual void RestartParticipant(std::string reason) override; - virtual void ShutdownParticipant(std::string reason) override; - virtual void Error(std::string reason) override; - -protected: - void InvalidStateTransition(std::string transitionName, bool triggerErrorState, std::string originalReason); - bool IsAnyOf(SystemState state, std::initializer_list stateList); - - void ProcessAbortCommandInCallback(); - void ProcessAbortCommand(std::string reason); - -protected: - LifecycleManagement* _lifecycleManager; - bool _abortRequested{false}; -}; - -class InvalidState : public State -{ -public: - InvalidState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void Initialize(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class ServicesCreatedState : public State -{ -public: - ServicesCreatedState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void ServicesCreated(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class CommunicationInitializingState : public State -{ -public: - CommunicationInitializingState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void ServicesCreated(std::string reason) override; - void CommunicationInitializing(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class CommunicationInitializedState : public State -{ -public: - CommunicationInitializedState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void CommunicationInitializing(std::string reason) override; - void CommunicationInitialized(std::string reason) override; - void CompleteCommunicationReadyHandler(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; - -private: - std::atomic _handlerExecuting{false}; -}; - -class ReadyToRunState : public State -{ -public: - ReadyToRunState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void CommunicationInitializing(std::string reason) override; - void CommunicationInitialized(std::string reason) override; - void ReadyToRun(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; - -private: - std::atomic _handlerExecuting{false}; -}; - -class RunningState : public State -{ -public: - RunningState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void CommunicationInitializing(std::string reason) override; - void CommunicationInitialized(std::string reason) override; - void ReadyToRun(std::string reason) override; - void PauseSimulation(std::string reason) override; - void ContinueSimulation(std::string reason) override; - void StopSimulation(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class PausedState : public State -{ -public: - PausedState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void PauseSimulation(std::string reason) override; - void ContinueSimulation(std::string reason) override; - void StopSimulation(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class StoppingState : public State -{ -public: - StoppingState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void StopSimulation(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class StoppedState : public State -{ -public: - StoppedState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void StopSimulation(std::string reason) override; - - void RestartParticipant(std::string reason) override; - void ShutdownParticipant(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class ShuttingDownState : public State -{ -public: - ShuttingDownState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void StopSimulation(std::string reason) override; - - void ShutdownParticipant(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class ShutdownState : public State -{ -public: - ShutdownState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - void Initialize(std::string reason) override; - - void CommunicationInitialized(std::string reason) override; - void ReadyToRun(std::string reason) override; - void StopSimulation(std::string reason) override; - void Error(std::string reason) override; - - void ShutdownParticipant(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class AbortingState : public State -{ -public: - AbortingState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - void Initialize(std::string reason) override; - - void ShutdownParticipant(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; - -class ErrorState : public State -{ -public: - ErrorState(LifecycleManagement* lifecycleManager) - : State(lifecycleManager) - { - } - - void Initialize(std::string reason) override; - - void StopSimulation(std::string reason) override; - void RestartParticipant(std::string reason) override; - void ShutdownParticipant(std::string reason) override; - void Error(std::string reason) override; - - void AbortSimulation(std::string reason) override; - void ResolveAbortSimulation(std::string reason) override; - auto toString() -> std::string override; - auto GetParticipantState() -> ParticipantState override; -}; -} // namespace Orchestration -} // namespace Services -} // namespace SilKit diff --git a/SilKit/source/services/orchestration/Test_LifecycleStateMachine.cpp b/SilKit/source/services/orchestration/Test_LifecycleStateMachine.cpp new file mode 100644 index 000000000..c343bcbf3 --- /dev/null +++ b/SilKit/source/services/orchestration/Test_LifecycleStateMachine.cpp @@ -0,0 +1,185 @@ +// SPDX-FileCopyrightText: 2024 Vector Informatik GmbH +// +// SPDX-License-Identifier: MIT + +#include "gtest/gtest.h" + +#include "LifecycleStateMachine.hpp" + +namespace { + +using namespace SilKit::Services::Orchestration; + +struct FakeEffects +{ + OperationMode operationMode{OperationMode::Coordinated}; + bool timeSyncActive{false}; + CallbackResult communicationReadyResult{CallbackResult::Completed}; + bool startingResult{true}; + bool stopResult{true}; + bool shutdownResult{true}; + bool abortResult{true}; + ParticipantState lastAbortState{ParticipantState::Invalid}; + std::vector publishedStates; + + struct Logger + { + void Warn(const std::string& msg) + { + warnings.push_back(msg); + } + + void Info(const std::string& msg) + { + infos.push_back(msg); + } + + void Debug(const std::string& msg) + { + debugs.push_back(msg); + } + + std::vector warnings; + std::vector infos; + std::vector debugs; + } logger; + + auto GetOperationMode() const -> OperationMode + { + return operationMode; + } + + auto IsTimeSyncActive() const -> bool + { + return timeSyncActive; + } + + void ChangeParticipantState(ParticipantState state, std::string) + { + publishedStates.push_back(state); + } + + auto HandleCommunicationReady() -> CallbackResult + { + return communicationReadyResult; + } + + auto HandleStarting() -> bool + { + return startingResult; + } + + auto HandleStop() -> bool + { + return stopResult; + } + + auto HandleShutdown() -> bool + { + return shutdownResult; + } + + auto HandleAbort(ParticipantState state) -> bool + { + lastAbortState = state; + return abortResult; + } + + void StartTime() + { + startTimeCalled = true; + } + + void StopTime() + { + stopTimeCalled = true; + } + + void AddAsyncSubscriptionsCompletionHandler(std::function handler) + { + asyncSubscriptionHandler = std::move(handler); + asyncSubscriptionHandler(); + } + + void CallAfterAllParticipantsReplied(std::function handler) + { + allParticipantsHandler = std::move(handler); + allParticipantsHandler(); + } + + void NotifyShutdownInConnection() + { + notifyShutdownCalled = true; + } + + void SetFinalStatePromise() + { + finalStatePromiseSet = true; + } + + auto GetLogger() -> Logger* + { + return &logger; + } + + bool startTimeCalled{false}; + bool stopTimeCalled{false}; + bool notifyShutdownCalled{false}; + bool finalStatePromiseSet{false}; + std::function asyncSubscriptionHandler; + std::function allParticipantsHandler; +}; + +TEST(Test_LifecycleStateMachine, autonomous_flow_reaches_running) +{ + FakeEffects effects; + effects.operationMode = OperationMode::Autonomous; + + LifecycleStateMachine machine{effects}; + + machine.Initialize("init"); + machine.StartAutonomous("autonomous"); + + EXPECT_EQ(machine.CurrentState(), LifecycleState::Running); + ASSERT_EQ(effects.publishedStates.size(), 5u); + EXPECT_EQ(effects.publishedStates[0], ParticipantState::ServicesCreated); + EXPECT_EQ(effects.publishedStates[1], ParticipantState::CommunicationInitializing); + EXPECT_EQ(effects.publishedStates[2], ParticipantState::CommunicationInitialized); + EXPECT_EQ(effects.publishedStates[3], ParticipantState::ReadyToRun); + EXPECT_EQ(effects.publishedStates[4], ParticipantState::Running); +} + +TEST(Test_LifecycleStateMachine, abort_reports_last_state_before_aborting) +{ + FakeEffects effects; + LifecycleStateMachine machine{effects}; + + machine.Initialize("init"); + machine.ServicesCreated("services"); + machine.CommunicationInitialized("comm initialized"); + machine.ReadyToRun("ready"); + + ASSERT_EQ(machine.CurrentState(), LifecycleState::Running); + + machine.AbortSimulation("abort"); + + EXPECT_EQ(machine.CurrentState(), LifecycleState::Shutdown); + EXPECT_EQ(effects.lastAbortState, ParticipantState::Running); + EXPECT_TRUE(effects.notifyShutdownCalled); + EXPECT_TRUE(effects.finalStatePromiseSet); +} + +TEST(Test_LifecycleStateMachine, invalid_pause_transitions_to_error) +{ + FakeEffects effects; + LifecycleStateMachine machine{effects}; + + machine.Initialize("init"); + machine.Pause("pause from services created"); + + EXPECT_EQ(machine.CurrentState(), LifecycleState::Error); + ASSERT_FALSE(effects.publishedStates.empty()); + EXPECT_EQ(effects.publishedStates.back(), ParticipantState::Error); +} + +} // namespace