gv_fsm 0.3.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86b23438e750da00df1eeb91db559d2176303a4a9c543fdb6acf471d5d112571
4
- data.tar.gz: cb3c1ef9e8d2fe0fdf84740539425a9aa8aca3d0358de6ae2b5a69c209d4dfd7
3
+ metadata.gz: f58c122355c77248d9b7ef5fa2ea612c7d9c0e38057a84660155ec4a686547dd
4
+ data.tar.gz: dfbf56baf72ae57f70f8016d27c16770e779c093b1f8be164e50d9ac5fa914d5
5
5
  SHA512:
6
- metadata.gz: 946b67acdc3dd2e435bdcda6588c2df81f4548d89003db424634b5d27e45e7a7b2698cd7583e5a51e5c1814dd5cdb5042fd719bd27d37c831dde30354935172f
7
- data.tar.gz: 83b6474365979c04ea29346acc518746ee1d760eb4b144da251e2f3816956e488d68ebf4d52c7e4d234f3dfe92a14ad746f7b37d67c743078e06a68deb885177
6
+ metadata.gz: 0c93b183ab778b42782f2b635a7ce07e96188f798b4e8d04608f604a697b806879f8555f49858bf0a2212821ec7493a45c4ce71492466549e09d98487a58a50f
7
+ data.tar.gz: dd27080c97ae8da158b75fa03949e060bd21e04d11f9ca20f8d6384ae436c089eddd7153e78e31c44ab0edcf5207867819fd4babf37531e7c78bc3f93dbe7b59
data/bin/gv_fsm CHANGED
@@ -16,14 +16,18 @@ op = OptionParser.new do |parser|
16
16
  EOB
17
17
 
18
18
  parser.on("-p", "--project PROJECT_NAME",
19
- "Set the project name to PROJECT_NAME") do |pn|
20
- sm.project_name = pn
19
+ "Set the project name to PROJECT_NAME (in C++ also namespace)") do |pn|
20
+ sm.project_name = pn.gsub(" ", "_")
21
21
  end
22
22
 
23
23
  parser.on("-d", "--description DESCRIPTION", "Use DESCRITION string in header") do |desc|
24
24
  sm.description = desc
25
25
  end
26
26
 
27
+ parser.on("--cpp", "Generate C++17 sources") do
28
+ sm.plain_c = false
29
+ end
30
+
27
31
  parser.on("-o", "--output_file NAME", "Use NAME for generated .c and .h files") do |f|
28
32
  sm.cname = f
29
33
  end
@@ -61,6 +65,11 @@ end
61
65
 
62
66
  op.parse!
63
67
 
68
+ if sm.ino then
69
+ sm.plain_c = true
70
+ sm.syslog = false
71
+ end
72
+
64
73
  unless ARGV[0]
65
74
  STDERR.puts "ERROR: I need the path to a Graphviz file!\n\n"
66
75
  STDERR.puts op
@@ -93,7 +102,7 @@ if !(top[:sources].count == 1 and top[:sinks].count <= 1) then
93
102
  exit 4
94
103
  end
95
104
 
96
- puts "Generating C functions for states: #{sm.states_list.join(", ")}."
105
+ puts "Generating #{sm.plain_c ? "C" : "C++17"} functions for states: #{sm.states_list.join(", ")}."
97
106
  puts " for transition: #{sm.transition_functions_list.join(", ")}."
98
107
 
99
108
  if (sm.sigint) then
@@ -104,7 +113,7 @@ if (sm.sigint) then
104
113
  end
105
114
 
106
115
  if options[:header] then
107
- name = sm.generate_h
116
+ name = sm.generate_h
108
117
  puts "Generated header #{name}"
109
118
  end
110
119
 
data/lib/gv_fsm.rb CHANGED
@@ -11,6 +11,7 @@ module GV_FSM
11
11
  class FSM
12
12
  attr_reader :states, :transitions, :dotfile, :prefix, :error
13
13
  attr_accessor :project_name, :description, :cname, :syslog, :ino, :sigint
14
+ attr_accessor :plain_c
14
15
  include GV_FSM::Templates
15
16
 
16
17
  def initialize(filename = nil)
@@ -21,6 +22,7 @@ module GV_FSM
21
22
  @matrix = nil
22
23
  @nodemap = {}
23
24
  @sigint = nil
25
+ @plain_c = true
24
26
  parse(filename) if filename
25
27
  end
26
28
 
@@ -141,20 +143,21 @@ module GV_FSM
141
143
  end
142
144
 
143
145
  def generate_c(filename = @cname)
144
- ext = @ino ? "cpp" : "c"
146
+ ext = (@ino || !@plain_c) ? "cpp" : "c"
145
147
  fname = "#{filename}.#{ext}"
146
148
  File.open(fname, "w") do |f|
147
- f.puts ERB.new(HEADER, trim_mode: "<>").result(binding)
148
- f.puts ERB.new(CC, trim_mode: "<>").result(binding)
149
+ f.puts ERB.new(HEADER, trim_mode: "<>-").result(binding)
150
+ f.puts ERB.new(@plain_c ? CC : CPP, trim_mode: "<>-").result(binding)
149
151
  end
150
152
  return fname
151
153
  end
152
154
 
153
155
  def generate_h(filename = @cname)
154
- fname = "#{filename}.h"
156
+ ext = @plain_c ? "h" : "hpp"
157
+ fname = "#{filename}.#{ext}"
155
158
  File.open(fname, "w") do |f|
156
- f.puts ERB.new(HEADER, trim_mode: "<>").result(binding)
157
- f.puts ERB.new(HH, trim_mode: "<>").result(binding)
159
+ f.puts ERB.new(HEADER, trim_mode: "<>-").result(binding)
160
+ f.puts ERB.new(@plain_c ? HH : HPP, trim_mode: "<>-").result(binding)
158
161
  end
159
162
  return fname
160
163
  end
