diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..777ad36 --- /dev/null +++ b/.clang-format @@ -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 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bcd6159 --- /dev/null +++ b/CMakeLists.txt @@ -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 +) \ No newline at end of file diff --git a/fsm.c b/fsm.c new file mode 100644 index 0000000..e85a711 --- /dev/null +++ b/fsm.c @@ -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; +} \ No newline at end of file diff --git a/fsm.h b/fsm.h new file mode 100644 index 0000000..2b1b8f2 --- /dev/null +++ b/fsm.h @@ -0,0 +1,72 @@ +// +// Created by Hydro on 2026/4/21. +// +#ifndef QF_FSM_FSM_H +#define QF_FSM_FSM_H +#include +#include +#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