This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 4662ac5edfe docs/applications/smf: added state machine framework 
example documentation
4662ac5edfe is described below

commit 4662ac5edfec747c4c9c0ecf0703715c2f5b4de9
Author: Felipe Moura <[email protected]>
AuthorDate: Thu Jan 1 21:02:36 2026 -0300

    docs/applications/smf: added state machine framework example documentation
    
    Update Doc - WIP
    
    SFM doc updated - WIP
    
    Finished first documentation release
    
    Update documentation after source code changes
    
    Signed-off-by: Felipe Moura <[email protected]>
---
 Documentation/applications/examples/smf/index.rst  |  59 +++
 .../smf/images/Flat-state-machine-diagram.png      | Bin 0 -> 21645 bytes
 .../images/Hierarchical-state-machine-diagram.png  | Bin 0 -> 32344 bytes
 Documentation/applications/system/smf/index.rst    | 570 +++++++++++++++++++++
 4 files changed, 629 insertions(+)

diff --git a/Documentation/applications/examples/smf/index.rst 
b/Documentation/applications/examples/smf/index.rst
new file mode 100644
index 00000000000..006a92ad092
--- /dev/null
+++ b/Documentation/applications/examples/smf/index.rst
@@ -0,0 +1,59 @@
+==================================================
+``smf`` State Machine Framework HSM PSiCC2 Example
+==================================================
+
+This example implements an event-driven hierarchical state machine using the
+State Machine Framework (SMF). It reproduces the statechart shown in Figure 
2.11
+of Practical UML Statecharts in C/C++, 2nd Edition, by Miro Samek (PSiCC2).
+The ebook is available at https://www.state-machine.com/psicc2.
+
+For each state, the entry, run, and exit actions are logged to the console, as
+well as logging when a state handles an event or explicitly ignores it and
+passes it up to the parent state.
+
+It should be possible to build and run this demo on most boards or emulators
+that support NSH, SMF, and message queues.
+
+Configuration
+=============
+
+- ``CONFIG_EXAMPLES_SMF`` – Enables the SMF PSiCC2 demo.
+- ``CONFIG_NSH_BUILTIN_APPS`` – Build the demo as an NSH built-in application.
+- ``CONFIG_SYSTEM_SMF`` – Enable the State Machine Framework support.
+- ``CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT`` – Enable ancestor/parent state 
support.
+- ``CONFIG_SYSTEM_SMF_INITIAL_TRANSITION`` – Enable initial transition support.
+- ``CONFIG_DISABLE_MQUEUE=n`` – Message queue support must be available.
+- ``CONFIG_EXAMPLES_SMF_PROGNAME`` – Program name, default ``hsm_psicc2``.
+- ``CONFIG_EXAMPLES_SMF_PRIORITY`` – Priority of the SMF task, default ``100``.
+- ``CONFIG_EXAMPLES_SMF_STACKSIZE`` – Stack size of the SMF task, default
+  ``2048``.
+- ``CONFIG_EXAMPLES_SMF_QUEUE_SIZE`` – Size of the message queue, default 
``10``.
+- ``CONFIG_EXAMPLES_SMF_MQ_NAME`` – Name of the message queue, default
+  ``/hsm_psicc2_mq``.
+
+Usage
+=====
+
+The demo registers the ``hsm_psicc2`` NSH command (configurable via
+``CONFIG_EXAMPLES_SMF_PROGNAME``):
+
+.. code-block:: bash
+
+   hsm_psicc2 start
+   hsm_psicc2 event <A..I>
+   hsm_psicc2 terminate
+
+- ``start`` spawns the state machine thread and initializes the SMF context.
+- ``event <A..I>`` sends events A through I to the state machine (PSiCC2
+  Figure 2.11).
+- ``terminate`` stops the state machine thread; there is no way to restart it
+  and further events are not processed.
+
+Comparison to PSiCC2 Output
+===========================
+
+Not all transitions modeled in UML may be supported by the State Machine
+Framework. Unsupported transitions may lead to results different from the
+example run in PSiCC2 Section 2.3.15. The differences are not listed here since
+it is hoped SMF will support these transitions in the future and the list would
+become outdated.
diff --git 
a/Documentation/applications/system/smf/images/Flat-state-machine-diagram.png 
b/Documentation/applications/system/smf/images/Flat-state-machine-diagram.png
new file mode 100644
index 00000000000..3c06c82b6a6
Binary files /dev/null and 
b/Documentation/applications/system/smf/images/Flat-state-machine-diagram.png 
differ
diff --git 
a/Documentation/applications/system/smf/images/Hierarchical-state-machine-diagram.png
 
