1 /*
2  * A sample of the simpliest agent which has several states.
3  * The agent uses different handlers for the same message.
4  * At the beginning of its work agent initiates a periodic message.
5  * Then agent handles this messages and switches from one state
6  * to another.
7  *
8  * A work of the SObjectizer Environment is finished after the agent
9  * switched to the final state.
10  *
11  * State switching is fixed by a state listener.
12  */
13 
14 #if defined( _MSC_VER )
15 	#if defined( __clang__ )
16 		#pragma clang diagnostic ignored "-Wreserved-id-macro"
17 	#endif
18 	#define _CRT_SECURE_NO_WARNINGS
19 #endif
20 
21 #include <iostream>
22 #include <time.h>
23 
24 // Main SObjectizer header file.
25 #include <so_5/all.hpp>
26 
27 // Periodic message.
28 class msg_periodic final : public so_5::signal_t {};
29 
30 // State listener for fixing state changes.
31 class state_monitor_t final : public so_5::agent_state_listener_t
32 {
33 	const std::string m_type_hint;
34 
35 	public:
state_monitor_t(const std::string & type_hint)36 		state_monitor_t( const std::string & type_hint )
37 			:	m_type_hint( type_hint )
38 		{}
39 
changed(so_5::agent_t &,const so_5::state_t & state)40 		void changed(
41 			so_5::agent_t &,
42 			const so_5::state_t & state ) noexcept override
43 		{
44 			std::cout << m_type_hint << " agent changed state to "
45 				<< state.query_name()
46 				<< std::endl;
47 		}
48 };
49 
50 // A sample agent class.
51 class a_state_swither_t final : public so_5::agent_t
52 {
53 		// Agent states.
54 		const state_t st_1{ this, "state_1" };
55 		const state_t st_2{ this, "state_2" };
56 		const state_t st_3{ this, "state_3" };
57 		const state_t st_shutdown{ this, "shutdown" };
58 
59 	public:
a_state_swither_t(context_t ctx)60 		a_state_swither_t( context_t ctx )
61 			:	so_5::agent_t{ ctx }
62 		{}
63 
64 		// Definition of the agent for SObjectizer.
65 		void so_define_agent() override;
66 
67 		// Reaction to start into SObjectizer.
68 		void so_evt_start() override;
69 
70 		// Message handler for the default state.
71 		void evt_handler_default( mhood_t<msg_periodic> );
72 
73 		// Message handler for the state_1.
74 		void evt_handler_1( mhood_t<msg_periodic> );
75 
76 		// Message handler for the state_2.
77 		void evt_handler_2( mhood_t<msg_periodic> );
78 
79 		// Message handler for the state_3.
80 		void evt_handler_3( mhood_t<msg_periodic> );
81 
82 		// Message handler for the shutdown_state.
83 		void evt_handler_shutdown( mhood_t<msg_periodic> );
84 
85 	private:
86 		// Timer event id.
87 		// If we do not store it the periodic message will
88 		// be canceled automatically.
89 		so_5::timer_id_t m_timer_id;
90 
91 		// Helper method for showing that event handler is called.
92 		void show_event_invocation( const char * event_name );
93 };
94 
so_define_agent()95 void a_state_swither_t::so_define_agent()
96 {
97 	// Message subsription.
98 	so_subscribe_self()
99 		.event( &a_state_swither_t::evt_handler_default );
100 
101 	so_subscribe_self().in( st_1 )
102 		.event( &a_state_swither_t::evt_handler_1 );
103 
104 	so_subscribe_self().in( st_2 )
105 		.event( &a_state_swither_t::evt_handler_2 );
106 
107 	so_subscribe_self().in( st_3 )
108 		.event( &a_state_swither_t::evt_handler_3 );
109 
110 	so_subscribe_self().in( st_shutdown )
111 		.event( &a_state_swither_t::evt_handler_shutdown );
112 }
113 
so_evt_start()114 void a_state_swither_t::so_evt_start()
115 {
116 	show_event_invocation( "so_evt_start()" );
117 
118 	// Periodic message should be initiated.
119 	m_timer_id = so_5::send_periodic< msg_periodic >(
120 			*this,
121 			std::chrono::seconds( 1 ),
122 			std::chrono::seconds( 1 ) );
123 }
124 
evt_handler_default(mhood_t<msg_periodic>)125 void a_state_swither_t::evt_handler_default( mhood_t<msg_periodic> )
126 {
127 	show_event_invocation( "evt_handler_default" );
128 
129 	// Switching to the next state.
130 	so_change_state( st_1 );
131 }
132 
evt_handler_1(mhood_t<msg_periodic>)133 void a_state_swither_t::evt_handler_1( mhood_t<msg_periodic> )
134 {
135 	show_event_invocation( "evt_handler_1" );
136 
137 	// Switching to the next state.
138 	so_change_state( st_2 );
139 }
140 
evt_handler_2(mhood_t<msg_periodic>)141 void a_state_swither_t::evt_handler_2( mhood_t<msg_periodic> )
142 {
143 	show_event_invocation( "evt_handler_2" );
144 
145 	// Switching to the next state.
146 	so_change_state( st_3 );
147 }
148 
evt_handler_3(mhood_t<msg_periodic>)149 void a_state_swither_t::evt_handler_3( mhood_t<msg_periodic> )
150 {
151 	show_event_invocation( "evt_handler_3" );
152 
153 	// Switching to the next state.
154 	so_change_state( st_shutdown );
155 }
156 
evt_handler_shutdown(mhood_t<msg_periodic>)157 void a_state_swither_t::evt_handler_shutdown( mhood_t<msg_periodic> )
158 {
159 	show_event_invocation( "evt_handler_3" );
160 
161 	// Switching to the default state.
162 	so_change_state( so_default_state() );
163 
164 	// Finishing SObjectizer's work.
165 	std::cout << "Stop sobjectizer..." << std::endl;
166 	so_environment().stop();
167 }
168 
show_event_invocation(const char * event_name)169 void a_state_swither_t::show_event_invocation( const char * event_name )
170 {
171 	time_t t = time( nullptr );
172 	std::cout << asctime( localtime( &t ) )
173 		<< event_name << ", state: " << so_current_state().query_name()
174 		<< std::endl;
175 }
176 
177 // A state listener.
178 state_monitor_t g_state_monitor( "nondestroyable_listener" );
179 
180 // The SObjectizer Environment initialization.
init(so_5::environment_t & env)181 void init( so_5::environment_t & env )
182 {
183 	auto ag = env.make_agent< a_state_swither_t >();
184 
185 	// Adding the state listener. Its lifetime is not controlled by the agent.
186 	ag->so_add_nondestroyable_listener( g_state_monitor );
187 
188 	// Adding another state listener.
189 	// Its lifetime is controlled by the agent.
190 	ag->so_add_destroyable_listener(
191 		std::make_unique< state_monitor_t >( "destroyable_listener" ) );
192 
193 	// Creating and registering a cooperation.
194 	env.register_agent_as_coop( std::move(ag) );
195 }
196 
main()197 int main()
198 {
199 	try
200 	{
201 		so_5::launch( &init );
202 	}
203 	catch( const std::exception & ex )
204 	{
205 		std::cerr << "Error: " << ex.what() << std::endl;
206 		return 1;
207 	}
208 
209 	return 0;
210 }
211 
212