/*!
 * Демонстрация описания одного и того же агента в нотации SObjectizer4
 * и предполагаемой нотации SObjectizer5.
 *
 * Для того, чтобы демонстрация выглядела более наглядной, исходники
 * располагаются рядом друг с другом в таблице. В исходных текстах
 * оставлены только те комментации, которые должны помочь понять
 * описание агента (в реальном проекте, из которого взят данный класс,
 * комментариев в несколько раз больше). Из исходных текстов удален
 * весь лишний код, который не связан с механизмами описания агента для
 * SObjectizer.
 *
 * Краткое пояснение назначения агента.
 *
 * Этот агент является менеджером дочерних процессов (в каждом из которых сидят
 * собственные агенты-исполнители). Его задачей является получение двух типов
 * запросов (phase_c и phase_p), передача этих запросов в подходящие дочерние
 * процессы, получение ответов от дочерних процессов (phase_c_result,
 * phase_p_result), преобразование ответов в нужный формат, контроль за
 * работоспособностью и наличию связи с дочерними процессами, поддержание
 * актуальной оперативной мониторинговой информации о текущем состоянии.
 * Информация о запросах/результатах, а также уведомления о проблемах с
 * дочерними процессами приходит асинхронно, в произвольном порядке. Контроль
 * состояния и обновление мониторинговой информации нужно делать по таймеру.
 *
 * Небольшое пояснение по поводу использования длинных имен пространств имен
 * в примере SObjectizer4. Это необходимо из-за того, что SObjectizer4
 * должен сохранять у себя строковые описания всех типов сообщений. И для
 * того, чтобы эти имена были уникальными в рамках большого проекта, приходится
 * в макросах SOL4_* использовать полные имена типов. В SObjectizer5 такой
 * необходимости нет и, поэтому, разумное использование using namespace
 * способно освободить разработчика от дублирования полных имен типов
 * для классов агентов и сообщений.
 */
