openc3 6.4.1 → 6.5.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +172 -97
  3. data/data/config/_graph_params.yaml +4 -4
  4. data/data/config/conversions.yaml +274 -0
  5. data/data/config/item_modifiers.yaml +8 -70
  6. data/data/config/parameter_modifiers.yaml +9 -69
  7. data/data/config/plugins.yaml +14 -1
  8. data/data/config/processors.yaml +51 -0
  9. data/data/config/telemetry_modifiers.yaml +1 -0
  10. data/lib/openc3/api/api.rb +1 -1
  11. data/lib/openc3/api/tlm_api.rb +10 -5
  12. data/lib/openc3/conversions/unix_time_conversion.rb +2 -2
  13. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +3 -3
  14. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +3 -3
  15. data/lib/openc3/core_ext/time.rb +2 -9
  16. data/lib/openc3/microservices/cleanup_microservice.rb +2 -2
  17. data/lib/openc3/microservices/decom_microservice.rb +2 -2
  18. data/lib/openc3/microservices/interface_microservice.rb +18 -12
  19. data/lib/openc3/microservices/log_microservice.rb +4 -2
  20. data/lib/openc3/microservices/multi_microservice.rb +2 -2
  21. data/lib/openc3/microservices/periodic_microservice.rb +2 -2
  22. data/lib/openc3/microservices/reducer_microservice.rb +2 -2
  23. data/lib/openc3/microservices/router_microservice.rb +2 -2
  24. data/lib/openc3/microservices/scope_cleanup_microservice.rb +2 -2
  25. data/lib/openc3/microservices/text_log_microservice.rb +19 -6
  26. data/lib/openc3/models/plugin_model.rb +5 -4
  27. data/lib/openc3/models/scope_model.rb +87 -57
  28. data/lib/openc3/models/script_engine_model.rb +93 -0
  29. data/lib/openc3/models/script_status_model.rb +26 -4
  30. data/lib/openc3/models/target_model.rb +33 -5
  31. data/lib/openc3/packets/packet_config.rb +1 -1
  32. data/lib/openc3/script/autonomic.rb +359 -0
  33. data/lib/openc3/script/script.rb +6 -1
  34. data/lib/openc3/script_engines/script_engine.rb +118 -0
  35. data/lib/openc3/topics/interface_topic.rb +23 -3
  36. data/lib/openc3/utilities/cli_generator.rb +42 -15
  37. data/lib/openc3/utilities/running_script.rb +1460 -0
  38. data/lib/openc3/version.rb +6 -6
  39. data/templates/conversion/conversion.py +1 -1
  40. data/templates/conversion/conversion.rb +1 -1
  41. data/templates/processor/processor.py +32 -0
  42. data/templates/processor/processor.rb +36 -0
  43. data/templates/tool_angular/package.json +2 -2
  44. data/templates/tool_react/package.json +1 -1
  45. data/templates/tool_svelte/package.json +1 -1
  46. data/templates/tool_vue/package.json +3 -3
  47. data/templates/widget/package.json +2 -2
  48. metadata +9 -1
