/*
 * Измерение производительности SObjectizer-а при помощи теста chameneos.
 *
 * Изменение в версии 4.4.0-beta6: для того, чтобы гарантировать, что
 * a_meeting_place_t не прозевает сообщение msg_creature_inside при
 * начале своей работы, хамелеоны создаются в виде дочерней кооперации
 * в a_meeting_place_t.evt_start.
 */

#include <iostream>
#include <vector>

#include <ace/OS.h>
#include <ace/Time_Value.h>

#include <so_4/rt/h/rt.hpp>
#include <so_4/api/h/api.hpp>
#include <so_4/disp/one_thread/h/pub.hpp>
#include <so_4/timer_thread/simple/h/pub.hpp>

#include <test/bench/time_value_msec_helper.hpp>

typedef int color_t;
const color_t blue = 1;
const color_t red = 2;
const color_t yellow = 3;
const color_t fadded = 4;

const int creatures_count = 4;

//
// a_meeting_place_t
//
class a_meeting_place_t
   :  public so_4::rt::agent_t
   {
      typedef so_4::rt::agent_t base_type_t;

   public :
      a_meeting_place_t(
         const std::string & name,
         int meetings )
         :  base_type_t( name )
         ,  m_remaining_meetings( meetings )
         {}

      virtual const char *
      so_query_type() const;

      struct msg_creature_inside
         {
            const so_4::rt::agent_t * m_who;
            color_t m_color;

            msg_creature_inside()
               :  m_who( 0 )
               ,  m_color( fadded )
               {}

            msg_creature_inside(
               const so_4::rt::agent_t * who,
               color_t color )
               :  m_who( who )
               ,  m_color( color )
               {}

            static bool check( const msg_creature_inside * msg )
               {
                  return ( 0 != msg && 0 != msg->m_who );
               }
         };

      struct msg_meet_completed
         {
            color_t m_other;

            msg_meet_completed()
               :  m_other( fadded )
               {}
            msg_meet_completed(
               color_t color )
               :  m_other( color )
               {}

            static bool check( const msg_meet_completed * msg )
               {
                  return ( 0 != msg );
               }
         };

      struct msg_creature_report
         {
            const so_4::rt::agent_t * m_who;
            int m_meetings_count;

            msg_creature_report()
               :  m_who( 0 )
               ,  m_meetings_count( 0 )
               {}
            msg_creature_report(
               const so_4::rt::agent_t * who,
               int meetings_count )
               :  m_who( who )
               ,  m_meetings_count( meetings_count )
               {}

            static bool check( const msg_creature_inside * msg )
               {
                  return ( 0 != msg && 0 != msg->m_who );
               }
         };

      virtual void
      so_on_subscription();

      void
      evt_start();

      void
      evt_first_in(
         const msg_creature_inside & cmd );

      void
      evt_second_in(
         const msg_creature_inside & cmd );

      void
      evt_creature_in_when_no_meetings_left(
         const msg_creature_inside & cmd );

      void
      evt_creature_report(
         const msg_creature_report & cmd );

   private :
      int m_remaining_meetings;

      msg_creature_inside m_first_in;

      typedef std::vector< msg_creature_report > reports_vector_t;
      reports_vector_t m_reports;

      ACE_Time_Value m_start_time;

   };

//
// a_creature_t
//

class a_creature_t
   :  public so_4::rt::agent_t
   {
      typedef so_4::rt::agent_t base_type_t;

   public :
      a_creature_t(
         const std::string & name,
         const std::string & meeting_place_name,
         color_t color )
         :  base_type_t( name )
         ,  m_meeting_place_name( meeting_place_name )
         ,  m_color( color )
         ,  m_creatures_met( 0 )
         {}

      virtual const char *
      so_query_type() const;

      virtual void
      so_on_subscription();

      void
      evt_start();

      void
      evt_meet_completed(
         const a_meeting_place_t::msg_meet_completed & cmd );

   private :
      const std::string m_meeting_place_name;
      color_t m_color;
      int m_creatures_met;
   };

//
// a_meeting_place_t implementation
//

