openc3 5.18.0 → 5.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -4
  3. data/bin/cstol_converter +14 -14
  4. data/bin/openc3cli +189 -7
  5. data/data/config/_interfaces.yaml +1 -1
  6. data/data/config/command_modifiers.yaml +55 -0
  7. data/data/config/interface_modifiers.yaml +1 -1
  8. data/data/config/param_item_modifiers.yaml +1 -1
  9. data/data/config/parameter_modifiers.yaml +1 -1
  10. data/data/config/plugins.yaml +6 -2
  11. data/data/config/screen.yaml +2 -2
  12. data/data/config/table_manager.yaml +2 -2
  13. data/data/config/tool.yaml +4 -1
  14. data/data/config/widgets.yaml +3 -3
  15. data/ext/openc3/ext/config_parser/config_parser.c +1 -1
  16. data/ext/openc3/ext/packet/packet.c +1 -1
  17. data/ext/openc3/ext/platform/platform.c +3 -3
  18. data/ext/openc3/ext/structure/structure.c +56 -76
  19. data/lib/openc3/accessors/binary_accessor.rb +4 -4
  20. data/lib/openc3/accessors/form_accessor.rb +2 -2
  21. data/lib/openc3/accessors/http_accessor.rb +1 -1
  22. data/lib/openc3/accessors/json_accessor.rb +6 -4
  23. data/lib/openc3/accessors/template_accessor.rb +6 -9
  24. data/lib/openc3/accessors/xml_accessor.rb +1 -1
  25. data/lib/openc3/api/cmd_api.rb +35 -11
  26. data/lib/openc3/api/limits_api.rb +1 -1
  27. data/lib/openc3/config/config_parser.rb +1 -1
  28. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +7 -7
  29. data/lib/openc3/core_ext/array.rb +5 -5
  30. data/lib/openc3/core_ext/exception.rb +9 -2
  31. data/lib/openc3/core_ext/string.rb +2 -2
  32. data/lib/openc3/interfaces/http_server_interface.rb +1 -0
  33. data/lib/openc3/interfaces/interface.rb +1 -1
  34. data/lib/openc3/interfaces/linc_interface.rb +3 -3
  35. data/lib/openc3/io/json_api.rb +11 -6
  36. data/lib/openc3/io/json_rpc.rb +1 -1
  37. data/lib/openc3/logs/buffered_packet_log_writer.rb +3 -3
  38. data/lib/openc3/logs/log_writer.rb +7 -8
  39. data/lib/openc3/logs/packet_log_writer.rb +7 -7
  40. data/lib/openc3/logs/text_log_writer.rb +4 -4
  41. data/lib/openc3/microservices/decom_microservice.rb +19 -4
  42. data/lib/openc3/microservices/interface_microservice.rb +41 -3
  43. data/lib/openc3/microservices/reaction_microservice.rb +2 -2
  44. data/lib/openc3/microservices/trigger_group_microservice.rb +3 -3
  45. data/lib/openc3/migrations/20240915000000_activity_uuid.rb +28 -0
  46. data/lib/openc3/models/activity_model.rb +109 -80
  47. data/lib/openc3/models/auth_model.rb +31 -2
  48. data/lib/openc3/models/cvt_model.rb +11 -5
  49. data/lib/openc3/models/gem_model.rb +8 -8
  50. data/lib/openc3/models/plugin_model.rb +3 -3
  51. data/lib/openc3/models/reducer_model.rb +2 -2
  52. data/lib/openc3/models/scope_model.rb +1 -1
  53. data/lib/openc3/models/sorted_model.rb +4 -4
  54. data/lib/openc3/models/target_model.rb +3 -3
  55. data/lib/openc3/models/tool_config_model.rb +1 -1
  56. data/lib/openc3/models/tool_model.rb +4 -4
  57. data/lib/openc3/models/widget_model.rb +11 -5
  58. data/lib/openc3/operators/operator.rb +5 -3
  59. data/lib/openc3/packets/command_validator.rb +48 -0
  60. data/lib/openc3/packets/commands.rb +6 -14
  61. data/lib/openc3/packets/packet.rb +31 -15
  62. data/lib/openc3/packets/packet_config.rb +10 -9
  63. data/lib/openc3/packets/parsers/packet_parser.rb +3 -3
  64. data/lib/openc3/packets/structure.rb +21 -13
  65. data/lib/openc3/packets/structure_item.rb +33 -47
  66. data/lib/openc3/packets/telemetry.rb +6 -27
  67. data/lib/openc3/script/api_shared.rb +7 -5
  68. data/lib/openc3/script/calendar.rb +2 -2
  69. data/lib/openc3/script/commands.rb +6 -4
  70. data/lib/openc3/script/metadata.rb +2 -2
  71. data/lib/openc3/script/suite.rb +17 -17
  72. data/lib/openc3/streams/serial_stream.rb +2 -3
  73. data/lib/openc3/streams/stream.rb +2 -2
  74. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +10 -10
  75. data/lib/openc3/tools/table_manager/table_manager_core.rb +11 -11
  76. data/lib/openc3/tools/table_manager/table_parser.rb +2 -3
  77. data/lib/openc3/topics/command_decom_topic.rb +2 -1
  78. data/lib/openc3/topics/command_topic.rb +3 -3
  79. data/lib/openc3/topics/decom_interface_topic.rb +2 -2
  80. data/lib/openc3/topics/telemetry_decom_topic.rb +1 -1
  81. data/lib/openc3/utilities/authorization.rb +2 -1
  82. data/lib/openc3/utilities/cli_generator.rb +15 -8
  83. data/lib/openc3/utilities/cosmos_rails_formatter.rb +60 -0
  84. data/lib/openc3/utilities/crc.rb +6 -6
  85. data/lib/openc3/utilities/local_mode.rb +2 -1
  86. data/lib/openc3/utilities/logger.rb +44 -34
  87. data/lib/openc3/utilities/metric.rb +1 -2
  88. data/lib/openc3/utilities/quaternion.rb +18 -18
  89. data/lib/openc3/utilities/target_file.rb +4 -4
  90. data/lib/openc3/version.rb +5 -5
  91. data/lib/openc3/win32/win32_main.rb +2 -2
  92. data/templates/tool_angular/package.json +21 -21
  93. data/templates/tool_react/package.json +10 -10
  94. data/templates/tool_svelte/package.json +11 -11
  95. data/templates/tool_svelte/src/services/openc3-api.js +17 -17
  96. data/templates/tool_vue/package.json +9 -9
  97. data/templates/widget/package.json +6 -7
  98. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6ba1e299807bdcbb67ae8b2e678d37152286d3061a8127fe982d0d9efe39fac
4
- data.tar.gz: 9b76aea8554d86a633ec2b2b35c250a2481550c2069a64e083d74d0cbb89d156
3
+ metadata.gz: 65a5c8a4ee0645f9d129c7c6140b9e8863e61e035f5e0269a977e8cd7d722c15
4
+ data.tar.gz: a023d452a32050ab5d81a3edcdc8fccaf10eed1a81b481ef11d89c633d3f6cba
5
5
  SHA512:
6
- metadata.gz: 34df5327bcfbcdff0c21e5856d7dcc11ee88a8f83e3e83f1a8fc9e70b37b37e36360e54f0f9ad8df8f505f9e7b6ed2cf253d0d4a8efaaec072cced50a0e104d7
7
- data.tar.gz: c78d41c4ed5b18ae17450c1e50889d169c714dbc2975cc710158de682f7012be9c8f39e1a0230aaa3d67c57c26fbdeaa82bfa8617658d3ab22ac709828248209
6
+ metadata.gz: 3ecd41dc83eb1d9af0e847fcb61d8784bfce374c33681008030423f2d1b04c09b62200ec5ca7f878273e6aed704982aeb55fddad1b1ae5490367e5fe48ea844f
7
+ data.tar.gz: f121f92ac5c6101eb9e3d9ebe52b525235090dc964a7bf409ebf7a47b1bc1c52c7f5a97a7194f5238ade4cee31078cb5822b486954d87983c8fdf9294f557257
data/Gemfile CHANGED
@@ -8,10 +8,13 @@ gemspec :name => 'openc3'
8
8
 
9
9
  # Include the rails gems for the convenience of custom microservice plugins
10
10
  gem 'bootsnap', '>= 1.9.3', require: false
11
- gem 'mock_redis', '0.44'
12
11
  gem 'rack-cors', '~> 2.0'
13
12
  gem 'rails', '~> 7.1.0'
14
- gem 'rspec-rails', '~> 6.0'
15
- gem 'simplecov', '~> 0.20'
16
- gem 'simplecov-cobertura', '~> 2.1'
17
13
  gem 'tzinfo-data'
14
+
15
+ group :test, :development do
16
+ gem 'mock_redis', '0.45'
17
+ gem 'rspec-rails', '~> 7.0'
18
+ gem 'simplecov', '~> 0.20'
19
+ gem 'simplecov-cobertura', '~> 2.1'
20
+ end
data/bin/cstol_converter CHANGED
@@ -18,7 +18,7 @@
18
18
  # All changes Copyright 2022, OpenC3, Inc.
19
19
  # All Rights Reserved
20
20
  #
21
- # This file may also be used under the terms of a commercial license
21
+ # This file may also be used under the terms of a commercial license
22
22
  # if purchased from OpenC3, Inc.
23
23
 
24
24
  # This file converts OASIS CSTOL files to OpenC3 scripts
@@ -163,7 +163,7 @@ def parse_expression(vars, quoted = false, in_eval = false)
163
163
  when /(\+|-|\*|\/)/ # Arithmetic operator
164
164
  str = str + " " + vars[i].tr('[]"', '') + " "
165
165
 
166
- # Verfies Number followed immediately by units
166
+ # Verifies Number followed immediately by units
167
167
  when /\dDN\z|\ddn\z|\dDEG\z|\ddeg\z|\dRAD\z|\drad\z|\dV\z|\dA\z|\dv\z|\da\z|\dc\z|\dC\z|\df\z|\dF\z|\dDPS\z|\dM\z|\dMPS\z|\dm\z|\dmps\z/ # Checks for decimal/degrees/volts and amps
168
168
  temp = vars[i].tr('[]"', '')
169
169
  temp = temp.gsub(/DN\z|dn\z|DEG\z|deg\z|RAD\z|rad\z|V\z|A\z|v\z|a\z|c\z|C\z|f\z|F\z|DPS\z|M\z|MPS\z|m\z|mps\z/, '')
@@ -190,7 +190,7 @@ def parse_expression(vars, quoted = false, in_eval = false)
190
190
  str = str + parse_time(vars[i].tr('[]"', ''))
191
191
 
192
192
  when /\dE/ # Floating point
193
- temp_number = vars[i].tr('E', '').to_f * 10**vars[i + 2].to_f
193
+ temp_number = vars[i].tr('E', '').to_f * (10**vars[i + 2].to_f)
194
194
  str = str + "#{temp_number}"
195
195
  finished = true
196
196
  when /\d/ # Decimal number