@@ -0,0 +1,359 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2025 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # This file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ module OpenC3
20
+ module Script
21
+ private
22
+
23
+ # Group Methods
24
+ def autonomic_group_list(scope: $openc3_scope)
25
+ begin
26
+ endpoint = "/openc3-api/autonomic/group"
27
+ response = $api_server.request('get', endpoint, scope: scope)
28
+ if response.nil? || response.status != 200
29
+ raise "Unexpected response to autonomic_group_list: #{response.inspect}"
30
+ end
31
+ return JSON.parse(response.body)
32
+ rescue => error
33
+ raise "autonomic_group_list failed due to #{error.formatted}"
34
+ end
35
+ end
36
+
37
+ def autonomic_group_create(name, scope: $openc3_scope)
38
+ begin
39
+ endpoint = "/openc3-api/autonomic/group"
40
+ response = $api_server.request('post', endpoint, data: { name: name }, json: true, scope: scope)
41
+ if response.nil? || response.status != 201
42
+ if response
43
+ parsed = JSON.parse(response.body)
44
+ raise "autonomic_group_create error: #{parsed['message']}"
45
+ else
46
+ raise "autonomic_group_create failed"
47
+ end
48
+ end
49
+ return JSON.parse(response.body)
50
+ rescue => error
51
+ raise "autonomic_group_create failed due to #{error.formatted}"
52
+ end
53
+ end
54
+
55
+ def autonomic_group_show(name, scope: $openc3_scope)
56
+ begin
57
+ endpoint = "/openc3-api/autonomic/group/#{name}"
58
+ response = $api_server.request('get', endpoint, scope: scope)
59
+ if response.nil? || response.status != 200
60
+ raise "Unexpected response to autonomic_group_show: #{response.inspect}"
61
+ end
62
+ return JSON.parse(response.body)
63
+ rescue => error
64
+ raise "autonomic_group_show failed due to #{error.formatted}"
65
+ end
66
+ end
67
+
68
+ def autonomic_group_destroy(name, scope: $openc3_scope)
69
+ begin
70
+ endpoint = "/openc3-api/autonomic/group/#{name}"
71
+ response = $api_server.request('delete', endpoint, scope: scope)
72
+ if response.nil? || response.status != 200
73
+ if response
74
+ parsed = JSON.parse(response.body)
75
+ raise "autonomic_group_destroy error: #{parsed['error']}"
76
+ else
77
+ raise "autonomic_group_destroy failed"
78
+ end
79
+ end
80
+ return
81
+ rescue => error
82
+ raise "autonomic_group_destroy failed due to #{error.formatted}"
83
+ end
84
+ end
85
+
86
+ # Trigger Methods
87
+ def autonomic_trigger_list(group: 'DEFAULT', scope: $openc3_scope)
88
+ begin
89
+ endpoint = "/openc3-api/autonomic/#{group}/trigger"
90
+ response = $api_server.request('get', endpoint, scope: scope)
91
+ if response.nil? || response.status != 200
92
+ raise "Unexpected response to autonomic_trigger_list: #{response.inspect}"
93
+ end
94
+ return JSON.parse(response.body)
95
+ rescue => error
96
+ raise "autonomic_trigger_list failed due to #{error.formatted}"
97
+ end
98
+ end
99
+
100
+ def autonomic_trigger_create(left:, operator:, right:, group: 'DEFAULT', scope: $openc3_scope)
101
+ begin
102
+ endpoint = "/openc3-api/autonomic/#{group}/trigger"
103
+ config = {
104
+ 'group' => group,
105
+ 'left' => left,
106
+ 'operator' => operator,
107
+ 'right' => right
108
+ }
109
+ response = $api_server.request('post', endpoint, data: config, json: true, scope: scope)
110
+ if response.nil? || response.status != 201
111
+ if response
112
+ parsed = JSON.parse(response.body)
113
+ raise "autonomic_trigger_create error: #{parsed['error']}"
114
+ else
115
+ raise "autonomic_trigger_create failed"
116
+ end
117
+ end
118
+ return JSON.parse(response.body)
119
+ rescue => error
120
+ raise "autonomic_trigger_create failed due to #{error.formatted}"
121
+ end
122
+ end
123
+
124
+ def autonomic_trigger_show(name, group: 'DEFAULT', scope: $openc3_scope)
125
+ begin
126
+ endpoint = "/openc3-api/autonomic/#{group}/trigger/#{name}"
127
+ response = $api_server.request('get', endpoint, scope: scope)
128
+ if response.nil? || response.status != 200
129
+ raise "Unexpected response to autonomic_trigger_show: #{response.inspect}"
130
+ end
131
+ return JSON.parse(response.body)
132
+ rescue => error
133
+ raise "autonomic_trigger_show failed due to #{error.formatted}"
134
+ end
135
+ end
136
+
137
+ def autonomic_trigger_enable(name, group: 'DEFAULT', scope: $openc3_scope)
138
+ begin
139
+ endpoint = "/openc3-api/autonomic/#{group}/trigger/#{name}/enable"
140
+ response = $api_server.request('post', endpoint, json: true, scope: scope)
141
+ if response.nil? || response.status != 200
142
+ if response
143
+ parsed = JSON.parse(response.body)
144
+ raise "autonomic_trigger_enable error: #{parsed['error']}"
145
+ else
146
+ raise "autonomic_trigger_enable failed"
147
+ end
148
+ end
149
+ return
150
+ rescue => error
151
+ raise "autonomic_trigger_enable failed due to #{error.formatted}"
152
+ end
153
+ end
154
+
155
+ def autonomic_trigger_disable(name, group: 'DEFAULT', scope: $openc3_scope)
156
+ begin
157
+ endpoint = "/openc3-api/autonomic/#{group}/trigger/#{name}/disable"
158
+ response = $api_server.request('post', endpoint, json: true, scope: scope)
159
+ if response.nil? || response.status != 200
160
+ if response
161
+ parsed = JSON.parse(response.body)
162
+ raise "autonomic_trigger_disable error: #{parsed['error']}"
163
+ else
164
+ raise "autonomic_trigger_disable failed"
165
+ end
166
+ end
167
+ return
168
+ rescue => error
169
+ raise "autonomic_trigger_disable failed due to #{error.formatted}"
170
+ end
171
+ end
172
+
173
+ def autonomic_trigger_update(name, group: 'DEFAULT', left: nil, operator: nil, right: nil, scope: $openc3_scope)
174
+ begin
175
+ endpoint = "/openc3-api/autonomic/#{group}/trigger/#{name}"
176
+ config = {}
177
+ config['left'] = left if left
178
+ config['operator'] = operator if operator
179
+ config['right'] = right if right
180
+ response = $api_server.request('put', endpoint, data: config, json: true, scope: scope)
181
+ if response.nil? || response.status != 200
182
+ if response
183
+ parsed = JSON.parse(response.body)
184
+ raise "autonomic_trigger_update error: #{parsed['error']}"
185
+ else
186
+ raise "autonomic_trigger_update failed"
187
+ end
188
+ end
189
+ return JSON.parse(response.body)
190
+ rescue => error
191
+ raise "autonomic_trigger_update failed due to #{error.formatted}"
192
+ end
193
+ end
194
+
195
+ def autonomic_trigger_destroy(name, group: 'DEFAULT', scope: $openc3_scope)
196
+ begin
197
+ endpoint = "/openc3-api/autonomic/#{group}/trigger/#{name}"
198
+ response = $api_server.request('delete', endpoint, scope: scope)
199
+ if response.nil? || response.status != 200
200
+ if response
201
+ parsed = JSON.parse(response.body)
202
+ raise "autonomic_trigger_destroy error: #{parsed['error']}"
203
+ else
204
+ raise "autonomic_trigger_destroy failed"
205
+ end
206
+ end
207
+ return
208
+ rescue => error
209
+ raise "autonomic_trigger_destroy failed due to #{error.formatted}"
210
+ end
211
+ end
212
+
213
+ # Reaction Methods
214
+ def autonomic_reaction_list(scope: $openc3_scope)
215
+ begin
216
+ endpoint = "/openc3-api/autonomic/reaction"
217
+ response = $api_server.request('get', endpoint, scope: scope)
218
+ if response.nil? || response.status != 200
219
+ raise "Unexpected response to autonomic_reaction_list: #{response.inspect}"
220
+ end
221
+ return JSON.parse(response.body)
222
+ rescue => error
223
+ raise "autonomic_reaction_list failed due to #{error.formatted}"
224
+ end
225
+ end
226
+
227
+ def autonomic_reaction_create(triggers:, actions:, trigger_level: 'EDGE', snooze: 0, scope: $openc3_scope)
228
+ begin
229
+ endpoint = "/openc3-api/autonomic/reaction"
230
+ config = {
231
+ 'triggers' => triggers,
232
+ 'actions' => actions,
233
+ 'trigger_level' => trigger_level,
234
+ 'snooze' => snooze,
235
+ }
236
+ response = $api_server.request('post', endpoint, data: config, json: true, scope: scope)
237
+ if response.nil? || response.status != 201
238
+ if response
239
+ parsed = JSON.parse(response.body)
240
+ raise "autonomic_reaction_create error: #{parsed['error']}"
241
+ else
242
+ raise "autonomic_reaction_create failed"
243
+ end
244
+ end
245
+ return JSON.parse(response.body)
246
+ rescue => error
247
+ raise "autonomic_reaction_create failed due to #{error.formatted}"
248
+ end
249
+ end
250
+
251
+ def autonomic_reaction_show(name, scope: $openc3_scope)
252
+ begin
253
+ endpoint = "/openc3-api/autonomic/reaction/#{name}"
254
+ response = $api_server.request('get', endpoint, scope: scope)
255
+ if response.nil? || response.status != 200
256
+ raise "Unexpected response to autonomic_reaction_show: #{response.inspect}"
257
+ end
258
+ return JSON.parse(response.body)
259
+ rescue => error
260
+ raise "autonomic_reaction_show failed due to #{error.formatted}"
261
+ end
262
+ end
263
+
264
+ def autonomic_reaction_enable(name, scope: $openc3_scope)
265
+ begin
266
+ endpoint = "/openc3-api/autonomic/reaction/#{name}/enable"
267
+ response = $api_server.request('post', endpoint, json: true, scope: scope)
268
+ if response.nil? || response.status != 200
269
+ if response
270
+ parsed = JSON.parse(response.body)
271
+ raise "autonomic_reaction_enable error: #{parsed['error']}"
272
+ else
273
+ raise "autonomic_reaction_enable failed"
274
+ end
275
+ end
276
+ return
277
+ rescue => error
278
+ raise "autonomic_reaction_enable failed due to #{error.formatted}"
279
+ end
280
+ end
281
+
282
+ def autonomic_reaction_disable(name, scope: $openc3_scope)
283
+ begin
284
+ endpoint = "/openc3-api/autonomic/reaction/#{name}/disable"
285
+ response = $api_server.request('post', endpoint, json: true, scope: scope)
286
+ if response.nil? || response.status != 200
287
+ if response
288
+ parsed = JSON.parse(response.body)
289
+ raise "autonomic_reaction_disable error: #{parsed['error']}"
290
+ else
291
+ raise "autonomic_reaction_disable failed"
292
+ end
293
+ end
294
+ return
295
+ rescue => error
296
+ raise "autonomic_reaction_disable failed due to #{error.formatted}"
297
+ end
298
+ end
299
+
300
+ def autonomic_reaction_execute(name, scope: $openc3_scope)
301
+ begin
302
+ endpoint = "/openc3-api/autonomic/reaction/#{name}/execute"
303
+ response = $api_server.request('post', endpoint, json: true, scope: scope)
304
+ if response.nil? || response.status != 200
305
+ if response
306
+ parsed = JSON.parse(response.body)
307
+ raise "autonomic_reaction_execute error: #{parsed['error']}"
308
+ else
309
+ raise "autonomic_reaction_execute failed"
310
+ end
311
+ end
312
+ return
313
+ rescue => error
314
+ raise "autonomic_reaction_execute failed due to #{error.formatted}"
315
+ end
316
+ end
317
+
318
+ def autonomic_reaction_update(name, triggers: nil, actions: nil, trigger_level: nil, snooze: nil, scope: $openc3_scope)
319
+ begin
320
+ endpoint = "/openc3-api/autonomic/reaction/#{name}"
321
+ config = {}
322
+ config['triggers'] = triggers if triggers
323
+ config['actions'] = actions if actions
324
+ config['trigger_level'] = trigger_level if trigger_level
325
+ config['snooze'] = snooze if snooze
326
+ response = $api_server.request('put', endpoint, data: config, json: true, scope: scope)
327
+ if response.nil? || response.status != 200
328
+ if response
329
+ parsed = JSON.parse(response.body)
330
+ raise "autonomic_reaction_update error: #{parsed['error']}"
331
+ else
332
+ raise "autonomic_reaction_update failed"
333
+ end
334
+ end
335
+ return JSON.parse(response.body)
336
+ rescue => error
337
+ raise "autonomic_reaction_update failed due to #{error.formatted}"
338
+ end
339
+ end
340
+
341
+ def autonomic_reaction_destroy(name, scope: $openc3_scope)
342
+ begin
343
+ endpoint = "/openc3-api/autonomic/reaction/#{name}"
344
+ response = $api_server.request('delete', endpoint, scope: scope)
345
+ if response.nil? || response.status != 200
346
+ if response
347
+ parsed = JSON.parse(response.body)
348
+ raise "autonomic_reaction_destroy error: #{parsed['error']}"
349
+ else
350
+ raise "autonomic_reaction_destroy failed"
351
+ end
352
+ end
353
+ return
354
+ rescue => error
355
+ raise "autonomic_reaction_destroy failed due to #{error.formatted}"
356
+ end
357
+ end
358
+ end
359
+ end
@@ -24,6 +24,7 @@ require 'openc3'
24
24
  require 'openc3/api/api'
25
25
  require 'openc3/io/json_drb_object'
26
26
  require 'openc3/script/api_shared'
27
+ require 'openc3/script/autonomic'
27
28
  require 'openc3/script/calendar'
28
29
  require 'openc3/script/commands'
29
30
  require 'openc3/script/critical_cmd'
@@ -225,7 +226,11 @@ module OpenC3
225
226
  # script_run("INST/procedures/collect.rb")
226
227
  #
227
228
  def initialize_offline_access
228
- auth = OpenC3KeycloakAuthentication.new(ENV['OPENC3_KEYCLOAK_URL'])
229
+ keycloak_url = ENV['OPENC3_KEYCLOAK_URL']
230
+ if keycloak_url.nil? or keycloak_url.empty?
231
+ raise "initialize_offline_access only valid in COSMOS Enterprise. OPENC3_KEYCLOAK_URL environment variable must be set."
232
+ end
233
+ auth = OpenC3KeycloakAuthentication.new(keycloak_url)
229
234
  auth.token(include_bearer: true, openid_scope: 'openid%20offline_access')
230
235
  set_offline_access(auth.refresh_token)
231
236
  end
@@ -0,0 +1,118 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2025 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # This file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ module OpenC3
20
+ class ScriptEngine
21
+ attr_accessor :running_script
22
+
23
+ def initialize(running_script)
24
+ @running_script = running_script
25
+ end
26
+
27
+ # Override this method in the subclass to implement the script engine
28
+ def run_line(line, lines, filename, line_no)
29
+ puts line
30
+ return line_no + 1
31
+ end
32
+
33
+ def run_text(text, filename: nil, line_no: 1, end_line_no: nil, bind_variables: false)
34
+ lines = text.lines
35
+ loop do
36
+ line = lines[line_no - 1]
37
+ return if line.nil?
38
+
39
+ begin
40
+ next_line_no = line_no + 1
41
+ running_script.pre_line_instrumentation(filename, line_no)
42
+ next_line_no = run_line(line, lines, filename, line_no)
43
+ running_script.post_line_instrumentation(filename, line_no)
44
+ rescue Exception => e
45
+ retry if running_script.exception_instrumentation(e, filename, line_no)
46
+ end
47
+
48
+ line_no = next_line_no
49
+ return if end_line_no and line_no > end_line_no
50
+ end
51
+ end
52
+
53
+ def debug(text)
54
+ run_line(text, [text], "DEBUG", 1)
55
+ end
56
+
57
+ def syntax_check(text, filename: nil)
58
+ puts "Not Implemented"
59
+ return 1
60
+ end
61
+
62
+ def mnemonic_check(text, filename: nil)
63
+ puts "Not Implemented"
64
+ return 1
65
+ end
66
+
67
+ def tokenizer(s, special_chars = '()><+-*/=;,')
68
+ result = []
69
+ i = 0
70
+ while i < s.length
71
+ # Skip whitespace
72
+ if s[i].match?(/\s/)
73
+ i += 1
74
+ next
75
+ end
76
+
77
+ # Handle quoted strings (single or double quotes)
78
+ if ['"', "'"].include?(s[i])
79
+ quote_char = s[i]
80
+ quote_start = i
81
+ i += 1
82
+ # Find the closing quote
83
+ while i < s.length
84
+ if s[i] == '\\' && i + 1 < s.length # Handle escaped characters
85
+ i += 2
86
+ elsif s[i] == quote_char # Found closing quote
87
+ i += 1
88
+ break
89
+ else
90
+ i += 1
91
+ end
92
+ end
93
+ # Include the quotes in the token
94
+ result << s[quote_start...i]
95
+ next
96
+ end
97
+
98
+ # Handle special characters
99
+ if special_chars.include?(s[i])
100
+ result << s[i]
101
+ i += 1
102
+ next
103
+ end
104
+
105
+ # Handle regular tokens
106
+ token_start = i
107
+ while i < s.length && !s[i].match?(/\s/) && !special_chars.include?(s[i]) && !['"', "'"].include?(s[i])
108
+ i += 1
109
+ end
110
+ if i > token_start
111
+ result << s[token_start...i]
112
+ end
113
+ end
114
+
115
+ return result
116
+ end
117
+ end
118
+ end
@@ -24,6 +24,8 @@ require 'openc3/topics/topic'
24
24
 