SOL4_CLASS_START( a_meeting_place_t )
   SOL4_MSG_START(
         msg_creature_inside,
         a_meeting_place_t::msg_creature_inside )
      SOL4_MSG_CHECKER( a_meeting_place_t::msg_creature_inside::check )
   SOL4_MSG_FINISH()

   SOL4_MSG_START(
         msg_meet_completed,
         a_meeting_place_t::msg_meet_completed )
      SOL4_MSG_CHECKER( a_meeting_place_t::msg_meet_completed::check )
   SOL4_MSG_FINISH()

   SOL4_MSG_START(
         msg_creature_report,
         a_meeting_place_t::msg_creature_report )
      SOL4_MSG_CHECKER( a_meeting_place_t::msg_creature_report::check )
   SOL4_MSG_FINISH()

   SOL4_EVENT( evt_start )
   SOL4_EVENT_STC( evt_first_in,
         a_meeting_place_t::msg_creature_inside )
   SOL4_EVENT_STC( evt_second_in,
         a_meeting_place_t::msg_creature_inside )
   SOL4_EVENT_STC( evt_creature_in_when_no_meetings_left,
         a_meeting_place_t::msg_creature_inside )
   SOL4_EVENT_STC( evt_creature_report,
         a_meeting_place_t::msg_creature_report )

   SOL4_STATE_START( st_initial )
      SOL4_STATE_EVENT( evt_start )
   SOL4_STATE_FINISH()

   SOL4_STATE_START( st_empty )
      SOL4_STATE_EVENT( evt_first_in )
   SOL4_STATE_FINISH()

   SOL4_STATE_START( st_first_in )
      SOL4_STATE_EVENT( evt_second_in )
   SOL4_STATE_FINISH()

   SOL4_STATE_START( st_no_meetings_left )
      SOL4_STATE_EVENT( evt_creature_in_when_no_meetings_left )
      SOL4_STATE_EVENT( evt_creature_report )
   SOL4_STATE_FINISH()
SOL4_CLASS_FINISH()

void
a_meeting_place_t::so_on_subscription()
   {
      so_subscribe(
            "evt_start",
            so_4::rt::sobjectizer_agent_name(),
            "msg_start",
            1 );

      so_subscribe( "evt_first_in", "msg_creature_inside" );
      so_subscribe( "evt_second_in", "msg_creature_inside" );
      so_subscribe( "evt_creature_in_when_no_meetings_left",
            "msg_creature_inside" );
      so_subscribe( "evt_creature_report", "msg_creature_report" );
   }

void
a_meeting_place_t::evt_start()
   {
      m_start_time = ACE_OS::gettimeofday();
      so_change_state( "st_empty" );

      // Теперь можно создавать дочерних хамелеонов.
      so_4::rt::agent_t * agents[] = {
            new a_creature_t( "a_first", so_query_name(), blue )
         ,  new a_creature_t( "a_second", so_query_name(), yellow )
         ,  new a_creature_t( "a_third", so_query_name(), red )
         ,  new a_creature_t( "a_forth", so_query_name(), blue )
         };
      so_4::rt::dyn_agent_coop_t * chameneos_coop =
            new so_4::rt::dyn_agent_coop_t(
                  "chameneos",
                  agents,
                  sizeof(agents) / sizeof(agents[0]) );
      chameneos_coop->set_parent_coop_name( so_query_coop()->query_name() );
      so_4::rt::dyn_agent_coop_helper_t chameneos_coop_registrator(
            chameneos_coop );
   }

void
a_meeting_place_t::evt_first_in(
   const msg_creature_inside & cmd )
   {
      m_first_in = cmd;
      so_change_state( "st_first_in" );
   }

inline void
send_msg_meet_completed(
   const so_4::rt::agent_t & meetplace,
   const so_4::rt::agent_t & receiver,
   color_t color )
   {
      so_4::api::send_msg_safely(
            meetplace.so_query_name(),
            "msg_meet_completed",
            new a_meeting_place_t::msg_meet_completed( color ),
            receiver.so_query_name() );
   }

void
a_meeting_place_t::evt_second_in(
   const msg_creature_inside & cmd )
   {
      send_msg_meet_completed( *this, *m_first_in.m_who, cmd.m_color );
      send_msg_meet_completed( *this, *cmd.m_who, m_first_in.m_color );

      so_change_state(
            ( --m_remaining_meetings > 0 ) ?
                  "st_empty" : "st_no_meetings_left" );
   }

