Module: GV_FSM::Templates
- Included in:
- FSM
- Defined in:
- lib/templates.rb
Constant Summary collapse
- HEADER =
_ _ _ _ __ _ __
| | | | ____| / \ | _ \| ____| _ \ | |_| | _| / _ \ | | | | _| | |_) | | _ | |___ / ___ \| |_| | |___| _ < |_| |_|_____/_/ \_\____/|_____|_| \_\
<<~EOHEADER /****************************************************************************** Finite State Machine Project: <%= @project_name or @dotfile %> Description: <%= @description or "<none given>" %> Generated by gv_fsm ruby gem, see https://rubygems.org/gems/gv_fsm gv_fsm version <%= GV_FSM::VERSION %> Generation date: <%= Time.now %> Generated from: <%= @dotfile %> The finite state machine has: <%= @states.count %> states <%= transition_functions_list.select {|e| e != 'NULL'}.count %> transition functions <% if @prefix != '' %> Functions and types have been generated with prefix "<%= @prefix %>" <% end %> ******************************************************************************/ EOHEADER
- HH =
<<~EOH <% if !@ino then %> #ifndef <%= File::basename(@cname).upcase %>_H #define <%= File::basename(@cname).upcase %>_H #ifdef __cplusplus extern "C" { #endif #include <stdlib.h> <% else -%> #include <arduino.h> <% end -%> // State data object // By default set to void; override this typedef or load the proper // header if you need typedef void <%= @prefix %>state_data_t; <% if !@ino then -%> // NOTHING SHALL BE CHANGED AFTER THIS LINE! <% end -%> // List of states typedef enum { <% @states.each_with_index do |s, i| -%> <%= @prefix.upcase %>STATE_<%= s[:id].upcase %><%= i == 0 ? " = 0" : "" %>, <% end -%> <%= @prefix.upcase %>NUM_STATES, <%= @prefix.upcase %>NO_CHANGE } <%= @prefix %>state_t; // State human-readable names extern const char *<%= @prefix %>state_names[]; <% if transition_functions_list.count > 0 then -%> // State function and state transition prototypes typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data); typedef void transition_func_t(<%= @prefix %>state_data_t *data); <% else -%> // State function prototype typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data); <%end -%> // State functions <% dest = destinations.dup -%> <% @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(", ") %> <%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data); <% end -%> // List of state functions extern state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES]; <% if transition_functions_list.count > 0 then -%> // Transition functions <% transition_functions_list.each do |t| -%> <% next if t == "NULL" -%> void <%= t %>(<%= @prefix %>state_data_t *data); <% end -%> // Table of transition functions extern transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES]; <% else -%> // No transition functions <% end -%> // state manager <%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data); <% if !@ino then -%> #ifdef __cplusplus } #endif #endif // <%= File::basename(@cname).upcase %>_H <% end -%> EOH
- CC =
<<~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 *<%= @prefix %>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", <%= @prefix %>state_names[next_state]); <% elsif log == :ino then -%> Serial.print("[FSM] Cannot pass from <%= s[:id] %> to "); Serial.print(<%= @prefix %>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; }; <% 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
- HPP =
<<~EOHPP #ifndef <%= File::basename(@cname).upcase %>_HPP #define <%= File::basename(@cname).upcase %>_HPP #include <functional> #include <iostream> #include <map> #include <string> #include <tuple> <% if @syslog then -%> <% log = :syslog -%> #include <syslog.h> <% end -%> <% if sigint then -%> // Install signal handler: // SIGINT requests a transition to state <%= self.sigint %> #include <csignal> <% end %> using namespace std::string_literals; <% ns = @project_name || "FSM" -%> namespace <%= ns %> { static bool <%= self.sigint %>_requested = false; // List of states typedef enum { <% @states.each do |s| -%> <%= @prefix.upcase %>STATE_<%= s[:id].upcase %>, <% end -%> <%= @prefix.upcase %>NUM_STATES, <%= @prefix.upcase %>NO_CHANGE, <%= @prefix.upcase %>UNIMPLEMENTED } <%= @prefix %>state_t; // State human-readable names std::map<<%= @prefix %>state_t, char const *> state_names = { <% @states.each do |s| -%> {<%= @prefix.upcase %>STATE_<%= s[:id].upcase %>, "<%= s[:id].upcase %>"}, <% end -%> {<%= @prefix.upcase %>NUM_STATES, "NUM_STATES"}, {<%= @prefix.upcase %>NO_CHANGE, "NO_CHANGE"}, {<%= @prefix.upcase %>UNIMPLEMENTED, "UNIMPLEMENTED"} }; // Custom state functions: <% @states.each do |s| -%> template<class T> <%= @prefix %>state_t <%= s[:function] %>(T &data); <% end -%> <% if transition_functions_list.count > 0 then -%> // Custom transition functions: <% transition_functions_list.each do |t| -%> template<class T> void <%= t %>(T &data); <% end -%> <% end -%> // Finite State Machine class template <typename DATA_T> class FiniteStateMachine { // Function templates using state_fun = std::function<<%= @prefix %>state_t(DATA_T &data)>; using transition_fun = std::function<void(DATA_T &data)>; using operation_fun = std::function<void(DATA_T &data)>; private: std::pair<<%= @prefix %>state_t, <%= @prefix %>state_t> _state{<%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>, <%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>}; std::map<<%= @prefix %>state_t, state_fun> _states; std::map<<%= @prefix %>state_t, std::map<<%= @prefix %>state_t, transition_fun>> _transitions; std::function<void()> _timing_func; DATA_T *_data; public: FiniteStateMachine(DATA_T *data) : _data(data) { install_functions(); } ~FiniteStateMachine(){}; void set_timing_function(std::function<void()> timing_func) { _timing_func = timing_func; } void add_state(<%= @prefix %>state_t name, state_fun func) { _states[name] = func; } void add_transition(<%= @prefix %>state_t from, <%= @prefix %>state_t to, transition_fun func) { _transitions[from][to] = func; } inline <%= @prefix %>state_t state() { return _state.second; } inline std::string state_name() { return std::string(state_names[_state.second]); } inline <%= @prefix %>state_t prev_state() { return _state.first; } <%= @prefix %>state_t operator()(<%= @prefix %>state_t state) { if (_states.find(state) == _states.end()) { throw std::runtime_error("State not found: "s + state_names[state]); } return _states[state](*_data); } void operator()(<%= @prefix %>state_t from, <%= @prefix %>state_t to) { if (_transitions.find(from) != _transitions.end()) { if (_transitions[from].find(to) != _transitions[from].end()) { _transitions[from][to](*_data); } } } // Run the FSM from a given state void run(<%= @prefix %>state_t state, operation_fun operation = nullptr) { <%= ns %>::<%= self.sigint %>_requested = false; <%= @prefix %>state_t prev_state = state; _state.first = state; _state.second = state; <% if sigint then -%> std::signal(SIGINT, [](int signum) { <% if log == :syslog then -%> syslog(LOG_WARNING, "[FSM] SIGINT transition to <%= sigint %>"); <% end -%> <%= ns %>::<%= self.sigint %>_requested = true; }); <% end -%> do { if (operation) { operation(*_data); } (*this)(_state.first, _state.second); _state.first = _state.second; _state.second = (*this)(_state.second); if (_timing_func) { _timing_func(); } } while (_state.second != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>); // Call the exit state once more: (*this)(<%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>); <% if sigint then -%> std::signal(SIGINT, SIG_DFL); <% end -%> } // Run the FSM from the initial state void run(operation_fun operation = nullptr) { run(<%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>, operation); } // install state and transition functions void install_functions() { // State functions <% dest = destinations.dup -%> <% @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 -%> add_state(<%= ns %>::<%= @prefix.upcase %>STATE_<%= s[:id].upcase %>, [](DATA_T &data) -> <%= ns %>::<%= @prefix %>state_t { <% if log == :syslog then -%> syslog(LOG_INFO, "[FSM] In state <%= s[:id].upcase %>"); <% end -%> <%= ns %>::<%= @prefix %>state_t next_state = <%= @prefix%>do_<%= s[:id] %>(data); switch (next_state) { case <%= ns %>::<%= @prefix.upcase %>UNIMPLEMENTED: throw std::runtime_error("State function not fully implemented: "s + "<%= s[:id].upcase %>"); break; <% dest[s[:id]].each do |str| -%> case <%= ns %>::<%= 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]); <% end -%> next_state = <%= ns %>::<%= @prefix.upcase %>NO_CHANGE; } <% if sigint && stable && self.topology[:sources][0] != s[:id] then -%> // SIGINT transition override if (<%= self.sigint %>_requested) next_state = <%= (@prefix+"STATE_"+self.sigint ).upcase %>; <% end -%> return next_state; }); <% end -%> <% if transition_functions_list.count > 0 then -%> // Transition functions <% transition_functions_list.each do |t| -%> add_transition(<%= @prefix.upcase %>STATE_<%= transitions_paths[t][0][:from].upcase %>, <%= @prefix.upcase %>STATE_<%= transitions_paths[t][0][:to].upcase %>, [](DATA_T &data) { <% if log == :syslog then -%> syslog(LOG_INFO, "[FSM] State transition <%= t %>"); <% end -%> <%= t %>(data); }); <% end -%> <% end -%> } }; // class FiniteStateMachine }; // namespace <%= @project_name || "FSM" %> #endif // <%= File::basename(@cname).upcase %>_HPP EOHPP
- CPP =
<<~EOCPP <% if @syslog then -%> <% log = :syslog -%> #include <syslog.h> <% end -%> #include "<%= File::basename(@cname) %>.hpp" using namespace std; <% ns = @project_name || "FSM" -%> <% placeholder = "Your Code Here" %> // SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS! namespace <%= @project_name || "FSM" %> { /* ____ _ _ * / ___|| |_ __ _| |_ ___ * \\___ \\| __/ _` | __/ _ \\ * ___) | || (_| | || __/ * |____/ \\__\\__,_|\\__\\___| * * __ _ _ * / _|_ _ _ __ ___| |_(_) ___ _ __ ___ * | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __| * | _| |_| | | | | (__| |_| | (_) | | | \\__ \\ * |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/ */ <% 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 STATE_<%= s[:id].upcase %> // valid return states: <%= dest[s[:id]].join(", ") %> <% if sigint && stable && topo[:sources][0] != s[:id] then -%> // SIGINT triggers an emergency transition to STATE_<%= self.sigint.upcase %> <% end -%> template<class T> <%= @prefix %>state_t <%= s[:function] %>(T &data) { <%= @prefix %>state_t next_state = <%= ns %>::<%= @prefix.upcase %>UNIMPLEMENTED; /* <%= placeholder %> */ 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 -%> template<class T> void <%= t %>(T &data) { /* <%= placeholder %> */ } <% end -%> <% end -%> }; // namespace <%= @project_name || "FSM" %> <% nsinks = topology[:sinks].count -%> // Example usage: #ifdef TEST_MAIN #include <unistd.h> #include <thread> struct Data { int count; }; int main() { Data data = {1}; auto fsm = <%= ns %>::FiniteStateMachine(&data); fsm.set_timing_function([]() { std::this_thread::sleep_for(std::chrono::seconds(1)); }); fsm.run([&](Data &s) { std::cout << "State: " << fsm.state() << " data: " << s.count << std::endl; }); return 0; } #endif // TEST_MAIN EOCPP