123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- /*
- Automaton.cpp - Reactive State Machine Framework for Arduino.
- Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen
- */
- #include "Automaton.h"
- /* The Machine class is a base class for creating and running State Machines
- *
- *********************************************************************************************
- *
- * Machine::state( void ) - Retrieves the current state for the machine
- *
- * (may be overridden by a subclass in which case it may return something else, like a value )
- */
- int Machine::state() {
- return current;
- }
- /*
- * Machine::state( state ) - Sets the next state for the machine
- *
- */
- Machine& Machine::state( int state ) {
- next = state;
- last_trigger = -1;
- flags &= ~ATM_SLEEP_FLAG;
- return *this;
- }
- /*
- * Machine::trigger( evt ) - Triggers an event for the machine
- *
- * The machine is cycled for maximum of 8 times until it is actively listening for the event
- * Then the event is triggered followed by two more cycles to process the event and the
- * following state change.
- *
- */
- Machine& Machine::trigger( int evt /* = 0 */ ) {
- int new_state;
- int max_cycle = 8;
- do {
- flags &= ~ATM_SLEEP_FLAG;
- cycle();
- new_state = read_state( state_table + ( current * state_width ) + evt + ATM_ON_EXIT + 1 );
- } while ( --max_cycle && ( new_state == -1 || next_trigger != -1 ) );
- if ( new_state > -1 ) {
- next_trigger = evt;
- flags &= ~ATM_SLEEP_FLAG;
- cycle(); // Pick up the trigger
- flags &= ~ATM_SLEEP_FLAG;
- cycle(); // Process the state change
- }
- return *this;
- }
- /*
- * Machine::setTrace( stream, callback, symbols ) - Sets up state tracing for the machine
- *
- * Connects a stream object, a callback (atm_serial_debug) and a symbol table (string) to the object
- *
- */
- Machine& Machine::setTrace( Stream* stream, swcb_sym_t callback, const char symbols[] ) {
- callback_trace = callback;
- stream_trace = stream;
- this->symbols = symbols;
- return *this;
- }
- /*
- * Machine::sleep( v ) - Sets or returns the current sleep flag setting
- *
- */
- uint8_t Machine::sleep( int8_t v /* = 1 */ ) {
- if ( v > -1 ) flags = v ? flags | ATM_SLEEP_FLAG : flags & ~ATM_SLEEP_FLAG;
- return ( flags & ATM_SLEEP_FLAG ) > 0;
- }
- /*
- * Machine::begin( state_table, width ) - Initializes the state table and sets the sleep flag
- *
- */
- Machine& Machine::begin( const state_t* tbl, int width ) {
- state_table = tbl;
- state_width = ATM_ON_EXIT + width + 2;
- flags &= ~ATM_SLEEP_FLAG;
- automaton.add( *this, false );
- current = -1;
- next = 0;
- next_trigger = -1;
- return *this;
- }
- /*
- * Machine::onPush( connectors, id, sub, slots, multi, dest, arg ) - Registers a connector destination
- *
- * connectors Connector table
- * id Connector id
- * sub Connector sub id (for multi-slot connectors)
- * slots Number of slots reserved for this connector
- * multi Register multiple (all) slots in one call
- * dest Destination: Machine object or callback
- * arg Argument for machine (event) or callback (idx)
- *
- */
- void Machine::onPush( atm_connector connectors[], int id, int sub, int slots, int fill, Machine& machine, int event ) {
- if ( sub == -1 ) { // auto store
- sub = 0;
- for ( int i = 0; i < slots; i++ ) {
- if ( connectors[id + i].mode() == 0 ) { // Find a free slot
- sub = i;
- }
- }
- }
- if ( slots > 1 && fill ) {
- for ( int i = 0; i < slots; i++ ) {
- connectors[id + i].set( &machine, event );
- }
- } else {
- connectors[id + sub].set( &machine, event );
- }
- }
- void Machine::onPush( atm_connector connectors[], int id, int sub, int slots, int fill, atm_cb_push_t callback, int idx ) {
- if ( sub == -1 ) { // auto store
- sub = 0;
- for ( int i = 0; i < slots; i++ ) {
- if ( connectors[id + i].mode() == 0 ) { // Find a free slot
- sub = i;
- }
- }
- }
- if ( slots > 1 && fill ) {
- for ( int i = 0; i < slots; i++ ) {
- connectors[id + i].set( callback, idx );
- }
- } else {
- connectors[id + sub].set( callback, idx );
- }
- }
- /*
- * Machine::push( connectors, id, sub, v, up ) - Pushes an action through the specified connector
- *
- * connectors Connector table
- * id Connector id
- * sub Connector sub id (for multi-slot connectors)
- * v Value to pass to a callback as 'v'
- * up Value to pass to a callback as 'up'
- *
- */
- void Machine::push( atm_connector connectors[], int id, int sub, int v, int up ) {
- if ( ( id & ATM_BROADCAST ) > 0 ) {
- id = id & ~ATM_BROADCAST;
- for ( int i = id; i < sub; i++ ) {
- connectors[id + i].push( v, up );
- }
- } else {
- connectors[id + sub].push( v, up );
- }
- }
- /*
- * Machine::mapSymbol( id, map ) - Maps a number ( event/state ) to a symbol
- *
- * 0 Machine class name (e.g. LED)
- * 1..ELSE Event name (e.g. EVT_TIMER)
- * ELSE.. State name (e.g. IDLE)
- *
- */
- const char* Machine::mapSymbol( int id, const char map[] ) {
- int cnt = 0;
- int i = 0;
- if ( id == -1 ) return "*NONE*";
- if ( id == 0 ) return map;
- while ( 1 ) {
- if ( map[i] == '\0' && ++cnt == id ) {
- i++;
- break;
- }
- i++;
- }
- return &map[i];
- }
- /*
- * Machine::cycle( time ) - Executes one cycle of a State Machine
- *
- * For every state change:
- * - Calls the ON_SWITCH action
- * - Calls the state trace function (if connected)
- * - Calls the previous state's ON_EXIT action
- * - Changes the active state (current) to the new
- * - Calls the new state's ON_ENTER action
- *
- * For every 'normal' cycle:
- * - Executes the ON_LOOP action
- * - Scans the event columns in the current table and calls active events
- *
- * If the 'time' argument is given, loops until that time has passed
- * otherwise executes only one cycle of the machine
- */
- Machine& Machine::cycle( uint32_t time /* = 0 */ ) {
- uint32_t cycle_start = millis();
- do {
- if ( ( flags & ( ATM_SLEEP_FLAG | ATM_CYCLE_FLAG ) ) == 0 ) {
- cycles++;
- flags |= ATM_CYCLE_FLAG;
- if ( next != -1 ) {
- action( ATM_ON_SWITCH );
- if ( callback_trace ) {
- callback_trace( stream_trace, *this, symbols, mapSymbol( current == -1 ? current : current + state_width - ATM_ON_EXIT, symbols ),
- mapSymbol( next == -1 ? next : next + state_width - ATM_ON_EXIT, symbols ),
- mapSymbol( last_trigger == -1 ? -1 : last_trigger + 1, symbols ), millis() - state_millis, cycles );
- }
- if ( current > -1 ) action( read_state( state_table + ( current * state_width ) + ATM_ON_EXIT ) );
- current = next;
- next = -1;
- state_millis = millis();
- action( read_state( state_table + ( current * state_width ) + ATM_ON_ENTER ) );
- if ( read_state( state_table + ( current * state_width ) + ATM_ON_LOOP ) == ATM_SLEEP ) {
- flags |= ATM_SLEEP_FLAG;
- } else {
- flags &= ~ATM_SLEEP_FLAG;
- }
- cycles = 0;
- }
- state_t i = read_state( state_table + ( current * state_width ) + ATM_ON_LOOP );
- if ( i != -1 ) {
- action( i );
- }
- for ( i = ATM_ON_EXIT + 1; i < state_width; i++ ) {
- state_t next_state = read_state( state_table + ( current * state_width ) + i );
- if ( ( next_state != -1 ) && ( i == state_width - 1 || event( i - ATM_ON_EXIT - 1 ) || next_trigger == i - ATM_ON_EXIT - 1 ) ) {
- state( next_state );
- last_trigger = i - ATM_ON_EXIT - 1;
- next_trigger = -1;
- break;
- }
- }
- flags &= ~ATM_CYCLE_FLAG;
- }
- } while ( millis() - cycle_start < time );
- return *this;
- }
|