C++程序  |  215行  |  5.2 KB

/*
 * 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.
 */

#pragma once

#include <map>
#include <utility>

#include <base/logging.h>

namespace bluetooth {

namespace common {

/**
 * State machine used by Bluetooth native stack.
 */
class StateMachine {
 public:
  enum { kStateInvalid = -1 };

  /**
   * A class to represent the state in the State Machine.
   */
  class State {
    friend class StateMachine;

   public:
    /**
     * Constructor.
     *
     * @param sm the State Machine to use
     * @param state_id the unique State ID. It should be a non-negative number.
     */
    State(StateMachine& 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(StateMachine::State* dest_state) {
      sm_.TransitionTo(dest_state);
    }

   private:
    StateMachine& sm_;
    int state_id_;
  };

  StateMachine()
      : initial_state_(nullptr),
        previous_state_(nullptr),
        current_state_(nullptr) {}
  ~StateMachine() {
    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);

    CHECK(it != states_.end()) << "Unknown State ID: " << 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(StateMachine::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_;
};

}  // namespace common

}  // namespace bluetooth