feat: 实现基础功能 #1

Merged
7iwind merged 7 commits from feature/v0.0.1 into develop 2026-04-21 15:05:53 +08:00
4 changed files with 400 additions and 0 deletions

137
.clang-format Normal file
View 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
View 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
View 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
View 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