SObjectizer-5 SObjectizer-4
class a_processor_t
  : public so_sysconf_3::agent_with_fatal_state_t
  {
  public :
    /*!
     * \name Типы сообщений, которыми владеет данный агент.
     * \{
     */
    //! Принудительная переконфигурация агента.
    struct reconfigure_t
      {
        std::string m_cfg_file_name
      };
    /*!
     * \}
     */

    /*!
     * \name Домены сообщений этого агента.
     * \{
     */
    // Пояснение: домены определяют сообщения, которыми владеет
    // данный агент. Т.е. либо их отсылает он сам, либо их
    // отсылают от его имени.
    //
    // Пояснение: поскольку ряд сообщений не передают данных,
    // а являются сигналами, то для них не нужно определять
    // каких-либо пустых структур (как в SO4). И для декларации
    // их домена используется специальный тип signal_domain_t.

    //! Сигнал о необходимости сборки мусора.
    so5::signal_domain_t msg_garbage_collection;

    //! Сигнал о необходимости контроля за дочерними процессами.
    so5::signal_domain_t msg_control_child_processes;

    //! Сигнал о необходимости обновления мониторинговой информации.
    so5::signal_domain_t msg_update_gemont_data;

    //! Сообщение о необходимости переконфигурировать агента.
    so5::message_domain_t< reconfigure_t > msg_reconfigure;
    /*!
     * \}
     */

    /*!
     * \name Слоты сообщений этого агента.
     */
    // Пояснение: слоты определяют сообщения, которые будет
    // получать данный агент.
    //
    // Пояснение: для сообщений-сигналов используется
    // специальный тип signal_slot_t.

    so5::signal_slot_t slot_garbage_collection;
    so5::signal_slot_t slot_control_child_processes;
    so5::signal_slot_t slot_update_gemont_data;
    so5::message_slot_t< reconfigure_t > slot_reconfigure;

    so5::message_slot_t< a_child_iface_t::hello_t > slot_child_hello;

    so5::message_slot_t< a_communicator_t::client_disconnected_t >
        slot_client_disconnected;

    so5::message_slot_t< a_request_producer_t::phase_c_t >
        slot_phase_c;

    so5::message_slot_t< a_child_iface_t::phase_c_result_t >
        slot_phase_c_result;

    so5::message_slot_t< a_request_producer_t::phase_p_t >
        slot_phase_p;

    so5::message_slot_t< a_child_iface_t::phase_p_result_t >
        slot_phase_p_result;
    /*!
     * \}
     */

    /*!
     * \name Состояния данного агента.
     * \{
     */
    //! Единственное собственное состояние.
    so5::state_t st_normal;
    /*!
     * \}
     */

    //! В конструкторе все домены, слоты и состояния должны
    //! быть проинициализированны.
    a_processor_t( /* какие-то параметры */ )
      : // Инициализация базового типа и каких-то других атрибутов.
      // Инициализация доменов сообщений. Каждый домен должен
      // быть связан с агентом.
      , msg_garbage_collection( so_self() )
      , msg_control_child_processes( so_self() )
      , msg_update_gemont_data( so_self() )
      , msg_reconfigure( so_self() )
      // Инициализация слотов сообщений.
      // Часть слотов сразу связывается с конкретными доменами.
      , slot_garbage_collection(
          so_self(),
          msg_garbage_collection )
      , slot_control_child_processes(
          so_self(),
          msg_control_child_processes )
      , slot_update_gemont_data(
          so_self(),
          msg_update_gemont_data )
      , slot_reconfigure(
          so_self(),
          msg_reconfigure )
      // Остальные слоты будут связаны с сообщениями позже.
      , slot_child_hello( so_self() )
      , slot_client_disconnected( so_self() )
      , slot_phase_c( so_self() )
      , slot_phase_c_result( so_self() )
      , slot_phase_p( so_self() )
      , slot_phase_p_result( so_self() )
      // Инициализация состояний.
      , st_normal( so_self() )
      {}

    //! Описание агента для SObjectizer.
    virtual void
    so_define_agent()
      {
        // Начальным состояние должно быть состояние st_normal.
        // Если этого не сказать, то начальным будет унаследованное
        // из agent_with_fatal_state_t состояние st_fatal.
        so_initial_state( st_normal );

        // Определение состояние st_normal.
        st_normal
          .on( slot_garbage_collection,
              &a_processor_t::evt_garbage_collection )
          .on( slot_control_child_processes,
              &a_processor_t::evt_control_child_processes )
          .on( slot_update_gemont_data,
              &a_processor_t::evt_update_gemont_data )
          .on( slot_reconfigure,
              &a_processor_t::evt_reconfigure )
          .on( slot_child_hello,
              &a_processor_t::evt_child_hello )
          .on( slot_client_disconnected,
              &a_processor_t::evt_client_disconnected )
          .on( slot_phase_c,
              &a_processor_t::evt_phase_c )
          .on( slot_phase_c_result,
              &a_processor_t::evt_phase_c_result )
          .on( slot_phase_p,
              &a_processor_t::evt_phase_p )
          .on( slot_phase_p_result,
              &a_processor_t::evt_phase_p_result );

        // Определение состояния st_fatal.
        // В нем должны быть определены реакции на новые сообщения
        // (которых не было у агента agent_with_fatal_state_t).
        st_fatal
          .on( slot_garbage_collection, SO_IGNORE_MESSAGE )
          .on( slot_control_child_processes, SO_IGNORE_MESSAGE )
          .on( slot_update_gemont_data, SO_IGNORE_MESSAGE )
          .on( slot_reconfigure, SO_IGNORE_MESSAGE )
          .on( slot_child_hello, SO_IGNORE_MESSAGE )
          .on( slot_client_disconnected, SO_IGNORE_MESSAGE )
          .on( slot_phase_c, SO_IGNORE_MESSAGE )
          .on( slot_phase_c_result, SO_IGNORE_MESSAGE )
          .on( slot_phase_p, SO_IGNORE_MESSAGE )
          .on( slot_phase_p_result, SO_IGNORE_MESSAGE );

        // Поскольку несколько слотов сообщения связаны с
        // чужими сообщениями, то их сейчас нужно подписать.
        so5::agent_reference_t< a_child_iface_t > child_iface(
            so5::lockup_agent< a_child_iface_t >( "a_child_iface" ) );
        slot_child_hello.connect( child_iface->msg_hello );
        slot_phase_c_result.connect( child_iface->msg_phase_c_result );
        slot_phase_p_result.connect( child_iface->msg_phase_p_result );

        so5::agent_reference_t< a_request_producer_t > producer(
            so5::lockup_agent< a_request_producer_t >( "a_producer" ) );
        slot_phase_c.connect( producer->msg_phase_c );
        slot_phase_p.connect( producer->msg_phase_p );

        slot_client_disconnected.connect(
          so5::lockup_agent< a_communicator_t >( "a_communicator" )->
              msg_client_disconnected );
      }

    /*!
     * \name Обработчики событий.
     * \{
     */
    void
    evt_garbage_collection();

    void
    evt_control_child_processes();

    void
    evt_update_gemont_data();

    void
    evt_reconfigure( const reconfigure_t & );

    void
    evt_child_hello(
      const so5::event_data_t &,
      const a_child_iface_t::hello_t & );

    void
    evt_client_disconnected(
      const a_communicator_t::client_disconnected_t & );

    void
    evt_phase_c(
      const a_request_producer_t::phase_c_t & );

    void
    evt_phase_c_result(
      const so5::event_data_t &,
      const a_child_iface_t::phase_c_result_t & );

    void
    evt_phase_p(
      const a_request_producer_t::phase_p_t & );

    void
    evt_phase_p_result(
      const so5::event_data_t &,
      const a_child_iface_t::phase_c_result_t & );
    /*!
     * \}
     */
  };
class a_processor_t
  : public so_sysconf_2::agent_with_fatal_state_t
  {
  public :
    /*!
     * \name Сообщения агента.
     * \{
     */
    //! Периодическое сообщение о необходимости сборки мусора.
    struct msg_garbage_collection {};

    //! Периодическое сообщение для проверки того, что дочерние
    //! процессы работают.
    struct msg_control_child_processes {};

    //! Периодическое сообщение для обновления мониторинговой информации.
    struct msg_update_gemont_data {};

    //! Сообщение для перезагрузки конфигурации.
    struct msg_reconfigure
      {
        std::string m_cfg_file_name;
      };
    /*!
     * \}
     */

    //! Конструктор агента.
    a_processor_t( /* какие-то параметры */ )
      :  // Инициализация базового типа и каких-то других атрибутов.
      {}

    // Этот метод должен быть задекларирован,
    // но его реализация спрятана в макросах SOL4_CLASS_*.
    virtual const char *
    so_query_type() const;

    //! Подписка событий агента.
    virtual void
    so_on_subscription()
      {
        so_4::api::make_global_agent(
            a_child_iface_t::agent_name(),
            a_child_iface_t::agent_type() );

        so_subscribe(
            "evt_start",
            so_4::rt::sobjectizer_agent_name(),
            "msg_start" );

        so_subscribe(
            "evt_child_process_hello",
            a_child_iface_t::agent_name(),
            "msg_hello" );

        so_subscribe(
            "evt_client_disconnected",
            so_4::rt::comm::communicator_agent_name(),
            "msg_client_disconnected" );

        so_subscribe(
            "evt_phase_c",
            a_request_producer_t::agent_name(),
            "msg_phase_c" );

        so_subscribe(
            "evt_phase_c_result_received",
            a_child_iface_t::agent_name(),
            "msg_phase_c_result" );

        so_subscribe(
            "evt_phase_p",
            a_request_producer_t::agent_name(),
            "msg_phase_p" );

        so_subscribe(
            "evt_phase_p_result_received",
            a_child_iface_t::agent_name(),
            "msg_phase_p_result" );

        so_subscribe(
            "evt_control_child_processes",
            "msg_control_child_processes" );
        so_subscribe(
            "evt_garbage_collection",
            "msg_garbage_collection" );
        so_subscribe(
            "evt_update_gemont_data",
            "msg_update_gemont_data" );

        so_subscribe(
            "evt_reconfigure",
            "msg_reconfigure" );
      }

    /*!
     * \name Обработчики событий.
     * \{
     */
    void
    evt_start();

    void
    evt_child_process_hello(
      const so_4::rt::event_data_t & event_data,
      const a_child_iface_t::msg_hello & cmd );

    void
    evt_client_disconnected(
      const so_4::rt::comm::msg_client_disconnected & cmd );

    void
    evt_phase_c(
      const a_request_producer_t::msg_phase_c & cmd );

    void
    evt_phase_c_result_received(
      const so_4::rt::event_data_t & event_data,
      const a_child_iface_t::msg_phase_c_result & cmd );

    void
    evt_phase_p(
      const a_request_producer_t::msg_phase_p & cmd );

    void
    evt_phase_p_result_received(
      const so_4::rt::event_data_t & event_data,
      const a_child_iface_t::msg_phase_p_result & cmd );

    void
    evt_control_child_processes();

    void
    evt_garbage_collection();

    void
    evt_update_gemont_data();

    void
    evt_reconfigure(
      const msg_reconfigure & cmd );
    /*!
     * \}
     */
  };