void
a_meeting_place_t::evt_creature_in_when_no_meetings_left(
   const msg_creature_inside & cmd )
   {
      send_msg_meet_completed( *this, *cmd.m_who, fadded );
   }

void
a_meeting_place_t::evt_creature_report(
   const msg_creature_report & cmd )
   {
      m_reports.push_back( cmd );

      if( creatures_count == m_reports.size() )
         {
            ACE_Time_Value finish_time = ACE_OS::gettimeofday();
            
            int total = 0;
            for( reports_vector_t::iterator it = m_reports.begin(),
                  it_end = m_reports.end();
                  it != it_end;
                  ++it )
               {
                  std::cout << "creature: '" << it->m_who->so_query_name()
                        << "' met: " << it->m_meetings_count << std::endl;
                  total += it->m_meetings_count;
               }
            std::cout << "Total: " << total << std::endl;

            double work_time =
                  ( milliseconds( finish_time ) -
                  milliseconds( m_start_time ) ) / 1000.0;
            long total_messages = total * 4;
            std::cout << "Time: " << work_time << " sec" << std::endl;
            std::cout << "Throughput: "
                  << total_messages / work_time << " msgs/sec" << std::endl;

            so_4::api::send_msg(
                  so_4::rt::sobjectizer_agent_name(),
                  "msg_normal_shutdown" );
         }
   }

//
// a_creature_t implementation
//

SOL4_CLASS_START( a_creature_t )
   SOL4_EVENT( evt_start )
   SOL4_EVENT_STC( evt_meet_completed,
         a_meeting_place_t::msg_meet_completed )

   SOL4_STATE_START( st_normal )
      SOL4_STATE_EVENT( evt_start )
      SOL4_STATE_EVENT( evt_meet_completed )
   SOL4_STATE_FINISH()
SOL4_CLASS_FINISH()

void
a_creature_t::so_on_subscription()
   {
      so_subscribe(
            "evt_start",
            so_4::rt::sobjectizer_agent_name(),
            "msg_start" );

      so_subscribe(
            "evt_meet_completed",
            m_meeting_place_name,
            "msg_meet_completed" );
   }

inline void
send_msg_creature_inside(
   const std::string & meeting_place_name,
   const so_4::rt::agent_t & who,
   color_t color )
   {
      so_4::api::send_msg_safely(
            meeting_place_name,
            "msg_creature_inside",
            new a_meeting_place_t::msg_creature_inside( &who, color ) );
   }

void
a_creature_t::evt_start()
   {
      send_msg_creature_inside( m_meeting_place_name, *this, m_color );
   }

void
a_creature_t::evt_meet_completed(
   const a_meeting_place_t::msg_meet_completed & cmd )
   {
      if( fadded != cmd.m_other )
         {
            ++m_creatures_met;

            switch( m_color )
               {
               case blue :
                  m_color = ( cmd.m_other == red ? yellow : red );
               break;

               case red :
                  m_color = ( cmd.m_other == blue ? yellow : blue );
               break;

               case yellow :
                  m_color = ( cmd.m_other == blue ? red : blue );
               }

            send_msg_creature_inside( m_meeting_place_name, *this, m_color );
         }
      else
         {
            m_color = fadded;
            so_4::api::send_msg_safely(
                  m_meeting_place_name,
                  "msg_creature_report",
                  new a_meeting_place_t::msg_creature_report(
                        this,
                        m_creatures_met ) );
         }
   }

int main( int argc, char **argv )
   {
      const int default_meeting_count = 100;
      int meeting_count = argc == 2 ?
            ACE_OS::atoi( argv[ 1 ] ) :
            default_meeting_count;

      a_meeting_place_t meeting_place( "a_meeting_place", meeting_count );
      so_4::rt::agent_coop_t coop( meeting_place );

      so_4::ret_code_t rc = so_4::api::start(
         so_4::disp::one_thread::create_disp(
            so_4::timer_thread::simple::create_timer_thread(),
            so_4::auto_destroy_timer ),
         so_4::auto_destroy_disp,
         &coop );
      if( rc )
         std::cerr << "SObjectizer start error: " << rc << std::endl;

      return int( rc );
   }

Hosted by uCoz