├── .gitignore ├── CMakeLists.txt ├── Config.cmake.in ├── LICENSE ├── Makefile ├── README.md ├── README_cn.md ├── include └── openfsm.hpp ├── src └── openfsm.cpp └── test └── test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | build* -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.12.1) 2 | PROJECT(openfsm) 3 | 4 | # Define the library target 5 | add_library(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/openfsm.cpp) 6 | target_include_directories( ${PROJECT_NAME} PUBLIC 7 | $ 8 | $ 9 | ) 10 | 11 | # Install rules 12 | include(GNUInstallDirs) 13 | 14 | if(DEFINED ENV{PREFIX}) 15 | message(STATUS "Set install prefix: $ENV{PREFIX}") 16 | file(TO_CMAKE_PATH "$ENV{PREFIX}" PREFIX_PATH) 17 | set(CMAKE_INSTALL_PREFIX ${PREFIX_PATH}) 18 | endif() 19 | 20 | install(TARGETS ${PROJECT_NAME} 21 | EXPORT ${PROJECT_NAME}-targets 22 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 23 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 24 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 25 | ) 26 | 27 | # Install include directory 28 | install( 29 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ 30 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openfsm/ 31 | FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" 32 | ) 33 | 34 | # Package configuration 35 | include(CMakePackageConfigHelpers) 36 | 37 | write_basic_package_version_file( 38 | "${CMAKE_CURRENT_BINARY_DIR}/openfsmConfigVersion.cmake" 39 | VERSION 1.0 40 | COMPATIBILITY AnyNewerVersion 41 | ) 42 | 43 | export(EXPORT ${PROJECT_NAME}-targets 44 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake" 45 | NAMESPACE openfsm:: 46 | ) 47 | 48 | configure_package_config_file( 49 | "${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in" 50 | "${CMAKE_CURRENT_BINARY_DIR}/openfsmConfig.cmake" 51 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 52 | ) 53 | 54 | install(EXPORT ${PROJECT_NAME}-targets 55 | FILE ${PROJECT_NAME}Targets.cmake 56 | NAMESPACE openfsm:: 57 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 58 | ) 59 | 60 | install( 61 | FILES 62 | "${CMAKE_CURRENT_BINARY_DIR}/openfsmConfig.cmake" 63 | "${CMAKE_CURRENT_BINARY_DIR}/openfsmConfigVersion.cmake" 64 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 65 | ) -------------------------------------------------------------------------------- /Config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CMAKE_COMMON_FLAGS ?= -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2 | CMAKE_DEBUG_FLAGS ?= 3 | CMAKE_RELEASE_FLAGS ?= 4 | NPROCS ?= $(shell nproc) 5 | CLANG_FORMAT ?= clang-format 6 | 7 | # NOTE: use Makefile.local to override the options defined above. 8 | -include Makefile.local 9 | 10 | CMAKE_DEBUG_FLAGS += -DCMAKE_BUILD_TYPE=Debug $(CMAKE_COMMON_FLAGS) 11 | CMAKE_RELEASE_FLAGS += -DCMAKE_BUILD_TYPE=Release $(CMAKE_COMMON_FLAGS) 12 | 13 | .PHONY: all 14 | all: test-debug test-release 15 | 16 | # Run cmake 17 | .PHONY: cmake-debug 18 | cmake-debug: 19 | cmake -B build_debug $(CMAKE_DEBUG_FLAGS) 20 | 21 | .PHONY: cmake-release 22 | cmake-release: 23 | cmake -B build_release $(CMAKE_RELEASE_FLAGS) 24 | 25 | build_debug/CMakeCache.txt: cmake-debug 26 | build_release/CMakeCache.txt: cmake-release 27 | 28 | # Build using cmake 29 | .PHONY: build-debug build-release 30 | build-debug build-release: build-%: build_%/CMakeCache.txt 31 | cmake --build build_$* -j $(NPROCS) 32 | 33 | # Cleanup data 34 | .PHONY: clean-debug clean-release 35 | clean-debug clean-release: clean-%: 36 | cmake --build build_$* --target clean 37 | 38 | .PHONY: dist-clean 39 | dist-clean: 40 | rm -rf build_* 41 | rm -rf .pgdata 42 | rm -rf .cache 43 | 44 | # Install 45 | .PHONY: install-debug install-release 46 | install-debug install-release: install-%: build-% 47 | cmake --install build_$* 48 | 49 | .PHONY: install 50 | install: install-release 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenFSM 2 | A simple and easy-to-use C++ finite state machine. 3 | 4 | 5 | ## Compilation and execution 6 | Please install the cmake tool. With cmake you can build a VS or XCode project and compile and run it on VS or XCode. 7 | Source code:https://github.com/openlinyou/openfsm 8 | ``` 9 | # Clone the project 10 | git clone https://github.com/openlinyou/openfsm 11 | cd ./openfsm 12 | # Create a build project directory 13 | mkdir build 14 | cd build 15 | cmake .. 16 | # If it's win32, openfsm.sln will appear in this directory. Click it to start VS for coding and debugging. 17 | make 18 | ./test 19 | ``` 20 | 21 | ## All source files 22 | + src/openfsm.h 23 | + src/openfsm.cpp 24 | 25 | 26 | ## Finite State Machine Design for a Spaceship 27 | 28 | The spaceship has four states: Ground Pressure Test (StateTest), Ignition Launch (StateLaunch), Recovery Return (ActionReturn), and Launch Failure (StateFailure). 29 | 30 | 31 | Each state consists of several actions. In this way, multiple actions make up a state, and multiple states make up a state machine. 32 | 1. Ground Pressure Test (StateTest): Engine Test (ActionTestEngine) and Fuel Tank Test (ActionTestTank); 33 | 2. Ignition Launch (StateLaunch): Spaceship Ignition (ActionFireUp) and Launch Lift-off (ActionLaunch); 34 | 3. Recovery Return (ActionReturn): Return to Ground for Recovery (ActionReturn); 35 | 4. Launch Failure (StateFailure): Launch Failure(ActionFailure); 36 | 37 | 38 | State transitions. There are two types of state transitions. 39 | 1. If the Ground Pressure Test(StateTest) is successful, it switches to Ignition Launch(StateLaunch), otherwise it switches to Launch Failure(StateFailure). 40 | 2. If the Ignition Launch(StateLaunch) is successful, it switches to Recovery Return(ActionReturn), otherwise it switches to Launch Failure(StateFailure). 41 | 42 | 43 | Before creating the state machine, register the actions first. 44 | ```C++ 45 | OpenFSM::RegisterAction("ActionTestEngine"); 46 | OpenFSM::RegisterAction("ActionTestTank"); 47 | OpenFSM::RegisterAction("ActionFireUp"); 48 | OpenFSM::RegisterAction("ActionLaunch"); 49 | OpenFSM::RegisterAction("ActionReturn"); 50 | OpenFSM::RegisterAction("ActionFailure"); 51 | ``` 52 | 53 | Select a set of actions to form a state. There are four states in total. 54 | ```C++ 55 | OpenFSM::RegisterState("StateTest", { "ActionTestEngine", "ActionTestTank" }, EStateTest); 56 | OpenFSM::RegisterState("StateLaunch", { "ActionFireUp", "ActionLaunch" }, EStateLaunch); 57 | OpenFSM::RegisterState("StateRecycle", { "ActionReturn" }, EStateRecycle); 58 | OpenFSM::RegisterState("StateFailure", { "ActionFailure"}, EStateFailure); 59 | ``` 60 | 61 | Specify that the state can switch to the next state. For example, if the spaceship is dealing with a launch failure explosion state, it cannot switch to other states. 62 | ```C++ 63 | OpenFSM::RegisterRelation("StateTest", { "StateLaunch", "StateFailure" }); 64 | OpenFSM::RegisterRelation("StateLaunch", { "StateRecycle", "StateFailure" }); 65 | ``` 66 | 67 | Assemble the state machine, which can be assembled using state names or ids. 68 | If it is a traditional rocket, there are only three states: StateTest, StateLaunch, StateFailure. 69 | Starship has a recycling state, with four states: StateTest, StateLaunch, StateRecycle, StateFailure. 70 | ```C++ 71 | openFSM_.setStates({ "StateTest", "StateLaunch", "StateRecycle", "StateFailure" }); 72 | //or 73 | openFSM_.setStates({ EStateTest, EStateLaunch, EStateRecycle, EStateFailure }); 74 | ``` 75 | 76 | Complete design code 77 | ```C++ 78 | #include 79 | #include 80 | #include 81 | #include "openfsm.h" 82 | 83 | using namespace open; 84 | 85 | enum EFMSState 86 | { 87 | EStateTest = 100, 88 | EStateLaunch = 101, 89 | EStateRecycle = 102, 90 | EStateFailure = 103, 91 | }; 92 | struct StarShip 93 | { 94 | OpenFSM openFSM_; 95 | void start() 96 | { 97 | std::srand((int)time(NULL)); 98 | openFSM_.setCustom(this); 99 | // or openFSM_.setStates({ "StateTest", "StateLaunch", "StateRecycle", "StateFailure" }); 100 | openFSM_.setStates({ EStateTest, EStateLaunch, EStateRecycle, EStateFailure }); 101 | openFSM_.enterState(EStateTest); 102 | } 103 | bool testEngine() {return std::rand() % 3 == 0;} 104 | bool testTank() {return std::rand() % 3 == 0;} 105 | bool fireUp() {return std::rand() % 3 == 0;} 106 | bool launch() {return std::rand() % 3 == 0;} 107 | bool update() 108 | { 109 | openFSM_.update(); 110 | return openFSM_.focusEState() == EStateRecycle; 111 | } 112 | }; 113 | 114 | //StateTest 115 | class ActionTestEngine: public OpenFSMAction 116 | { 117 | virtual void enter(OpenFSM& fsm) const 118 | { 119 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 120 | } 121 | virtual void update(OpenFSM& fsm) const 122 | { 123 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 124 | if (fsm.custom()->testEngine()) 125 | { 126 | printf("==>StarShip Engine Ok\n"); 127 | fsm.nextAction(); 128 | } 129 | else 130 | { 131 | printf("==>StarShip Engine Bad\n"); 132 | fsm.nextState(EStateFailure); 133 | } 134 | } 135 | virtual void exit(OpenFSM& fsm) const 136 | { 137 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 138 | } 139 | }; 140 | 141 | class ActionTestTank: public OpenFSMAction 142 | { 143 | virtual void enter(OpenFSM& fsm) const 144 | { 145 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 146 | } 147 | virtual void update(OpenFSM& fsm) const 148 | { 149 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 150 | if (fsm.custom()->testTank()) 151 | { 152 | printf("==>StarShip Tank Ok\n"); 153 | fsm.nextAction(); 154 | } 155 | else 156 | { 157 | printf("==>StarShip Tank Bad\n"); 158 | fsm.nextState(EStateFailure); 159 | } 160 | } 161 | virtual void exit(OpenFSM& fsm) const 162 | { 163 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 164 | } 165 | }; 166 | 167 | //StateLaunch 168 | class ActionFireUp : public OpenFSMAction 169 | { 170 | virtual void enter(OpenFSM& fsm) const 171 | { 172 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 173 | } 174 | virtual void update(OpenFSM& fsm) const 175 | { 176 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 177 | if (fsm.custom()->fireUp()) 178 | { 179 | printf("==>StarShip FireUp Ok\n"); 180 | fsm.nextAction(); 181 | } 182 | else 183 | { 184 | printf("==>StarShip FireUp Failed\n"); 185 | fsm.nextState(EStateFailure); 186 | } 187 | } 188 | virtual void exit(OpenFSM& fsm) const 189 | { 190 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 191 | } 192 | }; 193 | class ActionLaunch : public OpenFSMAction 194 | { 195 | virtual void enter(OpenFSM& fsm) const 196 | { 197 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 198 | } 199 | virtual void update(OpenFSM& fsm) const 200 | { 201 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 202 | if (fsm.custom()->launch()) 203 | { 204 | printf("==>StarShip Launch Ok\n"); 205 | fsm.nextAction(); 206 | } 207 | else 208 | { 209 | printf("==>StarShip Launch Failed\n"); 210 | fsm.nextState(EStateFailure); 211 | } 212 | } 213 | virtual void exit(OpenFSM& fsm) const 214 | { 215 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 216 | } 217 | }; 218 | 219 | //StateRecycle 220 | class ActionReturn : public OpenFSMAction 221 | { 222 | virtual void enter(OpenFSM& fsm) const 223 | { 224 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 225 | } 226 | virtual void update(OpenFSM& fsm) const 227 | { 228 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 229 | printf("==>Congratulation! Complete mission!\n"); 230 | } 231 | virtual void exit(OpenFSM& fsm) const 232 | { 233 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 234 | } 235 | }; 236 | 237 | //StateFailure 238 | class ActionFailure : public OpenFSMAction 239 | { 240 | virtual void enter(OpenFSM& fsm) const 241 | { 242 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 243 | } 244 | virtual void update(OpenFSM& fsm) const 245 | { 246 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 247 | printf("==>StarShip Launch again\n"); 248 | fsm.nextState(EStateTest); 249 | } 250 | virtual void exit(OpenFSM& fsm) const 251 | { 252 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 253 | } 254 | }; 255 | 256 | int main() 257 | { 258 | OpenFSM::RegisterAction("ActionTestEngine"); 259 | OpenFSM::RegisterAction("ActionTestTank"); 260 | OpenFSM::RegisterAction("ActionFireUp"); 261 | OpenFSM::RegisterAction("ActionLaunch"); 262 | OpenFSM::RegisterAction("ActionReturn"); 263 | OpenFSM::RegisterAction("ActionFailure"); 264 | 265 | OpenFSM::RegisterState("StateTest", { "ActionTestEngine", "ActionTestTank" }, EStateTest); 266 | OpenFSM::RegisterState("StateLaunch", { "ActionFireUp", "ActionLaunch" }, EStateLaunch); 267 | OpenFSM::RegisterState("StateRecycle", { "ActionReturn" }, EStateRecycle); 268 | OpenFSM::RegisterState("StateFailure", { "ActionFailure"}, EStateFailure); 269 | 270 | OpenFSM::RegisterRelation("StateTest", { "StateLaunch", "StateFailure" }); 271 | OpenFSM::RegisterRelation("StateLaunch", { "StateRecycle", "StateFailure" }); 272 | 273 | StarShip starShip; 274 | starShip.start(); 275 | int count = 1000; 276 | while (count-- > 0) 277 | { 278 | if (starShip.update()) 279 | break; 280 | } 281 | return getchar(); 282 | } 283 | ``` 284 | -------------------------------------------------------------------------------- /README_cn.md: -------------------------------------------------------------------------------- 1 | # OpenFSM 2 | 一个简单易用的C++有限状态机。 3 | 4 | **OpenLinyou项目致力于跨平台服务器框架,在VS或者XCode上写代码,无需任何改动就可以编译运行在Linux上,甚至是安卓和iOS.** 5 | OpenLinyou:https://github.com/openlinyou 6 | https://gitee.com/linyouhappy 7 | 8 | ## 编译和执行 9 | 请安装cmake工具,用cmake构建工程,可以在vs或者xcode上编译运行。 10 | 源代码:https://github.com/openlinyou/openfsm 11 | https://gitee.com/linyouhappy/openfsm 12 | ``` 13 | #克隆项目 14 | git clone https://github.com/openlinyou/openfsm 15 | cd ./openfsm 16 | #创建build工程目录 17 | mkdir build 18 | cd build 19 | cmake .. 20 | #如果是win32,在该目录出现openfsm.sln,点击它就可以启动vs写代码调试 21 | make 22 | ./test 23 | ``` 24 | 25 | ## 全部源文件 26 | + src/openfsm.h 27 | + src/openfsm.cpp 28 | 29 | 30 | ## 有限状态机设计星舰 31 | 32 | 星舰有四种状态:地面压力测试(StateTest),点火发射升空(StateLaunch),回收返航(ActionReturn)和发射失败(StateFailure)。 33 | 34 | 每一个状态由几个行为组成。这样,多个行为组成一个状态,多个状态组成一个状态机。 35 | 1. 地面压力测试(StateTest):引擎测试(ActionTestEngine)和燃料罐测试(ActionTestTank); 36 | 2. 点火发射升空(StateLaunch):星舰点火(ActionFireUp)和发射升空(ActionLaunch); 37 | 3. 回收返航(ActionReturn):返回地面回收(ActionReturn); 38 | 4. 发射失败(StateFailure):发射失败(ActionFailure); 39 | 40 | 状态切换。有两种状态切换。 41 | 1. 地面压力测试(StateTest)如果成功,就切换到点火发射升空(StateLaunch),否则,切换到发射失败(StateFailure) 42 | 2. 点火发射升空(StateLaunch)如果成功,就切换到回收返航(ActionReturn),否则,切换到发射失败(StateFailure) 43 | 44 | 在创建状态机之前,先进行动作注册。 45 | ```C++ 46 | OpenFSM::RegisterAction("ActionTestEngine"); 47 | OpenFSM::RegisterAction("ActionTestTank"); 48 | OpenFSM::RegisterAction("ActionFireUp"); 49 | OpenFSM::RegisterAction("ActionLaunch"); 50 | OpenFSM::RegisterAction("ActionReturn"); 51 | OpenFSM::RegisterAction("ActionFailure"); 52 | ``` 53 | 54 | 选择一组动作,组成一个状态。共四个状态。 55 | ```C++ 56 | OpenFSM::RegisterState("StateTest", { "ActionTestEngine", "ActionTestTank" }, EStateTest); 57 | OpenFSM::RegisterState("StateLaunch", { "ActionFireUp", "ActionLaunch" }, EStateLaunch); 58 | OpenFSM::RegisterState("StateRecycle", { "ActionReturn" }, EStateRecycle); 59 | OpenFSM::RegisterState("StateFailure", { "ActionFailure"}, EStateFailure); 60 | ``` 61 | 62 | 指定状态可以切换到下一个状态。例如,星舰处理发射失败爆炸状态,那就无法切换到其他状态。 63 | ```C++ 64 | OpenFSM::RegisterRelation("StateTest", { "StateLaunch", "StateFailure" }); 65 | OpenFSM::RegisterRelation("StateLaunch", { "StateRecycle", "StateFailure" }); 66 | ``` 67 | 68 | 组装状态机,可以用状态名称或者id进行组装。 69 | 如果是传统火箭,只有三个状态StateTest, StateLaunch, StateFailure 70 | 星舰有回收状态,有四个状态StateTest, StateLaunch, StateRecycle, StateFailure 71 | 72 | ```C++ 73 | openFSM_.setStates({ "StateTest", "StateLaunch", "StateRecycle", "StateFailure" }); 74 | //or 75 | openFSM_.setStates({ EStateTest, EStateLaunch, EStateRecycle, EStateFailure }); 76 | ``` 77 | 78 | 完整设计代码 79 | ```C++ 80 | #include 81 | #include 82 | #include 83 | #include "openfsm.h" 84 | 85 | using namespace open; 86 | 87 | enum EFMSState 88 | { 89 | EStateTest = 100, 90 | EStateLaunch = 101, 91 | EStateRecycle = 102, 92 | EStateFailure = 103, 93 | }; 94 | struct StarShip 95 | { 96 | OpenFSM openFSM_; 97 | void start() 98 | { 99 | std::srand((int)time(NULL)); 100 | openFSM_.setCustom(this); 101 | // or openFSM_.setStates({ "StateTest", "StateLaunch", "StateRecycle", "StateFailure" }); 102 | openFSM_.setStates({ EStateTest, EStateLaunch, EStateRecycle, EStateFailure }); 103 | openFSM_.enterState(EStateTest); 104 | } 105 | bool testEngine() {return std::rand() % 3 == 0;} 106 | bool testTank() {return std::rand() % 3 == 0;} 107 | bool fireUp() {return std::rand() % 3 == 0;} 108 | bool launch() {return std::rand() % 3 == 0;} 109 | bool update() 110 | { 111 | openFSM_.update(); 112 | return openFSM_.focusEState() == EStateRecycle; 113 | } 114 | }; 115 | 116 | //StateTest 117 | class ActionTestEngine: public OpenFSMAction 118 | { 119 | virtual void enter(OpenFSM& fsm) const 120 | { 121 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 122 | } 123 | virtual void update(OpenFSM& fsm) const 124 | { 125 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 126 | if (fsm.custom()->testEngine()) 127 | { 128 | printf("==>StarShip Engine Ok\n"); 129 | fsm.nextAction(); 130 | } 131 | else 132 | { 133 | printf("==>StarShip Engine Bad\n"); 134 | fsm.nextState(EStateFailure); 135 | } 136 | } 137 | virtual void exit(OpenFSM& fsm) const 138 | { 139 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 140 | } 141 | }; 142 | 143 | class ActionTestTank: public OpenFSMAction 144 | { 145 | virtual void enter(OpenFSM& fsm) const 146 | { 147 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 148 | } 149 | virtual void update(OpenFSM& fsm) const 150 | { 151 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 152 | if (fsm.custom()->testTank()) 153 | { 154 | printf("==>StarShip Tank Ok\n"); 155 | fsm.nextAction(); 156 | } 157 | else 158 | { 159 | printf("==>StarShip Tank Bad\n"); 160 | fsm.nextState(EStateFailure); 161 | } 162 | } 163 | virtual void exit(OpenFSM& fsm) const 164 | { 165 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 166 | } 167 | }; 168 | 169 | //StateLaunch 170 | class ActionFireUp : public OpenFSMAction 171 | { 172 | virtual void enter(OpenFSM& fsm) const 173 | { 174 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 175 | } 176 | virtual void update(OpenFSM& fsm) const 177 | { 178 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 179 | if (fsm.custom()->fireUp()) 180 | { 181 | printf("==>StarShip FireUp Ok\n"); 182 | fsm.nextAction(); 183 | } 184 | else 185 | { 186 | printf("==>StarShip FireUp Failed\n"); 187 | fsm.nextState(EStateFailure); 188 | } 189 | } 190 | virtual void exit(OpenFSM& fsm) const 191 | { 192 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 193 | } 194 | }; 195 | class ActionLaunch : public OpenFSMAction 196 | { 197 | virtual void enter(OpenFSM& fsm) const 198 | { 199 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 200 | } 201 | virtual void update(OpenFSM& fsm) const 202 | { 203 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 204 | if (fsm.custom()->launch()) 205 | { 206 | printf("==>StarShip Launch Ok\n"); 207 | fsm.nextAction(); 208 | } 209 | else 210 | { 211 | printf("==>StarShip Launch Failed\n"); 212 | fsm.nextState(EStateFailure); 213 | } 214 | } 215 | virtual void exit(OpenFSM& fsm) const 216 | { 217 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 218 | } 219 | }; 220 | 221 | //StateRecycle 222 | class ActionReturn : public OpenFSMAction 223 | { 224 | virtual void enter(OpenFSM& fsm) const 225 | { 226 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 227 | } 228 | virtual void update(OpenFSM& fsm) const 229 | { 230 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 231 | printf("==>Congratulation! Complete mission!\n"); 232 | } 233 | virtual void exit(OpenFSM& fsm) const 234 | { 235 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 236 | } 237 | }; 238 | 239 | //StateFailure 240 | class ActionFailure : public OpenFSMAction 241 | { 242 | virtual void enter(OpenFSM& fsm) const 243 | { 244 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 245 | } 246 | virtual void update(OpenFSM& fsm) const 247 | { 248 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 249 | printf("==>StarShip Launch again\n"); 250 | fsm.nextState(EStateTest); 251 | } 252 | virtual void exit(OpenFSM& fsm) const 253 | { 254 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 255 | } 256 | }; 257 | 258 | int main() 259 | { 260 | OpenFSM::RegisterAction("ActionTestEngine"); 261 | OpenFSM::RegisterAction("ActionTestTank"); 262 | OpenFSM::RegisterAction("ActionFireUp"); 263 | OpenFSM::RegisterAction("ActionLaunch"); 264 | OpenFSM::RegisterAction("ActionReturn"); 265 | OpenFSM::RegisterAction("ActionFailure"); 266 | 267 | OpenFSM::RegisterState("StateTest", { "ActionTestEngine", "ActionTestTank" }, EStateTest); 268 | OpenFSM::RegisterState("StateLaunch", { "ActionFireUp", "ActionLaunch" }, EStateLaunch); 269 | OpenFSM::RegisterState("StateRecycle", { "ActionReturn" }, EStateRecycle); 270 | OpenFSM::RegisterState("StateFailure", { "ActionFailure"}, EStateFailure); 271 | 272 | OpenFSM::RegisterRelation("StateTest", { "StateLaunch", "StateFailure" }); 273 | OpenFSM::RegisterRelation("StateLaunch", { "StateRecycle", "StateFailure" }); 274 | 275 | StarShip starShip; 276 | starShip.start(); 277 | int count = 1000; 278 | while (count-- > 0) 279 | { 280 | if (starShip.update()) 281 | break; 282 | } 283 | return getchar(); 284 | } 285 | ``` 286 | -------------------------------------------------------------------------------- /include/openfsm.hpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2023-, openlinyou, 3 | * 4 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 5 | * copies of the Software, and permit persons to whom the Software is 6 | * furnished to do so, under the terms of the COPYING file. 7 | ***************************************************************************/ 8 | 9 | #ifndef HEADER_OPEN_FSM_H 10 | #define HEADER_OPEN_FSM_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace open 18 | { 19 | 20 | class OpenFSM; 21 | class OpenFSMState; 22 | 23 | class OpenFSMAction 24 | { 25 | public: 26 | OpenFSMAction(); 27 | virtual ~OpenFSMAction(); 28 | virtual void enter(OpenFSM& fsm) const; 29 | virtual void update(OpenFSM& fsm) const; 30 | virtual void exit(OpenFSM& fsm) const; 31 | protected: 32 | std::string actionName_; 33 | friend class OpenFSM; 34 | }; 35 | 36 | class OpenFSMState 37 | { 38 | public: 39 | OpenFSMState(); 40 | OpenFSMState(int eState, const std::string& stateName); 41 | virtual ~OpenFSMState(); 42 | 43 | virtual void enter(OpenFSM& fsm) const; 44 | virtual void update(OpenFSM& fsm) const; 45 | virtual void exit(OpenFSM& fsm) const; 46 | protected: 47 | void nextAction(OpenFSM& fsm) const; 48 | int eState_; 49 | std::string stateName_; 50 | std::vector vectAction_; 51 | std::vector vectRelationState_; 52 | friend class OpenFSM; 53 | }; 54 | 55 | class OpenFSM 56 | { 57 | public: 58 | OpenFSM(); 59 | virtual ~OpenFSM(); 60 | virtual void update(); 61 | bool enterState(int eState); 62 | bool enterState(const std::string& stateName); 63 | 64 | bool nextState(int eState); 65 | bool nextState(const std::string& stateName); 66 | void nextAction(); 67 | 68 | bool setStates(std::vector& vectStateName); 69 | bool setStates(const std::initializer_list& list); 70 | bool setStates(std::vector& vectStateId); 71 | bool setStates(const std::initializer_list& list); 72 | 73 | inline int focusEState() { return fsmState_ ? fsmState_->eState_ : 0; } 74 | inline const std::string& focusStateName() 75 | { 76 | static std::string empty; 77 | return fsmState_ ? fsmState_->stateName_ : empty; 78 | } 79 | 80 | inline void operator=(const std::initializer_list& list) { setStates(list); } 81 | inline void operator=(const std::initializer_list& list) { setStates(list); } 82 | 83 | inline void setCustom(void* custom) { custom_ = custom; } 84 | template 85 | inline T* custom() { return dynamic_cast((T*)custom_); } 86 | 87 | private: 88 | OpenFSMState* getState(int eState); 89 | OpenFSMState* getState(const std::string& stateName); 90 | 91 | int eNextState_; 92 | int eLastState_; 93 | unsigned int actionIdx_; 94 | OpenFSMState* fsmState_; 95 | void* custom_; 96 | std::vector vectState_; 97 | friend class OpenFSMState; 98 | 99 | class OpenFSMPool 100 | { 101 | public: 102 | OpenFSMPool(); 103 | ~OpenFSMPool(); 104 | bool registerAction(OpenFSMAction* action); 105 | template 106 | bool registerAction(const std::string& actionName) 107 | { 108 | std::map::iterator iter = mapNameAction_.find(actionName); 109 | if (iter != mapNameAction_.end()) 110 | { 111 | printf("OpenFSMPool::registerAction [%s] is exist!\n", actionName.c_str()); 112 | assert(false); 113 | return false; 114 | } 115 | T* t = new T; 116 | t->actionName_ = actionName; 117 | mapNameAction_[actionName] = t; 118 | return true; 119 | } 120 | bool registerState(OpenFSMState* state); 121 | bool registerState(const std::string& stateName, std::vector& vectActionName, int eState = -1); 122 | bool registerRelation(const std::string& stateName, std::vector& vectStateName); 123 | OpenFSMAction* getAction(const std::string& actionName); 124 | OpenFSMState* getState(int eState); 125 | OpenFSMState* getState(const std::string& stateName); 126 | private: 127 | int uid_; 128 | std::map mapNameAction_; 129 | std::map mapNameState_; 130 | }; 131 | static OpenFSMPool OpenFSMPool_; 132 | 133 | public: 134 | template 135 | static void RegisterAction(const std::string& actionName) 136 | { 137 | OpenFSMPool_.registerAction(actionName); 138 | } 139 | static bool RegisterState(const std::string& stateName, std::vector& vectActionName, int eState = -1) 140 | { 141 | return OpenFSMPool_.registerState(stateName, vectActionName, eState); 142 | } 143 | static bool RegisterState(const std::string& stateName, const std::initializer_list& list, int eState = -1) 144 | { 145 | std::vector vectActionName = list; 146 | return OpenFSMPool_.registerState(stateName, vectActionName, eState); 147 | } 148 | static bool RegisterRelation(const std::string& stateName, std::vector& vectStateName) 149 | { 150 | return OpenFSMPool_.registerRelation(stateName, vectStateName); 151 | } 152 | static bool RegisterRelation(const std::string& stateName, const std::initializer_list& list) 153 | { 154 | std::vector vectStateName = list; 155 | return OpenFSMPool_.registerRelation(stateName, vectStateName); 156 | } 157 | }; 158 | 159 | }; 160 | 161 | #endif /* HEADER_OPEN_FSM_H */ 162 | -------------------------------------------------------------------------------- /src/openfsm.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2023-, openlinyou, 3 | * 4 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 5 | * copies of the Software, and permit persons to whom the Software is 6 | * furnished to do so, under the terms of the COPYING file. 7 | ***************************************************************************/ 8 | #include "openfsm.hpp" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace open 16 | { 17 | 18 | //OpenFSMAction 19 | OpenFSMAction::OpenFSMAction() 20 | { 21 | 22 | } 23 | OpenFSMAction::~OpenFSMAction() 24 | { 25 | } 26 | 27 | void OpenFSMAction::enter(OpenFSM& fsm) const 28 | { 29 | } 30 | 31 | void OpenFSMAction::update(OpenFSM& fsm) const 32 | { 33 | } 34 | 35 | void OpenFSMAction::exit(OpenFSM& fsm) const 36 | { 37 | } 38 | 39 | //OpenFSMState 40 | OpenFSMState::OpenFSMState() 41 | :eState_(-1) 42 | ,stateName_("UnkownState") 43 | { 44 | assert(false); 45 | } 46 | 47 | OpenFSMState::OpenFSMState(int eState, const std::string& stateName) 48 | :eState_(eState) 49 | ,stateName_(stateName) 50 | { 51 | } 52 | 53 | OpenFSMState::~OpenFSMState() 54 | { 55 | } 56 | 57 | void OpenFSMState::enter(OpenFSM& fsm) const 58 | { 59 | assert(eState_ != 0); 60 | fsm.actionIdx_ = 0; 61 | if (fsm.actionIdx_ >= vectAction_.size()) 62 | return; 63 | 64 | vectAction_[fsm.actionIdx_]->enter(fsm); 65 | } 66 | 67 | void OpenFSMState::update(OpenFSM& fsm) const 68 | { 69 | if (fsm.actionIdx_ >= vectAction_.size()) 70 | { 71 | if (!vectRelationState_.empty()) 72 | { 73 | fsm.nextState(vectRelationState_[0]->eState_); 74 | return; 75 | } 76 | assert(false); 77 | return; 78 | } 79 | vectAction_[fsm.actionIdx_]->update(fsm); 80 | } 81 | 82 | void OpenFSMState::exit(OpenFSM& fsm) const 83 | { 84 | if (fsm.actionIdx_ < vectAction_.size()) 85 | { 86 | vectAction_[fsm.actionIdx_]->exit(fsm); 87 | } 88 | } 89 | 90 | void OpenFSMState::nextAction(OpenFSM& fsm) const 91 | { 92 | if (fsm.actionIdx_ < vectAction_.size()) 93 | { 94 | vectAction_[fsm.actionIdx_]->exit(fsm); 95 | } 96 | fsm.actionIdx_++; 97 | if (fsm.actionIdx_ < vectAction_.size()) 98 | { 99 | vectAction_[fsm.actionIdx_]->enter(fsm); 100 | } 101 | else 102 | { 103 | if (!vectRelationState_.empty()) 104 | { 105 | fsm.nextState(vectRelationState_[0]->eState_); 106 | return; 107 | } 108 | assert(false); 109 | return; 110 | } 111 | } 112 | 113 | 114 | //OpenFSM 115 | OpenFSM::OpenFSMPool OpenFSM::OpenFSMPool_; 116 | OpenFSM::OpenFSM() 117 | :eNextState_(0) 118 | ,eLastState_(0) 119 | ,fsmState_(0) 120 | { 121 | } 122 | 123 | OpenFSM::~OpenFSM() 124 | { 125 | } 126 | 127 | bool OpenFSM::enterState(int eState) 128 | { 129 | if (fsmState_) 130 | { 131 | fsmState_->exit(*this); 132 | eLastState_ = fsmState_->eState_; 133 | } 134 | fsmState_ = getState(eState); 135 | if (!fsmState_) 136 | { 137 | return false; 138 | } 139 | fsmState_->enter(*this); 140 | return true; 141 | } 142 | 143 | bool OpenFSM::enterState(const std::string& stateName) 144 | { 145 | if (fsmState_) 146 | { 147 | fsmState_->exit(*this); 148 | eLastState_ = fsmState_->eState_; 149 | } 150 | fsmState_ = getState(stateName); 151 | if (!fsmState_) 152 | { 153 | return false; 154 | } 155 | fsmState_->enter(*this); 156 | return true; 157 | } 158 | 159 | bool OpenFSM::nextState(int eState) 160 | { 161 | OpenFSMState* state = getState(eState); 162 | if (!state) 163 | { 164 | return false; 165 | } 166 | assert(state->eState_ == eState); 167 | eNextState_ = eState; 168 | return true; 169 | } 170 | 171 | bool OpenFSM::nextState(const std::string& stateName) 172 | { 173 | OpenFSMState* state = getState(stateName); 174 | if (!state) 175 | { 176 | return false; 177 | } 178 | assert(state->stateName_ == stateName); 179 | eNextState_ = state->eState_; 180 | return true; 181 | } 182 | 183 | void OpenFSM::nextAction() 184 | { 185 | if (!fsmState_) 186 | { 187 | if (vectState_.empty()) 188 | { 189 | return; 190 | } 191 | nextState(vectState_[0]->eState_); 192 | return; 193 | } 194 | fsmState_->nextAction(*this); 195 | } 196 | 197 | void OpenFSM::update() 198 | { 199 | eNextState_ = 0; 200 | if (fsmState_) 201 | { 202 | fsmState_->update(*this); 203 | } 204 | while (eNextState_ != 0) 205 | { 206 | int state = eNextState_; 207 | eNextState_ = 0; 208 | enterState(state); 209 | if (fsmState_) 210 | { 211 | fsmState_->update(*this); 212 | } 213 | } 214 | } 215 | 216 | bool OpenFSM::setStates(std::vector& vectStateName) 217 | { 218 | vectState_.clear(); 219 | OpenFSMState* state = 0; 220 | for (size_t i = 0; i < vectStateName.size(); ++i) 221 | { 222 | state = OpenFSMPool_.getState(vectStateName[i]); 223 | if (!state) 224 | { 225 | vectState_.clear(); 226 | assert(false); 227 | return false; 228 | } 229 | assert(state->eState_ != 0); 230 | vectState_.push_back(state); 231 | } 232 | return true; 233 | } 234 | 235 | bool OpenFSM::setStates(const std::initializer_list& list) 236 | { 237 | vectState_.clear(); 238 | OpenFSMState* state = 0; 239 | std::initializer_list::const_iterator iter = list.begin(); 240 | for (; iter != list.end(); iter++) 241 | { 242 | state = OpenFSMPool_.getState(*iter); 243 | if (!state) 244 | { 245 | vectState_.clear(); 246 | assert(false); 247 | return false; 248 | } 249 | assert(state->eState_ != 0); 250 | vectState_.push_back(state); 251 | } 252 | return true; 253 | } 254 | 255 | bool OpenFSM::setStates(std::vector& vectStateId) 256 | { 257 | vectState_.clear(); 258 | OpenFSMState* state = 0; 259 | for (size_t i = 0; i < vectStateId.size(); ++i) 260 | { 261 | state = OpenFSMPool_.getState(vectStateId[i]); 262 | if (!state) 263 | { 264 | vectState_.clear(); 265 | assert(false); 266 | return false; 267 | } 268 | assert(state->eState_ != 0); 269 | vectState_.push_back(state); 270 | } 271 | return true; 272 | } 273 | 274 | bool OpenFSM::setStates(const std::initializer_list& list) 275 | { 276 | vectState_.clear(); 277 | OpenFSMState* state = 0; 278 | std::initializer_list::const_iterator iter = list.begin(); 279 | for (; iter != list.end(); iter++) 280 | { 281 | state = OpenFSMPool_.getState(*iter); 282 | if (!state) 283 | { 284 | vectState_.clear(); 285 | assert(false); 286 | return false; 287 | } 288 | assert(state->eState_ != 0); 289 | vectState_.push_back(state); 290 | } 291 | return true; 292 | } 293 | 294 | OpenFSMState* OpenFSM::getState(int eState) 295 | { 296 | for (size_t i = 0; i < vectState_.size(); ++i) 297 | { 298 | if (vectState_[i]->eState_ == eState) 299 | return vectState_[i]; 300 | } 301 | return 0; 302 | } 303 | 304 | OpenFSMState* OpenFSM::getState(const std::string& stateName) 305 | { 306 | for (size_t i = 0; i < vectState_.size(); ++i) 307 | { 308 | if (vectState_[i]->stateName_ == stateName) 309 | return vectState_[i]; 310 | } 311 | return 0; 312 | } 313 | 314 | 315 | //OpenFSMPool 316 | OpenFSM::OpenFSMPool::OpenFSMPool() 317 | :uid_(1) 318 | { 319 | } 320 | 321 | OpenFSM::OpenFSMPool::~OpenFSMPool() 322 | { 323 | std::map::iterator iter1 = mapNameState_.begin(); 324 | for (; iter1 != mapNameState_.end(); iter1++) 325 | { 326 | delete iter1->second; 327 | } 328 | mapNameState_.clear(); 329 | std::map::iterator iter2 = mapNameAction_.begin(); 330 | for (; iter2 != mapNameAction_.end(); iter2++) 331 | { 332 | delete iter2->second; 333 | } 334 | mapNameAction_.clear(); 335 | } 336 | 337 | bool OpenFSM::OpenFSMPool::registerAction(OpenFSMAction* action) 338 | { 339 | if (!action) 340 | { 341 | assert(false); 342 | return false; 343 | } 344 | const std::string& actionName = action->actionName_; 345 | std::map::iterator iter = mapNameAction_.find(actionName); 346 | if (iter != mapNameAction_.end()) 347 | { 348 | printf("OpenFSMPool::registerAction actionName [%s] is exist!\n", actionName.c_str()); 349 | assert(false); 350 | return false; 351 | } 352 | mapNameAction_[actionName] = action; 353 | return true; 354 | } 355 | 356 | bool OpenFSM::OpenFSMPool::registerState(OpenFSMState* state) 357 | { 358 | if (!state) 359 | { 360 | assert(false); 361 | return false; 362 | } 363 | const std::string& stateName = state->stateName_; 364 | std::map::iterator iter = mapNameState_.find(stateName); 365 | if (iter != mapNameState_.end()) 366 | { 367 | printf("OpenFSMPool::registerState stateName [%s] is exist!\n", stateName.c_str()); 368 | assert(false); 369 | return false; 370 | } 371 | if (state->eState_ <= 0) 372 | { 373 | state->eState_ = -uid_++; 374 | } 375 | assert(state->eState_ != 0); 376 | mapNameState_[stateName] = state; 377 | return true; 378 | } 379 | 380 | bool OpenFSM::OpenFSMPool::registerState(const std::string& stateName, std::vector& vectActionName, int eState) 381 | { 382 | std::map::iterator iter = mapNameState_.find(stateName); 383 | if (iter != mapNameState_.end()) 384 | { 385 | printf("OpenFSMPool::registerState stateName [%s] is exist!\n", stateName.c_str()); 386 | assert(false); 387 | return false; 388 | } 389 | if (eState <= 0) 390 | { 391 | eState = -uid_++; 392 | } 393 | OpenFSMState* state = new OpenFSMState(eState, stateName); 394 | OpenFSMAction* action = 0; 395 | for (size_t i = 0; i < vectActionName.size(); ++i) 396 | { 397 | action = OpenFSMPool_.getAction(vectActionName[i]); 398 | if (!action) 399 | { 400 | delete state; 401 | printf("OpenFSMPool::registerState actionName [%s] is not exist!\n", vectActionName[i].c_str()); 402 | assert(false); 403 | return false; 404 | } 405 | state->vectAction_.push_back(action); 406 | } 407 | assert(state->eState_ != 0); 408 | mapNameState_[stateName] = state; 409 | return true; 410 | } 411 | 412 | bool OpenFSM::OpenFSMPool::registerRelation(const std::string& stateName, std::vector& vectStateName) 413 | { 414 | if (vectStateName.empty()) 415 | { 416 | printf("OpenFSMPool::registerRelation[%s] vectStateName is empty!\n", stateName.c_str()); 417 | assert(false); 418 | return false; 419 | } 420 | OpenFSMState* focusState = getState(stateName); 421 | if (!focusState) 422 | { 423 | printf("OpenFSMPool::registerRelation[%s] state is not exist!\n", stateName.c_str()); 424 | assert(false); 425 | return false; 426 | } 427 | if (!focusState->vectRelationState_.empty()) 428 | { 429 | printf("OpenFSMPool::registerRelation[%s] state had relation!\n", stateName.c_str()); 430 | assert(false); 431 | return false; 432 | } 433 | std::vector vectState; 434 | OpenFSMState* state = 0; 435 | for (size_t i = 0; i < vectStateName.size(); ++i) 436 | { 437 | state = getState(vectStateName[i]); 438 | if (!state) 439 | { 440 | printf("OpenFSMPool::registerRelation stateName [%s] is not exist!\n", vectStateName[i].c_str()); 441 | assert(false); 442 | return false; 443 | } 444 | vectState.push_back(state); 445 | } 446 | focusState->vectRelationState_ = vectState; 447 | return true; 448 | } 449 | 450 | OpenFSMAction* OpenFSM::OpenFSMPool::getAction(const std::string& actionName) 451 | { 452 | std::map::iterator iter = mapNameAction_.find(actionName); 453 | if (iter != mapNameAction_.end()) 454 | { 455 | return iter->second; 456 | } 457 | return 0; 458 | } 459 | 460 | OpenFSMState* OpenFSM::OpenFSMPool::getState(const std::string& stateName) 461 | { 462 | std::map::iterator iter = mapNameState_.find(stateName); 463 | if (iter != mapNameState_.end()) 464 | { 465 | return iter->second; 466 | } 467 | return 0; 468 | } 469 | 470 | OpenFSMState* OpenFSM::OpenFSMPool::getState(int eState) 471 | { 472 | OpenFSMState* state = 0; 473 | std::map::iterator iter = mapNameState_.begin(); 474 | for (; iter != mapNameState_.end(); iter++) 475 | { 476 | state = iter->second; 477 | if (state->eState_ >= 0 && state->eState_ == eState) 478 | { 479 | return state; 480 | } 481 | } 482 | return 0; 483 | } 484 | 485 | }; -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "openfsm.h" 5 | 6 | using namespace open; 7 | 8 | enum EFMSState 9 | { 10 | EStateTest = 100, 11 | EStateLaunch = 101, 12 | EStateRecycle = 102, 13 | EStateFailure = 103, 14 | }; 15 | struct StarShip 16 | { 17 | OpenFSM openFSM_; 18 | void start() 19 | { 20 | std::srand((int)time(NULL)); 21 | openFSM_.setCustom(this); 22 | // or openFSM_.setStates({ "StateTest", "StateLaunch", "StateRecycle", "StateFailure" }); 23 | openFSM_.setStates({ EStateTest, EStateLaunch, EStateRecycle, EStateFailure }); 24 | openFSM_.enterState(EStateTest); 25 | } 26 | bool testEngine() {return std::rand() % 3 == 0;} 27 | bool testTank() {return std::rand() % 3 == 0;} 28 | bool fireUp() {return std::rand() % 3 == 0;} 29 | bool launch() {return std::rand() % 3 == 0;} 30 | bool update() 31 | { 32 | openFSM_.update(); 33 | return openFSM_.focusEState() == EStateRecycle; 34 | } 35 | }; 36 | 37 | //StateTest 38 | class ActionTestEngine: public OpenFSMAction 39 | { 40 | virtual void enter(OpenFSM& fsm) const 41 | { 42 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 43 | } 44 | virtual void update(OpenFSM& fsm) const 45 | { 46 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 47 | if (fsm.custom()->testEngine()) 48 | { 49 | printf("==>StarShip Engine Ok\n"); 50 | fsm.nextAction(); 51 | } 52 | else 53 | { 54 | printf("==>StarShip Engine Bad\n"); 55 | fsm.nextState(EStateFailure); 56 | } 57 | } 58 | virtual void exit(OpenFSM& fsm) const 59 | { 60 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 61 | } 62 | }; 63 | 64 | class ActionTestTank: public OpenFSMAction 65 | { 66 | virtual void enter(OpenFSM& fsm) const 67 | { 68 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 69 | } 70 | virtual void update(OpenFSM& fsm) const 71 | { 72 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 73 | if (fsm.custom()->testTank()) 74 | { 75 | printf("==>StarShip Tank Ok\n"); 76 | fsm.nextAction(); 77 | } 78 | else 79 | { 80 | printf("==>StarShip Tank Bad\n"); 81 | fsm.nextState(EStateFailure); 82 | } 83 | } 84 | virtual void exit(OpenFSM& fsm) const 85 | { 86 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 87 | } 88 | }; 89 | 90 | //StateLaunch 91 | class ActionFireUp : public OpenFSMAction 92 | { 93 | virtual void enter(OpenFSM& fsm) const 94 | { 95 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 96 | } 97 | virtual void update(OpenFSM& fsm) const 98 | { 99 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 100 | if (fsm.custom()->fireUp()) 101 | { 102 | printf("==>StarShip FireUp Ok\n"); 103 | fsm.nextAction(); 104 | } 105 | else 106 | { 107 | printf("==>StarShip FireUp Failed\n"); 108 | fsm.nextState(EStateFailure); 109 | } 110 | } 111 | virtual void exit(OpenFSM& fsm) const 112 | { 113 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 114 | } 115 | }; 116 | class ActionLaunch : public OpenFSMAction 117 | { 118 | virtual void enter(OpenFSM& fsm) const 119 | { 120 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 121 | } 122 | virtual void update(OpenFSM& fsm) const 123 | { 124 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 125 | if (fsm.custom()->launch()) 126 | { 127 | printf("==>StarShip Launch Ok\n"); 128 | fsm.nextAction(); 129 | } 130 | else 131 | { 132 | printf("==>StarShip Launch Failed\n"); 133 | fsm.nextState(EStateFailure); 134 | } 135 | } 136 | virtual void exit(OpenFSM& fsm) const 137 | { 138 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 139 | } 140 | }; 141 | 142 | //StateRecycle 143 | class ActionReturn : public OpenFSMAction 144 | { 145 | virtual void enter(OpenFSM& fsm) const 146 | { 147 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 148 | } 149 | virtual void update(OpenFSM& fsm) const 150 | { 151 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 152 | printf("==>Congratulation! Complete mission!\n"); 153 | } 154 | virtual void exit(OpenFSM& fsm) const 155 | { 156 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 157 | } 158 | }; 159 | 160 | //StateFailure 161 | class ActionFailure : public OpenFSMAction 162 | { 163 | virtual void enter(OpenFSM& fsm) const 164 | { 165 | printf("[%s.%s] enter\n", fsm.focusStateName().c_str(), actionName_.c_str()); 166 | } 167 | virtual void update(OpenFSM& fsm) const 168 | { 169 | printf("[%s.%s] update\n", fsm.focusStateName().c_str(), actionName_.c_str()); 170 | printf("==>StarShip Launch again\n"); 171 | fsm.nextState(EStateTest); 172 | } 173 | virtual void exit(OpenFSM& fsm) const 174 | { 175 | printf("[%s.%s] exit\n", fsm.focusStateName().c_str(), actionName_.c_str()); 176 | } 177 | }; 178 | 179 | int main() 180 | { 181 | OpenFSM::RegisterAction("ActionTestEngine"); 182 | OpenFSM::RegisterAction("ActionTestTank"); 183 | OpenFSM::RegisterAction("ActionFireUp"); 184 | OpenFSM::RegisterAction("ActionLaunch"); 185 | OpenFSM::RegisterAction("ActionReturn"); 186 | OpenFSM::RegisterAction("ActionFailure"); 187 | 188 | OpenFSM::RegisterState("StateTest", { "ActionTestEngine", "ActionTestTank" }, EStateTest); 189 | OpenFSM::RegisterState("StateLaunch", { "ActionFireUp", "ActionLaunch" }, EStateLaunch); 190 | OpenFSM::RegisterState("StateRecycle", { "ActionReturn" }, EStateRecycle); 191 | OpenFSM::RegisterState("StateFailure", { "ActionFailure"}, EStateFailure); 192 | 193 | OpenFSM::RegisterRelation("StateTest", { "StateLaunch", "StateFailure" }); 194 | OpenFSM::RegisterRelation("StateLaunch", { "StateRecycle", "StateFailure" }); 195 | 196 | StarShip starShip; 197 | starShip.start(); 198 | int count = 1000; 199 | while (count-- > 0) 200 | { 201 | if (starShip.update()) 202 | break; 203 | } 204 | return getchar(); 205 | } 206 | --------------------------------------------------------------------------------