Skip to content

Commit 4b71cf9

Browse files
yswaycfriedt
authored andcommitted
lib: smf: add API to get the current leaf and executing state
Retrieve the current leaf state by calling `smf_get_current_leaf_state` and the state which is currently-executing by calling `smf_get_current_executing_state`. Signed-off-by: Siwei Yang <[email protected]>
1 parent af6f88b commit 4b71cf9

File tree

6 files changed

+218
-67
lines changed

6 files changed

+218
-67
lines changed

doc/releases/release-notes-4.3.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ New APIs and options
253253
* :kconfig:option:`CONFIG_SHELL_MQTT_WORK_DELAY_MS`
254254
* :kconfig:option:`CONFIG_SHELL_MQTT_LISTEN_TIMEOUT_MS`
255255

256+
* State Machine Framework
257+
258+
* :c:func:`smf_get_current_leaf_state`
259+
* :c:func:`smf_get_current_executing_state`
260+
256261
* Storage
257262

258263
* :kconfig:option:`CONFIG_FILE_SYSTEM_SHELL_LS_SIZE`

doc/services/smf/index.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,33 @@ should be called. It can be called from the entry, run, or exit actions. The
130130
function takes a non-zero user defined value that will be returned by the
131131
:c:func:`smf_run_state` function.
132132

133+
Retrieving the Current State
134+
====================================
135+
136+
**Leaf State**: In the context of a hierarchical state machine, a *leaf state*
137+
is a state that does not contain any child states. It represents the most granular
138+
level of state in the hierarchy, where no further decomposition is possible.
139+
140+
**Executing State**: The *executing state* refers to the state whose entry,
141+
run, or exit action is currently being executed by the state machine. This
142+
may be a parent or leaf state, depending on the current operation.
143+
144+
To retrieve the current leaf state, the :c:func:`smf_get_current_leaf_state`
145+
function should be called.
146+
For example::
147+
148+
const struct smf_state *leaf_state = smf_get_current_leaf_state(SMF_CTX(&s_obj));
149+
150+
.. note:: If :kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION` is not enabled, or
151+
if the initial state of a parent state is not defined, always set the state
152+
to a leaf state. Otherwise, the state machine may enter a parent state directly,
153+
and :c:func:`smf_get_current_leaf_state` may return a parent state instead of
154+
a leaf state. Ensure initial transitions are properly configured for all parent
155+
states to avoid malformed hierarchical state machines.
156+
157+
To retrieve the state whose entry, run, or exit action is currently being executed,
158+
use the :c:func:`smf_get_current_executing_state` function.
159+
133160
UML State Machines
134161
==================
135162

@@ -380,6 +407,9 @@ When designing hierarchical state machines, the following should be considered:
380407
function is called.
381408
- The parent_run function only executes if the child_run function does not
382409
call either :c:func:`smf_set_state` or return :c:enum:`SMF_EVENT_HANDLED`.
410+
- Avoid malformed hierarchical state machines by ensuring the state always
411+
transitions to a leaf state when :kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION`
412+
is not enabled, or when a parent state's initial state is undefined.
383413

384414
Event Driven State Machine Example
385415
**********************************

include/zephyr/smf.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,36 @@ void smf_set_terminate(struct smf_ctx *ctx, int32_t val);
180180
*/
181181
int32_t smf_run_state(struct smf_ctx *ctx);
182182

183+
/**
184+
* @brief Get the current leaf state.
185+
*
186+
* @note This may be a PARENT state if the HSM is malformed
187+
* (i.e. the initial transitions are not set up correctly).
188+
*
189+
* @param ctx State machine context
190+
* @return The current leaf state.
191+
*/
192+
static inline const struct smf_state *smf_get_current_leaf_state(const struct smf_ctx *const ctx)
193+
{
194+
return ctx->current;
195+
}
196+
197+
/**
198+
* @brief Get the state that is currently executing. This may be a parent state.
199+
*
200+
* @param ctx State machine context
201+
* @return The state that is currently executing.
202+
*/
203+
static inline const struct smf_state *
204+
smf_get_current_executing_state(const struct smf_ctx *const ctx)
205+
{
206+
#ifdef CONFIG_SMF_ANCESTOR_SUPPORT
207+
return ctx->executing;
208+
#else
209+
return ctx->current;
210+
#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */
211+
}
212+
183213
#ifdef __cplusplus
184214
}
185215
#endif

