feat: 实现状态机的基本功能 #2
137
.clang-format
Normal file
137
.clang-format
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
Language: Cpp
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveMacros: true
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
AlignConsecutiveDeclarations: true
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: TopLevel
|
||||||
|
AlwaysBreakAfterReturnType: TopLevelDefinitions
|
||||||
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: true
|
||||||
|
AfterControlStatement: true
|
||||||
|
AfterEnum: true
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: true
|
||||||
|
AfterStruct: true
|
||||||
|
AfterUnion: true
|
||||||
|
AfterExternBlock: true
|
||||||
|
BeforeCatch: true
|
||||||
|
BeforeElse: true
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: All
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakInheritanceList: BeforeComma
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakConstructorInitializers: BeforeComma
|
||||||
|
BreakAfterJavaFieldAnnotations: true
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 80
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
DeriveLineEnding: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: false
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||||
|
Priority: 3
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
|
IncludeIsMainSourceRegex: ''
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
InsertBraces: true
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 4
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: false
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 200
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: true
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: Custom
|
||||||
|
SpaceBeforeParensOptions:
|
||||||
|
AfterControlStatements: true
|
||||||
|
AfterFunctionDefinitionName: true
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
Standard: Latest
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
|
TabWidth: 8
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: Never
|
||||||
16
CMakeLists.txt
Normal file
16
CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.28)
|
||||||
|
|
||||||
|
project(qffsm C)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME})
|
||||||
|
|
||||||
|
target_sources(${PROJECT_NAME} PRIVATE fsm.c)
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC .)
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE
|
||||||
|
-ffunction-sections -fdata-sections
|
||||||
|
-Wall -Wextra
|
||||||
|
)
|
||||||
|
target_link_options(${PROJECT_NAME} PRIVATE
|
||||||
|
-Wl,--gc-sections
|
||||||
|
)
|
||||||
175
fsm.c
Normal file
175
fsm.c
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
//
|
||||||
|
// Created by Hydro on 2026/4/21.
|
||||||
|
//
|
||||||
|
#include "fsm.h"
|
||||||
|
|
||||||
|
fsm_transition_t *
|
||||||
|
fsm_transition (fsm_handle_t *handle_ptr, const fsm_event_t *event)
|
||||||
|
{
|
||||||
|
fsm_transition_t *trans = NULL;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!(handle_ptr && event))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const fsm_state_t *current_state = fsm_get_state(handle_ptr);
|
||||||
|
for (size_t i = 0; i < handle_ptr->num_trans; i++)
|
||||||
|
{
|
||||||
|
if (handle_ptr->p_trans_list[i].state == current_state->state
|
||||||
|
&& handle_ptr->p_trans_list[i].event == event->event)
|
||||||
|
{
|
||||||
|
trans = &handle_ptr->p_trans_list[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
return trans;
|
||||||
|
}
|
||||||
|
fsm_result_e
|
||||||
|
fsm_init_with_queue (fsm_handle_t *handle_ptr,
|
||||||
|
uint16_t init_state,
|
||||||
|
const fsm_event_queue_init event_queue_init,
|
||||||
|
const fsm_event_queue_put event_queue_put,
|
||||||
|
const fsm_event_queue_get event_queue_get)
|
||||||
|
{
|
||||||
|
fsm_result_e result = FSM_OK;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!(handle_ptr && event_queue_init && event_queue_put
|
||||||
|
&& event_queue_get))
|
||||||
|
{
|
||||||
|
result = FSM_FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < handle_ptr->num_states; i++)
|
||||||
|
{
|
||||||
|
if (handle_ptr->p_state_list[i].state == init_state)
|
||||||
|
{
|
||||||
|
handle_ptr->current_state = &handle_ptr->p_state_list[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle_ptr->event_queue_get = event_queue_get;
|
||||||
|
handle_ptr->event_queue_put = event_queue_put;
|
||||||
|
handle_ptr->event_queue_init = event_queue_init;
|
||||||
|
handle_ptr->custom_queue = true;
|
||||||
|
result = handle_ptr->event_queue_init(handle_ptr->event_queue_handle,
|
||||||
|
handle_ptr->event_queue,
|
||||||
|
EVENT_QUEUE_LEN
|
||||||
|
* sizeof(fsm_event_t));
|
||||||
|
} while (0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
fsm_result_e
|
||||||
|
fsm_event_put (fsm_handle_t *handle_ptr, fsm_event_t *event)
|
||||||
|
{
|
||||||
|
fsm_result_e result = FSM_OK;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!(handle_ptr && event))
|
||||||
|
{
|
||||||
|
result = FSM_FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (handle_ptr->custom_queue)
|
||||||
|
{
|
||||||
|
result = handle_ptr->event_queue_put(handle_ptr->event_queue_handle,
|
||||||
|
event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO:补充默认队列的环形缓冲区填充逻辑
|
||||||
|
result = FSM_FAIL;
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
fsm_result_e
|
||||||
|
fsm_process_queue (fsm_handle_t *handle_ptr)
|
||||||
|
{
|
||||||
|
fsm_result_e result = FSM_OK;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!handle_ptr)
|
||||||
|
{
|
||||||
|
result = FSM_FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fsm_event_t event = { 0 };
|
||||||
|
if (handle_ptr->custom_queue)
|
||||||
|
{
|
||||||
|
result = handle_ptr->event_queue_get(handle_ptr->event_queue_handle,
|
||||||
|
&event);
|
||||||
|
}
|
||||||
|
if (result != FSM_OK)
|
||||||
|
{
|
||||||
|
result = FSM_FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fsm_dispatch(handle_ptr, &event);
|
||||||
|
} while (0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
fsm_result_e
|
||||||
|
fsm_dispatch (fsm_handle_t *handle_ptr, const fsm_event_t *event)
|
||||||
|
{
|
||||||
|
fsm_result_e result = FSM_OK;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!(handle_ptr && event))
|
||||||
|
{
|
||||||
|
result = FSM_FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const fsm_transition_t *trans = fsm_transition(handle_ptr, event);
|
||||||
|
fsm_state_t *current_state = fsm_get_state(handle_ptr);
|
||||||
|
if (trans)
|
||||||
|
{
|
||||||
|
// 发生状态切换
|
||||||
|
if (trans->next_state != current_state->state)
|
||||||
|
{
|
||||||
|
if (current_state->on_exit)
|
||||||
|
{
|
||||||
|
current_state->on_exit(current_state->user_data);
|
||||||
|
}
|
||||||
|
// 执行事件动作
|
||||||
|
if (trans->on_action)
|
||||||
|
{
|
||||||
|
trans->on_action(event->user_data);
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < handle_ptr->num_states; i++)
|
||||||
|
{
|
||||||
|
if (handle_ptr->p_state_list[i].state == trans->next_state)
|
||||||
|
{
|
||||||
|
current_state = &handle_ptr->p_state_list[i];
|
||||||
|
if (current_state->on_entry)
|
||||||
|
{
|
||||||
|
current_state->on_entry(current_state->user_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle_ptr->current_state = current_state;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 执行事件动作
|
||||||
|
if (trans->on_action)
|
||||||
|
{
|
||||||
|
trans->on_action(event->user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = FSM_FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
fsm_state_t *
|
||||||
|
fsm_get_state (const fsm_handle_t *h)
|
||||||
|
{
|
||||||
|
return h->current_state;
|
||||||
|
}
|
||||||
72
fsm.h
Normal file
72
fsm.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
//
|
||||||
|
// Created by Hydro on 2026/4/21.
|
||||||
|
//
|
||||||
|
#ifndef QF_FSM_FSM_H
|
||||||
|
#define QF_FSM_FSM_H
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#define EVENT_QUEUE_LEN 16
|
||||||
|
#define EVENT_QUEUE_HANDLE_MAX_SIZE 32
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FSM_OK,
|
||||||
|
FSM_FAIL
|
||||||
|
} fsm_result_e;
|
||||||
|
typedef struct qf_fsm_event fsm_event_t;
|
||||||
|
typedef struct qf_fsm fsm_handle_t;
|
||||||
|
typedef struct qf_fsm_transition fsm_transition_t;
|
||||||
|
typedef fsm_result_e (*fsm_cb)(void *userdata);
|
||||||
|
typedef fsm_result_e (*fsm_event_queue_init)(void *queue_handle,
|
||||||
|
void *data_ptr,
|
||||||
|
size_t size);
|
||||||
|
typedef fsm_result_e (*fsm_event_queue_get)(void *handle_ptr,
|
||||||
|
fsm_event_t *event);
|
||||||
|
typedef fsm_result_e (*fsm_event_queue_put)(void *handle_ptr,
|
||||||
|
fsm_event_t *event);
|
||||||
|
typedef struct qf_fsm_state
|
||||||
|
{
|
||||||
|
uint16_t state;
|
||||||
|
fsm_cb on_exit;
|
||||||
|
fsm_cb on_event;
|
||||||
|
fsm_cb on_entry;
|
||||||
|
void *user_data;
|
||||||
|
} fsm_state_t;
|
||||||
|
struct qf_fsm_transition
|
||||||
|
{
|
||||||
|
uint16_t state;
|
||||||
|
uint16_t event;
|
||||||
|
uint16_t next_state;
|
||||||
|
fsm_cb on_action;
|
||||||
|
};
|
||||||
|
struct qf_fsm_event
|
||||||
|
{
|
||||||
|
uint16_t event;
|
||||||
|
void *user_data;
|
||||||
|
};
|
||||||
|
struct qf_fsm
|
||||||
|
{
|
||||||
|
fsm_state_t *current_state;
|
||||||
|
char *name;
|
||||||
|
uint16_t num_states;
|
||||||
|
fsm_state_t *p_state_list;
|
||||||
|
uint32_t num_trans;
|
||||||
|
fsm_transition_t *p_trans_list;
|
||||||
|
uint8_t event_queue_handle[EVENT_QUEUE_HANDLE_MAX_SIZE];
|
||||||
|
fsm_event_t event_queue[EVENT_QUEUE_LEN];
|
||||||
|
bool custom_queue;
|
||||||
|
fsm_event_queue_init event_queue_init;
|
||||||
|
fsm_event_queue_put event_queue_put;
|
||||||
|
fsm_event_queue_get event_queue_get;
|
||||||
|
};
|
||||||
|
fsm_result_e fsm_init(fsm_handle_t *handle_ptr, uint16_t init_state);
|
||||||
|
fsm_result_e fsm_init_with_queue(fsm_handle_t *handle_ptr,
|
||||||
|
uint16_t init_state,
|
||||||
|
fsm_event_queue_init event_queue_init,
|
||||||
|
fsm_event_queue_put event_queue_put,
|
||||||
|
fsm_event_queue_get event_queue_get);
|
||||||
|
fsm_result_e fsm_event_put(fsm_handle_t *handle_ptr, fsm_event_t *event);
|
||||||
|
fsm_result_e fsm_process_queue(fsm_handle_t *handle_ptr);
|
||||||
|
fsm_result_e fsm_dispatch(fsm_handle_t *handle_ptr, const fsm_event_t *event);
|
||||||
|
fsm_state_t *fsm_get_state(const fsm_handle_t *h);
|
||||||
|
#endif // QF_FSM_FSM_H
|
||||||
Reference in New Issue
Block a user