Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lib: SMF: Add initial transitions to HSMs and add smf_set_handled() #66753

Conversation

glenn-andrews
Copy link
Contributor

@glenn-andrews glenn-andrews commented Dec 21, 2023

Brings SMF framework closer into alignment with accepted Hierarchical State Machine operation by:

  1. Allowing 'programming by difference' by having some child states handle events and prevent propagation up to the parent run actions while others propagate events up to a common handler in a parent state.
  2. Optionally allow initial transitions within a parent state to determine the most nested child state to transition to.
  3. Adding a test case for CONFIG_SMF_INITIAL_TRANSITION and smf_set_handled()
  4. Updating documentation for the new API (and fixing some references)

There was discussion in #55344 about not making the initial transition a Kconfig option, but I'm not sure of any way else of doing it without permanently adding a pointer to each smf_state entry, which is a problem for resource-constrained devices.

This does not fix #66341 but documentation has been updated to warn users of the issue.

@zephyrbot zephyrbot added the area: State Machine Framework State Machine Framework label Dec 21, 2023
Copy link

Hello @glenn-andrews, and thank you very much for your first pull request to the Zephyr project!
Our Continuous Integration pipeline will execute a series of checks on your Pull Request commit messages and code, and you are expected to address any failures by updating the PR. Please take a look at our commit message guidelines to find out how to format your commit messages, and at our contribution workflow to understand how to update your Pull Request. If you haven't already, please make sure to review the project's Contributor Expectations and update (by amending and force-pushing the commits) your pull request if necessary.
If you are stuck or need help please join us on Discord and ask your question there. Additionally, you can escalate the review when applicable. 😊

lib/smf/smf.c Outdated Show resolved Hide resolved
sambhurst
sambhurst previously approved these changes Jan 17, 2024
@@ -39,6 +39,10 @@ By default, a state can have no ancestor states, resulting in a flat state
machine. But to enable the creation of a hierarchical state machine, the
:kconfig:option:`CONFIG_SMF_ANCESTOR_SUPPORT` option must be enabled.

By default, the hierarchical state machine does not support initial transitions
to child states on entering a superstate. To enable them the
:kconfig:option:`SMF_INITIAL_TRANSITIONS` option must be enabled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broken link

Suggested change
:kconfig:option:`SMF_INITIAL_TRANSITIONS` option must be enabled.
:kconfig:option:`CONFIG_SMF_INITIAL_TRANSITIONS` option must be enabled.

lib/smf/Kconfig Outdated
@@ -13,4 +13,10 @@ config SMF_ANCESTOR_SUPPORT
help
If y, then the state machine framework includes ancestor state support

config SMF_INITIAL_TRANSITIONS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking suggestion:

Suggested change
config SMF_INITIAL_TRANSITIONS
config SMF_INITIAL_TRANSITION

*
* @param ctx State machine context
*/
void smf_set_handled(struct smf_ctx *ctx);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update/create a sample to demonstrate the use of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a rather large sample that implements the PSiCC2 Section 2.3.15 demo app with all levels of transitions. It's about 11K of source, given the large number of states the demo app encompasses.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably too large. I was thinking we had existing samples that use the SMF, but there are not.

However, there are some good unit tests for the SMF here.

Please extend one of the existing tests or create a new one that enables the CONFIG_SMF_INITIAL_TRANSITION option. The ensures that Zephyr's CI will validate that this new feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I've never used the unit tests before so feedback is appreciated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably too large. I was thinking we had existing samples that use the SMF, but there are not.

Would it be worth me submitting my demo app as a separate sample for SMF? I can probably simplify it a bit.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably too large. I was thinking we had existing samples that use the SMF, but there are not.

Would it be worth me submitting my demo app as a separate sample for SMF? I can probably simplify it a bit.

Yes, please!

Preventing Parent Run Actions
=============================

Calling ``smf_set_handled`` will prevent calling the run action of parent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Calling ``smf_set_handled`` will prevent calling the run action of parent
Calling ``smf_set_handled`` prevents calling the run action of parent

