目录
背景
例子
实现
结果
几年前的一个夏天,太阳晒得砖头有点烫手。。。。。。
噢,画面不太对,应该是几年前的夏天,我的leader叫我到办公室,布置了一个研究状态机的任务,需要以此解决项目遇到的问题。
我当时是个刚毕业的菜鸟,还没听过状态机这个概念,在查阅一些资料之后,发现它其实是比较好理解的。这里举个简单的例子:
有只小狗在门口睡觉,你轻轻踢了它一脚,它惊醒了,你驱逐它,它跑开了,你又抚摸下它,它又睡着了。这个过程中,小狗有三个状态:睡觉、清醒、奔跑。
外部有三个事件:轻踢、驱逐、抚摸。像这种事件导致状态变化就是最简单的有限状态机模型。如下图:
理解了这个示例后,善于联想的人其实已经发现这世间万物皆是状态机模型,它的运用非常广泛,这里不做展开,我们重点讨论这个模型的一种c++实现。
大致思路如下:
首先我们需要抽象出以下几个类:
事件类event状态类state状态机类StateMachine状态机设置类MachineSet事件类代码如下:
class __declspec(dllexport) Event { friend class MachineSet; friend class StateMachine; public: Event(const MachineSetSharedPtr& machine_set) : machine_set_(machine_set) { } virtual ~Event() { } public: virtual const MachineSetSharedPtr GetMachineSet() const { return machine_set_.lock(); } protected: MachineSetWeakPtr machine_set_; StateMachineWeakPtrVec target_machines_; }; template<class EventType, class = typename std::enable_if<std::is_enum<EventType>::value>::type> class EventTemplate : public Event { friend class MachineSet; friend class StateMachine; public: typedef EventType Type; EventTemplate(Type type) : Event(MachineSetSharedPtr()), type_(type) { } EventTemplate(Type type, const StateMachineWeakPtrVec& target_machines) : Event(MachineSetSharedPtr()), type_(type) { target_machines_ = std::move(target_machines); } EventTemplate(Type type, const StateMachineSharedPtr& target_machine) : Event(MachineSetSharedPtr()), type_(type) { if (target_machine) { target_machines_.emplace_back(target_machine); } } EventTemplate(Type type, const MachineSetSharedPtr& machine_set) : Event(machine_set), type_(type) { } virtual ~EventTemplate() { } Type GetType() const { return type_; } protected: Type type_; };状态类代码如下:
class __declspec(dllexport) State : public std::enable_shared_from_this<State> { public: static StateSharedPtr MakeState(StateMachine& owner, const char* name, time_t timerMS = 0); static StateSharedPtr MakeState(StateMachine& owner, const State& copy); public: virtual ~State(); private: State(StateMachine& owner, const char* name, time_t timerMS = 0); State(StateMachine& owner, const State& copy); public: const std::string& GetName() const { return name_; } time_t GetTimeout() const { return timeout_ms_; } void SetTimeout(time_t new_timeout_ms) { timeout_ms_ = new_timeout_ms; } void ClearActions(); bool operator==(const State& rhs) const; bool operator!=(const State& rhs) const; public: StateEvent OnEnter; StateEvent OnExit; private: friend class StateMachine; friend class Transition; std::string name_; time_t timeout_ms_; std::vector<TransitionSharedPtr> transitions_; private: State& operator=(const State&); };
状态机类代码如下:
class __declspec(dllexport) StateMachine { public: friend class State; public: StateMachine(const std::string& name); public: virtual ~StateMachine() {} public: bool ForceState(const StateSharedPtr& state); bool SetStartState(const StateSharedPtr& state); bool SetMetaState(const StateSharedPtr& state); void ClearMetaState(); const StateSharedPtr GetCurrent() const; const StateSharedPtr GetPrevious() const { return previous_state_; } public: virtual void Birth() virtual bool Process(EventSharedPtr) virtual void SetTimeout(unsigned long long timeout_ms); virtual unsigned long long GetTimeout() const; virtual bool IsTimeout() const; virtual bool Process(EventSharedPtr); protected: StateMachine(const StateMachine& rhs); private: StateMachine& operator=(const StateMachine& rhs); private: bool ProcessNormalStateTransition(EventSharedPtr event); bool ProcessMetaStateTransition(EventSharedPtr event); template<bool IsMetaState> bool InternalSetState(const StateSharedPtr& state); protected: unsigned long long timeout_ms_; StateSharedPtr current_state_; StateSharedPtr previous_state_; typedef std::vector<StateSharedPtr> StateListType; StateListType states_; StateSharedPtr meta_state_; };状态机设置类代码如下:
class __declspec(dllexport) MachineSet : public std::enable_shared_from_this<MachineSet> { public: static MachineSetSharedPtr MakeMachineSet(); private: MachineSet(); public: ~MachineSet(); public: // thread safe void Enqueue(EventSharedPtr event); void StartBackgroundThread(unsigned int sleep_ms); void StopBackgroundThread (); void Process(); void Process(EventSharedPtr event); void ProcessTimeoutMachine(MachineBaseSharedPtr machine); MachineBaseSharedPtr GetMachine(const MachineType& type, const std::string& name); void UpdateTimeoutMahcine(const MachineBase& machine, time_t timeout) ; bool HasHandler() const { return static_cast<bool>(event_handler_); } public: typedef std::function<void()> NotifyEvent; NotifyEvent OnProcessError; private: void AddMachine(MachineBaseSharedPtr machine); void RemoveMachine(MachineBaseSharedPtr machine); bool ProcessTargetMachineEvent(const EventSharedPtr& event); bool ProcessNoTargetMachineEvent(const EventSharedPtr& event); void InternalProcessTimeoutEvent(); private: typedef std::vector<MachineBaseSharedPtr> MachinePtrList; typedef std::set<MachineBaseSharedPtr> MachinePtrSet; MachinePtrList machine_list_; MachinePtrSet machine_set_; Fifo<EventSharedPtr> event_fifo_; MachineSetHandlerSharedPtr event_handler_; std::thread::id owner_thread_id_; std::atomic<bool> background_thread_stop_flag_; std::unique_ptr<std::thread> background_thread_; };
这里以状态机实现开头和小狗互动的例子,main.cpp代码如下:
enum class DogEventType { DT_KICK, DT_EXPEL, DT_CARESS, }; class DogEvent : public EventTemplate<DogEventType> { public: using underlying_type = std::underlying_type<DogEventType>::type; DogEvent(DogEventType type, const StateMachineSharedPtr& machine) : EventTemplate<DogEventType>(type, machine) { } }; class DogMachine : public StateMachine { public: DogMachine(const std::string& name); public: virtual void Birth(); public: StateSharedPtr sleep_; StateSharedPtr awake_; StateSharedPtr run_; TransitionSharedPtr sleep_awake_; TransitionSharedPtr awake_run_; TransitionSharedPtr awake_sleep_; TransitionSharedPtr run_sleep_; TransitionSharedPtr run_timeout_; }; DogMachine::DogMachine(const std::string& name) : StateMachine(name) { } void DogMachine::Birth() { sleep_ = State::MakeState(*this, "sleep"); awake_ = State::MakeState(*this, "awake"); run_ = State::MakeState(*this, "run", 5000); sleep_awake_ = Transition::MakeTransition("sleep_awake", sleep_, awake_, std::make_shared<SimplePredicate<DogEvent>>(DogEventType::DT_KICK)); awake_run_ = Transition::MakeTransition("awake_run", awake_, run_, std::make_shared<SimplePredicate<DogEvent>>(DogEventType::DT_EXPEL)); awake_sleep_ = Transition::MakeTransition("awake_sleep", awake_, sleep_, std::make_shared<SimplePredicate<DogEvent>>(DogEventType::DT_CARESS)); run_sleep_ = Transition::MakeTransition("run_sleep", run_, sleep_, std::make_shared<SimplePredicate<DogEvent>>(DogEventType::DT_CARESS)); run_timeout_ = Transition::MakeTransition("run_timeout", run_, run_, std::make_shared<TimeoutPredicate>(type_)); } int main() { MachineSetSharedPtr machine_set = MachineSet::MakeMachineSet(); if (machine_set) { machine_set->StartBackgroundThread(500); std::shared_ptr<DogMachine> dog_machine = MakeStateMachine<DogMachine>("dog_machine"); if (dog_machine) { dog_machine->SetStartState(dog_machine->sleep_); dog_machine->sleep_->OnEnter = [&](MachineBase & machine, const StateSharedPtr & state) { std::cout << "Enter " << state->GetName() << std::endl; }; dog_machine->sleep_->OnExit = [&](MachineBase & machine, const StateSharedPtr & state) { std::cout << "Exit " << state->GetName() << std::endl; }; dog_machine->awake_->OnEnter = [&](MachineBase & machine, const StateSharedPtr & state) { std::cout << "Enter " << state->GetName() << std::endl; }; dog_machine->awake_->OnExit = [&](MachineBase & machine, const StateSharedPtr & state) { std::cout << "Exit " << state->GetName() << std::endl; }; dog_machine->run_->OnEnter = [&](MachineBase & machine, const StateSharedPtr & state) { std::cout << "Enter " << state->GetName() << std::endl; }; dog_machine->run_->OnExit = [&](MachineBase & machine, const StateSharedPtr & state) { std::cout << "Exit " << state->GetName() << std::endl; }; dog_machine->sleep_awake_->OnTransition = [&](MachineBase & machine, const StateSharedPtr & from_state, ITransitionSharedPtr transition, EventSharedPtr event, const StateSharedPtr & to_state) { std::cout << transition->GetName() << " | " << from_state->GetName() << " -> " << to_state->GetName() << std::endl; }; dog_machine->awake_run_->OnTransition = [&](MachineBase&, const StateSharedPtr & from_state, ITransitionSharedPtr transition, EventSharedPtr event, const StateSharedPtr & to_state) { std::cout << transition->GetName() << " | " << from_state->GetName() << " -> " << to_state->GetName() << std::endl; }; dog_machine->awake_sleep_->OnTransition = [&](MachineBase&, const StateSharedPtr & from_state, ITransitionSharedPtr transition, EventSharedPtr event, const StateSharedPtr & to_state) { std::cout << transition->GetName() << " | " << from_state->GetName() << " -> " << to_state->GetName() << std::endl; }; dog_machine->run_sleep_->OnTransition = [&](MachineBase&, const StateSharedPtr & from_state, ITransitionSharedPtr transition, EventSharedPtr event, const StateSharedPtr & to_state) { std::cout << transition->GetName() << " | " << from_state->GetName() << " -> " << to_state->GetName() << std::endl; }; dog_machine->run_timeout_->OnTransition = [&](MachineBase&, const StateSharedPtr & from_state, ITransitionSharedPtr transition, EventSharedPtr event, const StateSharedPtr & to_state) { std::cout << transition->GetName() << " | " << from_state->GetName() << " -> " << to_state->GetName() << std::endl; }; machine_set->Enqueue(std::make_shared<MachineOperationEvent>( MachineOperator::MO_ADD, dog_machine)); machine_set->Enqueue(std::make_shared<DogEvent>( DogEventType::DT_KICK, dog_machine)); machine_set->Enqueue(std::make_shared<DogEvent>( DogEventType::DT_EXPEL, dog_machine)); machine_set->Enqueue(std::make_shared<DogEvent>( DogEventType::DT_CARESS, dog_machine)); } } getchar(); return 0; }Log输出如下:
enqueue AddMachineEvent[ dog_machine] Adding machine: dog_machine(dog_machine) enqueue DogEvent | 0 enqueue DogEvent | 1 enqueue DogEvent | 2 Handling event: DogEvent | 0 MachineSet::ProcessTargetMachineEvent | target machine found: dog_machine Process StateMachine name: dog_machine| Machine type:dog_machine| Current State: sleep Event (match) type: DogEvent | 0 , Type: dog_machine, Name: dog_machine Entering at Exit action: (dog_machine dog_machine) sleep Exit sleep Exited at Exit action: (dog_machine dog_machine) sleep Normal Transition from: sleep -> awake, for Machine (dog_machine dog_machine) Transition: [sleep_awake] sleep_awake | sleep -> awake Exited Normal Transition: [sleep_awake] from: sleep -> awake, for Machine (dog_machine dog_machine) Entering Enter action: (dog_machine dog_machine) awake Enter awake Exited Enter action: (dog_machine dog_machine) awake Handling event: DogEvent | 1 MachineSet::ProcessTargetMachineEvent | target machine found: dog_machine Process StateMachine name: dog_machine| Machine type:dog_machine| Current State: awake Event (match) type: DogEvent | 1 , Type: dog_machine, Name: dog_machine Entering at Exit action: (dog_machine dog_machine) awake Exit awake Exited at Exit action: (dog_machine dog_machine) awake Normal Transition from: awake -> run, for Machine (dog_machine dog_machine) Transition: [awake_run] awake_run | awake -> run Exited Normal Transition: [awake_run] from: awake -> run, for Machine (dog_machine dog_machine) Entering Enter action: (dog_machine dog_machine) run Enter run Exited Enter action: (dog_machine dog_machine) run Handling event: DogEvent | 2 MachineSet::ProcessTargetMachineEvent | target machine found: dog_machine Process StateMachine name: dog_machine| Machine type:dog_machine| Current State: run Event (match) type: DogEvent | 2 , Type: dog_machine, Name: dog_machine Entering at Exit action: (dog_machine dog_machine) run Exit run Exited at Exit action: (dog_machine dog_machine) run Normal Transition from: run -> sleep, for Machine (dog_machine dog_machine) Transition: [run_sleep] run_sleep | run -> sleep Exited Normal Transition: [run_sleep] from: run -> sleep, for Machine (dog_machine dog_machine) Entering Enter action: (dog_machine dog_machine) sleep Enter sleep Exited Enter action: (dog_machine dog_machine) sleep状态变化过程如下:
