Module: GV_FSM::Templates
- Included in:
- FSM
- Defined in:
- lib/templates.rb
Constant Summary collapse
- HEADER =
_ _ _ _ __ _ __
| | | | ____| / \ | _ \| ____| _ \ | |_| | _| / _ \ | | | | _| | |_) | | _ | |___ / ___ \| |_| | |___| _ < |_| |_|_____/_/ \_\____/|_____|_| \_\ "/******************************************************************************\nFinite State Machine\nProject: <%= @project_name or @dotfile %>\nDescription: <%= @description or \"<none given>\" %>\n \nGenerated by gv_fsm ruby gem, see https://rubygems.org/gems/gv_fsm\ngv_fsm version <%= GV_FSM::VERSION %>\nGeneration date: <%= Time.now %>\nGenerated from: <%= @dotfile %>\nThe finite state machine has:\n <%= @states.count %> states\n <%= transition_functions_list.select {|e| e != 'NULL'}.count %> transition functions\n<% if @prefix != '' %>\nFunctions and types have been generated with prefix \"<%= @prefix %>\"\n<% end %>\n******************************************************************************/\n\n"- HH =
"<% if !@ino then %>\n#ifndef <%= File::basename(@cname).upcase %>_H\n#define <%= File::basename(@cname).upcase %>_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#include <stdlib.h>\n<% else -%>\n#include <arduino.h>\n<% end -%>\n\n// State data object\n// By default set to void; override this typedef or load the proper\n// header if you need\ntypedef void <%= @prefix %>state_data_t;\n<% if !@ino then -%>\n\n// NOTHING SHALL BE CHANGED AFTER THIS LINE!\n<% end -%>\n\n// List of states\ntypedef enum {\n<% @states.each_with_index do |s, i| -%>\n <%= @prefix.upcase %>STATE_<%= s[:id].upcase %><%= i == 0 ? \" = 0\" : \"\" %>, \n<% end -%>\n <%= @prefix.upcase %>NUM_STATES,\n <%= @prefix.upcase %>NO_CHANGE\n} <%= @prefix %>state_t;\n\n// State human-readable names\nextern const char *<%= @prefix %>state_names[];\n\n<% if transition_functions_list.count > 0 then -%>\n// State function and state transition prototypes\ntypedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);\ntypedef void transition_func_t(<%= @prefix %>state_data_t *data);\n<% else -%>\n// State function prototype\ntypedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);\n<%end -%>\n\n// State functions\n<% dest = destinations.dup -%>\n<% @states.each do |s| -%>\n<% stable = true if dest[s[:id]].include? s[:id] -%>\n<% dest[s[:id]].map! {|n| (@prefix+\"STATE_\"+n).upcase} -%>\n<% if dest[s[:id]].empty? or stable then\n dest[s[:id]].unshift @prefix.upcase+\"NO_CHANGE\"\nend -%>\n// Function to be executed in state <%= s[:id] %>\n// valid return states: <%= dest[s[:id]].join(\", \") %>\n<%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data);\n<% end -%>\n\n\n// List of state functions\nextern state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES];\n\n\n<% if transition_functions_list.count > 0 then -%>\n// Transition functions\n<% transition_functions_list.each do |t| -%>\n<% next if t == \"NULL\" -%>\nvoid <%= t %>(<%= @prefix %>state_data_t *data);\n<% end -%>\n\n// Table of transition functions\nextern transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES];\n<% else -%>\n// No transition functions\n<% end -%>\n\n// state manager\n<%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data);\n\n<% if !@ino then -%>\n#ifdef __cplusplus\n}\n#endif\n#endif // <%= File::basename(@cname).upcase %>_H\n<% end -%>\n"- CC =
"<% if !@ino then -%>\n<% if @syslog then -%>\n<% log = :syslog %>\n#include <syslog.h>\n<% end -%>\n<% else -%>\n<% if @syslog then log = :ino end -%>\n<% end -%>\n#include \"<%= File::basename(@cname) %>.h\"\n\n<% if sigint then %>// Install signal handler: \n// SIGINT requests a transition to state <%= self.sigint %>\n#include <signal.h>\nstatic int _exit_request = 0;\nstatic void signal_handler(int signal) {\n if (signal == SIGINT) {\n_exit_request = 1;<% if log == :syslog then %>\nsyslog(LOG_WARNING, \"[FSM] SIGINT transition to <%= sigint %>\");<% elsif log == :ino then %>\nSerial.println(\"[FSM] SIGINT transition to <%= sigint %>\");<% end %>\n }\n}\n\n<% end -%>\n<% placeholder = \"Your Code Here\" -%>\n// SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!\n\n// GLOBALS\n// State human-readable names\nconst char *<%= @prefix %>state_names[] = {<%= states_list.map {|sn| '\"'+sn+'\"'}.join(\", \") %>};\n\n// List of state functions\n<% fw = state_functions_list.max {|a, b| a.length <=> b.length}.length -%>\nstate_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES] = {\n<% @states.each do |s| -%>\n <%= (s[:function] + ',').ljust(fw+1) %> // in state <%= s[:id] %>\n<% end -%>\n};\n<% if transition_functions_list.count > 0 then -%>\n\n// Table of transition functions\ntransition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES] = {\n<% sl = states_list -%>\n<% fw = transition_functions_list.max {|a, b| a.length <=> b.length}.length -%>\n<% sw = [states_list, \"states:\"].flatten.max {|a, b| a.length <=> b.length}.length -%>\n /* <%= \"states:\".ljust(sw) %> <%= sl.map {|e| e.ljust(fw) }.join(\", \") %> */\n<% transitions_map.each_with_index do |l, i| -%>\n /* <%= sl[i].ljust(sw) %> */ {<%= l.map {|e| e.ljust(fw)}.join(\", \") %>}, \n<% end -%>\n};\n<% else -%>\n// No transition functions\n<% end -%>\n\n/* ____ _ _ \n * / ___|| |_ __ _| |_ ___ \n * \\\\___ \\\\| __/ _` | __/ _ \\\\\n * ___) | || (_| | || __/\n * |____/ \\\\__\\\\__,_|\\\\__\\\\___|\n * \n * __ _ _ \n * / _|_ _ _ __ ___| |_(_) ___ _ __ ___ \n * | |_| | | | '_ \\\\ / __| __| |/ _ \\\\| '_ \\\\/ __|\n * | _| |_| | | | | (__| |_| | (_) | | | \\\\__ \\\\\n * |_| \\\\__,_|_| |_|\\\\___|\\\\__|_|\\\\___/|_| |_|___/\n */ \n<% dest = destinations.dup -%>\n<% topo = self.topology -%>\n<% @states.each do |s| -%>\n<% stable = true if dest[s[:id]].include? s[:id] -%>\n<% dest[s[:id]].map! {|n| (@prefix+\"STATE_\"+n).upcase} -%>\n<% if dest[s[:id]].empty? or stable then\n dest[s[:id]].unshift @prefix.upcase+\"NO_CHANGE\"\nend %>\n// Function to be executed in state <%= s[:id] %>\n// valid return states: <%= dest[s[:id]].join(\", \") %>\n<% if sigint && stable && topo[:sources][0] != s[:id] then -%>\n// SIGINT triggers an emergency transition to <%= self.sigint %>\n<% end -%>\n<%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data) {\n <%= @prefix %>state_t next_state = <%= dest[s[:id]].first -%>;\n<% if sigint && topo[:sources][0] == s[:id] then -%>\n signal(SIGINT, signal_handler); \n <% end -%>\n<% if log == :syslog then -%>\n syslog(LOG_INFO, \"[FSM] In state <%= s[:id] %>\");\n<% elsif log == :ino then -%>\n Serial.println(\"[FSM] In state <%= s[:id] %>\");\n<% end -%>\n /* <%= placeholder %> */\n \n switch (next_state) {\n<% dest[s[:id]].each do |str| -%>\n case <%= str %>:\n<% end -%>\nbreak;\n default:\n<% if log == :syslog then -%>\nsyslog(LOG_WARNING, \"[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state\", <%= @prefix %>state_names[next_state]);\n<% elsif log == :ino then -%>\nSerial.print(\"[FSM] Cannot pass from <%= s[:id] %> to \");\nSerial.print(<%= @prefix %>state_names[next_state]);\nSerial.println(\", remaining in this state\");\n<% end -%>\nnext_state = <%= @prefix.upcase %>NO_CHANGE;\n }\n<% if sigint && stable && topo[:sources][0] != s[:id] then -%>\n // SIGINT transition override\n if (_exit_request) \nnext_state = <%= (@prefix+\"STATE_\"+self.sigint ).upcase %>;\n<% end %>\n return next_state;\n}\n\n<% end %>\n\n<% if transition_functions_list.count > 0 then -%>\n/* _____ _ _ _ \n * |_ _| __ __ _ _ __ ___(_) |_(_) ___ _ __ \n * | || '__/ _` | '_ \\\\/ __| | __| |/ _ \\\\| '_ \\\\\n * | || | | (_| | | | \\\\__ \\\\ | |_| | (_) | | | | \n * |_||_| \\\\__,_|_| |_|___/_|\\\\__|_|\\\\___/|_| |_| \n * \n * __ _ _ \n * / _|_ _ _ __ ___| |_(_) ___ _ __ ___ \n * | |_| | | | '_ \\\\ / __| __| |/ _ \\\\| '_ \\\\/ __|\n * | _| |_| | | | | (__| |_| | (_) | | | \\\\__ \\\\\n * |_| \\\\__,_|_| |_|\\\\___|\\\\__|_|\\\\___/|_| |_|___/\n */ \n \n<% transition_functions_list.each do |t| -%>\n<% next if t == \"NULL\" -%>\n<% tpaths = transitions_paths[t] -%>\n// This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:\n<% tpaths.each_with_index do |e, i| -%>\n// <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>\n<% end -%>\nvoid <%= t %>(<%= @prefix %>state_data_t *data) {\n<% if log == :syslog then -%>\n syslog(LOG_INFO, \"[FSM] State transition <%= t %>\");\n<% elsif log == :ino then -%>\n Serial.println(\"[FSM] State transition <%= t %>\");\n<% end -%>\n /* <%= placeholder %> */\n}\n\n<% end -%>\n<% end -%>\n\n/* ____ _ _ \n * / ___|| |_ __ _| |_ ___ \n * \\\\___ \\\\| __/ _` | __/ _ \\\\\n * ___) | || (_| | || __/ \n * |____/ \\\\__\\\\__,_|\\\\__\\\\___| \n * \n * \n * _ __ ___ __ _ _ __ __ _ __ _ ___ _ __ \n * | '_ ` _ \\\\ / _` | '_ \\\\ / _` |/ _` |/ _ \\\\ '__|\n * | | | | | | (_| | | | | (_| | (_| | __/ | \n * |_| |_| |_|\\\\__,_|_| |_|\\\\__,_|\\\\__, |\\\\___|_| \n * |___/ \n */\n\n<%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data) {\n <%= @prefix %>state_t new_state = <%= @prefix %>state_table[cur_state](data);\n if (new_state == <%= @prefix.upcase %>NO_CHANGE) new_state = cur_state;\n<% if transition_functions_list.count > 0 then %>\n transition_func_t *transition = <%= @prefix %>transition_table[cur_state][new_state];\n if (transition)\ntransition(data);\n<% end %>\n return new_state;\n};\n\n<% if @ino then %>\n/* Example usage:\n<%= @prefix %>state_data_t data = {count: 1};\n\nvoid loop() {\n static <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_INIT;\n cur_state = <%= @prefix %>run_state(cur_state, &data);\n}\n*/\n<% else %>\n<% nsinks = topology[:sinks].count %>\n#ifdef TEST_MAIN\n#include <unistd.h>\nint main() {\n <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_<%= @states.first[:id].upcase %>;\n<% if @syslog then %>\n openlog(\"SM\", LOG_PID | LOG_PERROR, LOG_USER);\n syslog(LOG_INFO, \"Starting SM\");\n<% end %>\n do {\ncur_state = <%= @prefix %>run_state(cur_state, NULL);\nsleep(1);\n<% if nsinks == 1 %>\n } while (cur_state != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);\n <%= @prefix %>run_state(cur_state, NULL);\n<% else %>\n } while (1);\n<% end %>\n return 0;\n}\n#endif\n<% end %>\n"- HPP =
"#ifndef <%= File::basename(@cname).upcase %>_HPP\n#define <%= File::basename(@cname).upcase %>_HPP\n#include <functional>\n#include <iostream>\n#include <map>\n#include <string>\n#include <tuple>\n<% if @syslog then -%>\n<% log = :syslog -%>\n#include <syslog.h>\n<% end -%>\n<% if sigint then -%>\n// Install signal handler: \n// SIGINT requests a transition to state <%= self.sigint %>\n#include <csignal>\n<% end %>\n\nusing namespace std::string_literals;\n<% ns = @project_name || \"FSM\" -%>\nnamespace <%= ns %> {\nstatic bool <%= self.sigint %>_requested = false;\n\n// List of states\ntypedef enum {\n<% @states.each do |s| -%>\n <%= @prefix.upcase %>STATE_<%= s[:id].upcase %>,\n<% end -%>\n <%= @prefix.upcase %>NUM_STATES,\n <%= @prefix.upcase %>NO_CHANGE,\n <%= @prefix.upcase %>UNIMPLEMENTED\n} <%= @prefix %>state_t;\n\n// State human-readable names\nstd::map<<%= @prefix %>state_t, char const *> state_names = {\n<% @states.each do |s| -%>\n {<%= @prefix.upcase %>STATE_<%= s[:id].upcase %>, \"<%= s[:id].upcase %>\"},\n<% end -%>\n {<%= @prefix.upcase %>NUM_STATES, \"NUM_STATES\"},\n {<%= @prefix.upcase %>NO_CHANGE, \"NO_CHANGE\"},\n {<%= @prefix.upcase %>UNIMPLEMENTED, \"UNIMPLEMENTED\"}\n};\n\n// Custom state functions:\n<% @states.each do |s| -%>\ntemplate<class T> \n<%= @prefix %>state_t <%= s[:function] %>(T &data);\n<% end -%>\n\n<% if transition_functions_list.count > 0 then -%>\n// Custom transition functions:\n<% transition_functions_list.each do |t| -%>\ntemplate<class T>\nvoid <%= t %>(T &data);\n<% end -%>\n<% end -%>\n\n// Finite State Machine class\ntemplate <typename DATA_T> \nclass FiniteStateMachine {\n\n// Function templates\nusing state_fun = std::function<<%= @prefix %>state_t(DATA_T &data)>;\nusing transition_fun = std::function<void(DATA_T &data)>;\nusing operation_fun = std::function<void(DATA_T &data)>;\n\nprivate:\n std::pair<<%= @prefix %>state_t, <%= @prefix %>state_t> _state{<%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>, <%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>};\n std::map<<%= @prefix %>state_t, state_fun> _states;\n std::map<<%= @prefix %>state_t, std::map<<%= @prefix %>state_t, transition_fun>> _transitions;\n std::function<void()> _timing_func;\n DATA_T *_data;\n\npublic:\n\n FiniteStateMachine(DATA_T *data) : _data(data) {\n install_functions();\n }\n ~FiniteStateMachine(){};\n\n void set_timing_function(std::function<void()> timing_func) {\n _timing_func = timing_func;\n }\n\n void add_state(<%= @prefix %>state_t name, state_fun func) { _states[name] = func; }\n\n void add_transition(<%= @prefix %>state_t from, <%= @prefix %>state_t to, transition_fun func) {\n _transitions[from][to] = func;\n }\n\n inline <%= @prefix %>state_t state() { return _state.second; }\n inline std::string state_name() { return std::string(state_names[_state.second]); }\n inline <%= @prefix %>state_t prev_state() { return _state.first; }\n\n <%= @prefix %>state_t operator()(<%= @prefix %>state_t state) {\n if (_states.find(state) == _states.end()) {\n throw std::runtime_error(\"State not found: \"s + state_names[state]);\n }\n state_t next = _states[state](*_data);\n if (next == NO_CHANGE) {\n next = state;\n }\n return next;\n }\n\n void operator()(<%= @prefix %>state_t from, <%= @prefix %>state_t to) {\n if (_transitions.find(from) != _transitions.end()) {\n if (_transitions[from].find(to) != _transitions[from].end()) {\n _transitions[from][to](*_data);\n }\n }\n }\n\n\n // Setup initial state links\n void setup(state_t state) {\n <%= ns %>::<%= self.sigint %>_requested = false;\n _state.first = state;\n _state.second = state;\n }\n\n // Evaluate the current state and update the next state\n // to be used when main loop is customized (i.e., not using FSM::run())\n state_t eval_state() {\n (*this)(_state.first, _state.second);\n _state.first = _state.second;\n _state.second = (*this)(_state.second);\n return _state.second;\n }\n\n // Run the FSM from a given state\n void run(<%= @prefix %>state_t state, operation_fun operation = nullptr) {\n setup(state);\n<% if sigint then -%>\n std::signal(SIGINT, [](int signum) {\n<% if log == :syslog then -%>\n syslog(LOG_WARNING, \"[FSM] SIGINT transition to <%= sigint %>\");\n<% end -%>\n <%= ns %>::<%= self.sigint %>_requested = true; \n });\n<% end -%>\n do {\n if (operation) {\n operation(*_data);\n }\n eval_state();\n if (_timing_func) {\n _timing_func();\n }\n } while (_state.second != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);\n // Call the exit state once more:\n (*this)(<%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);\n<% if sigint then -%>\n std::signal(SIGINT, SIG_DFL);\n<% end -%>\n }\n\n // Run the FSM from the initial state\n void run(operation_fun operation = nullptr) { run(<%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>, operation); }\n\n // install state and transition functions\n void install_functions() {\n\n // State functions\n<% dest = destinations.dup -%>\n<% @states.each do |s| -%>\n<% stable = true if dest[s[:id]].include? s[:id] -%>\n<% dest[s[:id]].map! {|n| (@prefix+\"STATE_\"+n).upcase} -%>\n<% if dest[s[:id]].empty? or stable then\n dest[s[:id]].unshift @prefix.upcase+\"NO_CHANGE\"\nend -%>\n add_state(<%= ns %>::<%= @prefix.upcase %>STATE_<%= s[:id].upcase %>, [](DATA_T &data) -> <%= ns %>::<%= @prefix %>state_t {\n<% if log == :syslog then -%>\n syslog(LOG_INFO, \"[FSM] In state <%= s[:id].upcase %>\");\n<% end -%>\n <%= ns %>::<%= @prefix %>state_t next_state = <%= @prefix%>do_<%= s[:id] %>(data);\n \n switch (next_state) {\n case <%= ns %>::<%= @prefix.upcase %>UNIMPLEMENTED:\n throw std::runtime_error(\"State function not fully implemented: \"s + \"<%= s[:id].upcase %>\");\n break;\n<% dest[s[:id]].each do |str| -%>\n case <%= ns %>::<%= str %>:\n<% end -%>\n break;\n default:\n<% if log == :syslog then -%>\n syslog(LOG_WARNING, \"[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state\", state_names[next_state]);\n<% end -%>\n next_state = <%= ns %>::<%= @prefix.upcase %>NO_CHANGE;\n }\n<% if sigint && stable && self.topology[:sources][0] != s[:id] then -%>\n // SIGINT transition override\n if (<%= self.sigint %>_requested) next_state = <%= (@prefix+\"STATE_\"+self.sigint ).upcase %>;\n<% end -%>\n return next_state;\n });\n\n<% end -%>\n\n<% if transition_functions_list.count > 0 then -%>\n // Transition functions\n<% transition_functions_list.each do |t| -%>\n add_transition(<%= @prefix.upcase %>STATE_<%= transitions_paths[t][0][:from].upcase %>, <%= @prefix.upcase %>STATE_<%= transitions_paths[t][0][:to].upcase %>, [](DATA_T &data) {\n<% if log == :syslog then -%>\n syslog(LOG_INFO, \"[FSM] State transition <%= t %>\");\n<% end -%>\n <%= t %>(data);\n });\n\n<% end -%>\n<% end -%>\n }\n\n}; // class FiniteStateMachine\n\n}; // namespace <%= @project_name || \"FSM\" %>\n\n#endif // <%= File::basename(@cname).upcase %>_HPP\n\n"- CPP =
"<% if @syslog then -%>\n<% log = :syslog -%>\n#include <syslog.h>\n<% end -%>\n#include \"<%= File::basename(@cname) %>.hpp\"\n \nusing namespace std;\n<% ns = @project_name || \"FSM\" -%>\n \n<% placeholder = \"Your Code Here\" %>\n// SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!\n\n\nnamespace <%= @project_name || \"FSM\" %> {\n\n/* ____ _ _ \n * / ___|| |_ __ _| |_ ___ \n * \\\\___ \\\\| __/ _` | __/ _ \\\\\n * ___) | || (_| | || __/\n * |____/ \\\\__\\\\__,_|\\\\__\\\\___|\n * \n * __ _ _ \n * / _|_ _ _ __ ___| |_(_) ___ _ __ ___ \n * | |_| | | | '_ \\\\ / __| __| |/ _ \\\\| '_ \\\\/ __|\n * | _| |_| | | | | (__| |_| | (_) | | | \\\\__ \\\\\n * |_| \\\\__,_|_| |_|\\\\___|\\\\__|_|\\\\___/|_| |_|___/\n */ \n<% dest = destinations.dup -%>\n<% topo = self.topology -%>\n<% @states.each do |s| -%>\n<% stable = true if dest[s[:id]].include? s[:id] -%>\n<% dest[s[:id]].map! {|n| (@prefix+\"STATE_\"+n).upcase} -%>\n<% if dest[s[:id]].empty? or stable then\n dest[s[:id]].unshift @prefix.upcase+\"NO_CHANGE\"\nend %>\n// Function to be executed in state STATE_<%= s[:id].upcase %>\n// valid return states: <%= dest[s[:id]].join(\", \") %>\n<% if sigint && stable && topo[:sources][0] != s[:id] then -%>\n// SIGINT triggers an emergency transition to STATE_<%= self.sigint.upcase %>\n<% end -%>\ntemplate<class T> \n<%= @prefix %>state_t <%= s[:function] %>(T &data) {\n <%= @prefix %>state_t next_state = <%= ns %>::<%= @prefix.upcase %>UNIMPLEMENTED;\n /* <%= placeholder %> */\n \n return next_state;\n}\n<% end -%>\n\n\n<% if transition_functions_list.count > 0 then -%>\n/* _____ _ _ _ \n * |_ _| __ __ _ _ __ ___(_) |_(_) ___ _ __ \n * | || '__/ _` | '_ \\\\/ __| | __| |/ _ \\\\| '_ \\\\\n * | || | | (_| | | | \\\\__ \\\\ | |_| | (_) | | | | \n * |_||_| \\\\__,_|_| |_|___/_|\\\\__|_|\\\\___/|_| |_| \n * \n * __ _ _ \n * / _|_ _ _ __ ___| |_(_) ___ _ __ ___ \n * | |_| | | | '_ \\\\ / __| __| |/ _ \\\\| '_ \\\\/ __|\n * | _| |_| | | | | (__| |_| | (_) | | | \\\\__ \\\\\n * |_| \\\\__,_|_| |_|\\\\___|\\\\__|_|\\\\___/|_| |_|___/\n */ \n\n<% transition_functions_list.each do |t| -%>\n<% next if t == \"NULL\" -%>\n<% tpaths = transitions_paths[t] -%>\n// This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:\n<% tpaths.each_with_index do |e, i| -%>\n// <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>\n<% end -%>\ntemplate<class T>\nvoid <%= t %>(T &data) {\n /* <%= placeholder %> */\n}\n\n<% end -%>\n<% end -%>\n\n}; // namespace <%= @project_name || \"FSM\" %>\n\n\n<% nsinks = topology[:sinks].count -%>\n// Example usage:\n#ifdef TEST_MAIN\n#include <unistd.h>\n#include <thread>\n\nstruct Data {\n int count;\n};\n\nint main() {\n Data data = {1};\n auto fsm = <%= ns %>::FiniteStateMachine(&data);\n fsm.set_timing_function([]() {\n std::this_thread::sleep_for(std::chrono::seconds(1));\n });\n fsm.run([&](Data &s) {\n std::cout << \"State: \" << fsm.state() << \" data: \" << s.count << std::endl;\n });\n return 0;\n}\n#endif // TEST_MAIN\n"