25
25
  module OpenC3
26
26
  class InterfaceTopic < Topic
27
+ COMMAND_ACK_TIMEOUT_S = 30
28
+
27
29
  # Generate a list of topics for this interface. This includes the interface itself
28
30
  # and all the targets which are assigned to this interface.
29
31
  def self.topics(interface, scope:)
@@ -48,9 +50,27 @@ module OpenC3
48
50
  end
49
51
  end
50
52
 
51
- def self.write_raw(interface_name, data, scope:)
52
- Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'raw' => data }, '*', 100)
53
- # Todo: This should wait for the ack
53
+ def self.write_raw(interface_name, data, timeout: nil, scope:)
54
+ interface_name = interface_name.upcase
55
+
56
+ timeout = COMMAND_ACK_TIMEOUT_S unless timeout
57
+ ack_topic = "{#{scope}__ACKCMD}INTERFACE__#{interface_name}"
58
+ Topic.update_topic_offsets([ack_topic])
59
+
60
+ cmd_id = Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'raw' => data }, '*', 100)
61
+ time = Time.now
62
+ while (Time.now - time) < timeout
63
+ Topic.read_topics([ack_topic]) do |_topic, _msg_id, msg_hash, _redis|
64
+ if msg_hash["id"] == cmd_id
65
+ if msg_hash["result"] == "SUCCESS"
66
+ return
67
+ else
68
+ raise msg_hash["result"]
69
+ end
70
+ end
71
+ end
72
+ end
73
+ raise "Timeout of #{timeout}s waiting for cmd ack"
54
74
  end
55
75
 
56
76
  def self.connect_interface(interface_name, *interface_params, scope:)
@@ -1,6 +1,6 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- # Copyright 2024 OpenC3, Inc.
3
+ # Copyright 2025 OpenC3, Inc.
4
4
  # All Rights Reserved.
5
5
  #
6
6
  # This program is free software; you can modify and/or redistribute it
@@ -18,7 +18,7 @@
18
18
 
19
19
  module OpenC3
20
20
  class CliGenerator
21
- GENERATORS = %w(plugin target microservice widget conversion limits_response tool tool_vue tool_angular tool_react tool_svelte)
21
+ GENERATORS = %w(plugin target microservice widget conversion processor limits_response tool tool_vue tool_angular tool_react tool_svelte)
22
22
  TEMPLATES_DIR = "#{File.dirname(__FILE__)}/../../../templates"
23
23
 
24
24
  # Called by openc3cli with ARGV[1..-1]
@@ -32,8 +32,8 @@ module OpenC3
32
32
 
33
33
  def self.check_args(args)
34
34
  args.each do |arg|
35
- if arg =~ /\s/
36
- abort("#{args[0].to_s.downcase} arguments can not have spaces!") unless args[0].to_s.downcase[0..3] == 'tool'
35
+ if arg =~ /\s/ and args[0].to_s.downcase[0..3] != 'tool'
36
+ abort("#{args[0].to_s.downcase} arguments can not have spaces!")
37
37
  end
