четверг, 7 января 2016 г.

[prog.c++] Реализация иерархических конечных автоматов в SO-5 задышала...

...почти что полной грудью :) Под катом пример описания агента, реализующего конечный автомат из вот этого поста. Только пару недель назад это были первые наметки, а сейчас уже компилирующийся и работающий код. КА даже оказался чуток более навороченным, т.к. пришлось добавить еще парочку состояний, дабы имитация была более правдоподобной.

В принципе, сделал уже почти все, что хотел. Параллельных состояний нет и, вероятно, не будет. Аналогичных результатов можно добиваться и посредством вспомогательных агентов, как это и происходит в данном примере. А вот чего явно пока не хватает, так это автоматической смены состояния после определенного тайм-аута. Как такое сделать, в принципе, понятно. Но это утяжелит объект state_t на пару умных указателей... В общем, нужно подумать, стоит ли оно того...

Ну а вот, собственно, и получившийся код. Глядя на него уже недоумеваешь, а почему мы не сделали иерархические конечные автоматы раньше? Ведь это так классно! Особенно отлаживать ;)

class controller final : public so_5::agent_t
{
  state_t
    inactive{ this"inactive" },
    active{ this"active" },

      wait_activity{ initial_substate_of{ active }, "wait_activity" },

      number_selection{ substate_of{ active }, "number_selection" },

      dialling{ substate_of{ active }, "dialling" },

      special_code_selection{ substate_of{ active }, "special_code_selection" },

        special_code_selection_0{
            initial_substate_of{ special_code_selection }, "special_code_selection_0" },

        user_code_selection{
            substate_of{ special_code_selection }, "user_code_selection" },

          user_code_apartment_number{
              initial_substate_of{ user_code_selection }, "apartment_number" },
          user_code_secret{
              substate_of{ user_code_selection }, "secret_code" },

        service_code_selection{
            substate_of{ special_code_selection }, "service_code" },

        door_unlocked{
            substate_of{ special_code_selection }, "door_unlocked" };

  ...

public :
  controller( context_t ctx, so_5::mbox_t intercom_mbox )
    : so_5::agent_t{ ctx }
    , m_intercom_mbox{ std::move(intercom_mbox) }
    , m_apartments{ make_apartment_info() }
  {
    inactive
      .transfer_to_state< key_digit >( m_intercom_mbox, active )
      .transfer_to_state< key_grid >( m_intercom_mbox, active )
      .transfer_to_state< key_bell >( m_intercom_mbox, active )
      .transfer_to_state< key_cancel >( m_intercom_mbox, active );

    active
      .on_enter( [this]{ active_on_enter(); } )
      .event( m_intercom_mbox, &controller::active_on_grid )
      .event( m_intercom_mbox, &controller::active_on_cancel )
      .just_switch_to< intercom_messages::deactivate >(
          m_intercom_mbox, inactive );

    wait_activity
      .transfer_to_state< key_digit >( m_intercom_mbox, number_selection );

    number_selection
      .on_enter( [this]{ apartment_number_on_enter(); } )
      .event( m_intercom_mbox, &controller::apartment_number_on_digit )
      .event( m_intercom_mbox, &controller::apartment_number_on_bell )
      .suppress< key_grid >( m_intercom_mbox );

    dialling
      .on_enter( [this]{ dialling_on_enter(); } )
      .on_exit( [this]{ dialling_on_exit(); } )
      .suppress< key_grid >( m_intercom_mbox )
      .suppress< key_bell >( m_intercom_mbox )
      .suppress< key_digit >( m_intercom_mbox )
      .event( &controller::dialling_no_answer_from_apartment );

    special_code_selection_0
      .transfer_to_state< key_digit >( m_intercom_mbox, user_code_selection )
      .just_switch_to< key_grid >( m_intercom_mbox, service_code_selection );

    user_code_apartment_number
      .on_enter( [this]{ user_code_apartment_number_on_enter(); } )
      .event( m_intercom_mbox, &controller::apartment_number_on_digit )
      .just_switch_to< key_grid >( m_intercom_mbox, user_code_secret );

    user_code_secret
      .on_enter( [this]{ user_code_secret_on_enter(); } )
      .event( m_intercom_mbox, &controller::user_code_secret_on_digit )
      .event( m_intercom_mbox, &controller::user_code_secret_on_bell );

    service_code_selection
      .on_enter( [this]{ service_code_on_enter(); } )
      .event( m_intercom_mbox, &controller::service_code_on_digit )
      .event( m_intercom_mbox, &controller::service_code_on_grid );

    door_unlocked
      .on_enter( [this]{ door_unlocked_on_enter(); } )
      .on_exit( [this]{ door_unlocked_on_exit(); } )
      .suppress< key_grid >( m_intercom_mbox )
      .suppress< key_bell >( m_intercom_mbox )
      .suppress< key_digit >( m_intercom_mbox )
      .event( &controller::door_unlocked_on_lock_door );
  }

  virtual void so_evt_start() override
  {
    this >>= inactive;
  }

private :
  ...
};

Комментариев нет: