gv_fsm 0.3.7 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86b23438e750da00df1eeb91db559d2176303a4a9c543fdb6acf471d5d112571
4
- data.tar.gz: cb3c1ef9e8d2fe0fdf84740539425a9aa8aca3d0358de6ae2b5a69c209d4dfd7
3
+ metadata.gz: 73902a6a767d8a5642f9f611cd7d6d73e5edfa34d17181b95a17c94ac45dfeed
4
+ data.tar.gz: 7729bd9ef72ebe5ec2cdd8734d3aa9a42114932418ca9189b176af90012803c6
5
5
  SHA512:
6
- metadata.gz: 946b67acdc3dd2e435bdcda6588c2df81f4548d89003db424634b5d27e45e7a7b2698cd7583e5a51e5c1814dd5cdb5042fd719bd27d37c831dde30354935172f
7
- data.tar.gz: 83b6474365979c04ea29346acc518746ee1d760eb4b144da251e2f3816956e488d68ebf4d52c7e4d234f3dfe92a14ad746f7b37d67c743078e06a68deb885177
6
+ metadata.gz: 044106cb2470f4189a44ff8b3988119d3c65a25417a380a10bd115dab85738655429b3f90cd0bbaa40353a2de9cdbbee6cbe549d2cdf9554e0555af7b132d9e3
7
+ data.tar.gz: 359bbefb9df9567a012660b23ed6c04c323be54f2c411198e00c85833adb4a682099c2aea9386d5b4114ec6e9cedde5eb08246fc3b4d386d6838f4e33074990d
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
+ if (operation) {
472
+ operation(*_data);
309
473
  }
310
- #endif
311
- <% end %>
312
- EOC
474
+ (*this)(_state.first, _state.second);
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.1"
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.1
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