38
38
  end
39
39
  # All generators except 'plugin' must be within an existing plugin
@@ -58,12 +58,12 @@ module OpenC3
58
58
  def self.process_template(template_dir, the_binding)
59
59
  Dir.glob("#{template_dir}/**/*", File::FNM_DOTMATCH).each do |file|
60
60
  next if File.basename(file) == '.'
61
- if @@language == 'rb'
61
+ if @@language == 'rb' and File.extname(file) == '.py'
62
62
  # Ignore python files if we're ruby
63
- next if File.extname(file) == '.py'
64
- elsif @@language == 'py'
63
+ next
64
+ elsif @@language == 'py' and File.extname(file) == '.rb'
65
65
  # Ignore ruby files if we're python
66
- next if File.extname(file) == '.rb'
66
+ next
67
67
  end
68
68
  base_name = file.sub("#{template_dir}/", '')
69
69
  next if yield base_name
@@ -113,8 +113,8 @@ module OpenC3
113
113
  abort("Target #{target_path} already exists!")
114
114
  end
115
115
  target_lib_filename = "#{target_name.downcase}.#{@@language}"
116
- target_class = target_lib_filename.filename_to_class_name
117
- target_object = target_name.downcase
116
+ target_class = target_lib_filename.filename_to_class_name # NOSONAR
117
+ target_object = target_name.downcase # NOSONAR
118
118
 
