mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
system/smf: Port SMF .c/.h files to NuttX
This commit add state machine framework lib to the NuttX project. Also an example is added to help users understand and use this feature. Changes: Added some files. No impact in other features are expected. Adjust SMF macro names Fix issues requested during MR review update kconfig fix ci-cd issue Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
This commit is contained in:
parent
4f93ec0a43
commit
ca11a7e093
12 changed files with 1913 additions and 0 deletions
18
examples/smf/CMakeLists.txt
Normal file
18
examples/smf/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# ##############################################################################
|
||||
# apps/examples/hsm_psicc2/CMakeLists.txt
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# ##############################################################################
|
||||
|
||||
if(CONFIG_EXAMPLES_SMF)
|
||||
nuttx_add_application(
|
||||
NAME
|
||||
${CONFIG_EXAMPLES_SMF_PROGNAME}
|
||||
SRCS
|
||||
smf_main.c
|
||||
hsm_psicc2_thread.c
|
||||
STACKSIZE
|
||||
${CONFIG_EXAMPLES_SMF_STACKSIZE}
|
||||
PRIORITY
|
||||
${CONFIG_EXAMPLES_SMF_PRIORITY})
|
||||
endif()
|
||||
37
examples/smf/Kconfig
Normal file
37
examples/smf/Kconfig
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see the file kconfig-language.txt in the NuttX tools repository.
|
||||
#
|
||||
|
||||
config EXAMPLES_SMF
|
||||
tristate "State Machine Framework PSICC2 demo (HSM)"
|
||||
default n
|
||||
depends on NSH_BUILTIN_APPS
|
||||
depends on SYSTEM_SMF
|
||||
depends on SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
depends on SYSTEM_SMF_INITIAL_TRANSITION
|
||||
|
||||
if EXAMPLES_SMF
|
||||
|
||||
config EXAMPLES_SMF_PROGNAME
|
||||
string "Program name"
|
||||
default "hsm_psicc2"
|
||||
|
||||
config EXAMPLES_SMF_PRIORITY
|
||||
int "Priority"
|
||||
default 100
|
||||
|
||||
config EXAMPLES_SMF_STACKSIZE
|
||||
int "Stack size"
|
||||
default 2048
|
||||
|
||||
config EXAMPLES_SMF_EVENT_QUEUE_SIZE
|
||||
int "Event queue size"
|
||||
default 10
|
||||
|
||||
config EXAMPLES_SMF_MQ_NAME
|
||||
string "Message queue name"
|
||||
default "/hsm_psicc2_mq"
|
||||
|
||||
endif
|
||||
|
||||
25
examples/smf/Make.defs
Normal file
25
examples/smf/Make.defs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
############################################################################
|
||||
# apps/examples/hello/Make.defs
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
ifneq ($(CONFIG_EXAMPLES_SMF),)
|
||||
CONFIGURED_APPS += $(APPDIR)/examples/smf
|
||||
endif
|
||||
37
examples/smf/Makefile
Normal file
37
examples/smf/Makefile
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
############################################################################
|
||||
# apps/examples/hsm_psicc2/Makefile
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
# HSM PSICC2 (SMF demo) built-in application info
|
||||
|
||||
PROGNAME = $(CONFIG_EXAMPLES_SMF_PROGNAME)
|
||||
PRIORITY = $(CONFIG_EXAMPLES_SMF_PRIORITY)
|
||||
STACKSIZE = $(CONFIG_EXAMPLES_SMF_STACKSIZE)
|
||||
MODULE = $(CONFIG_EXAMPLES_SMF)
|
||||
|
||||
# HSM PSICC2 Example
|
||||
|
||||
MAINSRC = smf_main.c
|
||||
CSRCS += hsm_psicc2_thread.c
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
626
examples/smf/hsm_psicc2_thread.c
Normal file
626
examples/smf/hsm_psicc2_thread.c
Normal file
|
|
@ -0,0 +1,626 @@
|
|||
/****************************************************************************
|
||||
* apps/examples/smf/hsm_psicc2_thread.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2024 Glenn Andrews
|
||||
* State Machine example copyright (c) Miro Samek
|
||||
*
|
||||
* Implementation of the statechart in Figure 2.11 of
|
||||
* Practical UML Statecharts in C/C++, 2nd Edition by Miro Samek
|
||||
* https://www.state-machine.com/psicc2
|
||||
* Used with permission of the author.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <mqueue.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <system/smf.h>
|
||||
|
||||
#include "hsm_psicc2_thread.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct s_object
|
||||
{
|
||||
struct smf_ctx ctx; /* MUST be first (matches SMF_CTX(obj)) */
|
||||
struct hsm_psicc2_event event;
|
||||
int foo;
|
||||
};
|
||||
|
||||
enum demo_states
|
||||
{
|
||||
STATE_INITIAL,
|
||||
STATE_S,
|
||||
STATE_S1,
|
||||
STATE_S2,
|
||||
STATE_S11,
|
||||
STATE_S21,
|
||||
STATE_S211
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static struct s_object s_obj;
|
||||
static bool g_started;
|
||||
static const struct smf_state demo_states[];
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* STATE_INITIAL
|
||||
****************************************************************************/
|
||||
|
||||
static void initial_entry(void *o)
|
||||
{
|
||||
struct s_object *obj = (struct s_object *)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
obj->foo = false;
|
||||
}
|
||||
|
||||
static enum smf_state_result initial_run(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
return SMF_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void initial_exit(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* STATE_S
|
||||
****************************************************************************/
|
||||
|
||||
static void s_entry(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s", __func__);
|
||||
}
|
||||
|
||||
static enum smf_state_result s_run(void *o)
|
||||
{
|
||||
struct s_object *obj = (struct s_object *)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
|
||||
switch (obj->event.event_id)
|
||||
{
|
||||
case EVENT_E:
|
||||
printf("[psicc2] %s received EVENT_E", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S11]);
|
||||
break;
|
||||
|
||||
case EVENT_I:
|
||||
if (obj->foo)
|
||||
{
|
||||
printf("[psicc2] %s received EVENT_I and set foo false\n",
|
||||
__func__);
|
||||
obj->foo = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[psicc2] %s received EVENT_I and did nothing\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
return SMF_EVENT_HANDLED;
|
||||
|
||||
case EVENT_TERMINATE:
|
||||
printf("[psicc2] %s received EVENT_TERMINATE. Terminating\n",
|
||||
__func__);
|
||||
smf_set_terminate(SMF_CTX(obj), -1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SMF_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void s_exit(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("%s", __func__);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* STATE_S1
|
||||
****************************************************************************/
|
||||
|
||||
static void s1_entry(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("%s", __func__);
|
||||
}
|
||||
|
||||
static enum smf_state_result s1_run(void *o)
|
||||
{
|
||||
struct s_object *obj = (struct s_object *)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
|
||||
switch (obj->event.event_id)
|
||||
{
|
||||
case EVENT_A:
|
||||
printf("[psicc2] %s received EVENT_A\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]);
|
||||
break;
|
||||
|
||||
case EVENT_B:
|
||||
printf("[psicc2] %s received EVENT_B\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S11]);
|
||||
break;
|
||||
|
||||
case EVENT_C:
|
||||
printf("[psicc2] %s received EVENT_C\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S2]);
|
||||
break;
|
||||
|
||||
case EVENT_D:
|
||||
if (!obj->foo)
|
||||
{
|
||||
printf("[psicc2] %s received EVENT_D and acted on it\n",
|
||||
__func__);
|
||||
obj->foo = true;
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[psicc2] %s received EVENT_D and ignored it\n",
|
||||
__func__);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENT_F:
|
||||
printf("[psicc2] %s received EVENT_F\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S211]);
|
||||
break;
|
||||
|
||||
case EVENT_I:
|
||||
printf("[psicc2] %s received EVENT_I\n", __func__);
|
||||
return SMF_EVENT_HANDLED;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SMF_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void s1_exit(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* STATE_S11
|
||||
****************************************************************************/
|
||||
|
||||
static void s11_entry(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
static enum smf_state_result s11_run(void *o)
|
||||
{
|
||||
struct s_object *obj = (struct s_object *)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
|
||||
switch (obj->event.event_id)
|
||||
{
|
||||
case EVENT_D:
|
||||
if (obj->foo)
|
||||
{
|
||||
printf("[psicc2] %s received EVENT_D and acted upon it\n",
|
||||
__func__);
|
||||
obj->foo = false;
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[psicc2] %s received EVENT_D and ignored it\n",
|
||||
__func__);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENT_G:
|
||||
printf("[psicc2] %s received EVENT_G\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S21]);
|
||||
break;
|
||||
|
||||
case EVENT_H:
|
||||
printf("[psicc2] %s received EVENT_H\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SMF_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void s11_exit(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* STATE_S2
|
||||
****************************************************************************/
|
||||
|
||||
static void s2_entry(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
static enum smf_state_result s2_run(void *o)
|
||||
{
|
||||
struct s_object *obj = (struct s_object *)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
|
||||
switch (obj->event.event_id)
|
||||
{
|
||||
case EVENT_C:
|
||||
printf("[psicc2] %s received EVENT_C\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]);
|
||||
break;
|
||||
|
||||
case EVENT_F:
|
||||
printf("[psicc2] %s received EVENT_F\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S11]);
|
||||
break;
|
||||
|
||||
case EVENT_I:
|
||||
if (!obj->foo)
|
||||
{
|
||||
printf("[psicc2] %s received EVENT_I and set foo true\n",
|
||||
__func__);
|
||||
obj->foo = true;
|
||||
return SMF_EVENT_HANDLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[psicc2] %s received EVENT_I and did nothing\n",
|
||||
__func__);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SMF_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void s2_exit(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* STATE_S21
|
||||
****************************************************************************/
|
||||
|
||||
static void s21_entry(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
static enum smf_state_result s21_run(void *o)
|
||||
{
|
||||
struct s_object *obj = (struct s_object *)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
|
||||
switch (obj->event.event_id)
|
||||
{
|
||||
case EVENT_A:
|
||||
printf("[psicc2] %s received EVENT_A\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S21]);
|
||||
break;
|
||||
|
||||
case EVENT_B:
|
||||
printf("[psicc2] %s received EVENT_B\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S211]);
|
||||
break;
|
||||
|
||||
case EVENT_G:
|
||||
printf("[psicc2] %s received EVENT_G\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SMF_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void s21_exit(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* STATE_S211
|
||||
****************************************************************************/
|
||||
|
||||
static void s211_entry(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
static enum smf_state_result s211_run(void *o)
|
||||
{
|
||||
struct s_object *obj = (struct s_object *)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
|
||||
switch (obj->event.event_id)
|
||||
{
|
||||
case EVENT_D:
|
||||
printf("[psicc2] %s received EVENT_D\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S21]);
|
||||
break;
|
||||
|
||||
case EVENT_H:
|
||||
printf("[psicc2] %s received EVENT_H\n", __func__);
|
||||
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SMF_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void s211_exit(void *o)
|
||||
{
|
||||
(void)o;
|
||||
|
||||
printf("[psicc2] %s\n", __func__);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* State Table
|
||||
****************************************************************************/
|
||||
|
||||
static const struct smf_state demo_states[] =
|
||||
{
|
||||
[STATE_INITIAL] = SMF_CREATE_STATE(
|
||||
initial_entry,
|
||||
initial_run,
|
||||
initial_exit,
|
||||
NULL,
|
||||
&demo_states[STATE_S2]
|
||||
),
|
||||
[STATE_S] = SMF_CREATE_STATE(
|
||||
s_entry,
|
||||
s_run,
|
||||
s_exit,
|
||||
&demo_states[STATE_INITIAL],
|
||||
&demo_states[STATE_S11]
|
||||
),
|
||||
[STATE_S1] = SMF_CREATE_STATE(
|
||||
s1_entry,
|
||||
s1_run,
|
||||
s1_exit,
|
||||
&demo_states[STATE_S],
|
||||
&demo_states[STATE_S11]
|
||||
),
|
||||
[STATE_S2] = SMF_CREATE_STATE(
|
||||
s2_entry,
|
||||
s2_run,
|
||||
s2_exit,
|
||||
&demo_states[STATE_S],
|
||||
&demo_states[STATE_S211]
|
||||
),
|
||||
[STATE_S11] = SMF_CREATE_STATE(
|
||||
s11_entry,
|
||||
s11_run,
|
||||
s11_exit,
|
||||
&demo_states[STATE_S1],
|
||||
NULL
|
||||
),
|
||||
[STATE_S21] = SMF_CREATE_STATE(
|
||||
s21_entry,
|
||||
s21_run,
|
||||
s21_exit,
|
||||
&demo_states[STATE_S2],
|
||||
&demo_states[STATE_S211]
|
||||
),
|
||||
[STATE_S211] = SMF_CREATE_STATE(
|
||||
s211_entry,
|
||||
s211_run,
|
||||
s211_exit,
|
||||
&demo_states[STATE_S21],
|
||||
NULL
|
||||
)
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Name: hsm_psicc2_thread_main
|
||||
****************************************************************************/
|
||||
|
||||
static int hsm_psicc2_thread_main(int argc, char **argv)
|
||||
{
|
||||
struct mq_attr attr;
|
||||
mqd_t mq;
|
||||
ssize_t n;
|
||||
int rc;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.mq_maxmsg = CONFIG_EXAMPLES_SMF_EVENT_QUEUE_SIZE;
|
||||
attr.mq_msgsize = sizeof(struct hsm_psicc2_event);
|
||||
|
||||
mq = mq_open(CONFIG_EXAMPLES_SMF_MQ_NAME, O_CREAT | O_RDONLY, 0666, &attr);
|
||||
if (mq == (mqd_t)-1)
|
||||
{
|
||||
printf("psicc2][ERR] mq_open(%s) failed: %d\n",
|
||||
CONFIG_EXAMPLES_SMF_MQ_NAME, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("[psicc2] State Machine thread started\n");
|
||||
|
||||
memset(&s_obj, 0, sizeof(s_obj));
|
||||
smf_set_initial(SMF_CTX(&s_obj), &demo_states[STATE_INITIAL]);
|
||||
|
||||
while (true)
|
||||
{
|
||||
n = mq_receive(mq, (char *)&s_obj.event, sizeof(s_obj.event), NULL);
|
||||
if (n < 0)
|
||||
{
|
||||
printf("psicc2][ERR] mq_receive failed: %d\n", errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = smf_run_state(SMF_CTX(&s_obj));
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("[psicc2] SMF terminated (rc=%d). Exiting thread\n", rc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mq_close(mq);
|
||||
mq_unlink(CONFIG_EXAMPLES_SMF_MQ_NAME);
|
||||
|
||||
g_started = false;
|
||||
memset(&s_obj, 0, sizeof(s_obj));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: hsm_psicc2_thread_start
|
||||
****************************************************************************/
|
||||
|
||||
int hsm_psicc2_thread_start(void)
|
||||
{
|
||||
int pid;
|
||||
|
||||
if (g_started)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid = task_create("psicc2_thread",
|
||||
CONFIG_EXAMPLES_SMF_PRIORITY,
|
||||
CONFIG_EXAMPLES_SMF_STACKSIZE,
|
||||
hsm_psicc2_thread_main,
|
||||
NULL);
|
||||
if (pid < 0)
|
||||
{
|
||||
printf("psicc2][ERR] task_create failed: %d", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_started = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: hsm_psicc2_post_event
|
||||
****************************************************************************/
|
||||
|
||||
int hsm_psicc2_post_event(uint32_t event_id)
|
||||
{
|
||||
struct hsm_psicc2_event ev;
|
||||
mqd_t mq;
|
||||
int rc;
|
||||
|
||||
ev.event_id = event_id;
|
||||
|
||||
mq = mq_open(CONFIG_EXAMPLES_SMF_MQ_NAME, O_WRONLY | O_NONBLOCK);
|
||||
if (mq == (mqd_t)-1)
|
||||
{
|
||||
printf("psicc2][ERR] mq_open(O_WRONLY) failed: %d "
|
||||
"(did you run 'hsm_psicc2 start'?)\n",
|
||||
errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mq_send(mq, (const char *)&ev, sizeof(ev), 0);
|
||||
if (rc < 0)
|
||||
{
|
||||
printf("psicc2][ERR] mq_send failed: %d\n", errno);
|
||||
mq_close(mq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mq_close(mq);
|
||||
return 0;
|
||||
}
|
||||
89
examples/smf/hsm_psicc2_thread.h
Normal file
89
examples/smf/hsm_psicc2_thread.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/****************************************************************************
|
||||
* apps/examples/smf/hsm_psicc2_thread.h
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2024 Glenn Andrews
|
||||
* State Machine example copyright (c) Miro Samek
|
||||
*
|
||||
* Implementation of the statechart in Figure 2.11 of
|
||||
* Practical UML Statecharts in C/C++, 2nd Edition by Miro Samek
|
||||
* https://www.state-machine.com/psicc2
|
||||
* Used with permission of the author.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __APPS_EXAMPLES_SMF_HSM_PSICC2_THREAD_H
|
||||
#define __APPS_EXAMPLES_SMF_HSM_PSICC2_THREAD_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
struct hsm_psicc2_event
|
||||
{
|
||||
uint32_t event_id;
|
||||
};
|
||||
|
||||
enum demo_events
|
||||
{
|
||||
EVENT_A,
|
||||
EVENT_B,
|
||||
EVENT_C,
|
||||
EVENT_D,
|
||||
EVENT_E,
|
||||
EVENT_F,
|
||||
EVENT_G,
|
||||
EVENT_H,
|
||||
EVENT_I,
|
||||
EVENT_TERMINATE
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
int hsm_psicc2_thread_start(void);
|
||||
int hsm_psicc2_post_event(uint32_t event_id);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __APPS_EXAMPLES_SMF_HSM_PSICC2_THREAD_H */
|
||||
127
examples/smf/smf_main.c
Normal file
127
examples/smf/smf_main.c
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/****************************************************************************
|
||||
* apps/examples/smf/smf_main.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2024 Glenn Andrews
|
||||
* State Machine example copyright (c) Miro Samek
|
||||
*
|
||||
* Implementation of the statechart in Figure 2.11 of
|
||||
* Practical UML Statecharts in C/C++, 2nd Edition by Miro Samek
|
||||
* https://www.state-machine.com/psicc2
|
||||
* Used with permission of the author.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hsm_psicc2_thread.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: usage
|
||||
****************************************************************************/
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" hsm_psicc2 start\n");
|
||||
printf(" hsm_psicc2 event <A..I>\n");
|
||||
printf(" hsm_psicc2 terminate\n");
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: main
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "start") == 0)
|
||||
{
|
||||
printf("State Machine Framework Demo\n");
|
||||
printf("See PSiCC2 Fig 2.11 for the statechart\n");
|
||||
printf("https://www.state-machine.com/psicc2\n\n");
|
||||
return hsm_psicc2_thread_start();
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "terminate") == 0)
|
||||
{
|
||||
return hsm_psicc2_post_event(EVENT_TERMINATE);
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "event") == 0)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (argc < 3 || argv[2] == NULL || argv[2][0] == '\0')
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
c = toupper((unsigned char)argv[2][0]);
|
||||
switch (c)
|
||||
{
|
||||
case 'A':
|
||||
return hsm_psicc2_post_event(EVENT_A);
|
||||
case 'B':
|
||||
return hsm_psicc2_post_event(EVENT_B);
|
||||
case 'C':
|
||||
return hsm_psicc2_post_event(EVENT_C);
|
||||
case 'D':
|
||||
return hsm_psicc2_post_event(EVENT_D);
|
||||
case 'E':
|
||||
return hsm_psicc2_post_event(EVENT_E);
|
||||
case 'F':
|
||||
return hsm_psicc2_post_event(EVENT_F);
|
||||
case 'G':
|
||||
return hsm_psicc2_post_event(EVENT_G);
|
||||
case 'H':
|
||||
return hsm_psicc2_post_event(EVENT_H);
|
||||
case 'I':
|
||||
return hsm_psicc2_post_event(EVENT_I);
|
||||
default:
|
||||
printf("Invalid event '%c'\n", c);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
267
include/system/smf.h
Normal file
267
include/system/smf.h
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
/****************************************************************************
|
||||
* apps/include/system/smf.h
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __APPS_INCLUDE_SYSTEM_SMF_H
|
||||
#define __APPS_INCLUDE_SYSTEM_SMF_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/compiler.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
# ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION
|
||||
# define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \
|
||||
{ \
|
||||
.entry = (_entry), \
|
||||
.run = (_run), \
|
||||
.exit = (_exit), \
|
||||
.parent = (_parent), \
|
||||
.initial = (_initial), \
|
||||
}
|
||||
# else
|
||||
# define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \
|
||||
{ \
|
||||
.entry = (_entry), \
|
||||
.run = (_run), \
|
||||
.exit = (_exit), \
|
||||
.parent = (_parent), \
|
||||
}
|
||||
# endif
|
||||
#else
|
||||
# define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \
|
||||
{ \
|
||||
.entry = (_entry), \
|
||||
.run = (_run), \
|
||||
.exit = (_exit), \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SMF_CTX(o) ((struct smf_ctx *)(o))
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
enum smf_state_result
|
||||
{
|
||||
SMF_EVENT_HANDLED,
|
||||
SMF_EVENT_PROPAGATE
|
||||
};
|
||||
|
||||
typedef void (*state_method)(void *obj);
|
||||
|
||||
typedef enum smf_state_result (*state_execution)(void *obj);
|
||||
|
||||
struct smf_state
|
||||
{
|
||||
/* Optional method that will be run when this state is entered. */
|
||||
|
||||
state_method entry;
|
||||
|
||||
/* Optional method that will be run repeatedly during the state machine
|
||||
* loop.
|
||||
*/
|
||||
|
||||
state_execution run;
|
||||
|
||||
/* Optional method that will be run when this state exits. */
|
||||
|
||||
state_method exit;
|
||||
|
||||
#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
/* Optional parent state that contains common entry/run/exit
|
||||
* implementation among various child states.
|
||||
* entry: Parent function executes BEFORE child function.
|
||||
* run: Parent function executes AFTER child function.
|
||||
* exit: Parent function executes AFTER child function.
|
||||
* Note: When transitioning between two child states with a shared
|
||||
* parent, that parent's exit and entry functions do not execute.
|
||||
*/
|
||||
|
||||
const struct smf_state *parent;
|
||||
|
||||
# ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION
|
||||
/* Optional initial transition state. NULL for leaf states. */
|
||||
|
||||
const struct smf_state *initial;
|
||||
# endif /* CONFIG_SYSTEM_SMF_INITIAL_TRANSITION */
|
||||
#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */
|
||||
};
|
||||
|
||||
struct smf_ctx
|
||||
{
|
||||
/* Current state the state machine is executing. */
|
||||
|
||||
const struct smf_state *current;
|
||||
|
||||
/* Previous state the state machine executed. */
|
||||
|
||||
const struct smf_state *previous;
|
||||
|
||||
#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
/* Currently executing state (which may be a parent). */
|
||||
|
||||
const struct smf_state *executing;
|
||||
#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */
|
||||
|
||||
/* This value is set by the set_terminate function and should
|
||||
* terminate the state machine when its set to a value other than
|
||||
* zero when it's returned by the run_state function.
|
||||
*/
|
||||
|
||||
int32_t terminate_val;
|
||||
|
||||
/* The state machine casts this to a "struct internal_ctx" and it's
|
||||
* used to track state machine context.
|
||||
*/
|
||||
|
||||
uint32_t internal;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_set_initial
|
||||
*
|
||||
* Description:
|
||||
* Initialize the state machine and set its initial state.
|
||||
*
|
||||
* Input Parameters:
|
||||
* ctx - State machine context
|
||||
* init_state - Initial state the state machine starts in
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void smf_set_initial(struct smf_ctx *ctx,
|
||||
const struct smf_state *init_state);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_set_state
|
||||
*
|
||||
* Description:
|
||||
* Change the state machine state. This handles exiting the previous state
|
||||
* and entering the target state. For HSMs the entry and exit actions of
|
||||
* the Least Common Ancestor are not run.
|
||||
*
|
||||
* Input Parameters:
|
||||
* ctx - State machine context
|
||||
* new_state - State to transition to
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_set_terminate
|
||||
*
|
||||
* Description:
|
||||
* Terminate a state machine.
|
||||
*
|
||||
* Input Parameters:
|
||||
* ctx - State machine context
|
||||
* val - Non-zero termination value returned by smf_run_state
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void smf_set_terminate(struct smf_ctx *ctx, int32_t val);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_run_state
|
||||
*
|
||||
* Description:
|
||||
* Run one iteration of a state machine (including any parent states).
|
||||
*
|
||||
* Input Parameters:
|
||||
* ctx - State machine context
|
||||
*
|
||||
* Returned Value:
|
||||
* A non-zero value terminates the state machine. This non-zero value may
|
||||
* represent a terminal state being reached or detection of an error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int32_t smf_run_state(struct smf_ctx *ctx);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_get_current_leaf_state
|
||||
*
|
||||
* Description:
|
||||
* Get the current leaf state. This may be a parent state if the HSM is
|
||||
* malformed (initial transitions are not set up correctly).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline const struct smf_state *
|
||||
smf_get_current_leaf_state(const struct smf_ctx *ctx)
|
||||
{
|
||||
return ctx->current;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_get_current_executing_state
|
||||
*
|
||||
* Description:
|
||||
* Get the state that is currently executing. This may be a parent state.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline const struct smf_state *
|
||||
smf_get_current_executing_state(const struct smf_ctx *ctx)
|
||||
{
|
||||
#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
return ctx->executing;
|
||||
#else
|
||||
return ctx->current;
|
||||
#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */
|
||||
}
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __APPS_INCLUDE_SYSTEM_SMF_H */
|
||||
28
system/smf/Kconfig
Normal file
28
system/smf/Kconfig
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# SMF (State Machine Framework) configuration options
|
||||
#
|
||||
# For syntax reference, see kconfig-language.txt in the NuttX tools repository.
|
||||
|
||||
config SYSTEM_SMF
|
||||
bool "(SMF) support"
|
||||
default n
|
||||
---help---
|
||||
Enables the State Machine Framework (SMF) for implementing state machines
|
||||
|
||||
if SYSTEM_SMF
|
||||
|
||||
config SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
bool "Enable ancestor/parent state support"
|
||||
default n
|
||||
---help---
|
||||
Enables support for parent/ancestor relationships between SMF states.
|
||||
Required for hierarchical state machines.
|
||||
|
||||
config SYSTEM_SMF_INITIAL_TRANSITION
|
||||
bool "Enable initial transition support"
|
||||
default n
|
||||
depends on SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
---help---
|
||||
Enables support for initial transitions in hierarchical states.
|
||||
Depends on ancestor/parent state support.
|
||||
|
||||
endif # SYSTEM_SMF
|
||||
25
system/smf/Make.defs
Normal file
25
system/smf/Make.defs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
############################################################################
|
||||
# apps/smf/Make.defs
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
ifeq ($(CONFIG_SYSTEM_SMF),y)
|
||||
CONFIGURED_APPS += $(APPDIR)/system/smf
|
||||
endif
|
||||
29
system/smf/Makefile
Normal file
29
system/smf/Makefile
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
############################################################################
|
||||
# apps/smf/Makefile
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
# State Machine Framework library
|
||||
|
||||
CSRCS = smf.c
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
605
system/smf/smf.c
Normal file
605
system/smf/smf.c
Normal file
|
|
@ -0,0 +1,605 @@
|
|||
/****************************************************************************
|
||||
* apps/system/smf/smf.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright 2021 The Chromium OS Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <system/smf.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct internal_ctx
|
||||
{
|
||||
bool new_state : 1;
|
||||
bool terminate : 1;
|
||||
bool is_exit : 1;
|
||||
bool handled : 1;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
|
||||
/****************************************************************************
|
||||
* Name: is_descendant_of
|
||||
****************************************************************************/
|
||||
|
||||
static bool is_descendant_of(const struct smf_state *test_state,
|
||||
const struct smf_state *target_state)
|
||||
{
|
||||
const struct smf_state *state;
|
||||
|
||||
for (state = test_state; state != NULL; state = state->parent)
|
||||
{
|
||||
if (target_state == state)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: get_child_of
|
||||
****************************************************************************/
|
||||
|
||||
static const struct smf_state *
|
||||
get_child_of(const struct smf_state *states, const struct smf_state *parent)
|
||||
{
|
||||
const struct smf_state *state = states;
|
||||
|
||||
while (state != NULL)
|
||||
{
|
||||
if (state->parent == parent)
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
state = state->parent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: get_lca_of
|
||||
*
|
||||
* Description:
|
||||
* Find the Least Common Ancestor (LCA) of two states that are not
|
||||
* ancestors of one another.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static const struct smf_state *
|
||||
get_lca_of(const struct smf_state *source, const struct smf_state *dest)
|
||||
{
|
||||
const struct smf_state *ancestor;
|
||||
|
||||
for (ancestor = source->parent; ancestor != NULL;
|
||||
ancestor = ancestor->parent)
|
||||
{
|
||||
/* First common ancestor. */
|
||||
|
||||
if (is_descendant_of(dest, ancestor))
|
||||
{
|
||||
return ancestor;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_execute_all_entry_actions
|
||||
****************************************************************************/
|
||||
|
||||
static bool smf_execute_all_entry_actions(struct smf_ctx *const ctx,
|
||||
const struct smf_state *new_state,
|
||||
const struct smf_state *topmost)
|
||||
{
|
||||
struct internal_ctx *const internal = (void *)&ctx->internal;
|
||||
const struct smf_state *to_execute;
|
||||
|
||||
if (new_state == topmost)
|
||||
{
|
||||
/* There are no child states, so do nothing. */
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (to_execute = get_child_of(new_state, topmost);
|
||||
to_execute != NULL && to_execute != new_state;
|
||||
to_execute = get_child_of(new_state, to_execute))
|
||||
{
|
||||
/* Keep track of the executing entry action in case it calls
|
||||
* smf_set_state().
|
||||
*/
|
||||
|
||||
ctx->executing = to_execute;
|
||||
|
||||
/* Execute every entry action EXCEPT that of the topmost state. */
|
||||
|
||||
if (to_execute->entry != NULL)
|
||||
{
|
||||
to_execute->entry(ctx);
|
||||
|
||||
/* No need to continue if terminate was set. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
ctx->executing = ctx->current;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute the new state entry action. */
|
||||
|
||||
ctx->executing = new_state;
|
||||
if (new_state->entry != NULL)
|
||||
{
|
||||
new_state->entry(ctx);
|
||||
|
||||
/* No need to continue if terminate was set. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
ctx->executing = ctx->current;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->executing = ctx->current;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_execute_ancestor_run_actions
|
||||
****************************************************************************/
|
||||
|
||||
static bool smf_execute_ancestor_run_actions(struct smf_ctx *const ctx)
|
||||
{
|
||||
struct internal_ctx *const internal = (void *)&ctx->internal;
|
||||
const struct smf_state *tmp_state;
|
||||
|
||||
/* Execute all run actions in reverse order. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
/* Return if the current state terminated. */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (internal->new_state || internal->handled)
|
||||
{
|
||||
/* The child state transitioned or handled it. Stop propagating. */
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to run parent run actions. */
|
||||
|
||||
for (tmp_state = ctx->current->parent; tmp_state != NULL;
|
||||
tmp_state = tmp_state->parent)
|
||||
{
|
||||
enum smf_state_result rc;
|
||||
|
||||
/* Keep track of where we are in case an ancestor calls
|
||||
* smf_set_state().
|
||||
*/
|
||||
|
||||
ctx->executing = tmp_state;
|
||||
|
||||
/* Execute parent run action. */
|
||||
|
||||
if (tmp_state->run != NULL)
|
||||
{
|
||||
rc = tmp_state->run(ctx);
|
||||
|
||||
if (rc == SMF_EVENT_HANDLED)
|
||||
{
|
||||
internal->handled = true;
|
||||
}
|
||||
|
||||
/* No need to continue if terminate was set. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
ctx->executing = ctx->current;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* This state dealt with it. Stop propagating. */
|
||||
|
||||
if (internal->new_state || internal->handled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* All done executing the run actions. */
|
||||
|
||||
ctx->executing = ctx->current;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_execute_all_exit_actions
|
||||
****************************************************************************/
|
||||
|
||||
static bool smf_execute_all_exit_actions(struct smf_ctx *const ctx,
|
||||
const struct smf_state *topmost)
|
||||
{
|
||||
struct internal_ctx *const internal = (void *)&ctx->internal;
|
||||
const struct smf_state *tmp_state;
|
||||
const struct smf_state *to_execute;
|
||||
|
||||
tmp_state = ctx->executing;
|
||||
|
||||
for (to_execute = ctx->current;
|
||||
to_execute != NULL && to_execute != topmost;
|
||||
to_execute = to_execute->parent)
|
||||
{
|
||||
if (to_execute->exit != NULL)
|
||||
{
|
||||
ctx->executing = to_execute;
|
||||
to_execute->exit(ctx);
|
||||
|
||||
/* No need to continue if terminate was set in the exit action. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
ctx->executing = tmp_state;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->executing = tmp_state;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_clear_internal_state
|
||||
****************************************************************************/
|
||||
|
||||
static void smf_clear_internal_state(struct smf_ctx *const ctx)
|
||||
{
|
||||
struct internal_ctx *const internal = (void *)&ctx->internal;
|
||||
|
||||
internal->is_exit = false;
|
||||
internal->terminate = false;
|
||||
internal->handled = false;
|
||||
internal->new_state = false;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_set_initial
|
||||
****************************************************************************/
|
||||
|
||||
void smf_set_initial(struct smf_ctx *const ctx,
|
||||
const struct smf_state *init_state)
|
||||
{
|
||||
#ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION
|
||||
/* The final target will be the deepest leaf state that the target
|
||||
* contains. Set that as the real target.
|
||||
*/
|
||||
|
||||
while (init_state->initial != NULL)
|
||||
{
|
||||
init_state = init_state->initial;
|
||||
}
|
||||
#endif
|
||||
|
||||
smf_clear_internal_state(ctx);
|
||||
ctx->current = init_state;
|
||||
ctx->previous = NULL;
|
||||
ctx->terminate_val = 0;
|
||||
|
||||
#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
struct internal_ctx *const internal = (void *)&ctx->internal;
|
||||
const struct smf_state *topmost;
|
||||
|
||||
ctx->executing = init_state;
|
||||
|
||||
/* topmost is the root ancestor of init_state, its parent == NULL. */
|
||||
|
||||
topmost = get_child_of(init_state, NULL);
|
||||
|
||||
/* Execute topmost state entry action,
|
||||
* since smf_execute_all_entry_actions doesn't.
|
||||
*/
|
||||
|
||||
if (topmost->entry != NULL)
|
||||
{
|
||||
ctx->executing = topmost;
|
||||
topmost->entry(ctx);
|
||||
ctx->executing = init_state;
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
/* No need to continue if terminate was set. */
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (smf_execute_all_entry_actions(ctx, init_state, topmost))
|
||||
{
|
||||
/* No need to continue if terminate was set. */
|
||||
|
||||
return;
|
||||
}
|
||||
#else
|
||||
/* Execute entry action if it exists. */
|
||||
|
||||
if (init_state->entry != NULL)
|
||||
{
|
||||
init_state->entry(ctx);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_set_state
|
||||
****************************************************************************/
|
||||
|
||||
void smf_set_state(struct smf_ctx *const ctx,
|
||||
const struct smf_state *new_state)
|
||||
{
|
||||
struct internal_ctx *const internal = (void *)&ctx->internal;
|
||||
|
||||
if (new_state == NULL)
|
||||
{
|
||||
printf("SMF ERR: new_state cannot be NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* It does not make sense to call smf_set_state in an exit phase of a
|
||||
* state since we are already in a transition; we would always ignore
|
||||
* the intended state to transition into.
|
||||
*/
|
||||
|
||||
if (internal->is_exit)
|
||||
{
|
||||
printf("SMF ERR: Calling %s from exit action\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
const struct smf_state *topmost;
|
||||
|
||||
if (ctx->executing != new_state &&
|
||||
ctx->executing->parent == new_state->parent)
|
||||
{
|
||||
/* Optimize sibling transitions (different states under same parent). */
|
||||
|
||||
topmost = ctx->executing->parent;
|
||||
}
|
||||
else if (is_descendant_of(ctx->executing, new_state))
|
||||
{
|
||||
/* New state is a parent of where we are now. */
|
||||
|
||||
topmost = new_state;
|
||||
}
|
||||
else if (is_descendant_of(new_state, ctx->executing))
|
||||
{
|
||||
/* We are a parent of the new state. */
|
||||
|
||||
topmost = ctx->executing;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not directly related, find LCA. */
|
||||
|
||||
topmost = get_lca_of(ctx->executing, new_state);
|
||||
}
|
||||
|
||||
internal->is_exit = true;
|
||||
internal->new_state = true;
|
||||
|
||||
/* Call all exit actions up to (but not including) the topmost. */
|
||||
|
||||
if (smf_execute_all_exit_actions(ctx, topmost))
|
||||
{
|
||||
/* No need to continue if terminate was set in the exit action. */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* If self-transition, call the exit action. */
|
||||
|
||||
if (ctx->executing == new_state && new_state->exit != NULL)
|
||||
{
|
||||
new_state->exit(ctx);
|
||||
|
||||
/* No need to continue if terminate was set in the exit action. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internal->is_exit = false;
|
||||
|
||||
/* If self transition, call the entry action. */
|
||||
|
||||
if (ctx->executing == new_state && new_state->entry != NULL)
|
||||
{
|
||||
new_state->entry(ctx);
|
||||
|
||||
/* No need to continue if terminate was set in the entry action. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION
|
||||
/* The final target will be the deepest leaf state that the target
|
||||
* contains. Set that as the real target.
|
||||
*/
|
||||
|
||||
while (new_state->initial != NULL)
|
||||
{
|
||||
new_state = new_state->initial;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Update the state variables. */
|
||||
|
||||
ctx->previous = ctx->current;
|
||||
ctx->current = new_state;
|
||||
ctx->executing = new_state;
|
||||
|
||||
/* Call all entry actions (except those of topmost). */
|
||||
|
||||
if (smf_execute_all_entry_actions(ctx, new_state, topmost))
|
||||
{
|
||||
/* No need to continue if terminate was set in the entry action. */
|
||||
|
||||
return;
|
||||
}
|
||||
#else
|
||||
/* Flat state machines have a very simple transition. */
|
||||
|
||||
if (ctx->current->exit != NULL)
|
||||
{
|
||||
internal->is_exit = true;
|
||||
ctx->current->exit(ctx);
|
||||
|
||||
/* No need to continue if terminate was set in the exit action. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
internal->is_exit = false;
|
||||
}
|
||||
|
||||
/* Update the state variables. */
|
||||
|
||||
ctx->previous = ctx->current;
|
||||
ctx->current = new_state;
|
||||
|
||||
if (ctx->current->entry != NULL)
|
||||
{
|
||||
ctx->current->entry(ctx);
|
||||
|
||||
/* No need to continue if terminate was set in the entry action. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_set_terminate
|
||||
****************************************************************************/
|
||||
|
||||
void smf_set_terminate(struct smf_ctx *const ctx, int32_t val)
|
||||
{
|
||||
struct internal_ctx *const internal = (void *)&ctx->internal;
|
||||
|
||||
internal->terminate = true;
|
||||
ctx->terminate_val = val;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: smf_run_state
|
||||
****************************************************************************/
|
||||
|
||||
int32_t smf_run_state(struct smf_ctx *const ctx)
|
||||
{
|
||||
struct internal_ctx *const internal = (void *)&ctx->internal;
|
||||
|
||||
/* No need to continue if terminate was set. */
|
||||
|
||||
if (internal->terminate)
|
||||
{
|
||||
return ctx->terminate_val;
|
||||
}
|
||||
|
||||
/* Executing a state's run function could cause a transition, so clear
|
||||
* the internal state to ensure that the transition is handled correctly.
|
||||
*/
|
||||
|
||||
smf_clear_internal_state(ctx);
|
||||
|
||||
#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
|
||||
ctx->executing = ctx->current;
|
||||
|
||||
if (ctx->current->run != NULL)
|
||||
{
|
||||
enum smf_state_result rc;
|
||||
|
||||
rc = ctx->current->run(ctx);
|
||||
if (rc == SMF_EVENT_HANDLED)
|
||||
{
|
||||
internal->handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (smf_execute_ancestor_run_actions(ctx))
|
||||
{
|
||||
return ctx->terminate_val;
|
||||
}
|
||||
#else
|
||||
if (ctx->current->run != NULL)
|
||||
{
|
||||
ctx->current->run(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue