openc3 6.9.1 → 6.9.2

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.
@@ -237,7 +237,7 @@ module OpenC3
237
237
 
238
238
  # ["#{@scope}__DECOM__{#{@target}}__#{@packet}"]
239
239
  def generate_topics
240
- topics = Hash.new
240
+ topics = {}
241
241
  if @left['type'] == ITEM_TYPE
242
242
  topics["#{@scope}__DECOM__{#{left['target']}}__#{left['packet']}"] = 1
243
243
  end
@@ -232,7 +232,7 @@ module OpenC3
232
232
  raise "initialize_offline_access only valid in COSMOS Enterprise. OPENC3_KEYCLOAK_URL environment variable must be set."
233
233
  end
234
234
  auth = OpenC3KeycloakAuthentication.new(keycloak_url)
235
- auth.token(include_bearer: true, openid_scope: 'openid%20offline_access')
235
+ auth.token(include_bearer: true, openid_scope: 'openid offline_access')
236
236
  set_offline_access(auth.refresh_token)
237
237
  end
238
238
 
@@ -95,11 +95,11 @@ module OpenC3
95
95
  bucket_key = "#{scope}/target_archives/#{target_name}/#{target_name}_current.zip"
96
96
  Logger.info("Retrieving #{bucket_key} from targets bucket")
97
97
  bucket.get_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: bucket_key, path: zip_path)
98
+ targets_path = "#{base_dir}/targets"
99
+ FileUtils.mkdir_p(targets_path)
98
100
  Zip::File.open(zip_path) do |zip_file|
99
101
  zip_file.each do |entry|
100
- path = File.join("#{base_dir}/targets", entry.name)
101
- FileUtils.mkdir_p(File.dirname(path))
102
- zip_file.extract(entry, path) unless File.exist?(path)
102
+ zip_file.extract(entry.name, destination_directory: targets_path)
103
103
  end
104
104
  end
105
105
 
@@ -23,6 +23,7 @@
23
23
  require 'openc3/version'
24
24
  require 'openc3/io/json_drb'
25
25
  require 'faraday'
26
+ require 'uri'
26
27
 
27
28
  module OpenC3
28
29
  # Basic exception for known errors
@@ -112,9 +113,13 @@ module OpenC3
112
113
  client_id = ENV['OPENC3_API_CLIENT'] || 'api'
113
114
  if ENV['OPENC3_API_USER'] and ENV['OPENC3_API_PASSWORD']
114
115
  # Username and password