119
119
  process_template("#{TEMPLATES_DIR}/target", binding) do |filename|
120
120
  # Rename the template TARGET to our actual target named after the plugin
@@ -175,7 +175,7 @@ module OpenC3
175
175
  abort("Microservice #{microservice_path} already exists!")
176
176
  end
177
177
  microservice_filename = "#{microservice_name.downcase}.#{@@language}"
178
- microservice_class = microservice_filename.filename_to_class_name
178
+ microservice_class = microservice_filename.filename_to_class_name # NOSONAR
179
179
 
180
180
  process_template("#{TEMPLATES_DIR}/microservice", binding) do |filename|
181
181
  # Rename the template MICROSERVICE to our actual microservice name
@@ -310,9 +310,8 @@ module OpenC3
310
310
  abort("Target '#{target_name}' does not exist! Conversions must be created for existing targets.")
311
311
  end
312
312
  conversion_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_CONVERSION"
313
- conversion_path = "targets/#{target_name}/lib/"
314
313
  conversion_basename = "#{conversion_name.downcase}.#{@@language}"
315
- conversion_class = conversion_basename.filename_to_class_name
314
+ conversion_class = conversion_basename.filename_to_class_name # NOSONAR
316
315
  conversion_filename = "targets/#{target_name}/lib/#{conversion_basename}"
317
316
  if File.exist?(conversion_filename)
318
317
  abort("Conversion #{conversion_filename} already exists!")
@@ -329,6 +328,35 @@ module OpenC3
329
328
  return conversion_name
330
329
  end
331
330
 
331
+ def self.generate_processor(args)
332
+ if args.length < 3 or args.length > 4
333
+ abort("Usage: cli generate processor <TARGET> <NAME> (--ruby or --python)")
334
+ end
335
+
336
+ # Create the local variables
337
+ target_name = args[1].upcase
338
+ unless File.exist?("targets/#{target_name}")
339
+ abort("Target '#{target_name}' does not exist! Processors must be created for existing targets.")
340
+ end
341
+ processor_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_PROCESSOR"
342
+ processor_basename = "#{processor_name.downcase}.#{@@language}"
343
+ processor_class = processor_basename.filename_to_class_name # NOSONAR
344
+ processor_filename = "targets/#{target_name}/lib/#{processor_basename}"
345
+ if File.exist?(processor_filename)
346
+ abort("Processor #{processor_filename} already exists!")
347
+ end
348
+
349
+ process_template("#{TEMPLATES_DIR}/processor", binding) do |filename|
350
+ filename.sub!("processor.#{@@language}", processor_filename)
351
+ false
352
+ end
353
+
354
+ puts "Processor #{processor_filename} successfully generated!"
355
+ puts "To use the processor add the following to a telemetry packet:"
356
+ puts " PROCESSOR #{args[2].upcase} #{processor_basename} <PARAMS...>"
357
+ return processor_name
358
+ end
359
+
332
360
  def self.generate_limits_response(args)
333
361
  if args.length < 3 or args.length > 4
334
362
  abort("Usage: cli generate limits_response <TARGET> <NAME> (--ruby or --python)")
@@ -340,10 +368,9 @@ module OpenC3
340
368
  abort("Target '#{target_name}' does not exist! Limits responses must be created for existing targets.")
341
369
  end
342
370
  response_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_LIMITS_RESPONSE"
343
- response_path = "targets/#{target_name}/lib/"
344
371
  response_basename = "#{response_name.downcase}.#{@@language}"
345
- response_class = response_basename.filename_to_class_name
346
372
  response_filename = "targets/#{target_name}/lib/#{response_basename}"
373
+ response_class = response_basename.filename_to_class_name # NOSONAR
347
374
  if File.exist?(response_filename)
348
375
  abort("response #{response_filename} already exists!")
349
376
  end