#include <iostream>
#include <string>
#include <cctype>
struct key_digit
{
char m_value;
};
namespace intercom_messages
{
struct display_text
{
std::string m_what;
};
inline void show_on_display(
std::string what )
{
so_5::send< display_text >( intercom_mbox, std::move(what) );
}
inline void clear_display(
{
so_5::send< display_text >( intercom_mbox, "" );
}
}
{
state_t inactive{ this, "inactive" };
state_t
active{
this,
"active" };
const std::chrono::seconds inactivity_time{ 10 };
public :
inactivity_watcher(
context_t ctx,
, m_intercom_mbox{ std::move(intercom_mbox) }
{
inactive
.on_enter( [this] { m_timer.release(); } )
.just_switch_to< intercom_messages::activated >(
m_intercom_mbox, active );
.on_enter( [this] { reschedule_timer(); } )
.event( m_intercom_mbox,
[this](mhood_t< key_cancel >) { reschedule_timer(); } )
.event( m_intercom_mbox,
[this](mhood_t< key_bell >) { reschedule_timer(); } )
.event( m_intercom_mbox,
[this](mhood_t< key_grid >) { reschedule_timer(); } )
.event( m_intercom_mbox,
[this]( const key_digit & ) { reschedule_timer(); } )
.just_switch_to< intercom_messages::deactivate >(
m_intercom_mbox, inactive );
this >>= inactive;
}
private :
void reschedule_timer()
{
m_timer = so_5::send_periodic< intercom_messages::deactivate >(
m_intercom_mbox,
inactivity_time,
std::chrono::seconds::zero() );
}
};
{
state_t
off{
this,
"off" };
state_t
on{
this,
"on" };
public :
keyboard_lights(
context_t ctx,
{
.on_enter( []{ std::cout << "keyboard_lights OFF" << std::endl; } )
.just_switch_to< intercom_messages::activated >(
intercom_mbox, on );
.on_enter( []{ std::cout << "keyboard_lights ON" << std::endl; } )
.just_switch_to< intercom_messages::deactivate >(
intercom_mbox, off );
}
};
{
state_t
off{
this,
"off" };
state_t
on{
this,
"on" };
public :
display(
context_t ctx,
{
.on_enter( []{ std::cout << "display OFF" << std::endl; } )
.just_switch_to< intercom_messages::activated >(
intercom_mbox, on );
.on_enter( []{ std::cout << "display ON" << std::endl; } )
.event( intercom_mbox,
[]( const intercom_messages::display_text & msg ) {
std::cout << "display: '" << msg.m_what << "'" << std::endl;
} )
.just_switch_to< intercom_messages::deactivate >(
intercom_mbox, off );
}
};
{
state_t
ringing{ initial_substate_of{
on },
"ringing" },
sleeping{ substate_of{
on },
"sleeping" };
public :
struct dial_to { std::string m_number; };
ringer(
context_t ctx,
, m_intercom_mbox{ std::move(intercom_mbox) }
{
.on_enter( [this]{ m_timer.release(); } )
.event( m_intercom_mbox, [this]( const dial_to & msg ) {
m_number = msg.m_number;
} );
.on_enter( [this] {
m_timer = so_5::send_periodic< timer >(
*this,
std::chrono::milliseconds::zero(),
std::chrono::milliseconds{ 1500 } );
} )
.just_switch_to< stop_dialing >( m_intercom_mbox, off );
ringing
.on_enter( [this]{
intercom_messages::show_on_display(
m_intercom_mbox, "RING" );
} )
.just_switch_to< timer >( sleeping );
sleeping
.on_enter( [this]{
intercom_messages::show_on_display(
m_intercom_mbox, m_number );
} )
.just_switch_to< timer >( ringing );
}
private :
std::string m_number;
};
{
state_t
inactive{ this, "inactive" },
wait_activity{
initial_substate_of{
active },
"wait_activity" },
number_selection{ substate_of{
active },
"number_selection" },
dialling{ substate_of{
active },
"dialling" },
dial_apartment{
initial_substate_of{ dialling }, "dial_apartment" },
no_answer{
substate_of{ dialling }, "no_answer" },
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" }
;
struct apartment_info
{
std::string m_number;
std::string m_secret_key;
apartment_info( std::string n, std::string k )
: m_number( std::move(n) ), m_secret_key( std::move(k) )
{}
};
public :
controller(
context_t ctx,
, m_intercom_mbox{ std::move(intercom_mbox) }
, m_apartments( make_apartment_info() )
, m_actual_service_code( "12345" )
{
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 );
.on_enter( &controller::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( &controller::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
.suppress< key_grid >( m_intercom_mbox )
.suppress< key_bell >( m_intercom_mbox )
.suppress< key_digit >( m_intercom_mbox );
dial_apartment
.time_limit( std::chrono::seconds{ 8 }, no_answer )
.on_enter( &controller::dial_apartment_on_enter )
.on_exit( &controller::dial_apartment_on_exit );
no_answer
.time_limit( std::chrono::milliseconds{ 1500 }, wait_activity )
.on_enter( &controller::no_answer_on_enter )
.on_exit( &controller::no_answer_on_exit )
.suppress< key_cancel >( m_intercom_mbox );
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( &controller::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( &controller::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( &controller::service_code_on_enter )
.event( m_intercom_mbox, &controller::service_code_on_digit )
.event( m_intercom_mbox, &controller::service_code_on_grid );
door_unlocked
.time_limit( std::chrono::seconds{ 5 }, wait_activity )
.on_enter( &controller::door_unlocked_on_enter )
.on_exit( &controller::door_unlocked_on_exit )
.suppress< key_grid >( m_intercom_mbox )
.suppress< key_bell >( m_intercom_mbox )
.suppress< key_digit >( m_intercom_mbox )
.suppress< key_cancel >( m_intercom_mbox );
}
{
this >>= inactive;
}
private :
static const std::size_t max_apartment_number_size = 3u;
static const std::size_t max_secret_code_size = 4u;
static const std::size_t service_code_size = 5u;
const std::vector< apartment_info > m_apartments;
std::string m_apartment_number;
std::string m_user_secret_code;
std::string m_service_code;
const std::string m_actual_service_code;
static std::vector< apartment_info > make_apartment_info()
{
std::vector< apartment_info > result;
result.reserve( 10 );
result.emplace_back( "101", "1011" );
result.emplace_back( "102", "1022" );
result.emplace_back( "103", "1033" );
result.emplace_back( "104", "1044" );
result.emplace_back( "105", "1055" );
result.emplace_back( "106", "1066" );
result.emplace_back( "107", "1077" );
result.emplace_back( "108", "1088" );
result.emplace_back( "109", "1099" );
result.emplace_back( "110", "1100" );
return result;
}
void active_on_enter()
{
so_5::send< intercom_messages::activated >( m_intercom_mbox );
}
void active_on_cancel( mhood_t< key_cancel > )
{
this >>= wait_activity;
}
void active_on_grid( mhood_t< key_grid > )
{
this >>= special_code_selection;
}
void apartment_number_on_enter()
{
m_apartment_number.clear();
}
void apartment_number_on_digit( const key_digit & msg )
{
if( m_apartment_number.size() < max_apartment_number_size )
m_apartment_number += msg.m_value;
intercom_messages::show_on_display(
m_intercom_mbox, m_apartment_number );
}
void apartment_number_on_bell( mhood_t< key_bell > )
{
auto apartment = std::find_if( begin(m_apartments), end(m_apartments),
[this]( const apartment_info & info ) {
return info.m_number == m_apartment_number;
} );
if( apartment != end(m_apartments) )
this >>= dialling;
else
{
intercom_messages::show_on_display( m_intercom_mbox, "Err" );
this >>= wait_activity;
}
}
void dial_apartment_on_enter()
{
so_5::send< ringer::dial_to >( m_intercom_mbox, m_apartment_number );
}
void dial_apartment_on_exit()
{
so_5::send< ringer::stop_dialing >( m_intercom_mbox );
}
void no_answer_on_enter()
{
intercom_messages::show_on_display( m_intercom_mbox, "No Answer" );
}
void no_answer_on_exit()
{
intercom_messages::clear_display( m_intercom_mbox );
}
void user_code_apartment_number_on_enter()
{
m_apartment_number.clear();
}
void user_code_secret_on_enter()
{
m_user_secret_code.clear();
intercom_messages::clear_display( m_intercom_mbox );
}
void user_code_secret_on_digit( const key_digit & msg )
{
if( m_user_secret_code.size() < max_secret_code_size )
m_user_secret_code += msg.m_value;
intercom_messages::show_on_display(
m_intercom_mbox,
std::string( m_user_secret_code.size(), '*' ) );
}
void user_code_secret_on_bell( mhood_t< key_bell > )
{
auto apartment = std::find_if( begin(m_apartments), end(m_apartments),
[this]( const apartment_info & info ) {
return info.m_number == m_apartment_number;
} );
if( apartment != end(m_apartments) &&
m_user_secret_code == apartment->m_secret_key )
this >>= door_unlocked;
else
{
intercom_messages::show_on_display( m_intercom_mbox, "Err" );
this >>= wait_activity;
}
}
void door_unlocked_on_enter()
{
intercom_messages::show_on_display( m_intercom_mbox, "unlocked" );
}
void door_unlocked_on_exit()
{
intercom_messages::clear_display( m_intercom_mbox );
}
void service_code_on_enter()
{
m_service_code.clear();
}
void service_code_on_digit( const key_digit & msg )
{
if( m_service_code.size() < service_code_size )
m_service_code += msg.m_value;
intercom_messages::show_on_display(
m_intercom_mbox,
std::string( m_service_code.size(), '#' ) );
}
void service_code_on_grid( mhood_t< key_grid > )
{
if( !m_service_code.empty() )
{
if( m_service_code == m_actual_service_code )
this >>= door_unlocked;
else
{
intercom_messages::show_on_display( m_intercom_mbox, "Err" );
this >>= wait_activity;
}
}
}
};
{
coop.
make_agent< inactivity_watcher >( intercom_mbox );
coop.
make_agent< keyboard_lights >( intercom_mbox );
return intercom_mbox;
} );
}
void demo()
{
;
while( true )
{
std::cout << "enter digit or 'c' or 'b' or '#' (or 'exit' to stop): "
<< std::flush;
std::string choice;
std::cin >> choice;
if( "c" == choice ) so_5::send< key_cancel >( intercom );
else if( "b" == choice ) so_5::send< key_bell >( intercom );
else if( "#" == choice ) so_5::send< key_grid >( intercom );
else if( "exit" == choice ) break;
else if( 1 == choice.size() && std::isdigit( choice[ 0 ] ) )
so_5::send< key_digit >( intercom, choice[ 0 ] );
}
}
int main()
{
try
{
demo();
}
catch( const std::exception & ex )
{
std::cerr << "Error: " << ex.what() << std::endl;
}
return 0;
}