b/Documentation/applications/system/smf/images/Hierarchical-state-machine-diagram.png
new file mode 100644
index 00000000000..99310a1de5f
Binary files /dev/null and 
b/Documentation/applications/system/smf/images/Hierarchical-state-machine-diagram.png
 differ
diff --git a/Documentation/applications/system/smf/index.rst 
b/Documentation/applications/system/smf/index.rst
new file mode 100644
index 00000000000..179b619e752
--- /dev/null
+++ b/Documentation/applications/system/smf/index.rst
@@ -0,0 +1,570 @@
+.. _smf:
+
+=================================
+``smf`` State Machine Framework
+=================================
+
+Overview
+========
+
+The State Machine Framework (SMF) is a lightweight, application-agnostic
+framework for implementing finite and hierarchical state machines in NuttX.
+
+SMF provides:
+- Deterministic state transition semantics
+- Optional hierarchical state machine (HSM) support
+- Explicit entry, run, and exit actions
+- No dynamic memory allocation
+- Full control over the event loop by the application
+
+The framework is suitable for deeply embedded systems where predictability,
+low overhead, and explicit control flow are required.
+
+Conceptually, this implementation is a direct port of the SMF originally
+introduced in Zephyr RTOS, adapted to NuttX coding standards, build system,
+and documentation conventions.
+
+Architecture
+============
+
+SMF separates responsibilities clearly:
+
+- **Framework responsibilities**
+  - State transitions
+  - Entry/exit sequencing
+  - Hierarchy resolution (LCA)
+  - Termination handling
+
+- **Application responsibilities**
+  - Event acquisition
+  - Event dispatching
+  - State machine scheduling
+  - Data model ownership
+
+This separation ensures that SMF remains fully reusable across applications
+and execution models.
+
+State Model
+===========
+
+Each state is defined by up to three optional callbacks:
+
+- **Entry action**
+- **Run action**
+- **Exit action**
+
+All actions operate on a user-defined object whose first member is
+``struct smf_ctx``.
+
+.. code-block:: c
+
+   struct app_object
+   {
+     struct smf_ctx ctx;
+     /* Application-specific data */
+     int counter;
+     bool error;
+   };
+
+This layout enables zero-cost casting using the ``SMF_CTX()`` macro.
+
+State Machine Creation
+======================
+
+A state machine is created by defining a table of states that’s indexed by an 
enum.
+For example, the following creates three flat states:
+
+.. code-block:: c
+
+   enum demo_state
+   {
+     STATE_IDLE,
+     STATE_ACTIVE,
+     STATE_ERROR,
+   };
+
+   static const struct smf_state demo_states[] = {
+     [STATE_IDLE]   = SMF_CREATE_STATE(idle_entry, idle_run, idle_exit, NULL, 
NULL),
+     [STATE_ACTIVE] = SMF_CREATE_STATE(active_entry, active_run, active_exit, 
NULL, NULL),
+     [STATE_ERROR]  = SMF_CREATE_STATE(error_entry, error_run, error_exit, 
NULL, NULL),
+   };
+
+The example below creates three hierarchical states:
+
+.. code-block:: c
+
+   enum demo_state
+   {
+      STATE_IDLE,
+      STATE_ACTIVE,
+      STATE_ERROR,
+    };
+  
+    static const struct smf_state demo_states[] =
+    {
+      [STATE_IDLE]   = SMF_CREATE_STATE(idle_entry, idle_run, idle_exit, 
parent_idle, NULL),
+      [STATE_ACTIVE] = SMF_CREATE_STATE(active_entry, active_run, active_exit, 
parent_active, NULL),
+      [STATE_ERROR]  = SMF_CREATE_STATE(error_entry, error_run, error_exit, 
parent_error, NULL),
+    };
+
+The next example creates a three-level hierarchical state machine with initial 
transitions from
+parent state idle to child state error:
+
+.. code-block:: c
+
+   enum demo_state
+   {
+      STATE_IDLE,
+      STATE_ACTIVE,
+      STATE_ERROR,
+    };
+  
+    static const struct smf_state demo_states[] =
+    {
+      [STATE_IDLE]   = SMF_CREATE_STATE(idle_entry, idle_run, idle_exit, NULL, 
demo_states[STATE_ERROR]),
+      [STATE_ACTIVE] = SMF_CREATE_STATE(active_entry, active_run, active_exit, 
demo_states[STATE_IDLE], NULL),
+      [STATE_ERROR]  = SMF_CREATE_STATE(error_entry, error_run, error_exit, 
demo_states[STATE_IDLE], NULL),
+    };
+
+To set the initial state of the state machine, call ``smf_set_initial()``.
+To transition between states, call ``smf_set_state()`` from entry or run 
actions.
+
+.. note::
+
+   If ``CONFIG_SYSTEM_SMF_INITIAL_TRANSITION`` is not set, 
``smf_set_initial()`` and ``smf_set_state()`` function
+   should not be passed a parent state as the parent state does not know which 
child state to transition to.
+   Transitioning to a parent state is OK if an initial transition to a child 
state is defined.
+   A well-formed HSM should have initial transitions defined for all parent 
states.
+
+.. note::
+    While the state machine is running, ``smf_set_state()`` should only be 
called
+    from the Entry or Run function. Calling ``smf_set_state()`` from Exit 
functions will generate a warning
+    in the log and no transition will occur.
+
+State Machine Execution
+=======================
+
+To run the state machine, ``smf_run_state()`` function should be called in 
some application dependent way.
+An application should cease calling smf_run_state if it returns a non-zero 
value.
+
+State Machine Termination
+=========================
+
+To terminate the state machine, the ``smf_set_terminate()`` function should be 
called.
+It can be called from the entry, run, or exit actions.
+The function takes a non-zero user defined value that will be returned by the 
``smf_run_state()`` function.
+
+Retrieving Current State
+========================
+
+**Leaf State**: In the context of a hierarchical state machine, a leaf state 
is a state that does not contain
+any child states. It represents the most granular level of state in the 
hierarchy,
+where no further decomposition is possible.
+
+**Executing State**: The executing state refers to the state whose entry, run, 
or exit action is currently
+being executed by the state machine. This may be a parent or leaf state, 
depending on the current operation.
+
+To retrieve the current leaf state, the ``smf_get_current_leaf_state()`` 
function should be called.
+For example:
+
+.. code-block:: c
+
+   const struct smf_state *leaf_state;
+   leaf_state = smf_get_current_leaf_state(SMF_CTX(&s_obj));
+
+.. note::
+   If ``CONFIG_SYSTEM_SMF_INITIAL_TRANSITION`` is not enabled, or if the 
initial state of a parent state is not
+   defined, always set the state to a leaf state. Otherwise, the state machine 
may enter a parent state
+   directly, and ``smf_get_current_leaf_state()`` may return a parent state 
instead of a leaf state.
+   Ensure initial transitions are properly configured for all parent states to 
avoid malformed
+   hierarchical state machines.
+
+To retrieve the state whose entry, run, or exit action is currently being 
executed,
+use the ``smf_get_current_executing_state()`` function.
+
+UML State Machines
+==================
+SMF follows UML hierarchical state machine rules for transitions i.e., the 
entry and exit actions of the least
+common ancestor are not executed on transition, unless said transition is a 
transition to self.
+
+The UML Specification for StateMachines may be found in chapter 14 of the UML 
specification,
+available here: https://www.omg.org/spec/UML/
+
+SMF breaks from UML rules in:
+
+1. Transition actions execute in the source state context, rather than after 
the exit actions are performed.
+
+2. Only allowing external transitions to self, not to sub-states. A transition 
from a superstate to a child state is treated as a local transition.
+
+3. Prohibiting transitions using ``smf_set_state()`` in exit actions.
+
+SMF also does not provide any pseudostates except the Initial Pseudostate.
+Terminate pseudostates can be modelled by calling ``smf_set_terminate()`` from 
the entry action of a
+‘terminate’ state. Orthogonal regions are modelled by calling 
``smf_run_state()`` for each region.
+
+State Machine Examples
+======================
+
+Flat State Machine Example
+--------------------------
+This example turns the following state diagram into code using the SMF, where 
the initial state is STATE_IDLE.
+
+.. figure:: images/Flat-state-machine-diagram.png
+   :alt: Flat State Machine Diagram
+   :align: center
+   :width: 30%
+
+   Flat state machine example implemented using SMF.
+
+.. code-block:: c
+
+  #include <system/smf.h>
+
+  /* Forward declaration of state table */
+  static const struct smf_state demo_states[];
+
+  /* List of demo states */
+     enum demo_state
+   {
+     STATE_IDLE,
+     STATE_ACTIVE,
+     STATE_ERROR,
+   };
+
+  /* User defined object */
+  struct s_object {
+          /* This must be first */
+          struct smf_ctx ctx;
+
+          /* Other state specific data add here */
+  } s_obj;
+
+  /* State idle */
+  static void idle_entry(void *o)
+  {
+    /* Do something */
+  }
+
+  static enum smf_state_result idle_run(void *o)
+  {
+    smf_set_state(SMF_CTX(&s_obj), &demo_states[STATE_ACTIVE]);
+    return SMF_EVENT_HANDLED;
+  }
+
+  static void idle_exit(void *o)
+  {
+    /* Do something */
+  }
+
+  /* State active */
+  static void active_entry(void *o)
+    {
+      /* Do something */
+    }
+
+  static enum smf_state_result active_run(void *o)
+  {
+    smf_set_state(SMF_CTX(&s_obj), &demo_states[STATE_ERROR]);
+    return SMF_EVENT_HANDLED;
+  }
+
+  static void active_exit(void *o)
+  {
+    /* Do something */
+  }
+
+  /* State error */
+  static void error_entry(void *o)
+  {
+    /* Do something */
+  }
+
+  static enum smf_state_result error_run(void *o)
+  {
+    smf_set_state(SMF_CTX(&s_obj), &demo_states[STATE_IDLE]);
+    return SMF_EVENT_HANDLED;
+  }
+
+  static void error_exit(void *o)
+  {
+    /* Do something */
+  }
+
+  /* Populate state table */
+  static const struct smf_state demo_states[] = {
+    [STATE_IDLE]   = SMF_CREATE_STATE(idle_entry, idle_run, idle_exit, NULL, 
NULL),
+    /* State ACTIVE does not have an entry action */
+    [STATE_ACTIVE] = SMF_CREATE_STATE(NULL, active_run, active_exit, NULL, 
NULL),
+    /* State ERROR does not have an exit action */
+    [STATE_ERROR]  = SMF_CREATE_STATE(error_entry, error_run, NULL, NULL, 
NULL),
+  };
+
+  int main(void)
+  {
+    int32_t ret;
+
+    /* Set initial state */
+    smf_set_initial(SMF_CTX(&s_obj), &demo_states[STATE_IDLE]);
+
+    /* Run the state machine */
+    while(1) {
+      /* State machine terminates if a non-zero value is returned */
+      ret = smf_run_state(SMF_CTX(&s_obj));
+      if (ret) {
+        /* handle return code and terminate state machine */
+        break;
+      }
+      sleep(1);
+    }
+  }
+
+Hierarchical State Machine (HSM)
+--------------------------------
+
+When ``CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT`` is enabled, states may define a 
parent.
+The example below turns the following state diagram into code using SMF, where 
IDLE and ACTIVE share a parent
+state and IDLE is the initial state.
+
+.. figure:: images/Hierarchical-state-machine-diagram.png
+   :alt: Hierarchical State Machine Diagram
+   :align: center
+   :width: 40%
+
+   Hierarchical state machine example implemented using SMF.
+
+Code
+
+.. code-block:: c
+
+  #include <system/smf.h>
+
+  /* Forward declaration of state table */
+  static const struct smf_state demo_states[];
+
+  /* List of demo states */
+  enum demo_state { PARENT, IDLE, ACTIVE, ERROR };
+
+  /* User defined object */
+  struct s_object {
+    /* This must be first */
+    struct smf_ctx ctx;
+
+    /* Other state specific data add here */
+  } s_obj;
+
+  /* Parent State */
+  static void parent_entry(void *o)
+  {
+    /* Do something */
+  }
+  static void parent_exit(void *o)
+  {
+    /* Do something */
+  }
+
+  /* State IDLE */
+  static enum smf_state_result idle_run(void *o)
+  {
+    smf_set_state(SMF_CTX(&s_obj), &demo_states[ACTIVE]);
+    return SMF_EVENT_HANDLED;
+  }
+
+  /* State ACTIVE */
+  static enum smf_state_result active_run(void *o)
+  {
+    smf_set_state(SMF_CTX(&s_obj), &demo_states[ERROR]);
+    return SMF_EVENT_HANDLED;
+  }
+
+  /* State ERROR */
+  static enum smf_state_result error_run(void *o)
+  {
+    smf_set_state(SMF_CTX(&s_obj), &demo_states[IDLE]);
+    return SMF_EVENT_HANDLED;
+  }
+
+  /* Populate state table */
+  static const struct smf_state demo_states[] = {
+    /* Parent state does not have a run action */
+    [PARENT] = SMF_CREATE_STATE(parent_entry, NULL, parent_exit, NULL, NULL),
+    /* Child states do not have entry or exit actions */
+    [IDLE] = SMF_CREATE_STATE(NULL, idle_run, NULL, &demo_states[PARENT], 
NULL),
+    [ACTIVE] = SMF_CREATE_STATE(NULL, active_run, NULL, &demo_states[PARENT], 
NULL),
+    /* State ERROR do not have entry or exit actions and no parent */
+    [ERROR] = SMF_CREATE_STATE(NULL, error_run, NULL, NULL, NULL),
+  };
+
+  int main(void)
+  {
+    int32_t ret;
+
+    /* Set initial state */
+    smf_set_initial(SMF_CTX(&s_obj), &demo_states[IDLE]);
+
+    /* Run the state machine */
+    while(1) {
+      /* State machine terminates if a non-zero value is returned */
+      ret = smf_run_state(SMF_CTX(&s_obj));
+      if (ret) {
+        /* handle return code and terminate state machine */
+        break;
+      }
+      sleep(1);
+    }
+  }
+
+When designing hierarchical state machines, the following should be considered:
+
+- Ancestor entry actions are executed before the sibling entry actions. For 
example,
+  the parent_entry function is called before the ``idle_entry`` function.
+
+- Transitioning from one sibling to another with a shared ancestry does not 
re-execute the ancestor's entry
+  action or execute the exit action. For example, the parent_entry function is 
not called when transitioning
+  from IDLE to ACTIVE, nor is the parent_exit function called.
+
+- Ancestor exit actions are executed after the exit action of the current 
state.
+  For example, the idle_exit function is called before the parent_exit 
function is called.
+
+- The parent_run function only executes if the child_run function does not 
call either ``smf_set_state()``
+  or return ``SMF_EVENT_HANDLED``.
+
+- Avoid malformed hierarchical state machines by ensuring the state always 
transitions to a leaf state when
+  ``CONFIG_SYSTEM_SMF_INITIAL_TRANSITION`` is not enabled, or when a parent 
state's initial state is undefined.
+
+Initial Transitions
+===================
+
+If ``CONFIG_SYSTEM_SMF_INITIAL_TRANSITION`` is enabled, a parent state may 
define an
+initial child state.
+
+.. code-block:: c
+
+   static const struct smf_state demo_states[] =
+   {
+     [STATE_PARENT]  = SMF_CREATE_STATE(parent_entry, NULL, parent_exit, NULL, 
&demo_states[STATE_CHILD_A]),
+     [STATE_CHILD_A] = SMF_CREATE_STATE(NULL, child_a_run, NULL, 
&demo_states[STATE_PARENT], NULL),
+   };
+
+When entering ``STATE_PARENT``, the framework automatically transitions to
+``STATE_CHILD_A``.
+
+.. note::
+
+   Without initial transition support enabled, applications must always
+   transition directly to a leaf state.
+
+State Execution Model
+=====================
+
+The application controls execution explicitly.
+
+1. Set the initial state using ``smf_set_initial()``
+2. Call ``smf_run_state()`` from an event loop
+3. Stop execution when a non-zero value is returned
+
+.. code-block:: c
+
+   smf_set_initial(SMF_CTX(&app), &demo_states[STATE_IDLE]);
+
+   while (1)
+     {
+       int32_t rc = smf_run_state(SMF_CTX(&app));
+       if (rc != 0)
+         {
+           break;
+         }
+
+       /* Block, poll, or wait for an event */
+     }
+
+State Run Semantics
+-------------------
+
+The run action returns:
+
+- ``SMF_EVENT_HANDLED`` – event consumed
+- ``SMF_EVENT_PROPAGATE`` – propagate to parent (HSM only)
+
+If ``smf_set_state()`` is called, propagation stops immediately.
+
+State Transitions
+=================
+
+Transitions are requested explicitly by calling ``smf_set_state()`` from
+entry or run actions.
+
+.. code-block:: c
+
+   static enum smf_state_result active_run(void *obj)
+   {
+     struct s_object *s = (struct s_object *)obj;
+
+     if (s->error)
+       {
+         smf_set_state(SMF_CTX(s), &demo_states[STATE_ERROR]);
+         return SMF_EVENT_HANDLED;
+       }
+
+     return SMF_EVENT_HANDLED;
+   }
+
+Calling ``smf_set_state()`` from exit actions is rejected by design.
+
+Termination
+===========
+
+To terminate a state machine, call ``smf_set_terminate()``.
+
+.. code-block:: c
+
+   smf_set_terminate(SMF_CTX(&app), -ECANCELED);
+
+The value passed is returned by ``smf_run_state()`` and can be used to signal
+the termination reason.
+
+State Introspection
+===================
+
+SMF exposes two helper APIs:
+
+- ``smf_get_current_leaf_state()``
+- ``smf_get_current_executing_state()``
+
+These functions are primarily intended for diagnostics, logging, and testing.
+
+UML Compliance
+==============
+
+SMF follows UML hierarchical state machine semantics:
+
+- Entry/exit actions execute according to the least common ancestor (LCA)
+- Self-transitions execute full exit/entry sequences
+- Local transitions are supported implicitly
+
+Differences from UML:
+
+1. Transition actions execute in the source state context
+2. Only external self-transitions are supported
+3. No explicit terminate pseudostate
+
+Notes and Constraints
+=====================
+
+- SMF performs no dynamic allocation
+- State tables are typically ``static const``
+- Thread safety is the responsibility of the application
+- One SMF instance represents one execution region
+- Orthogonal regions require multiple SMF instances
+
+Configuration Options
+=====================
+
+- ``CONFIG_SYSTEM_SMF``
+- ``CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT``
+- ``CONFIG_SYSTEM_SMF_INITIAL_TRANSITION``
+
+Code Location
+=============
+
+- ``apps/system/smf`` – Framework implementation
+- ``apps/include/system/smf.h`` – Public API

Reply via email to