doc/services/smf/index.rst Show resolved Hide resolved
doc/services/smf/index.rst Outdated Show resolved Hide resolved
doc/services/smf/index.rst Outdated Show resolved Hide resolved
Comment on lines 348 to 358
- When a parent state intitiates a transition to self, the parents's exit
action is not called, e.g. instead of child_exit, parent_exit, parent_entry
it performs child_exit, parent_entry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this condition is tested. Can you update the test to include this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to be testing to confirm a bug exists? I don't want to see this codified as "the way things work" and add additional effort to fixing it by having to fix the test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation here defines an API contract: A parent state initiating a transition to self does not call the parent_exit. I didn't consider this a bug.
So I was wondering if this condition could explicitly be tested.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's considered a bug here: #66341

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems odd to document behavior that isn't supported at the moment. Does it make more sense to explicitly state "calling smf_set_state() to transition to self is not supported".

tests/lib/smf/src/test_lib_initial_transitions_smf.c Outdated Show resolved Hide resolved
@glenn-andrews glenn-andrews force-pushed the smf-initial-transitions branch 4 times, most recently from 2cd4fe2 to ff7521c Compare February 2, 2024 17:10
Comment on lines 348 to 358
- When a parent state intitiates a transition to self, the parents's exit
action is not called, e.g. instead of child_exit, parent_exit, parent_entry
it performs child_exit, parent_entry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation here defines an API contract: A parent state initiating a transition to self does not call the parent_exit. I didn't consider this a bug.
So I was wondering if this condition could explicitly be tested.

doc/services/smf/index.rst Show resolved Hide resolved
Comment on lines 116 to 144
Calling ``smf_set_handled`` prevents calling the run action of parent states.
It is not required to call ``smf_set_handled`` if the state calls
``smf_set_state``.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see suggested change re: how to have proper references for API functions. I realize that you probably just adopted the way things were done in the file so it would be nice (but not mandatory as I understand it might be a bit tedious) if you could maybe have a separate commit to update all references to use the :c:func: construct.

Suggested change
Calling ``smf_set_handled`` prevents calling the run action of parent states.
It is not required to call ``smf_set_handled`` if the state calls
``smf_set_state``.
Calling :c:func:`smf_set_handled`` prevents calling the run action of parent states.
It is not required to call :c:func:`smf_set_handled` if the state calls
:c:func:`smf_set_state`.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want me to refactor the entire document, or just the parts I've touched?

Comment on lines 346 to 358
without transitioning to another state, ie. calling smf_set_state, or if
``smf_set_handled`` is called within the child_run function.
- When a parent state intitiates a transition to self, the parents's exit
action is not called, e.g. instead of child_exit, parent_exit, parent_entry
it performs child_exit, parent_entry
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
without transitioning to another state, ie. calling smf_set_state, or if
``smf_set_handled`` is called within the child_run function.
- When a parent state intitiates a transition to self, the parents's exit
action is not called, e.g. instead of child_exit, parent_exit, parent_entry
it performs child_exit, parent_entry
without transitioning to another state, ie. calling :c:func:`smf_set_state`, or if
:c:func:`smf_set_handled` is called within the ``child_run`` function.
- When a parent state intitiates a transition to self, the parents's exit
action is not called, e.g. instead of ``child_exit`` |srarr| ``parent_exit`` |srarr| ``parent_entry``,
it performs ``child_exit`` |srarr| ``parent_entry``.

@@ -79,6 +117,13 @@ To transition from one state to another, the ``smf_set_state`` function is
used and it has the following prototype:
``void smf_set_state(smf_ctx *ctx, smf_state *state)``

**NOTE:** If :kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION` is not set,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a .. note:: Sphinx directive that would probably be appropriate here and in other places with **NOTE**s

doc/services/smf/index.rst Show resolved Hide resolved
@glenn-andrews
Copy link
Contributor Author

Anything else I should do?

Copy link
Collaborator

@kartben kartben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@glenn-andrews thanks much for your work on this! I've pushed a couple commits to your branch adding what was missing for the functions to properly render as links.

The one thing that I think needs to be changed is that you have all your changes into just one commit (which description now seems outdated/incomplete anyway?), which is not ideal. It would be great if you could split in several commits so that the additions of new Kconfigs and APIs are not mixed with what are "simple" documentation enhancements.