data/lib/templates.rb CHANGED
@@ -1,6 +1,13 @@
1
1
 
2
2
  module GV_FSM
3
3
  module Templates
4
+
5
+ # _ _ _____ _ ____ _____ ____
6
+ # | | | | ____| / \ | _ \| ____| _ \
7
+ # | |_| | _| / _ \ | | | | _| | |_) |
8
+ # | _ | |___ / ___ \| |_| | |___| _ <
9
+ # |_| |_|_____/_/ \_\____/|_____|_| \_\
10
+
4
11
  HEADER =<<~EOHEADER
5
12
  /******************************************************************************
6
13
  Finite State Machine
@@ -21,294 +28,643 @@ module GV_FSM
21
28
 
22
29
  EOHEADER
23
30
 
31
+ # ____ _ _ _____ _ ____ _____ ____
32
+ # / ___| | | | | ____| / \ | _ \| ____| _ \
33
+ # | | | |_| | _| / _ \ | | | | _| | |_) |
34
+ # | |___ | _ | |___ / ___ \| |_| | |___| _ <
35
+ # \____| |_| |_|_____/_/ \_\____/|_____|_| \_\
36
+
37
+
38
+
24
39
  HH =<<~EOH
25
- <% if !@ino then %>
26
- #ifndef <%= File::basename(@cname).upcase %>_H
27
- #define <%= File::basename(@cname).upcase %>_H
28
- #ifdef __cplusplus
29
- extern "C" {
30
- #endif
31
- #include <stdlib.h>
32
- <% else %>
33
- #include <arduino.h>
34
- <% end %>
40
+ <% if !@ino then %>
41
+ #ifndef <%= File::basename(@cname).upcase %>_H
42
+ #define <%= File::basename(@cname).upcase %>_H
43
+ #ifdef __cplusplus
44
+ extern "C" {
45
+ #endif
46
+ #include <stdlib.h>
47
+ <% else -%>
48
+ #include <arduino.h>
49
+ <% end -%>
35
50
 
36
- // State data object
37
- // By default set to void; override this typedef or load the proper
38
- // header if you need
39
- typedef void <%= @prefix %>state_data_t;
40
- <% if !@ino then %>
41
-
42
- // NOTHING SHALL BE CHANGED AFTER THIS LINE!
43
- <% end %>
51
+ // State data object
52
+ // By default set to void; override this typedef or load the proper
53
+ // header if you need
54
+ typedef void <%= @prefix %>state_data_t;
55
+ <% if !@ino then -%>
44
56
 
45
- // List of states
46
- typedef enum {
47
- <% @states.each_with_index do |s, i| %>
48
- <%= @prefix.upcase %>STATE_<%= s[:id].upcase %><%= i == 0 ? " = 0" : "" %>,
49
- <% end %>
50
- <%= @prefix.upcase %>NUM_STATES,
51
- <%= @prefix.upcase %>NO_CHANGE
52
- } <%= @prefix %>state_t;
57
+ // NOTHING SHALL BE CHANGED AFTER THIS LINE!
58
+ <% end -%>
53
59
 
54
- // State human-readable names
55
- extern const char *<%= @prefix %>state_names[];
56
-
57
- <% if transition_functions_list.count > 0 then %>
58
- // State function and state transition prototypes
59
- typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);
60
- typedef void transition_func_t(<%= @prefix %>state_data_t *data);
61
- <% else %>
62
- // State function prototype
63
- typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);
64
- <%end %>
65
-
66
- // State functions
67
- <% dest = destinations.dup %>
68
- <% @states.each do |s| %>
69
- <% stable = true if dest[s[:id]].include? s[:id] %>
70
- <% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} %>
71
- <% if dest[s[:id]].empty? or stable then
72
- dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
73
- end %>
74
- // Function to be executed in state <%= s[:id] %>
75
- // valid return states: <%= dest[s[:id]].join(", ") %>
76
- <%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data);
77
- <% end %>
60
+ // List of states
61
+ typedef enum {
62
+ <% @states.each_with_index do |s, i| -%>
63
+ <%= @prefix.upcase %>STATE_<%= s[:id].upcase %><%= i == 0 ? " = 0" : "" %>,
64
+ <% end -%>
65
+ <%= @prefix.upcase %>NUM_STATES,
66
+ <%= @prefix.upcase %>NO_CHANGE
67
+ } <%= @prefix %>state_t;
78
68
 
69
+ // State human-readable names
70
+ extern const char *<%= @prefix %>state_names[];
79
71
 
80
- // List of state functions
81
- extern state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES];
82
-
72
+ <% if transition_functions_list.count > 0 then -%>
73
+ // State function and state transition prototypes
74
+ typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);
75
+ typedef void transition_func_t(<%= @prefix %>state_data_t *data);
76
+ <% else -%>
77
+ // State function prototype
78
+ typedef <%= @prefix %>state_t state_func_t(<%= @prefix %>state_data_t *data);
79
+ <%end -%>
83
80
 
84
- <% if transition_functions_list.count > 0 then %>
85
- // Transition functions
86
- <% transition_functions_list.each do |t| %>
87
- <% next if t == "NULL" %>
88
- void <%= t %>(<%= @prefix %>state_data_t *data);
89
- <% end %>
81
+ // State functions
82
+ <% dest = destinations.dup -%>
83
+ <% @states.each do |s| -%>
84
+ <% stable = true if dest[s[:id]].include? s[:id] -%>
85
+ <% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} -%>
86
+ <% if dest[s[:id]].empty? or stable then
87
+ dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
88
+ end -%>
89
+ // Function to be executed in state <%= s[:id] %>
90
+ // valid return states: <%= dest[s[:id]].join(", ") %>
91
+ <%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data);
92
+ <% end -%>
90
93
 
91
- // Table of transition functions
92
- extern transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES];
93
- <% else %>
94
- // No transition functions
95
- <% end %>
96
94
 
97
- // state manager
98
- <%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data);
99
-
100
- <% if !@ino then %>
101
- #ifdef __cplusplus
102
- }
103
- #endif
104
- #endif // <%= File::basename(@cname).upcase %>_H
105
- <% end %>
95
+ // List of state functions
96
+ extern state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES];
97
+
98
+
99
+ <% if transition_functions_list.count > 0 then -%>
100
+ // Transition functions
101
+ <% transition_functions_list.each do |t| -%>
102
+ <% next if t == "NULL" -%>
103
+ void <%= t %>(<%= @prefix %>state_data_t *data);
104
+ <% end -%>
105
+
106
+ // Table of transition functions
107
+ extern transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES];
108
+ <% else -%>
109
+ // No transition functions
110
+ <% end -%>
111
+
112
+ // state manager
113
+ <%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data);
114
+
115
+ <% if !@ino then -%>
116
+ #ifdef __cplusplus
117
+ }
118
+ #endif
119
+ #endif // <%= File::basename(@cname).upcase %>_H
120
+ <% end -%>
106
121
  EOH
107
122
 
123
+
124
+ # ____ ____ ___ _ _ ____ ____ _____
125
+ # / ___| / ___| / _ \| | | | _ \ / ___| ____|
126
+ # | | \___ \| | | | | | | |_) | | | _|
127
+ # | |___ ___) | |_| | |_| | _ <| |___| |___
128
+ # \____| |____/ \___/ \___/|_| \_\\____|_____|
129
+
130
+
108
131
  CC =<<~EOC
109
- <% if !@ino then %>
110
- <% if @syslog then %>
111
- <% log = :syslog %>
112
- #include <syslog.h>
113
- <% end %>
114
- <% else %>
115
- <% if @syslog then log = :ino end %>
116
- <% end %>
117
- #include "<%= File::basename(@cname) %>.h"
118
-
119
- <% if sigint then %>// Install signal handler:
120
- // SIGINT requests a transition to state <%= self.sigint %>
121
- #include <signal.h>
122
- static int _exit_request = 0;
123
- static void signal_handler(int signal) {
124
- if (signal == SIGINT) {
125
- _exit_request = 1;<% if log == :syslog then %>
126
- syslog(LOG_WARNING, "[FSM] SIGINT transition to <%= sigint %>");<% elsif log == :ino then %>
127
- Serial.println("[FSM] SIGINT transition to <%= sigint %>");<% end %>
128
- }
129
- }
132
+ <% if !@ino then -%>
133
+ <% if @syslog then -%>
134
+ <% log = :syslog %>
135
+ #include <syslog.h>
136
+ <% end -%>
137
+ <% else -%>
138
+ <% if @syslog then log = :ino end -%>
139
+ <% end -%>
140
+ #include "<%= File::basename(@cname) %>.h"
130
141
 
131
- <% end %>
132
- <% placeholder = "Your Code Here" %>
133
- // SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!
134
-
135
- // GLOBALS
136
- // State human-readable names
137
- const char *<%= @prefix %>state_names[] = {<%= states_list.map {|sn| '"'+sn+'"'}.join(", ") %>};
138
-
139
- // List of state functions
140
- <% fw = state_functions_list.max {|a, b| a.length <=> b.length}.length %>
141
- state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES] = {
142
- <% @states.each do |s| %>
143
- <%= (s[:function] + ',').ljust(fw+1) %> // in state <%= s[:id] %>
144
- <% end %>
145
- };
146
- <% if transition_functions_list.count > 0 then %>
147
-
148
- // Table of transition functions
149
- transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES] = {
150
- <% sl = states_list %>
151
- <% fw = transition_functions_list.max {|a, b| a.length <=> b.length}.length %>
152
- <% sw = [states_list, "states:"].flatten.max {|a, b| a.length <=> b.length}.length %>
153
- /* <%= "states:".ljust(sw) %> <%= sl.map {|e| e.ljust(fw) }.join(", ") %> */
154
- <% transitions_map.each_with_index do |l, i| %>
155
- /* <%= sl[i].ljust(sw) %> */ {<%= l.map {|e| e.ljust(fw)}.join(", ") %>},
156
- <% end %>
157
- };
158
- <% else %>
159
- // No transition functions
160
- <% end %>
142
+ <% if sigint then %>// Install signal handler:
143
+ // SIGINT requests a transition to state <%= self.sigint %>
144
+ #include <signal.h>
145
+ static int _exit_request = 0;
146
+ static void signal_handler(int signal) {
147
+ if (signal == SIGINT) {
148
+ _exit_request = 1;<% if log == :syslog then %>
149
+ syslog(LOG_WARNING, "[FSM] SIGINT transition to <%= sigint %>");<% elsif log == :ino then %>
150
+ Serial.println("[FSM] SIGINT transition to <%= sigint %>");<% end %>
151
+ }
152
+ }
161
153
 
162
- /* ____ _ _
163
- * / ___|| |_ __ _| |_ ___
164
- * \\___ \\| __/ _` | __/ _ \\
165
- * ___) | || (_| | || __/
166
- * |____/ \\__\\__,_|\\__\\___|
167
- *
168
- * __ _ _
169
- * / _|_ _ _ __ ___| |_(_) ___ _ __ ___
170
- * | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
171
- * | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
172
- * |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
173
- */
174
- <% dest = destinations.dup %>
175
- <% topo = self.topology %>
176
- <% @states.each do |s| %>
177
- <% stable = true if dest[s[:id]].include? s[:id] %>
178
- <% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} %>
179
- <% if dest[s[:id]].empty? or stable then
180
- dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
181
- end %>
182
- // Function to be executed in state <%= s[:id] %>
183
- // valid return states: <%= dest[s[:id]].join(", ") %>
184
- <% if sigint && stable && topo[:sources][0] != s[:id] then %>
185
- // SIGINT triggers an emergency transition to <%= self.sigint %>
186
- <% end %>
187
- <%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data) {
188
- <%= @prefix %>state_t next_state = <%= dest[s[:id]].first %>;
189
- <% if sigint && topo[:sources][0] == s[:id] then %>signal(SIGINT, signal_handler);
190
- <% end %>
191
- <% if log == :syslog then %>
192
- syslog(LOG_INFO, "[FSM] In state <%= s[:id] %>");
193
- <% elsif log == :ino then %>
194
- Serial.println("[FSM] In state <%= s[:id] %>");
195
- <% end %>
196
- /* <%= placeholder %> */
197
-
198
- switch (next_state) {
199
- <% dest[s[:id]].each do |str| %>
200
- case <%= str %>:
201
- <% end %>
202
- break;
203
- default:
204
- <% if log == :syslog then %>
205
- syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", <%= @prefix %>state_names[next_state]);
206
- <% elsif log == :ino then %>
207
- Serial.print("[FSM] Cannot pass from <%= s[:id] %> to ");
208
- Serial.print(<%= @prefix %>state_names[next_state]);
209
- Serial.println(", remaining in this state");
210
- <% end %>
211
- next_state = <%= @prefix.upcase %>NO_CHANGE;
212
- }
213
- <% if sigint && stable && topo[:sources][0] != s[:id] then %>
214
- // SIGINT transition override
215
- if (_exit_request) next_state = <%= (@prefix+"STATE_"+self.sigint ).upcase %>;
216
- <% end %>
217
- return next_state;
218
- }
154
+ <% end -%>
155
+ <% placeholder = "Your Code Here" -%>
156
+ // SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!
219
157
 
