Machine.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. Automaton.cpp - Reactive State Machine Framework for Arduino.
  3. Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen
  4. */
  5. #include "Automaton.h"
  6. /* The Machine class is a base class for creating and running State Machines
  7. *
  8. *********************************************************************************************
  9. *
  10. * Machine::state( void ) - Retrieves the current state for the machine
  11. *
  12. * (may be overridden by a subclass in which case it may return something else, like a value )
  13. */
  14. int Machine::state() {
  15. return current;
  16. }
  17. /*
  18. * Machine::state( state ) - Sets the next state for the machine
  19. *
  20. */
  21. Machine& Machine::state( int state ) {
  22. next = state;
  23. last_trigger = -1;
  24. flags &= ~ATM_SLEEP_FLAG;
  25. return *this;
  26. }
  27. /*
  28. * Machine::trigger( evt ) - Triggers an event for the machine
  29. *
  30. * The machine is cycled for maximum of 8 times until it is actively listening for the event
  31. * Then the event is triggered followed by two more cycles to process the event and the
  32. * following state change.
  33. *
  34. */
  35. Machine& Machine::trigger( int evt /* = 0 */ ) {
  36. int new_state;
  37. int max_cycle = 8;
  38. do {
  39. flags &= ~ATM_SLEEP_FLAG;
  40. cycle();
  41. new_state = read_state( state_table + ( current * state_width ) + evt + ATM_ON_EXIT + 1 );
  42. } while ( --max_cycle && ( new_state == -1 || next_trigger != -1 ) );
  43. if ( new_state > -1 ) {
  44. next_trigger = evt;
  45. flags &= ~ATM_SLEEP_FLAG;
  46. cycle(); // Pick up the trigger
  47. flags &= ~ATM_SLEEP_FLAG;
  48. cycle(); // Process the state change
  49. }
  50. return *this;
  51. }
  52. /*
  53. * Machine::setTrace( stream, callback, symbols ) - Sets up state tracing for the machine
  54. *
  55. * Connects a stream object, a callback (atm_serial_debug) and a symbol table (string) to the object
  56. *
  57. */
  58. Machine& Machine::setTrace( Stream* stream, swcb_sym_t callback, const char symbols[] ) {
  59. callback_trace = callback;
  60. stream_trace = stream;
  61. this->symbols = symbols;
  62. return *this;
  63. }
  64. /*
  65. * Machine::sleep( v ) - Sets or returns the current sleep flag setting
  66. *
  67. */
  68. uint8_t Machine::sleep( int8_t v /* = 1 */ ) {
  69. if ( v > -1 ) flags = v ? flags | ATM_SLEEP_FLAG : flags & ~ATM_SLEEP_FLAG;
  70. return ( flags & ATM_SLEEP_FLAG ) > 0;
  71. }
  72. /*
  73. * Machine::begin( state_table, width ) - Initializes the state table and sets the sleep flag
  74. *
  75. */
  76. Machine& Machine::begin( const state_t* tbl, int width ) {
  77. state_table = tbl;
  78. state_width = ATM_ON_EXIT + width + 2;
  79. flags &= ~ATM_SLEEP_FLAG;
  80. automaton.add( *this, false );
  81. current = -1;
  82. next = 0;
  83. next_trigger = -1;
  84. return *this;
  85. }
  86. /*
  87. * Machine::onPush( connectors, id, sub, slots, multi, dest, arg ) - Registers a connector destination
  88. *
  89. * connectors Connector table
  90. * id Connector id
  91. * sub Connector sub id (for multi-slot connectors)
  92. * slots Number of slots reserved for this connector
  93. * multi Register multiple (all) slots in one call
  94. * dest Destination: Machine object or callback
  95. * arg Argument for machine (event) or callback (idx)
  96. *
  97. */
  98. void Machine::onPush( atm_connector connectors[], int id, int sub, int slots, int fill, Machine& machine, int event ) {
  99. if ( sub == -1 ) { // auto store
  100. sub = 0;
  101. for ( int i = 0; i < slots; i++ ) {
  102. if ( connectors[id + i].mode() == 0 ) { // Find a free slot
  103. sub = i;
  104. }
  105. }
  106. }
  107. if ( slots > 1 && fill ) {
  108. for ( int i = 0; i < slots; i++ ) {
  109. connectors[id + i].set( &machine, event );
  110. }
  111. } else {
  112. connectors[id + sub].set( &machine, event );
  113. }
  114. }
  115. void Machine::onPush( atm_connector connectors[], int id, int sub, int slots, int fill, atm_cb_push_t callback, int idx ) {
  116. if ( sub == -1 ) { // auto store
  117. sub = 0;
  118. for ( int i = 0; i < slots; i++ ) {
  119. if ( connectors[id + i].mode() == 0 ) { // Find a free slot
  120. sub = i;
  121. }
  122. }
  123. }
  124. if ( slots > 1 && fill ) {
  125. for ( int i = 0; i < slots; i++ ) {
  126. connectors[id + i].set( callback, idx );
  127. }
  128. } else {
  129. connectors[id + sub].set( callback, idx );
  130. }
  131. }
  132. /*
  133. * Machine::push( connectors, id, sub, v, up ) - Pushes an action through the specified connector
  134. *
  135. * connectors Connector table
  136. * id Connector id
  137. * sub Connector sub id (for multi-slot connectors)
  138. * v Value to pass to a callback as 'v'
  139. * up Value to pass to a callback as 'up'
  140. *
  141. */
  142. void Machine::push( atm_connector connectors[], int id, int sub, int v, int up ) {
  143. if ( ( id & ATM_BROADCAST ) > 0 ) {
  144. id = id & ~ATM_BROADCAST;
  145. for ( int i = id; i < sub; i++ ) {
  146. connectors[id + i].push( v, up );
  147. }
  148. } else {
  149. connectors[id + sub].push( v, up );
  150. }
  151. }
  152. /*
  153. * Machine::mapSymbol( id, map ) - Maps a number ( event/state ) to a symbol
  154. *
  155. * 0 Machine class name (e.g. LED)
  156. * 1..ELSE Event name (e.g. EVT_TIMER)
  157. * ELSE.. State name (e.g. IDLE)
  158. *
  159. */
  160. const char* Machine::mapSymbol( int id, const char map[] ) {
  161. int cnt = 0;
  162. int i = 0;
  163. if ( id == -1 ) return "*NONE*";
  164. if ( id == 0 ) return map;
  165. while ( 1 ) {
  166. if ( map[i] == '\0' && ++cnt == id ) {
  167. i++;
  168. break;
  169. }
  170. i++;
  171. }
  172. return &map[i];
  173. }
  174. /*
  175. * Machine::cycle( time ) - Executes one cycle of a State Machine
  176. *
  177. * For every state change:
  178. * - Calls the ON_SWITCH action
  179. * - Calls the state trace function (if connected)
  180. * - Calls the previous state's ON_EXIT action
  181. * - Changes the active state (current) to the new
  182. * - Calls the new state's ON_ENTER action
  183. *
  184. * For every 'normal' cycle:
  185. * - Executes the ON_LOOP action
  186. * - Scans the event columns in the current table and calls active events
  187. *
  188. * If the 'time' argument is given, loops until that time has passed
  189. * otherwise executes only one cycle of the machine
  190. */
  191. Machine& Machine::cycle( uint32_t time /* = 0 */ ) {
  192. uint32_t cycle_start = millis();
  193. do {
  194. if ( ( flags & ( ATM_SLEEP_FLAG | ATM_CYCLE_FLAG ) ) == 0 ) {
  195. cycles++;
  196. flags |= ATM_CYCLE_FLAG;
  197. if ( next != -1 ) {
  198. action( ATM_ON_SWITCH );
  199. if ( callback_trace ) {
  200. callback_trace( stream_trace, *this, symbols, mapSymbol( current == -1 ? current : current + state_width - ATM_ON_EXIT, symbols ),
  201. mapSymbol( next == -1 ? next : next + state_width - ATM_ON_EXIT, symbols ),
  202. mapSymbol( last_trigger == -1 ? -1 : last_trigger + 1, symbols ), millis() - state_millis, cycles );
  203. }
  204. if ( current > -1 ) action( read_state( state_table + ( current * state_width ) + ATM_ON_EXIT ) );
  205. current = next;
  206. next = -1;
  207. state_millis = millis();
  208. action( read_state( state_table + ( current * state_width ) + ATM_ON_ENTER ) );
  209. if ( read_state( state_table + ( current * state_width ) + ATM_ON_LOOP ) == ATM_SLEEP ) {
  210. flags |= ATM_SLEEP_FLAG;
  211. } else {
  212. flags &= ~ATM_SLEEP_FLAG;
  213. }
  214. cycles = 0;
  215. }
  216. state_t i = read_state( state_table + ( current * state_width ) + ATM_ON_LOOP );
  217. if ( i != -1 ) {
  218. action( i );
  219. }
  220. for ( i = ATM_ON_EXIT + 1; i < state_width; i++ ) {
  221. state_t next_state = read_state( state_table + ( current * state_width ) + i );
  222. if ( ( next_state != -1 ) && ( i == state_width - 1 || event( i - ATM_ON_EXIT - 1 ) || next_trigger == i - ATM_ON_EXIT - 1 ) ) {
  223. state( next_state );
  224. last_trigger = i - ATM_ON_EXIT - 1;
  225. next_trigger = -1;
  226. break;
  227. }
  228. }
  229. flags &= ~ATM_CYCLE_FLAG;
  230. }
  231. } while ( millis() - cycle_start < time );
  232. return *this;
  233. }