#include <iostream>
#include <map>
enum class engine_state_t {
on,
off };
enum class cooler_state_t {
on,
off };
struct machine_status
{
const std::string m_id;
const engine_state_t m_engine_status;
const cooler_state_t m_cooler_status;
const float m_engine_temperature;
};
enum class attention_t
{
none,
engine_cooling_done,
engine_cooling_needed,
engine_overheat_detected
};
struct machine_needs_attention
{
const std::string m_id;
const attention_t m_attention;
const engine_state_t m_engine_status;
const cooler_state_t m_cooler_status;
};
{
public :
a_machine_t(
context_t ctx,
std::string id,
float initial_temperature,
float engine_heating_step,
float cooler_impact_step )
, m_id( std::move( id ) )
, m_status_distrib_mbox{ std::move( status_distrib_mbox ) }
, m_initial_temperature{ initial_temperature }
, m_engine_heating_step{ engine_heating_step }
, m_cooler_impact_step{ cooler_impact_step }
, m_engine_temperature{ initial_temperature }
{}
{
this >>= st_engine_off;
st_engine_on
.event( &a_machine_t::evt_turn_engine_off )
.event( &a_machine_t::evt_turn_cooler_on )
.event( &a_machine_t::evt_turn_cooler_off )
.event( &a_machine_t::evt_update_status_when_engine_on );
st_engine_off
.event( &a_machine_t::evt_turn_engine_on )
.event( &a_machine_t::evt_turn_cooler_on )
.event( &a_machine_t::evt_turn_cooler_off )
.event( &a_machine_t::evt_update_status_when_engine_off );
}
{
m_update_status_timer = so_5::send_periodic< update_status >(
*this,
std::chrono::milliseconds(0),
std::chrono::milliseconds(200) );
}
private :
const state_t st_engine_on{ this, "on" };
const state_t st_engine_off{ this, "off" };
const std::string m_id;
const float m_initial_temperature;
const float m_engine_heating_step;
const float m_cooler_impact_step;
float m_engine_temperature;
void evt_turn_engine_off(mhood_t< turn_engine_off >)
{
this >>= st_engine_off;
}
void evt_turn_engine_on(mhood_t< turn_engine_on >)
{
this >>= st_engine_on;
}
void evt_turn_cooler_off(mhood_t< turn_cooler_off >)
{
}
void evt_turn_cooler_on(mhood_t< turn_cooler_on >)
{
}
void evt_update_status_when_engine_on(mhood_t< update_status >)
{
m_engine_temperature += m_engine_heating_step;
if( cooler_state_t::on == m_cooler_status )
m_engine_temperature -= m_cooler_impact_step;
distribute_status();
}
void evt_update_status_when_engine_off(mhood_t< update_status >)
{
if( cooler_state_t::on == m_cooler_status )
{
m_engine_temperature -= m_cooler_impact_step;
if( m_engine_temperature < m_initial_temperature )
m_engine_temperature = m_initial_temperature;
}
distribute_status();
}
void distribute_status()
{
so_5::send< machine_status >(
m_status_distrib_mbox,
m_id,
m_engine_status,
m_cooler_status,
m_engine_temperature );
}
};
{
public :
a_total_status_dashboard_t(
context_t ctx,
, m_status_distrib_mbox{ std::move( status_distrib_mbox ) }
{}
{
.
event( &a_total_status_dashboard_t::evt_machine_status );
&a_total_status_dashboard_t::evt_show_dashboard );
}
{
const auto period = std::chrono::milliseconds( 1500 );
m_show_timer = so_5::send_periodic< show_dashboard >( *this,
period, period );
}
private :
struct one_machine_status_t
{
engine_state_t m_engine_status;
cooler_state_t m_cooler_status;
float m_engine_temperature;
};
using machine_status_map_t = std::map< std::string, one_machine_status_t >;
machine_status_map_t m_machine_statuses;
void evt_machine_status(
const machine_status &
status )
{
m_machine_statuses[
status.m_id ] = one_machine_status_t{
};
}
void evt_show_dashboard(mhood_t< show_dashboard >)
{
auto old_precision = std::cout.precision( 5 );
std::cout << "=== The current status ===" << std::endl;
for( const auto & m : m_machine_statuses )
{
show_one_status( m );
}
std::cout << "==========================" << std::endl;
std::cout.precision( old_precision );
}
void show_one_status( const machine_status_map_t::value_type & v )
{
std::cout << v.first << ": e["
<< (engine_state_t::on == v.second.m_engine_status ?
"ON " : "off") << "] c["
<< (cooler_state_t::on == v.second.m_cooler_status ?
"ON " : "off") << "] t="
<< v.second.m_engine_temperature
<< std::endl;
}
};
class machine_dictionary_t
{
public :
using dictionary_type_t = std::map< std::string, so_5::mbox_t >;
machine_dictionary_t( dictionary_type_t values )
: m_dictionary( std::move( values ) )
{}
{
auto r = m_dictionary.find( name );
if( r == m_dictionary.end() )
throw std::runtime_error( "machine not found: " + name );
return r->second;
}
template< typename L >
void for_each( L lambda ) const
{
for( const auto & m : m_dictionary )
lambda( m.first, m.second );
}
private :
const dictionary_type_t m_dictionary;
};
{
public :
a_statuses_analyzer_t(
context_t ctx,
float safe_temperature,
float warn_temperature,
float high_temperature)
, m_status_distrib_mbox{ std::move( status_distrib_mbox ) }
, m_safe_temperature{ safe_temperature }
, m_warn_temperature{ warn_temperature }
, m_high_temperature{ high_temperature }
{}
{
&a_statuses_analyzer_t::evt_machine_status );
}
private :
const float m_safe_temperature;
const float m_warn_temperature;
const float m_high_temperature;
struct last_machine_info_t
{
attention_t m_attention;
float m_engine_temperature;
};
using last_info_map_t = std::map< std::string, last_machine_info_t >;
last_info_map_t m_last_infos;
void evt_machine_status(
const machine_status &
status )
{
auto it = m_last_infos.find(
status.m_id );
if( it == m_last_infos.end() )
it = m_last_infos.insert( last_info_map_t::value_type {
status.m_id,
last_machine_info_t {
attention_t::none,
status.m_engine_temperature
} } ).first;
handle_new_status(
status, it->second );
}
void handle_new_status(
const machine_status &
status,
last_machine_info_t & last_info ) const
{
const auto fresh_info = last_machine_info_t {
detect_attention(
status, last_info ),
};
if( last_info.m_attention != fresh_info.m_attention )
so_5::send< machine_needs_attention >(
m_status_distrib_mbox,
fresh_info.m_attention,
last_info = fresh_info;
}
attention_t detect_attention(
const machine_status &
status,
const last_machine_info_t & last ) const
{
if( last.m_engine_temperature <
status.m_engine_temperature )
{
if(
status.m_engine_temperature > m_high_temperature )
{
if( attention_t::engine_overheat_detected != last.m_attention )
return attention_t::engine_overheat_detected;
}
else if(
status.m_engine_temperature > m_warn_temperature )
{
if( attention_t::engine_cooling_needed != last.m_attention )
return attention_t::engine_cooling_needed;
}
}
else
{
if(
status.m_engine_temperature < m_safe_temperature )
if( attention_t::none != last.m_attention &&
attention_t::engine_cooling_done != last.m_attention )
return attention_t::engine_cooling_done;
}
return last.m_attention;
}
};
template< class Logic >
{
public :
a_machine_controller_t(
context_t ctx,
const machine_dictionary_t & machines )
:
so_5::agent_t( ctx + priority )
, m_status_distrib_mbox( std::move( status_distrib_mbox ) )
, m_machines( machines )
, m_logic()
{}
{
[this]( const machine_needs_attention & msg ) {
return m_logic.filter( msg );
} );
.
event( [
this](
const machine_needs_attention & evt ) {
m_logic.action( m_machines, evt );
} );
}
private :
const machine_dictionary_t & m_machines;
const Logic m_logic;
};
struct engine_stopper_t
{
bool filter( const machine_needs_attention & msg ) const
{
return msg.m_attention == attention_t::engine_overheat_detected;
}
void action(
const machine_dictionary_t & machines,
const machine_needs_attention & evt ) const
{
so_5::send< turn_engine_off >( machines.find_mbox( evt.m_id ) );
}
};
struct engine_starter_t
{
bool filter( const machine_needs_attention & msg ) const
{
return msg.m_attention == attention_t::engine_cooling_done &&
msg.m_engine_status == engine_state_t::off;
}
void action(
const machine_dictionary_t & machines,
const machine_needs_attention & evt ) const
{
so_5::send< turn_engine_on >( machines.find_mbox( evt.m_id ) );
}
};
struct cooler_starter_t
{
bool filter( const machine_needs_attention & msg ) const
{
return (msg.m_attention == attention_t::engine_overheat_detected ||
msg.m_attention == attention_t::engine_cooling_needed) &&
msg.m_cooler_status == cooler_state_t::off;
}
void action(
const machine_dictionary_t & machines,
const machine_needs_attention & evt ) const
{
so_5::send< turn_cooler_on >( machines.find_mbox( evt.m_id ) );
}
};
struct cooler_stopper_t
{
bool filter( const machine_needs_attention & msg ) const
{
return msg.m_attention == attention_t::engine_cooling_done &&
msg.m_cooler_status == cooler_state_t::on;
}
void action(
const machine_dictionary_t & machines,
const machine_needs_attention & evt ) const
{
so_5::send< turn_cooler_off >( machines.find_mbox( evt.m_id ) );
}
};
const machine_dictionary_t & create_machines(
{
machine_dictionary_t::dictionary_type_t dict_data;
auto make_machine = [&]( const std::string & name,
float initial, float warming_step, float cooling_step )
{
machine_disp.binder(),
name, status_distrib_mbox,
initial, warming_step, cooling_step );
dict_data[ name ] = machine->so_direct_mbox();
};
make_machine( "Mch01", 20.0f, 0.3f, 0.2f );
make_machine( "Mch02", 20.0f, 0.45f, 0.2f );
make_machine( "Mch03", 20.0f, 0.25f, 0.3f );
make_machine( "Mch04", 20.0f, 0.26f, 0.27f );
std::make_unique< machine_dictionary_t >( std::move( dict_data ) ) ) );
}
void create_machine_controllers(
const machine_dictionary_t & machines )
{
disp.binder(),
status_distrib_mbox,
machines );
disp.binder(),
status_distrib_mbox,
machines );
disp.binder(),
status_distrib_mbox,
machines );
disp.binder(),
status_distrib_mbox,
machines );
}
void create_starter_agent(
const machine_dictionary_t & dict )
{
const machine_dictionary_t & m_dict;
public :
starter_t( context_t ctx, const machine_dictionary_t & dict )
:
so_5::agent_t{ std::move(ctx) }
, m_dict{ dict }
{}
m_dict.for_each(
so_5::send< turn_engine_on >(
mbox );
} );
}
};
}
{
const auto & machine_dict = create_machines( coop, status_distrib_mbox );
status_distrib_mbox );
status_distrib_mbox,
50.0f,
70.0f,
95.0f
);
create_machine_controllers( coop, status_distrib_mbox, machine_dict );
create_starter_agent( coop, machine_dict );
}
int main()
{
try
{
} );
}
catch( const std::exception & ex )
{
std::cerr << "Error: " << ex.what() << std::endl;
return 1;
}
return 0;
}