<<~EOC
<% if !@ino then %>
<% if @syslog then %>
<% log = :syslog %>
#include <syslog.h>
<% end %>
<% else %>
<% if @syslog then log = :ino end %>
<% end %>
#include "<%= File::basename(@cname) %>.h"
<% if sigint then %>// Install signal handler:
// SIGINT requests a transition to state <%= self.sigint %>
#include <signal.h>
static int _exit_request = 0;
static void signal_handler(int signal) {
if (signal == SIGINT) {
_exit_request = 1;<% if log == :syslog then %>
syslog(LOG_WARNING, "[FSM] SIGINT transition to <%= sigint %>");<% elsif log == :ino then %>
Serial.println("[FSM] SIGINT transition to <%= sigint %>");<% end %>
}
}
<% end %>
<% placeholder = "Your Code Here" %>
// SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!
// GLOBALS
// State human-readable names
const char *state_names[] = {<%= states_list.map {|sn| '"'+sn+'"'}.join(", ") %>};
// List of state functions
<% fw = state_functions_list.max {|a, b| a.length <=> b.length}.length %>
state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES] = {
<% @states.each do |s| %>
<%= (s[:function] + ',').ljust(fw+1) %> // in state <%= s[:id] %>
<% end %>
};
<% if transition_functions_list.count > 0 then %>
// Table of transition functions
transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES] = {
<% sl = states_list %>
<% fw = transition_functions_list.max {|a, b| a.length <=> b.length}.length %>
<% sw = [states_list, "states:"].flatten.max {|a, b| a.length <=> b.length}.length %>
/* <%= "states:".ljust(sw) %> <%= sl.map {|e| e.ljust(fw) }.join(", ") %> */
<% transitions_map.each_with_index do |l, i| %>
/* <%= sl[i].ljust(sw) %> */ {<%= l.map {|e| e.ljust(fw)}.join(", ") %>},
<% end %>
};
<% else %>
// No transition functions
<% end %>
// ____ _ _
// / ___|| |_ __ _| |_ ___
// \\___ \\| __/ _` | __/ _ \\
// ___) | || (_| | || __/
// |____/ \\__\\__,_|\\__\\___|
//
// __ _ _
// / _|_ _ _ __ ___| |_(_) ___ _ __ ___
// | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
// | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
// |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
//
<% dest = destinations.dup %>
<% topo = self.topology %>
<% @states.each do |s| %>
<% stable = true if dest[s[:id]].include? s[:id] %>
<% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} %>
<% if dest[s[:id]].empty? or stable then
dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
end %>
// Function to be executed in state <%= s[:id] %>
// valid return states: <%= dest[s[:id]].join(", ") %>
<% if sigint && stable && topo[:sources][0] != s[:id] then %>
// SIGINT triggers an emergency transition to <%= self.sigint %>
<% end %>
<%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data) {
<%= @prefix %>state_t next_state = <%= dest[s[:id]].first %>;
<% if sigint && topo[:sources][0] == s[:id] then %>signal(SIGINT, signal_handler);
<% end %>
<% if log == :syslog then %>
syslog(LOG_INFO, "[FSM] In state <%= s[:id] %>");
<% elsif log == :ino then %>
Serial.println("[FSM] In state <%= s[:id] %>");
<% end %>
/* <%= placeholder %> */
switch (next_state) {
<% dest[s[:id]].each do |str| %>
case <%= str %>:
<% end %>
break;
default:
<% if log == :syslog then %>
syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", state_names[next_state]);
<% elsif log == :ino then %>
Serial.print("[FSM] Cannot pass from <%= s[:id] %> to ");
Serial.print(state_names[next_state]);
Serial.println(", remaining in this state");
<% end %>
next_state = <%= @prefix.upcase %>NO_CHANGE;
}
<% if sigint && stable && topo[:sources][0] != s[:id] then %>
// SIGINT transition override
if (_exit_request) next_state = <%= (@prefix+"STATE_"+self.sigint ).upcase %>;
<% end %>
return next_state;
}
<% end %>
<% if transition_functions_list.count > 0 then %>
// _____ _ _ _
// |_ _| __ __ _ _ __ ___(_) |_(_) ___ _ __
// | || '__/ _` | '_ \\/ __| | __| |/ _ \\| '_ \\
// | || | | (_| | | | \\__ \\ | |_| | (_) | | | |
// |_||_| \\__,_|_| |_|___/_|\\__|_|\\___/|_| |_|
//
// __ _ _
// / _|_ _ _ __ ___| |_(_) ___ _ __ ___
// | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
// | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
// |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
//
<% transition_functions_list.each do |t| %>
<% next if t == "NULL" %>
<% tpaths = transitions_paths[t] %>
// This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:
<% tpaths.each_with_index do |e, i| %>
// <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>
<% end %>
void <%= t %>(<%= @prefix %>state_data_t *data) {
<% if log == :syslog then %>
syslog(LOG_INFO, "[FSM] State transition <%= t %>");
<% elsif log == :ino then %>
Serial.println("[FSM] State transition <%= t %>");
<% end %>
/* <%= placeholder %> */
}
<% end %>
<% end %>
// ____ _ _
// / ___|| |_ __ _| |_ ___
// \\___ \\| __/ _` | __/ _ \\
// ___) | || (_| | || __/
// |____/ \\__\\__,_|\\__\\___|
//
//
// _ __ ___ __ _ _ __ __ _ __ _ ___ _ __
// | '_ ` _ \\ / _` | '_ \\ / _` |/ _` |/ _ \\ '__|
// | | | | | | (_| | | | | (_| | (_| | __/ |
// |_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_|
// |___/
<%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data) {
<%= @prefix %>state_t new_state = <%= @prefix %>state_table[cur_state](data);
if (new_state == <%= @prefix.upcase %>NO_CHANGE) new_state = cur_state;
<% if transition_functions_list.count > 0 then %>
transition_func_t *transition = <%= @prefix %>transition_table[cur_state][new_state];
if (transition)
transition(data);
<% end %>
return new_state == <%= @prefix.upcase %>NO_CHANGE ? cur_state : new_state;
};
<% if @ino then %>
/* Example usage:
<%= @prefix %>state_data_t data = {count: 1};
void loop() {
static <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_INIT;
cur_state = <%= @prefix %>run_state(cur_state, &data);
}
*/
<% else %>
<% nsinks = topology[:sinks].count %>
#ifdef TEST_MAIN
#include <unistd.h>
int main() {
<%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_<%= @states.first[:id].upcase %>;
<% if @syslog then %>
openlog("SM", LOG_PID | LOG_PERROR, LOG_USER);
syslog(LOG_INFO, "Starting SM");
<% end %>
do {
cur_state = <%= @prefix %>run_state(cur_state, NULL);
sleep(1);
<% if nsinks == 1 %>
} while (cur_state != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);
<%= @prefix %>run_state(cur_state, NULL);
<% else %>
} while (1);
<% end %>
return 0;
}
#endif
<% end %>
EOC