220
- <% end %>
158
+ // GLOBALS
159
+ // State human-readable names
160
+ const char *<%= @prefix %>state_names[] = {<%= states_list.map {|sn| '"'+sn+'"'}.join(", ") %>};
221
161
 
222
- <% if transition_functions_list.count > 0 then %>
223
- /* _____ _ _ _
224
- * |_ _| __ __ _ _ __ ___(_) |_(_) ___ _ __
225
- * | || '__/ _` | '_ \\/ __| | __| |/ _ \\| '_ \\
226
- * | || | | (_| | | | \\__ \\ | |_| | (_) | | | |
227
- * |_||_| \\__,_|_| |_|___/_|\\__|_|\\___/|_| |_|
228
- *
229
- * __ _ _
230
- * / _|_ _ _ __ ___| |_(_) ___ _ __ ___
231
- * | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
232
- * | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
233
- * |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
234
- */
235
-
236
- <% transition_functions_list.each do |t| %>
237
- <% next if t == "NULL" %>
238
- <% tpaths = transitions_paths[t] %>
239
- // This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:
240
- <% tpaths.each_with_index do |e, i| %>
241
- // <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>
242
- <% end %>
243
- void <%= t %>(<%= @prefix %>state_data_t *data) {
244
- <% if log == :syslog then %>
245
- syslog(LOG_INFO, "[FSM] State transition <%= t %>");
246
- <% elsif log == :ino then %>
247
- Serial.println("[FSM] State transition <%= t %>");
248
- <% end %>
249
- /* <%= placeholder %> */
250
- }
162
+ // List of state functions
163
+ <% fw = state_functions_list.max {|a, b| a.length <=> b.length}.length -%>
164
+ state_func_t *const <%= @prefix %>state_table[<%= @prefix.upcase %>NUM_STATES] = {
165
+ <% @states.each do |s| -%>
166
+ <%= (s[:function] + ',').ljust(fw+1) %> // in state <%= s[:id] %>
167
+ <% end -%>
168
+ };
169
+ <% if transition_functions_list.count > 0 then -%>
251
170
 