lib/smf/smf.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ static bool smf_execute_all_entry_actions(struct smf_ctx *const ctx,
110110

111111
/* No need to continue if terminate was set */
112112
if (internal->terminate) {
113+
ctx->executing = ctx->current;
113114
return true;
114115
}
115116
}
@@ -122,10 +123,13 @@ static bool smf_execute_all_entry_actions(struct smf_ctx *const ctx,
122123

123124
/* No need to continue if terminate was set */
124125
if (internal->terminate) {
126+
ctx->executing = ctx->current;
125127
return true;
126128
}
127129
}
128130

131+
ctx->executing = ctx->current;
132+
129133
return false;
130134
}
131135

@@ -165,6 +169,7 @@ static bool smf_execute_ancestor_run_actions(struct smf_ctx *const ctx)
165169
}
166170
/* No need to continue if terminate was set */
167171
if (internal->terminate) {
172+
ctx->executing = ctx->current;
168173
return true;
169174
}
170175

@@ -177,6 +182,8 @@ static bool smf_execute_ancestor_run_actions(struct smf_ctx *const ctx)
177182

178183
/* All done executing the run actions */
179184

185+
ctx->executing = ctx->current;
186+
180187
return false;
181188
}
182189

@@ -190,19 +197,24 @@ static bool smf_execute_ancestor_run_actions(struct smf_ctx *const ctx)
190197
static bool smf_execute_all_exit_actions(struct smf_ctx *const ctx, const struct smf_state *topmost)
191198
{
192199
struct internal_ctx *const internal = (void *)&ctx->internal;
200+
const struct smf_state *tmp_state = ctx->executing;
193201

194202
for (const struct smf_state *to_execute = ctx->current;
195203
to_execute != NULL && to_execute != topmost; to_execute = to_execute->parent) {
196204
if (to_execute->exit) {
205+
ctx->executing = to_execute;
197206
to_execute->exit(ctx);
198207

199208
/* No need to continue if terminate was set in the exit action */
200209
if (internal->terminate) {
210+
ctx->executing = tmp_state;
201211
return true;
202212
}
203213
}
204214
}
205215

216+
ctx->executing = tmp_state;
217+
206218
return false;
207219
}
208220
#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */
@@ -250,7 +262,9 @@ void smf_set_initial(struct smf_ctx *const ctx, const struct smf_state *init_sta
250262
* doesn't
251263
*/
252264
if (topmost->entry) {
265+
ctx->executing = topmost;
253266
topmost->entry(ctx);
267+
ctx->executing = init_state;
254268
if (internal->terminate) {
255269
/* No need to continue if terminate was set */
256270
return;
@@ -345,6 +359,7 @@ void smf_set_state(struct smf_ctx *const ctx, const struct smf_state *new_state)
345359
/* update the state variables */
346360
ctx->previous = ctx->current;
347361
ctx->current = new_state;
362+
ctx->executing = new_state;
348363

349364
/* call all entry actions (except those of topmost) */
350365
if (smf_execute_all_entry_actions(ctx, new_state, topmost)) {

tests/lib/smf/src/test_lib_flat_smf.c

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,34 @@
1010
/*
1111
* Flat Test Transition:
1212
*
13-
* A_ENTRY --> A_RUN --> A_EXIT --> B_ENTRY --> B_RUN --|
14-
* |
15-
* |----------------------------------------------------|
16-
* |
17-
* |--> B_EXIT --> C_ENTRY --> C_RUN --> C_EXIT
13+
* A_ENTRY --> A_RUN --> A_EXIT --> B_ENTRY --> B_RUN --|
14+
* |
15+
* |----------------------------------------------------|
16+
* |
17+
* |--> B_EXIT --> C_ENTRY --> C_RUN --> C_EXIT --> D_ENTRY
1818
*
1919
*/
2020

21-
2221
#define TEST_OBJECT(o) ((struct test_object *)o)
2322

24-
#define SMF_RUN 3
23+
#define SMF_RUN 3
2524

26-
#define STATE_A_ENTRY_BIT (1 << 0)
27-
#define STATE_A_RUN_BIT (1 << 1)
28-
#define STATE_A_EXIT_BIT (1 << 2)
25+
#define STATE_A_ENTRY_BIT (1 << 0)
26+
#define STATE_A_RUN_BIT (1 << 1)
27+
#define STATE_A_EXIT_BIT (1 << 2)
2928

30-
#define STATE_B_ENTRY_BIT (1 << 3)
31-
#define STATE_B_RUN_BIT (1 << 4)
32-
#define STATE_B_EXIT_BIT (1 << 5)
29+
#define STATE_B_ENTRY_BIT (1 << 3)
30+
#define STATE_B_RUN_BIT (1 << 4)
31+
#define STATE_B_EXIT_BIT (1 << 5)
3332

34-
#define STATE_C_ENTRY_BIT (1 << 6)
35-
#define STATE_C_RUN_BIT (1 << 7)
36-
#define STATE_C_EXIT_BIT (1 << 8)
33+
#define STATE_C_ENTRY_BIT (1 << 6)
34+
#define STATE_C_RUN_BIT (1 << 7)
35+
#define STATE_C_EXIT_BIT (1 << 8)
3736

38-
#define TEST_ENTRY_VALUE_NUM 0
39-
#define TEST_RUN_VALUE_NUM 4
40-
#define TEST_EXIT_VALUE_NUM 8
41-
#define TEST_VALUE_NUM 9
37+
#define TEST_ENTRY_VALUE_NUM 0
38+
#define TEST_RUN_VALUE_NUM 4
39+
#define TEST_EXIT_VALUE_NUM 8
40+
#define TEST_VALUE_NUM 9
4241

4342
static uint32_t test_value[] = {
4443
0x00, /* STATE_A_ENTRY */
@@ -80,11 +79,15 @@ static struct test_object {
8079

8180
static void state_a_entry(void *obj)
8281
{
82+
zassert_equal(smf_get_current_executing_state(SMF_CTX(obj)), &test_states[STATE_A],
83+
"Fail to get the currently-executing state at entry. Expected: State A");
84+
zassert_equal(smf_get_current_leaf_state(SMF_CTX(obj)), &test_states[STATE_A],
85+
"Fail to get the current leaf state at entry. Expected: State A");
86+
8387
struct test_object *o = TEST_OBJECT(obj);
8488

8589
o->tv_idx = 0;
86-
zassert_equal(o->transition_bits, test_value[o->tv_idx],
87-
"Test State A entry failed");
90+
zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State A entry failed");
8891

8992
if (o->terminate == ENTRY) {
9093
smf_set_terminate(obj, -1);
@@ -96,11 +99,15 @@ static void state_a_entry(void *obj)
9699

97100
static enum smf_state_result state_a_run(void *obj)
98101
{
102+
zassert_equal(smf_get_current_executing_state(SMF_CTX(obj)), &test_states[STATE_A],
103+
"Fail to get the currently-executing state at run. Expected: State A");
104+
zassert_equal(smf_get_current_leaf_state(SMF_CTX(obj)), &test_states[STATE_A],
105+
"Fail to get the current leaf state at run. Expected: State A");
106+
99107
struct test_object *o = TEST_OBJECT(obj);
100108

101109
o->tv_idx++;
102-
zassert_equal(o->transition_bits, test_value[o->tv_idx],
103-
"Test State A run failed");
110+
zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State A run failed");
104111

105112
o->transition_bits |= STATE_A_RUN_BIT;
106113

@@ -110,33 +117,45 @@ static enum smf_state_result state_a_run(void *obj)
110117

111118
static void state_a_exit(void *obj)
112119
{
120+
zassert_equal(smf_get_current_executing_state(SMF_CTX(obj)), &test_states[STATE_A],
121+
"Fail to get the currently-executing state at exit. Expected: State A");
122+
zassert_equal(smf_get_current_leaf_state(SMF_CTX(obj)), &test_states[STATE_A],
123+
"Fail to get the current leaf state at exit. Expected: State A");
124+
113125
struct test_object *o = TEST_OBJECT(obj);
114126

115127
o->tv_idx++;
116-
zassert_equal(o->transition_bits, test_value[o->tv_idx],
117-
"Test State A exit failed");
128+
zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State A exit failed");
118129

119130
o->transition_bits |= STATE_A_EXIT_BIT;
120131
}
121132

122133
static void state_b_entry(void *obj)
123134
{
135+
zassert_equal(smf_get_current_executing_state(SMF_CTX(obj)), &test_states[STATE_B],
136+
"Fail to get the currently-executing state at entry. Expected: State B");
137+
zassert_equal(smf_get_current_leaf_state(SMF_CTX(obj)), &test_states[STATE_B],
138+
"Fail to get the current leaf state at entry. Expected: State B");
139+
124140
struct test_object *o = TEST_OBJECT(obj);
125141

126142
o->tv_idx++;
127-
zassert_equal(o->transition_bits, test_value[o->tv_idx],
128-
"Test State B entry failed");
143+
zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State B entry failed");
129144

130145
o->transition_bits |= STATE_B_ENTRY_BIT;
131146
}
132147

133148
static enum smf_state_result state_b_run(void *obj)
134149
{
150+
zassert_equal(smf_get_current_executing_state(SMF_CTX(obj)), &test_states[STATE_B],
151+
"Fail to get the currently-executing state at run. Expected: State B");
152+
zassert_equal(smf_get_current_leaf_state(SMF_CTX(obj)), &test_states[STATE_B],
153+
"Fail to get the current leaf state at run. Expected: State B");
154+
135155
struct test_object *o = TEST_OBJECT(obj);
136156

137157
o->tv_idx++;
138-
zassert_equal(o->transition_bits, test_value[o->tv_idx],
139-
"Test State B run failed");
158+
zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State B run failed");
140159

141160
if (o->terminate == RUN) {
142161
smf_set_terminate(obj, -1);
@@ -151,31 +170,43 @@ static enum smf_state_result state_b_run(void *obj)
151170

152171
static void state_b_exit(void *obj)
153172
{
173+
zassert_equal(smf_get_current_executing_state(SMF_CTX(obj)), &test_states[STATE_B],
174+
"Fail to get the currently-executing state at exit. Expected: State B");
175+
zassert_equal(smf_get_current_leaf_state(SMF_CTX(obj)), &test_states[STATE_B],
176+
"Fail to get the current leaf state at exit. Expected: State B");
177+
154178
struct test_object *o = TEST_OBJECT(obj);
155179

156180
o->tv_idx++;
157-
zassert_equal(o->transition_bits, test_value[o->tv_idx],
158-
"Test State B exit failed");
181+
zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State B exit failed");
159182
o->transition_bits |= STATE_B_EXIT_BIT;
160183
}
161184

162185
static void state_c_entry(void *obj)
163186
{
187+
zassert_equal(smf_get_current_executing_state(SMF_CTX(obj)), &test_states[STATE_C],
188+
"Fail to get the currently-executing state at entry. Expected: State C");
189+
zassert_equal(smf_get_current_leaf_state(SMF_CTX(obj)), &test_states[STATE_C],
190+
"Fail to get the current leaf state at entry. Expected: State C");
191+
164192
struct test_object *o = TEST_OBJECT(obj);
165193

166194
o->tv_idx++;
167-
zassert_equal(o->transition_bits, test_value[o->tv_idx],
168-
"Test State C entry failed");
195+
zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State C entry failed");
169196
o->transition_bits |= STATE_C_ENTRY_BIT;
170197
}
171198

172199
static enum smf_state_result state_c_run(void *obj)
173200
{
201+
zassert_equal(smf_get_current_executing_state(SMF_CTX(obj)), &test_states[STATE_C],
202+
"Fail to get the currently-executing state at run. Expected: State C");
203+
zassert_equal(smf_get_current_leaf_state(SMF_CTX(obj)), &test_states[STATE_C],
204+
"Fail to get the current leaf state at run. Expected: State C");
205+
174206
struct test_object *o = TEST_OBJECT(obj);
175207

176208
o->tv_idx++;
177-
zassert_equal(o->transition_bits, test_value[o->tv_idx],
178-
"Test State C run failed");
209+
zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State C run failed");
179210
o->transition_bits |= STATE_C_RUN_BIT;
180211

181212
smf_set_state(SMF_CTX(obj), &test_states[STATE_D]);
@@ -184,11 +215,15 @@ static enum smf_state_result state_c_run(void *obj)
184215

185216
static void state_c_exit(void *obj)
186217
{
218+
zassert_equal(smf_get_current_executing_state(SMF_CTX(obj)), &test_states[STATE_C],
219+
"Fail to get the currently-executing state at exit. Expected: State C");
220+
zassert_equal(smf_get_current_leaf_state(SMF_CTX(obj)), &test_states[STATE_C],
221+
"Fail to get the current leaf state at exit. Expected: State C");
222+
187223
struct test_object *o = TEST_OBJECT(obj);
188224

189225
o->tv_idx++;
190-
zassert_equal(o->transition_bits, test_value[o->tv_idx],
191-
"Test State C exit failed");
226+
zassert_equal(o->transition_bits, test_value[o->tv_idx], "Test State C exit failed");
192227

193228
if (o->terminate == EXIT) {
194229
smf_set_terminate(obj, -1);
@@ -237,8 +272,7 @@ ZTEST(smf_tests, test_smf_flat)
237272
}
238273
}
239274

240-
zassert_equal(TEST_VALUE_NUM, test_obj.tv_idx,
241-
"Incorrect test value index");
275+
zassert_equal(TEST_VALUE_NUM, test_obj.tv_idx, "Incorrect test value index");
242276
zassert_equal(test_obj.transition_bits, test_value[test_obj.tv_idx],
243277
"Final state not reached");
244278

0 commit comments

Comments
 (0)