// Описание класса агента для SObjectizer.
// Должна быть сделана в .cpp файле с помощью макрсов.
SOL4_CLASS_START( sample_project::child_processor::processor::a_processor_t )
  SOL4_SUPER_CLASS( so_sysconf_2::agent_with_fatal_state_t )

  SOL4_MSG_START( msg_garbage_collection,
      sample_project::child_processor::processor::a_processor_t::
          msg_garbage_collection )
  SOL4_MSG_FINISH()

  SOL4_MSG_START( msg_control_child_processes,
      sample_project::child_processor::processor::a_processor_t::
          msg_control_child_processes )
  SOL4_MSG_FINISH()

  SOL4_MSG_START( msg_update_gemont_data,
      sample_project::child_processor::processor::a_processor_t::
          msg_update_gemont_data )
  SOL4_MSG_FINISH()

  SOL4_MSG_START( msg_reconfigure,
      sample_project::child_processor::processor::a_processor_t::
          msg_reconfigure )

    SOL4_MSG_FIELD( m_cfg_file_name )
  SOL4_MSG_FINISH()

  SOL4_EVENT( evt_start )

  SOL4_EVENT_STC(
    evt_child_process_hello,
    sample_project::child_processor::child_iface::a_child_iface_t::
        msg_hello )

  SOL4_EVENT_STC(
    evt_phase_c,
    a_request_producer_t::msg_phase_c )
  SOL4_EVENT_STC(
    evt_phase_c_result_received,
    sample_project::child_processor::child_iface::a_child_iface_t::
        msg_phase_c_result )
  SOL4_EVENT_STC(
    evt_client_disconnected,
    so_4::rt::comm::msg_client_disconnected )

  SOL4_EVENT_STC(
    evt_phase_p,
    a_request_producer_t::msg_phase_p )
  SOL4_EVENT_STC(
    evt_phase_p_result_received,
    sample_project::child_processor::child_iface::a_child_iface_t::
        msg_phase_p_result )

  SOL4_EVENT( evt_control_child_processes )
  SOL4_EVENT( evt_garbage_collection )
  SOL4_EVENT( evt_update_gemont_data )

  SOL4_EVENT_STC(
    evt_reconfigure,
    sample_project::child_processor::processor::a_processor_t::
        msg_reconfigure )

  SOL4_INITIAL_STATE( st_normal )

  SOL4_STATE_START( st_normal )
    SOL4_STATE_EVENT( evt_start )
    SOL4_STATE_EVENT( evt_child_process_hello )
    SOL4_STATE_EVENT( evt_client_disconnected )
    SOL4_STATE_EVENT( evt_phase_c )
    SOL4_STATE_EVENT( evt_phase_c_result_received )
    SOL4_STATE_EVENT( evt_phase_p )
    SOL4_STATE_EVENT( evt_phase_p_result_received )
    SOL4_STATE_EVENT( evt_control_child_processes )
    SOL4_STATE_EVENT( evt_garbage_collection )
    SOL4_STATE_EVENT( evt_update_gemont_data )
    SOL4_STATE_EVENT( evt_reconfigure )
  SOL4_STATE_FINISH()

SOL4_CLASS_FINISH()
Hosted by uCoz