252
- <% end %>
253
- <% end %>
171
+ // Table of transition functions
172
+ transition_func_t *const <%= @prefix %>transition_table[<%= @prefix.upcase %>NUM_STATES][<%= @prefix.upcase %>NUM_STATES] = {
173
+ <% sl = states_list -%>
174
+ <% fw = transition_functions_list.max {|a, b| a.length <=> b.length}.length -%>
175
+ <% sw = [states_list, "states:"].flatten.max {|a, b| a.length <=> b.length}.length -%>
176
+ /* <%= "states:".ljust(sw) %> <%= sl.map {|e| e.ljust(fw) }.join(", ") %> */
177
+ <% transitions_map.each_with_index do |l, i| -%>
178
+ /* <%= sl[i].ljust(sw) %> */ {<%= l.map {|e| e.ljust(fw)}.join(", ") %>},
179
+ <% end -%>
180
+ };
181
+ <% else -%>
182
+ // No transition functions
183
+ <% end -%>
184
+
185
+ /* ____ _ _
186
+ * / ___|| |_ __ _| |_ ___
187
+ * \\___ \\| __/ _` | __/ _ \\
188
+ * ___) | || (_| | || __/
189
+ * |____/ \\__\\__,_|\\__\\___|
190
+ *
191
+ * __ _ _
192
+ * / _|_ _ _ __ ___| |_(_) ___ _ __ ___
193
+ * | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
194
+ * | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
195
+ * |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
196
+ */
197
+ <% dest = destinations.dup -%>
198
+ <% topo = self.topology -%>
199
+ <% @states.each do |s| -%>
200
+ <% stable = true if dest[s[:id]].include? s[:id] -%>
201
+ <% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} -%>
202
+ <% if dest[s[:id]].empty? or stable then
203
+ dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
204
+ end %>
205
+ // Function to be executed in state <%= s[:id] %>
206
+ // valid return states: <%= dest[s[:id]].join(", ") %>
207
+ <% if sigint && stable && topo[:sources][0] != s[:id] then -%>
208
+ // SIGINT triggers an emergency transition to <%= self.sigint %>
209
+ <% end -%>
210
+ <%= @prefix %>state_t <%= s[:function] %>(<%= @prefix %>state_data_t *data) {
211
+ <%= @prefix %>state_t next_state = <%= dest[s[:id]].first -%>;
212
+ <% if sigint && topo[:sources][0] == s[:id] then -%>
213
+ signal(SIGINT, signal_handler);
214
+ <% end -%>
215
+ <% if log == :syslog then -%>
216
+ syslog(LOG_INFO, "[FSM] In state <%= s[:id] %>");
217
+ <% elsif log == :ino then -%>
218
+ Serial.println("[FSM] In state <%= s[:id] %>");
219
+ <% end -%>
220
+ /* <%= placeholder %> */
221
+
222
+ switch (next_state) {
223
+ <% dest[s[:id]].each do |str| -%>
224
+ case <%= str %>:
225
+ <% end -%>
226
+ break;
227
+ default:
228
+ <% if log == :syslog then -%>
229
+ syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", <%= @prefix %>state_names[next_state]);
230
+ <% elsif log == :ino then -%>
231
+ Serial.print("[FSM] Cannot pass from <%= s[:id] %> to ");
232
+ Serial.print(<%= @prefix %>state_names[next_state]);
233
+ Serial.println(", remaining in this state");
234
+ <% end -%>
235
+ next_state = <%= @prefix.upcase %>NO_CHANGE;
236
+ }
237
+ <% if sigint && stable && topo[:sources][0] != s[:id] then -%>
238
+ // SIGINT transition override
239
+ if (_exit_request)
240
+ next_state = <%= (@prefix+"STATE_"+self.sigint ).upcase %>;
241
+ <% end %>
242
+ return next_state;
243
+ }
244
+
245
+ <% end %>
246
+
247
+ <% if transition_functions_list.count > 0 then -%>
248
+ /* _____ _ _ _
249
+ * |_ _| __ __ _ _ __ ___(_) |_(_) ___ _ __
250
+ * | || '__/ _` | '_ \\/ __| | __| |/ _ \\| '_ \\
251
+ * | || | | (_| | | | \\__ \\ | |_| | (_) | | | |
252
+ * |_||_| \\__,_|_| |_|___/_|\\__|_|\\___/|_| |_|
253
+ *
254
+ * __ _ _
255
+ * / _|_ _ _ __ ___| |_(_) ___ _ __ ___
256
+ * | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
257
+ * | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
258
+ * |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
259
+ */
260
+
261
+ <% transition_functions_list.each do |t| -%>
262
+ <% next if t == "NULL" -%>
263
+ <% tpaths = transitions_paths[t] -%>
264
+ // This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:
265
+ <% tpaths.each_with_index do |e, i| -%>
266
+ // <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>
267
+ <% end -%>
268
+ void <%= t %>(<%= @prefix %>state_data_t *data) {
269
+ <% if log == :syslog then -%>
270
+ syslog(LOG_INFO, "[FSM] State transition <%= t %>");
271
+ <% elsif log == :ino then -%>
272
+ Serial.println("[FSM] State transition <%= t %>");
273
+ <% end -%>
274
+ /* <%= placeholder %> */
275
+ }
276
+
277
+ <% end -%>
278
+ <% end -%>
279
+
280
+ /* ____ _ _
281
+ * / ___|| |_ __ _| |_ ___
282
+ * \\___ \\| __/ _` | __/ _ \\
283
+ * ___) | || (_| | || __/
284
+ * |____/ \\__\\__,_|\\__\\___|
285
+ *
286
+ *
287
+ * _ __ ___ __ _ _ __ __ _ __ _ ___ _ __
288
+ * | '_ ` _ \\ / _` | '_ \\ / _` |/ _` |/ _ \\ '__|
289
+ * | | | | | | (_| | | | | (_| | (_| | __/ |
290
+ * |_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_|
291
+ * |___/
292
+ */
293
+
294
+ <%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data) {
295
+ <%= @prefix %>state_t new_state = <%= @prefix %>state_table[cur_state](data);
296
+ if (new_state == <%= @prefix.upcase %>NO_CHANGE) new_state = cur_state;
297
+ <% if transition_functions_list.count > 0 then %>
298
+ transition_func_t *transition = <%= @prefix %>transition_table[cur_state][new_state];
299
+ if (transition)
300
+ transition(data);
301
+ <% end %>
302
+ return new_state;
303
+ };
304
+
305
+ <% if @ino then %>
306
+ /* Example usage:
307
+ <%= @prefix %>state_data_t data = {count: 1};
308
+
309
+ void loop() {
310
+ static <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_INIT;
311
+ cur_state = <%= @prefix %>run_state(cur_state, &data);
312
+ }
313
+ */
314
+ <% else %>
315
+ <% nsinks = topology[:sinks].count %>
316
+ #ifdef TEST_MAIN
317
+ #include <unistd.h>
318
+ int main() {
319
+ <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_<%= @states.first[:id].upcase %>;
320
+ <% if @syslog then %>
321
+ openlog("SM", LOG_PID | LOG_PERROR, LOG_USER);
322
+ syslog(LOG_INFO, "Starting SM");
323
+ <% end %>
324
+ do {
325
+ cur_state = <%= @prefix %>run_state(cur_state, NULL);
326
+ sleep(1);
327
+ <% if nsinks == 1 %>
328
+ } while (cur_state != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);
329
+ <%= @prefix %>run_state(cur_state, NULL);
330
+ <% else %>
331
+ } while (1);
332
+ <% end %>
333
+ return 0;
334
+ }
335
+ #endif
336
+ <% end %>
337
+ EOC
254
338
 