Thanks!

@glenn-andrews
Copy link
Contributor Author

It would be great if you could split in several commits so that the additions of new Kconfigs and APIs are not mixed with what are "simple" documentation enhancements.

I'm not entirely sure how such a split would work. We've got about 8 instances where I replaced double-backticks with the appropriate sphinx directives, all the rest is directly relevant to the API being added. I could revert the changes to the double-backticks, then artificially add them again, but that seems silly.

Can you give me a clearer idea of what is required? I feel I'm missing the intent of this request.

@glenn-andrews
Copy link
Contributor Author

glenn-andrews commented Feb 14, 2024

I've pushed a couple commits to your branch adding what was missing for the functions to properly render as links.

I'm terribly sorry, but I think I managed to nuke your changes by not pulling them before I amended my commit message. I believe I repaired the damage. Please confirm.

Brings SMF framework closer into alignment with accepted Hierarchical State
Machine operation by:
1. Allowing 'programming by difference' by having some child states handle
   events and prevent propagation up to the parent run actions while others
   propagate events up to a common handler in a parent state.
2. Optionally allow initial transitions within a parent state to determine
   the most nested child state to transition to.
3. Adding a test case for `CONFIG_SMF_INITIAL_TRANSITION` and
   `smf_set_handled()`
4. Updating documentation for the new API (and fixing some references)

There was discussion in zephyrproject-rtos#55344
about not making the initial transition a Kconfig option, but I'm not sure
of any way else of doing it without permanently adding a pointer to each
`smf_state` entry, which is a problem for resource-constrained devices.

This does not fix zephyrproject-rtos#66341
but documentation has been updated to warn users of the issue.

Signed-off-by: Glenn Andrews <glenn.andrews.42@gmail.com>
Fixed where `smf_set_terminate` was written `smf_terminate`

Signed-off-by: Glenn Andrews <glenn.andrews.42@gmail.com>
Added Doxygen markup.

Signed-off-by: Glenn Andrews <glenn.andrews.42@gmail.com>
@glenn-andrews
Copy link
Contributor Author

How's this looking now? Any more action items for me?

functions doesn't make sense and will generate a warning.
.. note:: If :kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION` is not set,
:c:func:`smf_set_initial` and :c:func:`smf_set_state` function should
not be passed a parent state as they will not know which child state
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking suggestion:

Suggested change
not be passed a parent state as they will not know which child state
not be passed a parent state as the parent state does not know which child state

Comment on lines 348 to 358
- When a parent state intitiates a transition to self, the parents's exit
action is not called, e.g. instead of child_exit, parent_exit, parent_entry
it performs child_exit, parent_entry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems odd to document behavior that isn't supported at the moment. Does it make more sense to explicitly state "calling smf_set_state() to transition to self is not supported".

Incorporated changes requested by @keith-zephyr:
1. Grammar fix
2. Make explicit that transition to self in super-states is not supported

Signed-off-by: Glenn Andrews <glenn.andrews.42@gmail.com>
@glenn-andrews
Copy link
Contributor Author

#66753 (comment)

Updated. Does the new wording seem okay?

Copy link
Collaborator

@kartben kartben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for all the hard work, @glenn-andrews!

@glenn-andrews
Copy link
Contributor Author

Thank you all so much for reviewing this!

@henrikbrixandersen henrikbrixandersen merged commit 3203bd6 into zephyrproject-rtos:main Mar 4, 2024
24 checks passed
Copy link

github-actions bot commented Mar 4, 2024

Hi @glenn-andrews!
Congratulations on getting your very first Zephyr pull request merged 🎉🥳. This is a fantastic achievement, and we're thrilled to have you as part of our community!

To celebrate this milestone and showcase your contribution, we'd love to award you the Zephyr Technical Contributor badge. If you're interested, please claim your badge by filling out this form: Claim Your Zephyr Badge.

Thank you for your valuable input, and we look forward to seeing more of your contributions in the future! 🪁

The statechart for this test is below.

.. graphviz::
:caption: Test state machine for initial trnasitions and ``smf_set_handled``

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo "trnasitions"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: State Machine Framework State Machine Framework
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SMF ancestor states do not handle transition to self correctly
7 participants