Program Listing for File FSM.h#
↰ Return to documentation for file (include/Karana/KUtils/FSM.h)
/*
* Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
*
* NOTICE TO USER:
*
* This source code and/or documentation (the "Licensed Materials") is
* the confidential and proprietary information of Karana Dynamics Inc.
* Use of these Licensed Materials is governed by the terms and conditions
* of a separate software license agreement between Karana Dynamics and the
* Licensee ("License Agreement"). Unless expressly permitted under that
* agreement, any reproduction, modification, distribution, or disclosure
* of the Licensed Materials, in whole or in part, to any third party
* without the prior written consent of Karana Dynamics is strictly prohibited.
*
* THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
* KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
* FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
* INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
* OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
*
* U.S. Government End Users: The Licensed Materials are a "commercial item"
* as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
* only as a commercial end item under the terms of this license.
*
* Any use of the Licensed Materials in individual or commercial software must
* include, in the user documentation and internal source code comments,
* this Notice, Disclaimer, and U.S. Government Use Provision.
*/
/**
* @file
* @brief Contains the declarations for the FSM class and associated classes.
*/
#pragma once
#include "Karana/KCore/CallbackRegistry.h"
#include "Karana/KCore/LockingBase.h"
#include "Karana/KCore/UsageTrackingMap.h"
namespace Karana::KUtils {
namespace kc = Karana::Core;
// Forward declaration
class FSM;
class FSMTransition;
/**
* @class FSMState
* @brief Represents a state in an FSM.
*
* This is a node in the graph that makes up an FSM.
*/
class FSMState : public Karana::Core::Base {
// For access to _fsm
friend class FSMTransition;
// For access to _transitions
friend class FSM;
public:
/**
* @brief FSMState constructor. The constructor is not meant to be called
* directly. Please use the create(...) method instead to create an instance.
*
* @param name The name of the FSMState. The names for states in an FSM must be unique.
* @param fsm The FSM that this FSMState will be a part of.
*/
FSMState(std::string_view name, const kc::ks_ptr<FSM> &fsm);
/**
* @brief Create a new instance of FSMState.
*
* @param name The name of the FSMState. The names for states in an FSM must be unique.
* @param fsm The FSM that this FSMState will be a part of.
* @returns A pointer to the newly created instance of FSMState.
*/
// Adding suppression, as CodeChecker complains this method has
// the same name as a method in the base version. This is just a
// CodeChecker false positive.
// codechecker_suppress [cppcheck-duplInheritedMember]
static Karana::Core::ks_ptr<FSMState> create(std::string_view name,
const kc::ks_ptr<FSM> &fsm);
/// Functions that execute when entering the state
Karana::Core::CallbackRegistry<void> on_enter_fns;
/// Functions that execute when exiting the state
Karana::Core::CallbackRegistry<void> on_exit_fns;
/// Functions that execute during the state
Karana::Core::CallbackRegistry<void> during_fns;
/**
* @brief Get all the transitions that connect this state to its children.
*
* @returns All the transitions that connect this state to its children.
*/
std::vector<kc::ks_ptr<FSMTransition>> getChildTransitions();
/**
* @brief Get all the transitions that connect this state to its parents.
*
* @returns All the transitions that connect this state to its parents.
*/
std::vector<kc::ks_ptr<FSMTransition>> getParentTransitions();
std::string
dumpString(std::string_view prefix = "",
const Karana::Core::Base::DumpOptions *options = nullptr) const override;
protected:
void _discard(Karana::Core::ks_ptr<Karana::Core::Base> &base) override;
private:
/// The FSM this FSMState is associated with
kc::ks_ptr<FSM> _fsm;
/// The transitions that connect this state to its children states
std::vector<kc::ks_ptr<FSMTransition>> _transitions;
/// The transitions that connect this state to its parent states
std::vector<kc::ks_ptr<FSMTransition>> _parent_transitions;
};
/**
* @class FSMTransition
* @brief An edge in the graph that makes up an FSM.
*/
class FSMTransition : public Karana::Core::Base {
// For access to _transition_fn
friend class FSM;
public:
/**
* @brief FSMTransition constructor. The constructor is not meant to be called
* directly. Please use the create(...) method instead to create an instance.
*
* @param parent_state The parent FSMState of the edge.
* @param child_state The child FSMState of the edge.
* @param should_transition_fn The function that indicates whether this state should
* transition or not. This should return true when it is time to transition, and false when
* it is not. The FSM.update method checks these functions to decide when to transition.
*/
FSMTransition(const kc::ks_ptr<FSMState> &parent_state,
const kc::ks_ptr<FSMState> &child_state,
std::function<bool()> should_transition_fn);
/**
* @brief Create a new instance of FSMTransition.
*
* @param parent_state The parent FSMState of the edge.
* @param child_state The child FSMState of the edge.
* @param should_transition_fn The function that indicates whether this state should
* transition or not. This should return true when it is time to transition, and false when
* it is not. The FSM.update method checks these functions to decide when to transition.
* @returns A pointer to the newly created instance of FSMTransition.
*/
static kc::ks_ptr<FSMTransition> create(const kc::ks_ptr<FSMState> &parent_state,
const kc::ks_ptr<FSMState> &child_state,
std::function<bool()> should_transition_fn);
/// Function that indicates if we should transition. true for yes and false for no.
std::function<bool()> should_transition_fn;
/// Functions that execute when transitioning on this edge
Karana::Core::CallbackRegistry<void> on_transition_fns;
/**
* @brief Get the parent state for this edge.
*
* @returns The parent state for this edge.
*/
const kc::ks_ptr<FSMState> &getParentState() const;
/**
* @brief Get the child state for this edge.
*
* @returns The child state for this edge.
*/
const kc::ks_ptr<FSMState> &getChildState() const;
protected:
void _discard(Karana::Core::ks_ptr<Karana::Core::Base> &base) override;
private:
/// The FSM this FSMState is associated with
kc::ks_ptr<FSM> _fsm;
/// The parent state for this edge
kc::ks_ptr<FSMState> _parent_state;
/// The child state for this edge
kc::ks_ptr<FSMState> _child_state;
};
/**
* @class FSMVars
* @brief Vars for the FSM class.
*/
class FSMVars : public Karana::Core::LockingBaseVars {
public:
/**
* @brief FSMVars constructor. The constructor is not meant to be
* called directly. Please use the create(...) method instead to create
* an instance.
*
* @param fsm The FSM associated with this FSMVars.
*/
FSMVars(const kc::ks_ptr<const FSM> &fsm);
/**
* @brief Create an instance of the FSMVars.
*
* @param fsm The FSM associated with this FSMVars.
* @returns A pointer to the newly created instance of FSMVars.
*/
// Adding suppression, as CodeChecker complains this method has the same name as a method in
// the LockingBase class. This is just a CodeChecker false positive. codechecker_suppress
// [cppcheck-duplInheritedMember]
static Karana::Core::ks_ptr<FSMVars> create(const kc::ks_ptr<const FSM> &fsm);
/**
* @brief Destructor.
*/
~FSMVars();
/**
* @brief Get all the Vars that this FSMVars has.
*
* @returns NestedVars with all the Vars that this FSMVars has.
*/
Karana::Core::NestedVars getAllVars() const override;
/// The current state of the FSM
kc::ks_ptr<kc::Var_T<std::string>> current_state;
};
/**
* @class FSM
* @brief A class to implement finite state machines.
*
* The finite state machine is implemented as a graph that connects FSMStates with
* FSMTransitions.
*/
class FSM : public Karana::Core::LockingBase {
// For access to _trackState and _untrackState
friend class FSMState;
public:
using LockingBase::LockingBase;
/**
* @brief Create a new instance of FSM.
*
* @param name The name of the FSM.
* @returns A pointer to the newly created instance of FSM.
*/
// Adding suppression, as CodeChecker complains this method has the same name as a method in
// the LockingBase class. This is just a CodeChecker false positive.
// codechecker_suppress [cppcheck-duplInheritedMember]
static Karana::Core::ks_ptr<FSM> create(std::string_view name);
/**
* @brief Get the current state.
*
* @returns The current FSMState.
*/
const kc::ks_ptr<FSMState> &getCurrentState() const;
/**
* @brief Set the current state of this FSM to the state with the provided name.
*
* @param name The name of the state to set the FSM to.
*/
void setCurrentState(std::string_view name);
/**
* @brief Set the current state of this FSM to the provided state.
*
* @param state The state to set the FSM to.
*/
void setCurrentState(const Karana::Core::ks_ptr<FSMState> &state);
/**
* @brief Get the state with the provided name.
*
* @param name The name of the state to retrieve.
* @returns The FSMState with the provided name.
*/
const kc::ks_ptr<FSMState> &getState(std::string_view name) const;
/**
* @brief Get the transition that connects the two states with the provided names.
*
* @param parent_state The name of the parent of the edge.
* @param child_state The name of the child of the edge.
* @returns The transition that connects the parent and child states with the names
* provided.
*/
const kc::ks_ptr<FSMTransition> &getTransition(std::string_view parent_state,
std::string_view child_state);
/**
* @brief Get a list of all the FSMStates in this FSM.
*
* @returns A list of all the FSMStates in this FSM.
*/
std::vector<kc::ks_ptr<FSMState>> getStates() const;
/**
* @brief Update the FSM.
*
* This updates the current state using the should_transition_fns of the FSMTransitions for
* the current state.
*/
void update();
/// These functions will run whenever we transition to a new state
Karana::Core::CallbackRegistry<void, const kc::ks_ptr<FSMTransition> &>
on_state_transition_fns;
/**
* @brief Get whether this FSM is printing trace messages or not.
*
* @return true if trace messages are being printed, false otherwise.
*/
bool getTraceFSM();
/**
* @brief Set whether this FSM prints trace messages or not.
*
* @param trace_fsm true to set the FSM to print trace messages,
* false to stop printing trace messages.
*/
void setTraceFSM(bool trace_fsm);
std::string
dumpString(std::string_view prefix = "",
const Karana::Core::Base::DumpOptions *options = nullptr) const override;
protected:
void _discard(Karana::Core::ks_ptr<Karana::Core::Base> &base) override;
void _makeHealthy() override;
Karana::Core::ks_ptr<Karana::Core::BaseVars> _getVars() const override;
private:
/**
* @brief Track the provided state in a UsageTrackingMap.
*
* @param state The state to track.
*/
void _trackState(const kc::ks_ptr<FSMState> &state);
/**
* @brief Untrack the provided state.
*
* This will erase the state from the UsageTrackingMap.
*
* @param state The state to untrack.
*/
void _untrackState(kc::ks_ptr<FSMState> &state);
/**
* @brief Get the state with the provided name.
*
* @param name The name of the state to find.
* @returns The state with the associated name.
* nullptr if a state with the provided name does not exist.
*/
Karana::Core::ks_ptr<FSMState> _findStateWithName(std::string_view name);
/// The current state
FSMState *_current_state = nullptr;
/**
* @brief The state transitions.
*
* The first value in the pair is the upstream state and the second
* value in the pair is the downstream state.
*/
std::vector<std::pair<Karana::Core::id_t, Karana::Core::id_t>> _state_transitions;
/// Usage tracking map for tracking the states
Karana::Core::UsageTrackingMap<Karana::Core::id_t, FSMState> _states;
/**
* @brief Print a standard trace message.
*
* @param msg The message to print.
*/
void _stdTraceMsg(std::string_view msg);
/// If true, then print out trace messages.
bool _trace_fsm = false;
};
} // namespace Karana::KUtils