255
- /* ____ _ _
256
- * / ___|| |_ __ _| |_ ___
257
- * \\___ \\| __/ _` | __/ _ \\
258
- * ___) | || (_| | || __/
259
- * |____/ \\__\\__,_|\\__\\___|
260
- *
261
- *
262
- * _ __ ___ __ _ _ __ __ _ __ _ ___ _ __
263
- * | '_ ` _ \\ / _` | '_ \\ / _` |/ _` |/ _ \\ '__|
264
- * | | | | | | (_| | | | | (_| | (_| | __/ |
265
- * |_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_|
266
- * |___/
267
- */
268
-
269
- <%= @prefix %>state_t <%= @prefix %>run_state(<%= @prefix %>state_t cur_state, <%= @prefix %>state_data_t *data) {
270
- <%= @prefix %>state_t new_state = <%= @prefix %>state_table[cur_state](data);
271
- if (new_state == <%= @prefix.upcase %>NO_CHANGE) new_state = cur_state;
272
- <% if transition_functions_list.count > 0 then %>
273
- transition_func_t *transition = <%= @prefix %>transition_table[cur_state][new_state];
274
- if (transition)
275
- transition(data);
276
- <% end %>
277
- return new_state;
278
- };
279
339
 
280
- <% if @ino then %>
281
- /* Example usage:
282
- <%= @prefix %>state_data_t data = {count: 1};
340
+ # ____ ____ ____ _ _ _____ _ ____ _____ ____
341
+ # / ___| _ \| _ \ | | | | ____| / \ | _ \| ____| _ \
342
+ # | | | |_) | |_) | | |_| | _| / _ \ | | | | _| | |_) |
343
+ # | |___| __/| __/ | _ | |___ / ___ \| |_| | |___| _ <
344
+ # \____|_| |_| |_| |_|_____/_/ \_\____/|_____|_| \_\
345
+
283
346
 
284
- void loop() {
285
- static <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_INIT;
286
- cur_state = <%= @prefix %>run_state(cur_state, &data);
347
+ HPP =<<~EOHPP
348
+ #ifndef <%= File::basename(@cname).upcase %>_HPP
349
+ #define <%= File::basename(@cname).upcase %>_HPP
350
+ #include <functional>
351
+ #include <iostream>
352
+ #include <map>
353
+ #include <string>
354
+ #include <tuple>
355
+ <% if @syslog then -%>
356
+ <% log = :syslog -%>
357
+ #include <syslog.h>
358
+ <% end -%>
359
+ <% if sigint then -%>
360
+ // Install signal handler:
361
+ // SIGINT requests a transition to state <%= self.sigint %>
362
+ #include <csignal>
363
+ <% end %>
364
+
365
+ using namespace std::string_literals;
366
+ <% ns = @project_name || "FSM" -%>
367
+ namespace <%= ns %> {
368
+ static bool <%= self.sigint %>_requested = false;
369
+
370
+ // List of states
371
+ typedef enum {
372
+ <% @states.each do |s| -%>
373
+ <%= @prefix.upcase %>STATE_<%= s[:id].upcase %>,
374
+ <% end -%>
375
+ <%= @prefix.upcase %>NUM_STATES,
376
+ <%= @prefix.upcase %>NO_CHANGE,
377
+ <%= @prefix.upcase %>UNIMPLEMENTED
378
+ } <%= @prefix %>state_t;
379
+
380
+ // State human-readable names
381
+ std::map<<%= @prefix %>state_t, char const *> state_names = {
382
+ <% @states.each do |s| -%>
383
+ {<%= @prefix.upcase %>STATE_<%= s[:id].upcase %>, "<%= s[:id].upcase %>"},
384
+ <% end -%>
385
+ {<%= @prefix.upcase %>NUM_STATES, "NUM_STATES"},
386
+ {<%= @prefix.upcase %>NO_CHANGE, "NO_CHANGE"},
387
+ {<%= @prefix.upcase %>UNIMPLEMENTED, "UNIMPLEMENTED"}
388
+ };
389
+
390
+ // Custom state functions:
391
+ <% @states.each do |s| -%>
392
+ template<class T>
393
+ <%= @prefix %>state_t <%= s[:function] %>(T &data);
394
+ <% end -%>
395
+
396
+ <% if transition_functions_list.count > 0 then -%>
397
+ // Custom transition functions:
398
+ <% transition_functions_list.each do |t| -%>
399
+ template<class T>
400
+ void <%= t %>(T &data);
401
+ <% end -%>
402
+ <% end -%>
403
+
404
+ // Finite State Machine class
405
+ template <typename DATA_T>
406
+ class FiniteStateMachine {
407
+
408
+ // Function templates
409
+ using state_fun = std::function<<%= @prefix %>state_t(DATA_T &data)>;
410
+ using transition_fun = std::function<void(DATA_T &data)>;
411
+ using operation_fun = std::function<void(DATA_T &data)>;
412
+
413
+ private:
414
+ std::pair<<%= @prefix %>state_t, <%= @prefix %>state_t> _state{<%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>, <%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>};
415
+ std::map<<%= @prefix %>state_t, state_fun> _states;
416
+ std::map<<%= @prefix %>state_t, std::map<<%= @prefix %>state_t, transition_fun>> _transitions;
417
+ std::function<void()> _timing_func;
418
+ DATA_T *_data;
419
+
420
+ public:
421
+
422
+ FiniteStateMachine(DATA_T *data) : _data(data) {
423
+ install_functions();
424
+ }
425
+ ~FiniteStateMachine(){};
426
+
427
+ void set_timing_function(std::function<void()> timing_func) {
428
+ _timing_func = timing_func;
429
+ }
430
+
431
+ void add_state(<%= @prefix %>state_t name, state_fun func) { _states[name] = func; }
432
+
433
+ void add_transition(<%= @prefix %>state_t from, <%= @prefix %>state_t to, transition_fun func) {
434
+ _transitions[from][to] = func;
435
+ }
436
+
437
+ inline <%= @prefix %>state_t state() { return _state.second; }
438
+ inline std::string state_name() { return std::string(state_names[_state.second]); }
439
+ inline <%= @prefix %>state_t prev_state() { return _state.first; }
440
+
441
+ <%= @prefix %>state_t operator()(<%= @prefix %>state_t state) {
442
+ if (_states.find(state) == _states.end()) {
443
+ throw std::runtime_error("State not found: "s + state_names[state]);
444
+ }
445
+ return _states[state](*_data);
446
+ }
447
+
448
+ void operator()(<%= @prefix %>state_t from, <%= @prefix %>state_t to) {
449
+ if (_transitions.find(from) != _transitions.end()) {
450
+ if (_transitions[from].find(to) != _transitions[from].end()) {
451
+ _transitions[from][to](*_data);
287
452
  }
288
- */
289
- <% else %>
290
- <% nsinks = topology[:sinks].count %>
291
- #ifdef TEST_MAIN
292
- #include <unistd.h>
293
- int main() {
294
- <%= @prefix %>state_t cur_state = <%= @prefix.upcase %>STATE_<%= @states.first[:id].upcase %>;
295
- <% if @syslog then %>
296
- openlog("SM", LOG_PID | LOG_PERROR, LOG_USER);
297
- syslog(LOG_INFO, "Starting SM");
298
- <% end %>
299
- do {
300
- cur_state = <%= @prefix %>run_state(cur_state, NULL);
301
- sleep(1);
302
- <% if nsinks == 1 %>
303
- } while (cur_state != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);
304
- <%= @prefix %>run_state(cur_state, NULL);
305
- <% else %>
306
- } while (1);
307
- <% end %>
308
- return 0;
453
+ }
454
+ }
455
+
456
+ // Run the FSM from a given state
457
+ void run(<%= @prefix %>state_t state, operation_fun operation = nullptr) {
458
+ <%= ns %>::<%= self.sigint %>_requested = false;
459
+ <%= @prefix %>state_t prev_state = state;
460
+ _state.first = state;
461
+ _state.second = state;
462
+ <% if sigint then -%>
463
+ std::signal(SIGINT, [](int signum) {
464
+ <% if log == :syslog then -%>
465
+ syslog(LOG_WARNING, "[FSM] SIGINT transition to <%= sigint %>");
466
+ <% end -%>
467
+ <%= ns %>::<%= self.sigint %>_requested = true;
468
+ });
469
+ <% end -%>
470
+ do {
471
+ (*this)(_state.first, _state.second);
472
+ if (operation) {
473
+ operation(*_data);
309
474
  }
310
- #endif
311
- <% end %>
312
- EOC
475
+ _state.first = _state.second;
476
+ _state.second = (*this)(_state.second);
477
+ if (_timing_func) {
478
+ _timing_func();
479
+ }
480
+ } while (_state.second != <%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);
481
+ // Call the exit state once more:
482
+ (*this)(<%= @prefix.upcase %>STATE_<%= topology[:sinks][0].upcase %>);
483
+ <% if sigint then -%>
484
+ std::signal(SIGINT, SIG_DFL);
485
+ <% end -%>
486
+ }
487
+
488
+ // Run the FSM from the initial state
489
+ void run(operation_fun operation = nullptr) { run(<%= @prefix.upcase %>STATE_<%= states[0][:id].upcase %>, operation); }
490
+
491
+ // install state and transition functions
492
+ void install_functions() {
493
+
494
+ // State functions
495
+ <% dest = destinations.dup -%>
496
+ <% @states.each do |s| -%>
497
+ <% stable = true if dest[s[:id]].include? s[:id] -%>
498
+ <% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} -%>
499
+ <% if dest[s[:id]].empty? or stable then
500
+ dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
501
+ end -%>
502
+ add_state(<%= ns %>::<%= @prefix.upcase %>STATE_<%= s[:id].upcase %>, [](DATA_T &data) -> <%= ns %>::<%= @prefix %>state_t {
503
+ <% if log == :syslog then -%>
504
+ syslog(LOG_INFO, "[FSM] In state <%= s[:id].upcase %>");
505
+ <% end -%>
506
+ <%= ns %>::<%= @prefix %>state_t next_state = <%= @prefix%>do_<%= s[:id] %>(data);
507
+
508
+ switch (next_state) {
509
+ case <%= ns %>::<%= @prefix.upcase %>UNIMPLEMENTED:
510
+ throw std::runtime_error("State function not fully implemented: "s + "<%= s[:id].upcase %>");
511
+ break;
512
+ <% dest[s[:id]].each do |str| -%>
513
+ case <%= ns %>::<%= str %>:
514
+ <% end -%>
515
+ break;
516
+ default:
517
+ <% if log == :syslog then -%>
518
+ syslog(LOG_WARNING, "[FSM] Cannot pass from <%= s[:id] %> to %s, remaining in this state", state_names[next_state]);
519
+ <% end -%>
520
+ next_state = <%= ns %>::<%= @prefix.upcase %>NO_CHANGE;
521
+ }
522
+ <% if sigint && stable && self.topology[:sources][0] != s[:id] then -%>
523
+ // SIGINT transition override
524
+ if (<%= self.sigint %>_requested) next_state = <%= (@prefix+"STATE_"+self.sigint ).upcase %>;
525
+ <% end -%>
526
+ return next_state;
527
+ });
528
+
529
+ <% end -%>
530
+
531
+ <% if transition_functions_list.count > 0 then -%>
532
+ // Transition functions
533
+ <% transition_functions_list.each do |t| -%>
534
+ add_transition(<%= @prefix.upcase %>STATE_<%= transitions_paths[t][0][:from].upcase %>, <%= @prefix.upcase %>STATE_<%= transitions_paths[t][0][:to].upcase %>, [](DATA_T &data) {
535
+ <% if log == :syslog then -%>
536
+ syslog(LOG_INFO, "[FSM] State transition <%= t %>");
537
+ <% end -%>
538
+ <%= t %>(data);
539
+ });
540
+
541
+ <% end -%>
542
+ <% end -%>
543
+ }
544
+
545
+ }; // class FiniteStateMachine
546
+
547
+ }; // namespace <%= @project_name || "FSM" %>
548
+
549
+ #endif // <%= File::basename(@cname).upcase %>_HPP
550
+
551
+ EOHPP
552
+
553
+
554
+ # ____ ____ ____ ____ ___ _ _ ____ ____ _____
555
+ # / ___| _ \| _ \ / ___| / _ \| | | | _ \ / ___| ____|
556
+ # | | | |_) | |_) | \___ \| | | | | | | |_) | | | _|
557
+ # | |___| __/| __/ ___) | |_| | |_| | _ <| |___| |___
558
+ # \____|_| |_| |____/ \___/ \___/|_| \_\\____|_____|
559
+
560
+
561
+
562
+ CPP =<<~EOCPP
563
+ <% if @syslog then -%>
564
+ <% log = :syslog -%>
565
+ #include <syslog.h>
566
+ <% end -%>
567
+ #include "<%= File::basename(@cname) %>.hpp"
568
+
569
+ using namespace std;
570
+ <% ns = @project_name || "FSM" -%>
571
+
572
+ <% placeholder = "Your Code Here" %>
573
+ // SEARCH FOR <%= placeholder %> FOR CODE INSERTION POINTS!
574
+
575
+
576
+ namespace <%= @project_name || "FSM" %> {
577
+
578
+ /* ____ _ _
579
+ * / ___|| |_ __ _| |_ ___
580
+ * \\___ \\| __/ _` | __/ _ \\
581
+ * ___) | || (_| | || __/
582
+ * |____/ \\__\\__,_|\\__\\___|
583
+ *
584
+ * __ _ _
585
+ * / _|_ _ _ __ ___| |_(_) ___ _ __ ___
586
+ * | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
587
+ * | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
588
+ * |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
589
+ */
590
+ <% dest = destinations.dup -%>
591
+ <% topo = self.topology -%>
592
+ <% @states.each do |s| -%>
593
+ <% stable = true if dest[s[:id]].include? s[:id] -%>
594
+ <% dest[s[:id]].map! {|n| (@prefix+"STATE_"+n).upcase} -%>
595
+ <% if dest[s[:id]].empty? or stable then
596
+ dest[s[:id]].unshift @prefix.upcase+"NO_CHANGE"
597
+ end %>
598
+ // Function to be executed in state STATE_<%= s[:id].upcase %>
599
+ // valid return states: <%= dest[s[:id]].join(", ") %>
600
+ <% if sigint && stable && topo[:sources][0] != s[:id] then -%>
601
+ // SIGINT triggers an emergency transition to STATE_<%= self.sigint.upcase %>
602
+ <% end -%>
603
+ template<class T>
604
+ <%= @prefix %>state_t <%= s[:function] %>(T &data) {
605
+ <%= @prefix %>state_t next_state = <%= ns %>::<%= @prefix.upcase %>UNIMPLEMENTED;
606
+ /* <%= placeholder %> */
607
+
608
+ return next_state;
609
+ }
610
+ <% end -%>
611
+
612
+
613
+ <% if transition_functions_list.count > 0 then -%>
614
+ /* _____ _ _ _
615
+ * |_ _| __ __ _ _ __ ___(_) |_(_) ___ _ __
616
+ * | || '__/ _` | '_ \\/ __| | __| |/ _ \\| '_ \\
617
+ * | || | | (_| | | | \\__ \\ | |_| | (_) | | | |
618
+ * |_||_| \\__,_|_| |_|___/_|\\__|_|\\___/|_| |_|
619
+ *
620
+ * __ _ _
621
+ * / _|_ _ _ __ ___| |_(_) ___ _ __ ___
622
+ * | |_| | | | '_ \\ / __| __| |/ _ \\| '_ \\/ __|
623
+ * | _| |_| | | | | (__| |_| | (_) | | | \\__ \\
624
+ * |_| \\__,_|_| |_|\\___|\\__|_|\\___/|_| |_|___/
625
+ */
626
+
627
+ <% transition_functions_list.each do |t| -%>
628
+ <% next if t == "NULL" -%>
629
+ <% tpaths = transitions_paths[t] -%>
630
+ // This function is called in <%= tpaths.count %> transition<%= tpaths.count == 1 ? '' : 's' %>:
631
+ <% tpaths.each_with_index do |e, i| -%>
632
+ // <%= i+1 %>. from <%= e[:from] %> to <%= e[:to] %>
633
+ <% end -%>
634
+ template<class T>
635
+ void <%= t %>(T &data) {
636
+ /* <%= placeholder %> */
637
+ }
638
+
639
+ <% end -%>
640
+ <% end -%>
641
+
642
+ }; // namespace <%= @project_name || "FSM" %>
643
+
644
+
645
+ <% nsinks = topology[:sinks].count -%>
646
+ // Example usage:
647
+ #ifdef TEST_MAIN
648
+ #include <unistd.h>
649
+ #include <thread>
650
+
651
+ struct Data {
652
+ int count;
653
+ };
654
+
655
+ int main() {
656
+ Data data = {1};
657
+ auto fsm = <%= ns %>::FiniteStateMachine(&data);
658
+ fsm.set_timing_function([]() {
659
+ std::this_thread::sleep_for(std::chrono::seconds(1));
660
+ });
661
+ fsm.run([&](Data &s) {
662
+ std::cout << "State: " << fsm.state() << " data: " << s.count << std::endl;
663
+ });
664
+ return 0;
665
+ }
666
+ #endif // TEST_MAIN
667
+ EOCPP
668
+
313
669
  end
314
670
  end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module GV_FSM
2
- VERSION = "0.3.7"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gv_fsm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paolo Bosetti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-08 00:00:00.000000000 Z
11
+ date: 2024-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-graphviz