115
- data = "username=#{ENV['OPENC3_API_USER']}&password=#{ENV['OPENC3_API_PASSWORD']}"
116
- data << "&client_id=#{client_id}"
117
- data << "&grant_type=password&scope=#{openid_scope}"
116
+ data = {
117
+ 'username' => ENV['OPENC3_API_USER'],
118
+ 'password' => ENV['OPENC3_API_PASSWORD'],
119
+ 'client_id' => client_id,
120
+ 'grant_type' => 'password',
121
+ 'scope' => openid_scope
122
+ }
118
123
  headers = {
119
124
  'Content-Type' => 'application/x-www-form-urlencoded',
120
125
  'User-Agent' => "OpenC3KeycloakAuthorization / #{OPENC3_VERSION} (ruby/openc3/lib/utilities/authentication)",
@@ -134,7 +139,11 @@ module OpenC3
134
139
  # Refresh the token and save token to instance
135
140
  def _refresh_token(current_time)
136
141
  client_id = ENV['OPENC3_API_CLIENT'] || 'api'
137
- data = "client_id=#{client_id}&refresh_token=#{@refresh_token}&grant_type=refresh_token"
142
+ data = {
143
+ 'client_id' => client_id,
144
+ 'refresh_token' => @refresh_token,
145
+ 'grant_type' => 'refresh_token'
146
+ }
138
147
  headers = {
139
148
  'Content-Type' => 'application/x-www-form-urlencoded',
140
149
  'User-Agent' => "OpenC3KeycloakAuthorization / #{OPENC3_VERSION} (ruby/openc3/lib/utilities/authentication)",
@@ -150,11 +159,21 @@ module OpenC3
150
159
  def _make_request(headers, data)
151
160
  realm = ENV['OPENC3_KEYCLOAK_REALM'] || 'openc3'
152
161
  uri = URI("#{@url}/realms/#{realm}/protocol/openid-connect/token")
153
- @log[0] = "request uri: #{uri} header: #{headers} body: #{data}"
162
+ # Obfuscate password and refresh token in logs unless debug mode is enabled
163
+ if JsonDRb.debug?
164
+ log_data = data.inspect
165
+ else
166
+ # Create a copy of the hash with sensitive values obfuscated
167
+ log_data = data.dup
168
+ log_data['password'] = '***' if log_data.key?('password')
169
+ log_data['refresh_token'] = '***' if log_data.key?('refresh_token')
170
+ log_data = log_data.inspect
171
+ end
172
+ @log[0] = "request uri: #{uri} header: #{headers} body: #{log_data}"
154
173
  STDOUT.puts @log[0] if JsonDRb.debug?
155
174
  saved_verbose = $VERBOSE; $VERBOSE = nil
156
175
  begin
157
- resp = @http.post(uri, data, headers)
176
+ resp = @http.post(uri, URI.encode_www_form(data), headers)
158
177
  ensure
159
178
  $VERBOSE = saved_verbose
160
179
  end
@@ -18,15 +18,55 @@
18
18
 
19
19
  module OpenC3
20
20
  class CliGenerator
21
- GENERATORS = %w(plugin target microservice widget conversion processor 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 command_validator)
22
22
  TEMPLATES_DIR = "#{File.dirname(__FILE__)}/../../../templates"
23
23
 
24
24
  # Called by openc3cli with ARGV[1..-1]
25
25
  def self.generate(args)
26
+ if args[0].nil? || args[0] == '--help' || args[0] == '-h'
27
+ puts "Usage: cli generate GENERATOR [ARGS...] (--ruby or --python)"
28
+ puts ""
29
+ puts "Generate COSMOS components from templates"
30
+ puts ""
31
+ puts "Available Generators:"
32
+ puts " plugin Create a new COSMOS plugin"
33
+ puts " target Create a new target within a plugin"
34
+ puts " microservice Create a new microservice within a plugin"
35
+ puts " widget Create a new custom widget"
36
+ puts " conversion Create a new conversion class for a target"
37
+ puts " processor Create a new processor for a target"
38
+ puts " limits_response Create a new limits response for a target"
39
+ puts " command_validator Create a new command validator for a target"
40
+ puts " tool Create a new tool (Vue.js by default)"
41
+ puts " tool_vue Create a new Vue.js tool"
42
+ puts " tool_angular Create a new Angular tool"
43
+ puts " tool_react Create a new React tool"
44
+ puts " tool_svelte Create a new Svelte tool"
45
+ puts ""
46
+ puts "Run 'cli generate GENERATOR --help' for detailed help on each generator."
47
+ puts ""
48
+ puts "Options:"
49
+ puts " --ruby Generate Ruby code (or set OPENC3_LANGUAGE=ruby)"
50
+ puts " --python Generate Python code (or set OPENC3_LANGUAGE=python)"
51
+ puts " -h, --help Show this help message"
52
+ puts ""
53
+ puts "Examples:"
54
+ puts " cli generate plugin MyPlugin --ruby"
55
+ puts " cli generate target EXAMPLE --python"
56
+ puts " cli generate widget SuperdataWidget --ruby"
57
+ puts " cli generate conversion EXAMPLE STATUS --ruby"
58
+ puts ""
59
+ puts "Documentation:"
60
+ puts " https://docs.openc3.com/docs/development/developing"
61
+ exit(args[0].nil? ? 1 : 0)
62
+ end
26
63
  unless GENERATORS.include?(args[0])
27
64
  abort("Unknown generator '#{args[0]}'. Valid generators: #{GENERATORS.join(', ')}")
28
65
  end
29
- check_args(args)
66
+ # Skip argument validation if user is requesting help
67
+ unless args[1] == '--help' || args[1] == '-h'
68
+ check_args(args)
69
+ end
30
70
  send("generate_#{args[0].to_s.downcase.gsub('-', '_')}", args)
31
71
  end
32
72
 
@@ -79,6 +119,29 @@ module OpenC3
79
119
  end
80
120
 
81
121
  def self.generate_plugin(args)
122
+ if args[1].nil? || args[1] == '--help' || args[1] == '-h'
123
+ puts "Usage: cli generate plugin NAME (--ruby or --python)"
124
+ puts ""
125
+ puts "Generate a new COSMOS plugin"
126
+ puts ""
127
+ puts "Arguments:"
128
+ puts " NAME Name of the plugin (required)"
129
+ puts " Will be prefixed with 'openc3-cosmos-'"
130
+ puts " Spaces, underscores, and hyphens will be converted to hyphens"
131
+ puts ""
132
+ puts "Options:"
133
+ puts " --ruby Generate Ruby plugin (or set OPENC3_LANGUAGE=ruby)"
134
+ puts " --python Generate Python plugin (or set OPENC3_LANGUAGE=python)"
135
+ puts " -h, --help Show this help message"
136
+ puts ""
137
+ puts "Example:"
138
+ puts " cli generate plugin demo --ruby"
139
+ puts " Creates: openc3-cosmos-demo/"
140
+ puts ""
141
+ puts "Documentation:"
142
+ puts " https://docs.openc3.com/docs/configuration/plugins"
143
+ exit(args[1].nil? ? 1 : 0)
144
+ end
82
145
  if args.length < 2 or args.length > 3
83
146
  abort("Usage: cli generate #{args[0]} <NAME> (--ruby or --python)")
84
147
  end
@@ -102,6 +165,30 @@ module OpenC3
102
165
  end
103
166
 
104
167
  def self.generate_target(args)
168
+ if args[1].nil? || args[1] == '--help' || args[1] == '-h'
169
+ puts "Usage: cli generate target NAME (--ruby or --python)"
170
+ puts ""
171
+ puts "Generate a new target within an existing plugin"
172
+ puts ""
173
+ puts "Arguments:"
174
+ puts " NAME Name of the target (required)"
175
+ puts " Will be uppercased and underscores/hyphens converted to underscores"
176
+ puts ""
177
+ puts "Options:"
178
+ puts " --ruby Generate Ruby target (or set OPENC3_LANGUAGE=ruby)"
179
+ puts " --python Generate Python target (or set OPENC3_LANGUAGE=python)"
180
+ puts " -h, --help Show this help message"
181
+ puts ""
182
+ puts "Example:"
183
+ puts " cli generate target EXAMPLE --ruby"
184
+ puts " Creates: targets/EXAMPLE/"
185
+ puts ""
186
+ puts "Note: Must be run from within an existing plugin directory"
187
+ puts ""
188
+ puts "Documentation:"
189
+ puts " https://docs.openc3.com/docs/configuration/target"
190
+ exit(args[1].nil? ? 1 : 0)
191
+ end
105
192
  if args.length < 2 or args.length > 3
106
193
  abort("Usage: cli generate #{args[0]} <NAME> (--ruby or --python)")
107
194
  end
@@ -133,7 +220,17 @@ module OpenC3
133
220
  end
134
221
  gemspec_filename = Dir['*.gemspec'][0]
135
222
  gemspec = File.read(gemspec_filename)
136
- gemspec.gsub!('plugin.txt', 'plugin.txt requirements.txt')
223
+ gemspec.gsub!(/s\.files = Dir\.glob.*\n/) do |match|
224
+ <<RUBY
225
+ # Prefer pyproject.toml over requirements.txt
226
+ python_dep_file = if File.exist?('pyproject.toml')
227
+ 'pyproject.toml'
228
+ else
229
+ 'requirements.txt'
230
+ end
231
+ s.files = Dir.glob("{targets,lib,tools,microservices}/**/*") + %w(Rakefile README.md LICENSE.txt plugin.txt) + [python_dep_file]
232
+ RUBY
233
+ end
137
234
  File.write(gemspec_filename, gemspec)
138
235
 
139
236
  target_txt_filename = "targets/#{target_name}/target.txt"
@@ -164,6 +261,30 @@ module OpenC3
164
261
  end
165
262
 
166
263
  def self.generate_microservice(args)
264
+ if args[1].nil? || args[1] == '--help' || args[1] == '-h'
265
+ puts "Usage: cli generate microservice NAME (--ruby or --python)"
266
+ puts ""
267
+ puts "Generate a new microservice within an existing plugin"
268
+ puts ""
269
+ puts "Arguments:"
270
+ puts " NAME Name of the microservice (required)"
271
+ puts " Will be uppercased and underscores/hyphens converted to underscores"
272
+ puts ""
273
+ puts "Options:"
274
+ puts " --ruby Generate Ruby microservice (or set OPENC3_LANGUAGE=ruby)"
275
+ puts " --python Generate Python microservice (or set OPENC3_LANGUAGE=python)"
276
+ puts " -h, --help Show this help message"
277
+ puts ""
278
+ puts "Example:"
279
+ puts " cli generate microservice DATA_PROCESSOR --ruby"
280
+ puts " Creates: microservices/DATA_PROCESSOR/"
281
+ puts ""
282
+ puts "Note: Must be run from within an existing plugin directory"
283
+ puts ""
284
+ puts "Documentation:"
285
+ puts " https://docs.openc3.com/docs/configuration/plugins#microservices"
286
+ exit(args[1].nil? ? 1 : 0)
287
+ end
167
288
  if args.length < 2 or args.length > 3
168
289
  abort("Usage: cli generate #{args[0]} <NAME> (--ruby or --python)")
169
290
  end
@@ -203,6 +324,31 @@ module OpenC3
203
324
  end
204
325
 
205
326
  def self.generate_widget(args)
327
+ if args[1].nil? || args[1] == '--help' || args[1] == '-h'
328
+ puts "Usage: cli generate widget NAME (--ruby or --python)"
329
+ puts ""
330
+ puts "Generate a new custom Vue.js widget within an existing plugin"
331
+ puts ""
332
+ puts "Arguments:"
333
+ puts " NAME Name of the widget (required)"
334
+ puts " Must be CapitalCase ending with 'Widget'"
335
+ puts " Example: SuperdataWidget, StatusWidget"
336
+ puts ""
337
+ puts "Options:"
338
+ puts " --ruby Generate Ruby plugin (or set OPENC3_LANGUAGE=ruby)"
339
+ puts " --python Generate Python plugin (or set OPENC3_LANGUAGE=python)"
340
+ puts " -h, --help Show this help message"
341
+ puts ""
342
+ puts "Example:"
343
+ puts " cli generate widget SuperdataWidget --ruby"
344
+ puts " Creates: src/SuperdataWidget.vue"
345
+ puts ""
346
+ puts "Note: Must be run from within an existing plugin directory"
347
+ puts ""
348
+ puts "Documentation:"
349
+ puts " https://docs.openc3.com/docs/guides/custom-widgets"
350
+ exit(args[1].nil? ? 1 : 0)
351
+ end
206
352
  if args.length < 2 or args.length > 3
207
353
  abort("Usage: cli generate #{args[0]} <SuperdataWidget> (--ruby or --python)")
208
354
  end
@@ -249,6 +395,75 @@ module OpenC3
249
395
  end
250
396
 
251
397
  def self.generate_tool(args)
398
+ if args[1].nil? || args[1] == '--help' || args[1] == '-h'
399
+ tool_type = args[0].to_s.downcase.gsub('-', '_')
400
+
401
+ # Specific help for tool variants
402
+ if tool_type != 'tool'
403
+ framework = case tool_type
404
+ when 'tool_vue' then 'Vue.js'
405
+ when 'tool_react' then 'React'
406
+ when 'tool_angular' then 'Angular'
407
+ when 'tool_svelte' then 'Svelte'
408
+ else 'Custom'
409
+ end
410
+
411
+ puts "Usage: cli generate #{args[0]} 'TOOL NAME' (--ruby or --python)"
412
+ puts ""
413
+ puts "Generate a new #{framework} tool within an existing plugin"
414
+ puts ""
415
+ puts "Arguments:"
416
+ puts " TOOL NAME Display name of the tool (required, can include spaces)"
417
+ puts " Will be converted to lowercase without spaces for directory name"
418
+ puts ""
419
+ puts "Options:"
420
+ puts " --ruby Generate Ruby plugin (or set OPENC3_LANGUAGE=ruby)"
421
+ puts " --python Generate Python plugin (or set OPENC3_LANGUAGE=python)"
422
+ puts " -h, --help Show this help message"
423
+ puts ""
424
+ puts "Example:"
425
+ puts " cli generate #{args[0]} 'Data Viewer' --ruby"
426
+ puts " Creates: tools/dataviewer/ (#{framework}-based)"
427
+ puts ""
428
+ puts "Note: Must be run from within an existing plugin directory"
429
+ puts " For other tool types, see: cli generate tool --help"
430
+ puts ""
431
+ puts "Documentation:"
432
+ puts " https://docs.openc3.com/docs/guides/custom-tools"
433
+ exit(args[1].nil? ? 1 : 0)
434
+ else
435
+ # Generic help showing all types
436
+ puts "Usage: cli generate #{args[0]} 'TOOL NAME' (--ruby or --python)"
437
+ puts ""
438
+ puts "Generate a new custom tool within an existing plugin"
439
+ puts ""
440
+ puts "Arguments:"
441
+ puts " TOOL NAME Display name of the tool (required, can include spaces)"
442
+ puts " Will be converted to lowercase without spaces for directory name"
443
+ puts ""
444
+ puts "Options:"
445
+ puts " --ruby Generate Ruby plugin (or set OPENC3_LANGUAGE=ruby)"
446
+ puts " --python Generate Python plugin (or set OPENC3_LANGUAGE=python)"
447
+ puts " -h, --help Show this help message"
448
+ puts ""
449
+ puts "Tool Types:"
450
+ puts " tool Generate Vue.js tool (default)"
451
+ puts " tool_vue Generate Vue.js tool"
452
+ puts " tool_angular Generate Angular tool"
453
+ puts " tool_react Generate React tool"
454
+ puts " tool_svelte Generate Svelte tool"
455
+ puts ""
456
+ puts "Example:"
457
+ puts " cli generate tool 'Data Viewer' --ruby"
458
+ puts " Creates: tools/dataviewer/"
459
+ puts ""
460
+ puts "Note: Must be run from within an existing plugin directory"
461
+ puts ""
462
+ puts "Documentation:"
463
+ puts " https://docs.openc3.com/docs/guides/custom-tools"
464
+ exit(args[1].nil? ? 1 : 0)
465
+ end
466
+ end
252
467
  if args.length < 2 or args.length > 3
253
468
  abort("Usage: cli generate #{args[0]} 'Tool Name' (--ruby or --python)")
254
469
  end
@@ -300,6 +515,31 @@ module OpenC3
300
515
  self.singleton_class.send(:alias_method, :generate_tool_svelte, :generate_tool)
301
516
 
302
517
  def self.generate_conversion(args)
518
+ if args[1].nil? || args[2].nil? || args[1] == '--help' || args[1] == '-h'
519
+ puts "Usage: cli generate conversion TARGET NAME (--ruby or --python)"
520
+ puts ""
521
+ puts "Generate a new conversion class for an existing target"
522
+ puts ""
523
+ puts "Arguments:"
524
+ puts " TARGET Target name (required, must exist)"
525
+ puts " NAME Conversion name (required)"
526
+ puts " Will be uppercased with '_CONVERSION' suffix"
527
+ puts ""
528
+ puts "Options:"
529
+ puts " --ruby Generate Ruby conversion (or set OPENC3_LANGUAGE=ruby)"
530
+ puts " --python Generate Python conversion (or set OPENC3_LANGUAGE=python)"
531
+ puts " -h, --help Show this help message"
532
+ puts ""
533
+ puts "Example:"
534
+ puts " cli generate conversion EXAMPLE STATUS --ruby"
535
+ puts " Creates: targets/EXAMPLE/lib/status_conversion.rb"
536
+ puts ""
537
+ puts "Note: Must be run from within an existing plugin directory"
538
+ puts ""
539
+ puts "Documentation:"
540
+ puts " https://docs.openc3.com/docs/configuration/telemetry#read_conversion"
541
+ exit(args[1].nil? || args[2].nil? ? 1 : 0)
542
+ end
303
543
  if args.length < 3 or args.length > 4
304
544
  abort("Usage: cli generate conversion <TARGET> <NAME> (--ruby or --python)")
305
545
  end
@@ -329,6 +569,31 @@ module OpenC3
329
569
  end
330
570
 
331
571
  def self.generate_processor(args)
572
+ if args[1].nil? || args[2].nil? || args[1] == '--help' || args[1] == '-h'
573
+ puts "Usage: cli generate processor TARGET NAME (--ruby or --python)"
574
+ puts ""
575
+ puts "Generate a new processor for an existing target"
576
+ puts ""
577
+ puts "Arguments:"
578
+ puts " TARGET Target name (required, must exist)"
579
+ puts " NAME Processor name (required)"
580
+ puts " Will be uppercased with '_PROCESSOR' suffix"
581
+ puts ""
582
+ puts "Options:"
583
+ puts " --ruby Generate Ruby processor (or set OPENC3_LANGUAGE=ruby)"
584
+ puts " --python Generate Python processor (or set OPENC3_LANGUAGE=python)"
585
+ puts " -h, --help Show this help message"
586
+ puts ""
587
+ puts "Example:"
588
+ puts " cli generate processor EXAMPLE DATA --ruby"
589
+ puts " Creates: targets/EXAMPLE/lib/data_processor.rb"
590
+ puts ""
591
+ puts "Note: Must be run from within an existing plugin directory"
592
+ puts ""
593
+ puts "Documentation:"
594
+ puts " https://docs.openc3.com/docs/configuration/telemetry#processor"
595
+ exit(args[1].nil? || args[2].nil? ? 1 : 0)
596
+ end
332
597
  if args.length < 3 or args.length > 4
333
598
  abort("Usage: cli generate processor <TARGET> <NAME> (--ruby or --python)")
334
599
  end
@@ -358,6 +623,31 @@ module OpenC3
358
623
  end
359
624
 
360
625
  def self.generate_limits_response(args)
626
+ if args[1].nil? || args[2].nil? || args[1] == '--help' || args[1] == '-h'
627
+ puts "Usage: cli generate limits_response TARGET NAME (--ruby or --python)"
628
+ puts ""
629
+ puts "Generate a new limits response for an existing target"
630
+ puts ""
631
+ puts "Arguments:"
632
+ puts " TARGET Target name (required, must exist)"
633
+ puts " NAME Limits response name (required)"
634
+ puts " Will be uppercased with '_LIMITS_RESPONSE' suffix"
635
+ puts ""
636
+ puts "Options:"
637
+ puts " --ruby Generate Ruby limits response (or set OPENC3_LANGUAGE=ruby)"
638
+ puts " --python Generate Python limits response (or set OPENC3_LANGUAGE=python)"
639
+ puts " -h, --help Show this help message"
640
+ puts ""
641
+ puts "Example:"
642
+ puts " cli generate limits_response EXAMPLE CUSTOM --ruby"
643
+ puts " Creates: targets/EXAMPLE/lib/custom_limits_response.rb"
644
+ puts ""
645
+ puts "Note: Must be run from within an existing plugin directory"
646
+ puts ""
647
+ puts "Documentation:"
648
+ puts " https://docs.openc3.com/docs/configuration/telemetry#limits_response"
649
+ exit(args[1].nil? || args[2].nil? ? 1 : 0)
650
+ end
361
651
  if args.length < 3 or args.length > 4
362
652
  abort("Usage: cli generate limits_response <TARGET> <NAME> (--ruby or --python)")
363
653
  end
@@ -385,5 +675,59 @@ module OpenC3
385
675
  puts " LIMITS_RESPONSE #{response_basename}"
386
676
  return response_name
387
677
  end
678
+
679
+ def self.generate_command_validator(args)
680
+ if args[1].nil? || args[2].nil? || args[1] == '--help' || args[1] == '-h'
681
+ puts "Usage: cli generate command_validator TARGET NAME (--ruby or --python)"
682
+ puts ""
683
+ puts "Generate a new command validator for an existing target"
684
+ puts ""
685
+ puts "Arguments:"
686
+ puts " TARGET Target name (required, must exist)"
687
+ puts " NAME Command validator name (required)"
688
+ puts " Will be uppercased with '_COMMAND_VALIDATOR' suffix"
689
+ puts ""
690
+ puts "Options:"
691
+ puts " --ruby Generate Ruby command validator (or set OPENC3_LANGUAGE=ruby)"
692
+ puts " --python Generate Python command validator (or set OPENC3_LANGUAGE=python)"
693
+ puts " -h, --help Show this help message"
694
+ puts ""
695
+ puts "Example:"
696
+ puts " cli generate command_validator EXAMPLE RANGE --ruby"
697
+ puts " Creates: targets/EXAMPLE/lib/range_command_validator.rb"
698
+ puts ""
699
+ puts "Note: Must be run from within an existing plugin directory"
700
+ puts ""
701
+ puts "Documentation:"
702
+ puts " https://docs.openc3.com/docs/configuration/command#validator"
703
+ exit(args[1].nil? || args[2].nil? ? 1 : 0)
704
+ end
705
+ if args.length < 3 or args.length > 4
706
+ abort("Usage: cli generate command_validator <TARGET> <NAME> (--ruby or --python)")
707
+ end
708
+
709
+ # Create the local variables
710
+ target_name = args[1].upcase
711
+ unless File.exist?("targets/#{target_name}")
712
+ abort("Target '#{target_name}' does not exist! Command validators must be created for existing targets.")
713
+ end
714
+ validator_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_COMMAND_VALIDATOR"
715
+ validator_basename = "#{validator_name.downcase}.#{@@language}"
716
+ validator_class = validator_basename.filename_to_class_name # NOSONAR
717
+ validator_filename = "targets/#{target_name}/lib/#{validator_basename}"
718
+ if File.exist?(validator_filename)
719
+ abort("Command validator #{validator_filename} already exists!")
720
+ end
721
+
722
+ process_template("#{TEMPLATES_DIR}/command_validator", binding) do |filename|
723
+ filename.sub!("command_validator.#{@@language}", validator_filename)
724
+ false
725
+ end
726
+
727
+ puts "Command validator #{validator_filename} successfully generated!"
728
+ puts "To use the command validator add the following to a command:"
729
+ puts " VALIDATOR #{validator_basename}"
730
+ return validator_name
731
+ end
388
732
  end
389
733
  end
@@ -1,14 +1,14 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- OPENC3_VERSION = '6.9.1'
3
+ OPENC3_VERSION = '6.9.2'
4
4
  module OpenC3
5
5
  module Version
6
6
  MAJOR = '6'
7
7
  MINOR = '9'
8
- PATCH = '1'
8
+ PATCH = '2'
9
9
  OTHER = ''
10
- BUILD = 'e1529f7b88799a9f85f31378db40a48b6c106899'
10
+ BUILD = '0b659342611ec28c5dac351032c18442820977a3'
11
11
  end
12
- VERSION = '6.9.1'
13
- GEM_VERSION = '6.9.1'
12
+ VERSION = '6.9.2'
13
+ GEM_VERSION = '6.9.2'
14
14
  end
@@ -0,0 +1,49 @@
1
+ from openc3.packets.command_validator import CommandValidator
2
+ # Using the OpenC3 API requires the following imports:
3
+ # from openc3.api import wait_check
4
+
5
+ # Custom command validator class
6
+ # See https://docs.openc3.com/docs/configuration/command
7
+ class <%= validator_class %>(CommandValidator):
8
+ def __init__(self, *args):
9
+ super().__init__()
10
+ self.args = args
11
+
12
+ # Called before a command is sent
13
+ # @param command [dict] The command dictionary containing all the command details
14
+ # @return [list] First element is True/False/None for success/failure/unknown,
15
+ # second element is an optional message string
16
+ def pre_check(self, command):
17
+ # Add your pre-command validation logic here
18
+ # Example:
19
+ # target_name = command['target_name']
20
+ # command_name = command['cmd_name']
21
+ # params = command['cmd_params']
22
+ # self.count = tlm("TARGET PACKET COUNT")
23
+ #
24
+ # if some_condition:
25
+ # return [False, "Command validation failed: reason"]
26
+
27
+ # Return True to indicate Success, False to indicate Failure,
28
+ # and None to indicate Unknown. The second value is the optional message.
29
+ return [True, None]
30
+
31
+ # Called after a command is sent
32
+ # @param command [dict] The command dictionary containing all the command details
33
+ # @return [list] First element is True/False/None for success/failure/unknown,
34
+ # second element is an optional message string
35
+ def post_check(self, command):
36
+ # Add your post-command validation logic here
37
+ # Example:
38
+ # Use the OpenC3 API to check telemetry or wait for responses
39
+ # wait_check("TARGET PACKET ITEM == 'EXPECTED'", 5) # Wait up to 5 seconds
40
+ # wait_check(f"TARGET PACKET COUNT > {self.count}", 5) # Wait up to 5 seconds
41
+ #
42
+ # if some_condition:
43
+ # return [False, "Post-command validation failed: reason"]
44
+ #
45
+ # Wait for telemetry
46
+
47
+ # Return True to indicate Success, False to indicate Failure,
48
+ # and None to indicate Unknown. The second value is the optional message.
49
+ return [True, None]
@@ -0,0 +1,54 @@
1
+ # encoding: ascii-8bit
2
+ require 'openc3/packets/command_validator'
3
+
4
+ module OpenC3
5
+ # Custom command validator class
6
+ # See https://docs.openc3.com/docs/configuration/command
7
+ class <%= validator_class %> < CommandValidator
8
+ def initialize(*args)
9
+ super()
10
+ @args = args
11
+ end
12
+
13
+ # Called before a command is sent
14
+ # @param command [Hash] The command hash containing all the command details
15
+ # @return [Array<Boolean, String>] First element is true/false/nil for success/failure/unknown,
16
+ # second element is an optional message string
17
+ def pre_check(command)
18
+ # Add your pre-command validation logic here
19
+ # Example:
20
+ # target_name = command['target_name']
21
+ # command_name = command['cmd_name']
22
+ # params = command['cmd_params']
23
+ # @count = tlm("TARGET PACKET COUNT")
24
+ #
25
+ # if some_condition
26
+ # return [false, "Command validation failed: reason"]
27
+ # end
28
+
29
+ # Return true to indicate Success, false to indicate Failure,
30
+ # and nil to indicate Unknown. The second value is the optional message.
31
+ return [true, nil]
32
+ end
33
+
34
+ # Called after a command is sent
35
+ # @param command [Hash] The command hash containing all the command details
36
+ # @return [Array<Boolean, String>] First element is true/false/nil for success/failure/unknown,
37
+ # second element is an optional message string
38
+ def post_check(command)
39
+ # Add your post-command validation logic here
40
+ # Example:
41
+ # Use the OpenC3 API to check telemetry or wait for responses
42
+ # wait_check("TARGET PACKET ITEM == 'EXPECTED'", 5) # Wait up to 5 seconds
43
+ # wait_check("TARGET PACKET COUNT > #{@count}", 5) # Wait up to 5 seconds
44
+ #
45
+ # if some_condition
46
+ # return [false, "Post-command validation failed: reason"]
47
+ # end
48
+
49
+ # Return true to indicate Success, false to indicate Failure,
50
+ # and nil to indicate Unknown. The second value is the optional message.
51
+ return [true, nil]
52
+ end
53
+ end
54
+ end