@@ -279,7 +279,7 @@ def parse_id(id, quoted = false, in_eval = false, in_command = false, in_single_
279
279
  elsif id.match?(/\dE\+\d/) # Floating point
280
280
  id = id.gsub('+', ' + ')
281
281
  temp_num_arr = id.split(' ')
282
- temp_number = temp_num_arr[0].tr('E', '').to_f * 10**temp_num_arr[2].to_f
282
+ temp_number = temp_num_arr[0].tr('E', '').to_f * (10**temp_num_arr[2].to_f)
283
283
  str = str + "#{temp_number}"
284
284
  elsif id.match(/\A\d/) or id.match(/\A-\d/) # starts with Decimal number
285
285
  str = id
@@ -332,7 +332,7 @@ def parse_time(time)
332
332
  end
333
333
 
334
334
  # Compute the number of seconds
335
- secs = (3600 * tok[0].to_i + 60 * tok[1].to_i + tok[2].to_i).to_s
335
+ secs = ((3600 * tok[0].to_i) + (60 * tok[1].to_i) + tok[2].to_i).to_s
336
336
 
337
337
  # Other cases not handled
338
338
  else
@@ -341,7 +341,7 @@ def parse_time(time)
341
341
  end
342
342
 
343
343
  def parse_tlm_item(tlm)
344
- # Remove preceeding parentheses
344
+ # Remove preceding parentheses
345
345
  if tlm[0].index("(") == 0
346
346
  tlm[0] = tlm[0][1..-1]
347
347
  end
@@ -371,7 +371,7 @@ def parse_line(full_line, loc_out_file, wait_check_flag = false)
371
371
  return
372
372
  end
373
373
 
374
- # Detemine the location of any comments in this line
374
+ # Determine the location of any comments in this line
375
375
  commentIdx = full_line.index(";")
376
376
 
377
377
  # If we found a comment operator
@@ -653,7 +653,7 @@ def parse_line(full_line, loc_out_file, wait_check_flag = false)
653
653
  # TODO not sure if this is wise, for some files the loop is infinite and
654
654
  # there's no exist case
655
655
  if words[1] == nil
656
- str = str + "# TODO Possible inifinite loop case check script file" +
656
+ str = str + "# TODO Possible infinite loop case check script file" +
657
657
  "\n" + str + "while(true)"
658
658
  else
659
659
  str = str + words[1] + ".times do |i|"
@@ -694,7 +694,7 @@ def parse_line(full_line, loc_out_file, wait_check_flag = false)
694
694
  # Print the function name and the first argument
695
695
  str = str + "def " + words[1].downcase + "(" + parse_id(listWords[0])
696
696
 
697
- # Print the remaining arguments preceeded by a separator
697
+ # Print the remaining arguments preceded by a separator
698
698
  (listWords.length - 1).times do |i|
699
699
  str = str + ", " + parse_id(listWords[i + 1])
700
700
  end
@@ -759,7 +759,7 @@ def parse_line(full_line, loc_out_file, wait_check_flag = false)
759
759
  # Print the function name and the first argument
760
760
  str = str + words[1].downcase + "(" + parse_id(listWords[0])
761
761
 
762
- # Print the remaining arguments preceeded by a separator
762
+ # Print the remaining arguments preceded by a separator
763
763
  (listWords.length - 1).times do |i|
764
764
  str = str + ", " + parse_id(listWords[i + 1])
765
765
  end
@@ -783,7 +783,7 @@ def parse_line(full_line, loc_out_file, wait_check_flag = false)
783
783
  str = str + "# SCL Ignored: " + line
784
784
 
785
785
  when "wait"
786
- # add a space incase there's no space between or and parantheses
786
+ # add a space in case there's no space between or and parentheses
787
787
  line = line.gsub(')or', ') or')
788
788
  words = line.split(' ')
789
789
  # Only a wait
@@ -1055,8 +1055,8 @@ end
1055
1055
 
1056
1056
  begin
1057
1057
  opts.parse!(ARGV)
1058
- rescue => err
1059
- puts err
1058
+ rescue => e
1059
+ puts e
1060
1060
  puts opts
1061
1061
  exit
1062
1062
  end
@@ -1164,7 +1164,7 @@ else
1164
1164
  @universal_index = 0
1165
1165
  @data_by_lines = @data.lines.to_a
1166
1166
  # Parse each line in the file
1167
- @data_by_lines.each do |line|
1167
+ @data_by_lines.each do |_line|
1168
1168
  if @verify_wait_check == true
1169
1169
  # Skip line
1170
1170
  @verify_wait_check = false
data/bin/openc3cli CHANGED
@@ -58,17 +58,30 @@ MIGRATE_PARSER = OptionParser.new do |opts|
58
58
  end
59
59
  ERROR_CODE = 1
60
60
 
61
+ CLI_SCRIPT_ACTIONS = %w(help list run spawn)
62
+ $script_interrupt_text = ''
63
+ trap('INT') do
64
+ abort("Interrupted at console; exiting.#{$script_interrupt_text}")
65
+ end
66
+
61
67
  # Prints the usage text for the openc3cli executable
62
68
  def print_usage
63
69
  puts "Usage:"
64
70
  puts " cli help # Displays this information"
65
71
  puts " cli rake # Runs rake in the local directory"
66
72
  puts " cli irb # Runs irb in the local directory"
73
+ puts " cli script list /PATH SCOPE # lists script names filtered by path within scope, 'DEFAULT' if not given"
74
+ puts " cli script spawn NAME SCOPE variable1=value1 variable2=value2 # Starts named script remotely"
75
+ puts " cli script run NAME SCOPE variable1=value1 variable2=value2 # Starts named script, monitoring status on console,\
76
+ by default until error or exit"
77
+ puts " PARAMETERS name-value pairs to form the script's runtime environment"
78
+ puts " OPTIONS: --wait 0 seconds to monitor status before detaching from the running script; ie --wait 100"
79
+ puts " --disconnect run the script in disconnect mode"
67
80
  puts " cli validate /PATH/FILENAME.gem SCOPE variables.txt # Validate a COSMOS plugin gem file"
68
81
  puts " cli load /PATH/FILENAME.gem SCOPE variables.txt # Loads a COSMOS plugin gem file"
69
82
  puts " cli list <SCOPE> # Lists installed plugins, SCOPE is DEFAULT if not given"
70
83
  puts " cli generate TYPE OPTIONS # Generate various COSMOS entities"
71
- puts " OPTIONS: --ruby or --python to specify the language in the generated code"
84
+ puts " OPTIONS: --ruby or --python is required to specify the language in the generated code unless OPENC3_LANGUAGE is set"
72
85
  puts " #{MIGRATE_PARSER}"
73
86
  puts " cli bridge CONFIG_FILENAME # Run COSMOS host bridge"
74
87
  puts " cli bridgegem gem_name variable1=value1 variable2=value2 # Runs bridge using gem bridge.txt"
@@ -160,8 +173,8 @@ def migrate(args)
160
173
  end
161
174
 
162
175
  # Migrate cmd_tlm_server.txt info to plugin.txt
163
- Dir.glob('targets/**/cmd_tlm_server*.txt') do |file|
164
- File.open(file) do |file|
176
+ Dir.glob('targets/**/cmd_tlm_server*.txt') do |cmd_tlm_server_file|
177
+ File.open(cmd_tlm_server_file) do |file|
165
178
  file.each do |line|
166
179
  next if line =~ /^\s*#/ # Ignore comments
167
180
  next if line.strip.empty? # Ignore empty lines
@@ -193,9 +206,9 @@ def migrate(args)
193
206
  lines = screen.split("\n")
194
207
  lines.map! do |line|
195
208
  if line.include?('Qt.')
196
- line = "# FIXME (no Qt): #{line.sub("<%", "< %").sub("%>", "% >")}"
209
+ "# FIXME (no Qt): #{line.sub("<%", "< %").sub("%>", "% >")}"
197
210
  elsif line.include?('Cosmos::')
198
- line = "# FIXME (no Cosmos::): #{line.sub("<%", "< %").sub("%>", "% >")}"
211
+ "# FIXME (no Cosmos::): #{line.sub("<%", "< %").sub("%>", "% >")}"
199
212
  else
200
213
  line
201
214
  end
@@ -493,7 +506,6 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
493
506
  else
494
507
  # Outside Cluster
495
508
  require 'openc3/script'
496
-
497
509
  if plugin_hash_file
498
510
  plugin_hash = JSON.parse(File.read(plugin_hash_file), :allow_nan => true, :create_additions => true)
499
511
  else
@@ -648,6 +660,167 @@ def run_bridge(filename, params)
648
660
  end
649
661
  end
650
662
 
663
+ def cli_script_monitor(script_id)
664
+ ret_code = ERROR_CODE
665
+ require 'openc3/script'
666
+ OpenC3::RunningScriptWebSocketApi.new(id: script_id) do |api|
667
+ while (resp = api.read) do
668
+ # see ScriptRunner.vue for types and states
669
+ case resp['type']
670
+ when 'error', 'fatal'
671
+ $script_interrupt_text = ''
672
+ puts 'script failed'
673
+ break
674
+ when 'file'
675
+ puts "Filename #{resp['filename']} scope #{resp['scope']}"
676
+ when 'line'
677
+ fn = resp['filename'].nil? ? '<no file>' : resp['filename']
678
+ puts "At [#{fn}:#{resp['line_no']}] state [#{resp['state']}]"
679
+ if resp['state'] == 'error'
680
+ $script_interrupt_text = ''
681
+ puts 'script failed'
682
+ break
683
+ end
684
+ when 'output'
685
+ puts resp['line']
686
+ when 'paused'
687
+ if resp['state'] == 'fatal'
688
+ $script_interrupt_text = ''
689
+ puts 'script failed'
690
+ break
691
+ end
692
+ when 'complete'
693
+ $script_interrupt_text = ''
694
+ puts 'script complete'
695
+ ret_code = 0
696
+ break
697
+ # These conditions are all handled by the else
698
+ # when 'running', 'breakpoint', 'waiting', 'time'
699
+ else
700
+ puts resp.pretty_inspect
701
+ end
702
+ end
703
+ end
704
+ return ret_code
705
+ end
706
+
707
+ def cli_script_list(args=[])
708
+ path = ''
709
+ if (args[0] && args[0][0] == '/')
710
+ path = (args.shift)[1..-1]+'/'
711
+ end
712
+ scope = args[1]
713
+ scope ||= 'DEFAULT'
714
+ require 'openc3/script'
715
+ script_list(scope: scope).each do |script_name|
716
+ puts(script_name) if script_name.start_with?(path)
717
+ end
718
+ return 0
719
+ end
720
+
721
+ def cli_script_run(disconnect=false, environment={}, args=[])
722
+ # we are limiting the wait for status, not the script run time
723
+ # we make 0 mean 'forever'
724
+ ret_code = ERROR_CODE
725
+ wait_limit = 0
726
+ if (i = args.index('--wait'))
727
+ begin
728
+ args.delete('--wait')
729
+ # pull out the flag
730
+ seconds = args[i]
731
+ wait_limit = Integer(seconds, 10) # only decimal, ignore leading 0
732
+ args.delete_at(i)
733
+ # and its value
734
+ rescue ArgumentError
735
+ abort(" --wait requires a number of seconds to wait, not [#{seconds}]")
736
+ end
737
+ end
738
+ abort("No script file provided") if args[0].nil?
739
+ scope = args[1]
740
+ scope ||= 'DEFAULT'
741
+ require 'openc3/script'
742
+ id = script_run(args[0], disconnect: disconnect, environment: environment, scope: scope) # could raise
743
+ $script_interrupt_text = " Script #{args[0]} still running remotely.\n" # for Ctrl-C
744
+ if (wait_limit < 1) then
745
+ ret_code = cli_script_monitor(id)
746
+ else
747
+ Timeout::timeout(wait_limit, nil, "--wait #{wait_limit} exceeded") do
748
+ ret_code = cli_script_monitor(id)
749
+ rescue Timeout::ExitException, Timeout::Error => e
750
+ # Timeout exceptions are also raised by the Websocket API, so we check
751
+ if e.message =~ /^--wait /
752
+ puts e.message + ", detaching from running script #{args[0]}"
753
+ else
754
+ raise
755
+ end
756
+ end
757
+ end
758
+ return ret_code
759
+ end
760
+
761
+ def cli_script_spawn(disconnect=false, environment={}, args=[])
762
+ ret_code = ERROR_CODE
763
+ if (args.index('--wait'))
764
+ abort("Did you mean \"script run --wait <seconds> [...]\"?")
765
+ end
766
+ abort("No script file provided") if args[0].nil?
767
+ # heaven help you if you left out the script name
768
+ scope = args[1]
769
+ scope ||= 'DEFAULT'
770
+ require 'openc3/script'
771
+ if (id = script_run(args[0], disconnect: disconnect, environment: environment, scope: scope))
772
+ puts id
773
+ ret_code = 0
774
+ end
775
+ return ret_code
776
+ end
777
+
778
+ ## cli_script(args) turns an ARGV of [spawn|run] <--wait 123...> <--disconnect> SCRIPT <scope> <ENV_A=1 ENV_B=2 ...>
779
+ # into function calls and tidied parameters to remote-control a script via RunningScriptWebSocketApi
780
+ def cli_script(args=[])
781
+ ret_code = ERROR_CODE
782
+ check_environment()
783
+ # Double check for the OPENC3_API_PASSWORD because it is absolutely required
784
+ # We always pass it via openc3.sh even if it's not defined so check for empty
785
+ if ENV['OPENC3_API_PASSWORD'].nil? or ENV['OPENC3_API_PASSWORD'].empty?
786
+ abort "OPENC3_API_PASSWORD environment variable is required for cli script"
787
+ end
788
+ command = args.shift
789
+ # pull out the disconnect flag
790
+ discon = args.delete('--disconnect')
791
+ discon = (discon.is_a? String) ? true : false
792
+ environ = {}
793
+ args.each do |arg|
794
+ name, value = arg.split('=')
795
+ if name and value
796
+ # add env[k]=v ; pull out "k=v"
797
+ environ[name] = value
798
+ args.delete(arg)
799
+ end
800
+ end
801
+ case command
802
+ # for list
803
+ # args[] should now be ["/<path>", "<scope>"]
804
+ # or ["/<path>"]
805
+ # or ["<scope>"]
806
+ # or []
807
+ when 'list'
808
+ ret_code = cli_script_list(args)
809
+ # for spawn or run
810
+ # args[] should now be ["--wait 100", "script_name", "<scope>"]
811
+ # or ["--wait 100", "script_name"]
812
+ # or ["script_name", "<scope>"]
813
+ # or ["script_name"]
814
+ when 'spawn'
815
+ ret_code = cli_script_spawn(discon, environ, args)
816
+ when 'run'
817
+ ret_code = cli_script_run(discon, environ, args)
818
+ else
819
+ abort 'openc3cli internal error: parsing arguments'
820
+ end
821
+ exit(ret_code)
822
+ end
823
+
651
824
  if not ARGV[0].nil? # argument(s) given
652
825
 
653
826
  # Handle each task
@@ -657,11 +830,20 @@ if not ARGV[0].nil? # argument(s) given
657
830
  ARGV.clear
658
831
  IRB.start
659
832
 
833
+ when 'script'
834
+ case ARGV[1]
835
+ when 'list', 'run', 'spawn'
836
+ cli_script(ARGV[1..-1])
837
+ else
838
+ # invalid actions, misplaced and malformed leading options and 'help' come here
839
+ abort("cli script <action> must be one of #{CLI_SCRIPT_ACTIONS}, not [#{ARGV[1]}]")
840
+ end
841
+
660
842
  when 'rake'
661
843
  if File.exist?('Rakefile')
662
844
  puts `rake #{ARGV[1..-1].join(' ')}`
663
845
  else
664
- puts "No Rakefile found! Only run 'rake' in the presense of a Rakefile which is typically at the root of your COSMOS project."
846
+ puts "No Rakefile found! Only run 'rake' in the presence of a Rakefile which is typically at the root of your COSMOS project."
665
847
  end
666
848
 
667
849
  when 'validate'
@@ -76,7 +76,7 @@ udp_interface:
76
76
  description: Port on the local machine to send commands from. Default is
77
77
  'nil' (socket is not bound to an outgoing port).
78
78
  values: \d{2,5}
79
- - name: Inteface Address
79
+ - name: Interface Address
80
80
  required: false
81
81
  description:
82
82
  If the remote machine supports multicast the interface address
@@ -171,6 +171,10 @@ ACCESSOR:
171
171
  required: true
172
172
  description: The name of the accessor class
173
173
  values: .+
174
+ - name: Argument
175
+ required: false
176
+ description: Additional argument passed to the accessor class constructor
177
+ values: .+
174
178
  since: 5.0.10
175
179
  TEMPLATE:
176
180
  summary: Defines a template string used to initialize the command before default values are filled in
@@ -248,3 +252,54 @@ VIRTUAL:
248
252
  summary: Marks this packet as virtual and not participating in identification
249
253
  description: Used for packet definitions that can be used as structures for items with a given packet.
250
254
  since: 5.18.0
255
+ VALIDATOR:
256
+ summary: Defines a validator class for a command
257
+ description: Validator class is used to validate the command success or failure with both a pre_check and post_check method.
258
+ parameters:
259
+ - name: Class Filename
260
+ required: true
261
+ description: The filename which contains the Ruby or Python class. The filename must
262
+ be named after the class such that the class is a CamelCase version of the
263
+ underscored filename. For example, 'command_validator.rb' should contain
264
+ 'class CommandValidator'.
265
+ values: .*
266
+ - name: Argument
267
+ required: false
268
+ description: Additional argument passed to the validator class constructor
269
+ values: .*
270
+ ruby_example: |
271
+ VALIDATOR custom_validator.rb
272
+
273
+ Defined in custom_validator.rb:
274
+
275
+ require 'openc3/packets/command_validator'
276
+ class CustomValidator < OpenC3::CommandValidator
277
+ def pre_check(packet)
278
+ if tlm("TGT PKT ITEM") == 0
279
+ return [false, "TGT PKT ITEM is 0"]
280
+ end
281
+ @cmd_acpt_cnt = tlm("TGT PKT CMD_ACPT_CNT")
282
+ return [true, nil]
283
+ end
284
+ def post_check(packet)
285
+ wait_check("TGT PKT CMD_ACPT_CNT > #{@cmd_acpt_cnt}", 10)
286
+ return [true, nil]
287
+ end
288
+ end
289
+
290
+ python_example: |
291
+ VALIDATOR custom_validator.rb
292
+
293
+ Defined in custom_validator.py:
294
+
295
+ class CustomValidator(CommandValidator):
296
+ def pre_check(self, command):
297
+ if tlm("TGT PKT ITEM") == 0:
298
+ return [False, "TGT PKT ITEM is 0"]
299
+ self.cmd_acpt_cnt = tlm("INST HEALTH_STATUS CMD_ACPT_CNT")
300
+ return [True, None]
301
+
302
+ def post_check(self, command):
303
+ wait_check(f"INST HEALTH_STATUS CMD_ACPT_CNT > {self.cmd_acpt_cnt}", 10)
304
+ return [True, None]
305
+ since: 5.19.0
@@ -60,7 +60,7 @@ DISABLE_DISCONNECT:
60
60
  description:
61
61
  Use this keyword to prevent the user from disconnecting from the interface.
62
62
  This is typically used in a 'production' environment where you would not want
63
- the user to inadvertantly disconnect from a target.
63
+ the user to inadvertently disconnect from a target.
64
64
  LOG_RAW:
65
65
  summary: Deprecated, use LOG_STREAM
66
66
  LOG_STREAM:
@@ -48,7 +48,7 @@ OVERLAP:
48
48
  summary: This item is allowed to overlap other items in the packet
49
49
  description:
50
50
  If an item's bit offset overlaps another item, OpenC3 issues a warning. This keyword explicitly
51
- allows an item to overlap another and supresses the warning message.
51
+ allows an item to overlap another and suppresses the warning message.
52
52
  since: 4.4.1
53
53
  KEY:
54
54
  summary: Defines the key used to access this raw value in the packet.
@@ -207,7 +207,7 @@ OVERFLOW:
207
207
  the value by eliminating any high order bits. You can also set 'SATURATE' which
208
208
  causes OpenC3 to replace the value with the maximum or minimum allowable value
209
209
  for that type. Finally you can specify 'ERROR_ALLOW_HEX' which will allow for
210
- a maximum hex value to be writen, e.g. you can successfully write 255 to a 8
210
+ a maximum hex value to be written, e.g. you can successfully write 255 to a 8
211
211
  bit signed value.
212
212
  example: OVERFLOW TRUNCATE
213
213
  parameters:
@@ -17,7 +17,7 @@ NEEDS_DEPENDENCIES:
17
17
  summary: Indicates the plugin needs dependencies and sets the GEM_HOME environment variable
18
18
  description: If the plugin has a top level lib folder or lists runtime dependencies in the gemspec,
19
19
  NEEDS_DEPENDENCIES is effectively already set. Note that in Enterprise Edition, having
20
- NEEDS_DEPENDENCIES adds the NFS volume mount to the Kuberentes pod.
20
+ NEEDS_DEPENDENCIES adds the NFS volume mount to the Kubernetes pod.
21
21
  since: 5.5.0
22
22
  INTERFACE:
23
23
  modifiers:
@@ -71,10 +71,14 @@ WIDGET:
71
71
  description: Defines a custom widget that can be used in Telemetry Viewer screens.
72
72
  parameters:
73
73
  - name: Widget Name
74
- description: The name of the widget wil be used to build a path to the widget implementation. For example, `WIDGET HELLOWORLD` will find the as-built file tools/widgets/HelloworldWidget/HelloworldWidget.umd.min.js. See the [Custom Widgets](../guides/custom-widgets.md)
74
+ description: The name of the widget will be used to build a path to the widget implementation. For example, `WIDGET HELLOWORLD` will find the as-built file tools/widgets/HelloworldWidget/HelloworldWidget.umd.min.js. See the [Custom Widgets](../guides/custom-widgets.md)
75
75
  guide for more details.
76
76
  required: true
77
77
  values: .+
78
+ - name: Label
79
+ description: The label for the widget that will appear in the Data Viewer component drop down
80
+ required: false
81
+ values: .+
78
82
  modifiers:
79
83
  DISABLE_ERB:
80
84
  summary: Disable ERB processing
@@ -93,7 +93,7 @@ SETTING:
93
93
  GLOBAL_SETTING and GLOBAL_SUBSETTING applies to all widgets.
94
94
 
95
95
  Common wiget settings are defined here. Some widgets define their own
96
- unqiue settings which are documented under that specific widget.
96
+ unique settings which are documented under that specific widget.
97
97
  collection:
98
98
  <%= MetaConfigParser.load('settings.yaml').to_meta_config_yaml(4) %>
99
99
  SUBSETTING:
@@ -127,7 +127,7 @@ SUBSETTING:
127
127
  END
128
128
  NAMED_WIDGET:
129
129
  summary: Name a widget to allow access to it via the getNamedWidget method
130
- description: To programatically access parts of a telemetry screen you need
130
+ description: To programmatically access parts of a telemetry screen you need
131
131
  to name the widget. This is useful when creating screens with buttons that
132
132
  read values from other widgets.
133
133
  warning: getNamedWidget returns the widget itself and thus must be operated
@@ -61,7 +61,7 @@ TABLE:
61
61
  used in mouseover popups and status line information.
62
62
  values: "['\"].*['\"]"
63
63
  ROW_COLUMN:
64
- summary: Table rows will be indentical with multiple columns (previously TWO_DIMENSIONAL, now deprecated)
64
+ summary: Table rows will be identical with multiple columns (previously TWO_DIMENSIONAL, now deprecated)
65
65
  parameters:
66
66
  - name: Rows
67
67
  requires: true
@@ -77,7 +77,7 @@ SELECT_TABLE:
77
77
  parameters:
78
78
  - name: Table
79
79
  required: true
80
- description: The name of the existin table
80
+ description: The name of the existing table
81
81
  values: .*
82
82
  DEFAULT:
83
83
  summary: Specify default values for a SINGLE row in a multi-column table
@@ -63,7 +63,10 @@ TOOL:
63
63
  values: ["true", "false"]
64
64
  POSITION:
65
65
  summary: Position of the tool in the nav bar
66
- description: Position of the tool as an integer starting at 1. Tools without a position are appended to the end as they are installed.
66
+ description:
67
+ Position of the tool starting at 2 (1 is reserved for Admin Console).
68
+ Tools without a position are appended to the end as they are installed.
69
+ All COSMOS open source tools have consecutive integer values for position.
67
70
  since: 5.0.8
68
71
  parameters:
69
72
  - name: Position
@@ -121,7 +121,7 @@ Layout Widgets:
121
121
  parameters:
122
122
  - name: Tab text
123
123
  required: true
124
- description: Text to diplay in the tab
124
+ description: Text to display in the tab
125
125
  values: .*
126
126
  example: |
127
127
  TABBOOK
@@ -1034,7 +1034,7 @@ Telemetry Widgets:
1034
1034
  IMAGEVIEWER INST IMAGE IMAGE jpg
1035
1035
  PROGRESSBAR:
1036
1036
  summary: Displays a progress bar that is useful for displaying percentages
1037
- parmeters:
1037
+ parameters:
1038
1038
  - name: Target name
1039
1039
  required: true
1040
1040
  description: The target name
@@ -1108,7 +1108,7 @@ Telemetry Widgets:
1108
1108
  RANGEBAR INST HEALTH_STATUS TEMP1 -100 100
1109
1109
  # RANGECOLUMN:
1110
1110
  # summary: Displays a graphical representation of where
1111
- # an item's value falls withing a range vertically
1111
+ # an item's value falls within a range vertically
1112
1112
  # parameters:
1113
1113
  # - name: Target name
1114
1114
  # required: true
@@ -77,7 +77,7 @@ static VALUE string_remove_quotes(VALUE self)
77
77
 
78
78
  /*
79
79
  * Method to read a line from the config file.
80
- * This is a seperate method so that it can be protected.
80
+ * This is a separate method so that it can be protected.
81
81
  */
82
82
  static VALUE config_parser_readline(VALUE io)
83
83
  {
@@ -177,7 +177,7 @@ static VALUE received_count_equals(VALUE self, VALUE received_count)
177
177
  return rb_ivar_get(self, id_ivar_received_count);
178
178
  }
179
179
 
180
- /* Creates a new packet by initalizing the attributes.
180
+ /* Creates a new packet by initializing the attributes.
181
181
  *
182
182
  * @param target_name [String] Name of the target this packet is associated with
183
183
  * @param packet_name [String] Name of the packet
@@ -18,7 +18,7 @@
18
18
  # All changes Copyright 2022, OpenC3, Inc.
19
19
  # All Rights Reserved
20
20
  #
21
- # This file may also be used under the terms of a commercial license
21
+ # This file may also be used under the terms of a commercial license
22
22
  # if purchased from OpenC3, Inc.
23
23
  */
24
24
 
@@ -93,7 +93,7 @@ __attribute__((noreturn)) static void catch_sigsegv(int sig_num)
93
93
  timeinfo.tm_min,
94
94
  timeinfo.tm_sec);
95
95
 
96
- // Fortify warns about Path Manipulation here. We explictly allow this to let
96
+ // Fortify warns about Path Manipulation here. We explicitly allow this to let
97
97
  // segfault files be written to a directory of their choosing.
98
98
  // The input is validated above for length and to ensure it is a writable directory.
99
99
  // If the checks fail the directory is set to the current directory without additional info.
@@ -124,7 +124,7 @@ __attribute__((noreturn)) static void catch_sigsegv(int sig_num)
124
124
  void Init_platform(void)
125
125
  {
126
126
  #ifdef _WIN32
127
- /* Only supprt linux segfault catching */
127
+ /* Only support linux segfault catching */
128
128
  #else
129
129
  signal(SIGSEGV, catch_sigsegv);
130
130
  signal(SIGILL, catch_sigsegv);