本文主要是抽离与Bluedroid(Android P - system/bt)中用到的状态机,将其抽离出来再其它的项目中也可以直接使用.
在指定的路径创建了statemachine目录,然后创建文件如下:
➜ statemachine tree
.
├── btif_av.cc
└── btif_state_machine.h
btif_state_machine.h,
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BTIF_STATE_MACHINE_H
#define BTIF_STATE_MACHINE_H
#include <map>
#include <utility>
/**
* State machine used by BTIF components.
*/
class BtifStateMachine {
public:
enum { kStateInvalid = -1 };
/**
* A class to represent the state in the State Machine.
*/
class State {
friend class BtifStateMachine;
public:
/**
* Constructor.
*
* @param sm the State Machine to use
* @param state_id the unique State ID. It should be a non-negative number.
*/
State(BtifStateMachine& sm, int state_id) : sm_(sm), state_id_(state_id) {}
virtual ~State() = default;
/**
* Process an event.
* TODO: The arguments are wrong - used for backward compatibility.
* Will be replaced later.
*
* @param event the event type
* @param p_data the event data
* @return true if the processing was completed, otherwise false
*/
virtual bool ProcessEvent(uint32_t event, void* p_data) = 0;
/**
* Get the State ID.
*
* @return the State ID
*/
int StateId() const { return state_id_; }
protected:
/**
* Called when a state is entered.
*/
virtual void OnEnter() {}
/**
* Called when a state is exited.
*/
virtual void OnExit() {}
/**
* Transition the State Machine to a new state.
*
* @param dest_state_id the state ID to transition to. It must be one
* of the unique state IDs when the corresponding state was created.
*/
void TransitionTo(int dest_state_id) { sm_.TransitionTo(dest_state_id); }
/**
* Transition the State Machine to a new state.
*
* @param dest_state the state to transition to. It cannot be nullptr.
*/
void TransitionTo(BtifStateMachine::State* dest_state) {
sm_.TransitionTo(dest_state);
}
private:
BtifStateMachine& sm_;
int state_id_;
};
BtifStateMachine()
: initial_state_(nullptr),
previous_state_(nullptr),
current_state_(nullptr) {}
~BtifStateMachine() {
for (auto& kv : states_) delete kv.second;
}
/**
* Start the State Machine operation.
*/
void Start() { TransitionTo(initial_state_); }
/**
* Quit the State Machine operation.
*/
void Quit() { previous_state_ = current_state_ = nullptr; }
/**
* Get the current State ID.
*
* @return the current State ID
*/
int StateId() const {
if (current_state_ != nullptr) {
return current_state_->StateId();
}
return kStateInvalid;
}
/**
* Get the previous current State ID.
*
* @return the previous State ID
*/
int PreviousStateId() const {
if (previous_state_ != nullptr) {
return previous_state_->StateId();
}
return kStateInvalid;
}
/**
* Process an event.
* TODO: The arguments are wrong - used for backward compatibility.
* Will be replaced later.
*
* @param event the event type
* @param p_data the event data
* @return true if the processing was completed, otherwise false
*/
bool ProcessEvent(uint32_t event, void* p_data) {
if (current_state_ == nullptr) return false;
return current_state_->ProcessEvent(event, p_data);
}
/**
* Transition the State Machine to a new state.
*
* @param dest_state_id the state ID to transition to. It must be one
* of the unique state IDs when the corresponding state was created.
*/
void TransitionTo(int dest_state_id) {
auto it = states_.find(dest_state_id);
State* dest_state = it->second;
TransitionTo(dest_state);
}
/**
* Transition the State Machine to a new state.
*
* @param dest_state the state to transition to. It cannot be nullptr.
*/
void TransitionTo(BtifStateMachine::State* dest_state) {
if (current_state_ != nullptr) {
current_state_->OnExit();
}
previous_state_ = current_state_;
current_state_ = dest_state;
current_state_->OnEnter();
}
/**
* Add a state to the State Machine.
* The state machine takes ownership on the state - i.e., the state will
* be deleted by the State Machine itself.
*
* @param state the state to add
*/
void AddState(State* state) {
states_.insert(std::make_pair(state->StateId(), state));
}
/**
* Set the initial state of the State Machine.
*
* @param initial_state the initial state
*/
void SetInitialState(State* initial_state) { initial_state_ = initial_state; }
private:
State* initial_state_;
State* previous_state_;
State* current_state_;
std::map<int, State*> states_;
};
#endif // BTIF_STATE_MACHINE_H
btif_state_machine.h 中实现了BtifStateMachine 这一状态机基类,接下来会有一个简单的示例来讲解如何基于BtifStateMachine进行具体场景的状态机实现.
btif_av.cc的实现,
#include "btif_state_machine.h"
#include <iostream>
#include <unistd.h>
class BtifAvStateMachine : public BtifStateMachine {
public:
enum {
kStateIdle, // AVDTP disconnected
kStateOpening, // Opening AVDTP connection
};
class StateIdle : public State {
public:
StateIdle(BtifAvStateMachine& sm)
: State(sm, kStateIdle) {}
void OnEnter() override;
void OnExit() override;
bool ProcessEvent(uint32_t event, void* p_data) override;
};
class StateOpening : public State {
public:
StateOpening(BtifAvStateMachine& sm)
: State(sm, kStateOpening) {}
void OnEnter() override;
void OnExit() override;
bool ProcessEvent(uint32_t event, void* p_data) override;
};
BtifAvStateMachine() {
state_idle_ = new StateIdle(*this);
state_opening_ = new StateOpening(*this);
AddState(state_idle_);
AddState(state_opening_);
SetInitialState(state_idle_);
}
private:
StateIdle* state_idle_;
StateOpening* state_opening_;
};
void BtifAvStateMachine::StateIdle::OnEnter() {
std::cout << __func__ << " : StateIdle : Hello" << std::endl;
}
void BtifAvStateMachine::StateIdle::OnExit() {
std::cout << __func__ << " : StateIdle : Hello" << std::endl;
}
bool BtifAvStateMachine::StateIdle::ProcessEvent(uint32_t event, void* p_data) {
std::cout << __func__ << " : StateIdle : Hello" << std::endl;
}
/**/
void BtifAvStateMachine::StateOpening::OnEnter() {
std::cout << __func__ << " : StateOpening : Hello" << std::endl;
}
void BtifAvStateMachine::StateOpening::OnExit() {
std::cout << __func__ << " : StateOpening : Hello" << std::endl;
}
bool BtifAvStateMachine::StateOpening::ProcessEvent(uint32_t event, void* p_data) {
std::cout << __func__ << " : StateOpening : Hello" << std::endl;
}
int main(int argc, char **argv)
{
std::cout << __func__ << ": Hi : Hello" << std::endl;
BtifAvStateMachine st_test;
st_test.TransitionTo(BtifAvStateMachine::kStateIdle);
st_test.ProcessEvent(1, NULL);
sleep(2);
st_test.TransitionTo(BtifAvStateMachine::kStateOpening);
st_test.ProcessEvent(1, NULL);
}
编译运行:
➜ statemachine g++ btif_av.cc -I./
➜ statemachine ./a.out
main: Hi : Hello
OnEnter : StateIdle : Hello
ProcessEvent : StateIdle : Hello
OnExit : StateIdle : Hello
OnEnter : StateOpening : Hello
ProcessEvent : StateOpening : Hello
总结
在嵌入式开发中,状态的切换和状态管理的实现是非常常见的,之前总是简单的s使用变量和if-else进行状态的处理,在学习了Bluedroid中的statemachine后,在以后遇到类似的场景时需要注意是否可以用上述的这种方式进行状态机的实现.