openc3 5.18.0 → 5.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -4
  3. data/bin/cstol_converter +14 -14
  4. data/bin/openc3cli +189 -7
  5. data/bin/pipinstall +5 -3
  6. data/data/config/_interfaces.yaml +1 -1
  7. data/data/config/command_modifiers.yaml +59 -0
  8. data/data/config/interface_modifiers.yaml +1 -1
  9. data/data/config/param_item_modifiers.yaml +1 -1
  10. data/data/config/parameter_modifiers.yaml +1 -1
  11. data/data/config/plugins.yaml +6 -2
  12. data/data/config/screen.yaml +2 -2
  13. data/data/config/table_manager.yaml +2 -2
  14. data/data/config/tool.yaml +4 -1
  15. data/data/config/widgets.yaml +31 -3
  16. data/ext/openc3/ext/config_parser/config_parser.c +1 -1
  17. data/ext/openc3/ext/packet/packet.c +4 -1
  18. data/ext/openc3/ext/platform/platform.c +3 -3
  19. data/ext/openc3/ext/structure/structure.c +56 -76
  20. data/lib/openc3/accessors/binary_accessor.rb +4 -4
  21. data/lib/openc3/accessors/form_accessor.rb +2 -2
  22. data/lib/openc3/accessors/http_accessor.rb +1 -1
  23. data/lib/openc3/accessors/json_accessor.rb +6 -4
  24. data/lib/openc3/accessors/template_accessor.rb +6 -9
  25. data/lib/openc3/accessors/xml_accessor.rb +1 -1
  26. data/lib/openc3/api/cmd_api.rb +36 -13
  27. data/lib/openc3/api/limits_api.rb +4 -4
  28. data/lib/openc3/api/tlm_api.rb +2 -1
  29. data/lib/openc3/bridge/bridge_config.rb +1 -2
  30. data/lib/openc3/ccsds/ccsds_parser.rb +12 -8
  31. data/lib/openc3/config/config_parser.rb +5 -1
  32. data/lib/openc3/conversions/bit_reverse_conversion.rb +1 -0
  33. data/lib/openc3/conversions/conversion.rb +5 -1
  34. data/lib/openc3/conversions/generic_conversion.rb +3 -8
  35. data/lib/openc3/conversions/object_read_conversion.rb +1 -8
  36. data/lib/openc3/conversions/polynomial_conversion.rb +3 -8
  37. data/lib/openc3/conversions/processor_conversion.rb +13 -11
  38. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +10 -18
  39. data/lib/openc3/conversions/unix_time_conversion.rb +4 -7
  40. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +4 -3
  41. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +4 -3
  42. data/lib/openc3/core_ext/array.rb +5 -5
  43. data/lib/openc3/core_ext/exception.rb +9 -2
  44. data/lib/openc3/core_ext/string.rb +2 -2
  45. data/lib/openc3/interfaces/http_server_interface.rb +2 -2
  46. data/lib/openc3/interfaces/interface.rb +2 -3
  47. data/lib/openc3/interfaces/linc_interface.rb +3 -3
  48. data/lib/openc3/interfaces/tcpip_server_interface.rb +1 -2
  49. data/lib/openc3/interfaces/udp_interface.rb +5 -3
  50. data/lib/openc3/io/json_api.rb +11 -6
  51. data/lib/openc3/io/json_drb.rb +5 -0
  52. data/lib/openc3/io/json_rpc.rb +11 -10
  53. data/lib/openc3/io/udp_sockets.rb +7 -5
  54. data/lib/openc3/logs/buffered_packet_log_writer.rb +3 -3
  55. data/lib/openc3/logs/log_writer.rb +7 -8
  56. data/lib/openc3/logs/packet_log_writer.rb +7 -7
  57. data/lib/openc3/logs/text_log_writer.rb +4 -4
  58. data/lib/openc3/microservices/critical_cmd_microservice.rb +74 -0
  59. data/lib/openc3/microservices/decom_microservice.rb +43 -11
  60. data/lib/openc3/microservices/interface_microservice.rb +104 -8
  61. data/lib/openc3/microservices/microservice.rb +1 -2
  62. data/lib/openc3/microservices/reaction_microservice.rb +2 -2
  63. data/lib/openc3/microservices/timeline_microservice.rb +14 -16
  64. data/lib/openc3/microservices/trigger_group_microservice.rb +3 -3
  65. data/lib/openc3/migrations/20240915000000_activity_uuid.rb +28 -0
  66. data/lib/openc3/migrations/20241016000000_scope_critical_cmd.rb +24 -0
  67. data/lib/openc3/models/activity_model.rb +116 -83
  68. data/lib/openc3/models/auth_model.rb +31 -2
  69. data/lib/openc3/models/cvt_model.rb +18 -6
  70. data/lib/openc3/models/gem_model.rb +8 -8
  71. data/lib/openc3/models/model.rb +1 -0
  72. data/lib/openc3/models/plugin_model.rb +9 -9
  73. data/lib/openc3/models/python_package_model.rb +5 -3
  74. data/lib/openc3/models/reducer_model.rb +2 -2
  75. data/lib/openc3/models/scope_model.rb +49 -5
  76. data/lib/openc3/models/sorted_model.rb +4 -4
  77. data/lib/openc3/models/target_model.rb +3 -3
  78. data/lib/openc3/models/tool_config_model.rb +1 -1
  79. data/lib/openc3/models/tool_model.rb +4 -4
  80. data/lib/openc3/models/widget_model.rb +11 -5
  81. data/lib/openc3/operators/microservice_operator.rb +2 -3
  82. data/lib/openc3/operators/operator.rb +5 -3
  83. data/lib/openc3/packets/command_validator.rb +48 -0
  84. data/lib/openc3/packets/commands.rb +6 -14
  85. data/lib/openc3/packets/packet.rb +40 -15
  86. data/lib/openc3/packets/packet_config.rb +44 -10
  87. data/lib/openc3/packets/packet_item.rb +5 -1
  88. data/lib/openc3/packets/parsers/packet_parser.rb +3 -3
  89. data/lib/openc3/packets/structure.rb +21 -13
  90. data/lib/openc3/packets/structure_item.rb +33 -47
  91. data/lib/openc3/packets/telemetry.rb +6 -27
  92. data/lib/openc3/script/api_shared.rb +7 -5
  93. data/lib/openc3/script/calendar.rb +3 -8
  94. data/lib/openc3/script/commands.rb +23 -15
  95. data/lib/openc3/script/critical_cmd.rb +91 -0
  96. data/lib/openc3/script/metadata.rb +2 -2
  97. data/lib/openc3/script/screen.rb +2 -2
  98. data/lib/openc3/script/script.rb +17 -0
  99. data/lib/openc3/script/suite.rb +17 -17
  100. data/lib/openc3/script/web_socket_api.rb +3 -3
  101. data/lib/openc3/streams/serial_stream.rb +2 -3
  102. data/lib/openc3/streams/stream.rb +2 -2
  103. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +10 -10
  104. data/lib/openc3/tools/table_manager/table_manager_core.rb +11 -11
  105. data/lib/openc3/tools/table_manager/table_parser.rb +2 -3
  106. data/lib/openc3/top_level.rb +9 -0
  107. data/lib/openc3/topics/command_decom_topic.rb +2 -1
  108. data/lib/openc3/topics/command_topic.rb +19 -3
  109. data/lib/openc3/topics/decom_interface_topic.rb +2 -2
  110. data/lib/openc3/topics/interface_topic.rb +2 -0
  111. data/lib/openc3/topics/telemetry_decom_topic.rb +1 -1
  112. data/lib/openc3/utilities/authentication.rb +7 -3
  113. data/lib/openc3/utilities/authorization.rb +2 -1
  114. data/lib/openc3/utilities/cli_generator.rb +15 -8
  115. data/lib/openc3/utilities/cosmos_rails_formatter.rb +60 -0
  116. data/lib/openc3/utilities/crc.rb +6 -6
  117. data/lib/openc3/utilities/local_mode.rb +2 -1
  118. data/lib/openc3/utilities/logger.rb +45 -34
  119. data/lib/openc3/utilities/metric.rb +1 -2
  120. data/lib/openc3/utilities/quaternion.rb +18 -18
  121. data/lib/openc3/utilities/store_queued.rb +1 -0
  122. data/lib/openc3/utilities/target_file.rb +4 -4
  123. data/lib/openc3/version.rb +5 -5
  124. data/lib/openc3/win32/win32_main.rb +2 -2
  125. data/templates/conversion/conversion.rb +2 -0
  126. data/templates/tool_angular/package.json +22 -22
  127. data/templates/tool_react/package.json +10 -10
  128. data/templates/tool_svelte/build/smui.css +1 -1
  129. data/templates/tool_svelte/package.json +12 -12
  130. data/templates/tool_svelte/src/services/openc3-api.js +17 -17
  131. data/templates/tool_vue/package.json +11 -11
  132. data/templates/widget/package.json +8 -9
  133. metadata +22 -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: afacc218fcbddf94eaf648df3232e9f6c99f9637fb0694cde932267542968d5e
4
+ data.tar.gz: 1ce5bf3127b8b7ea3c1adae3de0a294f435302854bb08835d5fdb18a4b786804
5
5
  SHA512:
6
- metadata.gz: 34df5327bcfbcdff0c21e5856d7dcc11ee88a8f83e3e83f1a8fc9e70b37b37e36360e54f0f9ad8df8f505f9e7b6ed2cf253d0d4a8efaaec072cced50a0e104d7
7
- data.tar.gz: c78d41c4ed5b18ae17450c1e50889d169c714dbc2975cc710158de682f7012be9c8f39e1a0230aaa3d67c57c26fbdeaa82bfa8617658d3ab22ac709828248209
6
+ metadata.gz: ad58ad437fb0f7391d33fef0bafdf6f0b670cb6f59b63f60f2f03bda7a65c06acf59cad85ba92069c79a505da0fe4c33d8fb928d9cd926e0c4a9be0d42ee20c9
7
+ data.tar.gz: 503095241e1a8d34f71875abc4f1313c036470c86bb7627a3bd70b482281d03eeb6479d9a3109753b2f3b7927e1ceef49a62532197c5827f360ba66f23f4c4ff
data/Gemfile CHANGED
@@ -8,10 +8,6 @@ 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'
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'
data/bin/pipinstall CHANGED
@@ -1,11 +1,13 @@
1
1
  #!/bin/sh
2
-
3
- pip install "$@"
2
+ python3 -m venv $PYTHONUSERBASE
3
+ source $PYTHONUSERBASE/bin/activate
4
+ echo "pip3 install $@"
5
+ pip3 install "$@"
4
6
  if [ $? -eq 0 ]; then
5
7
  echo "Command succeeded"
6
8
  else
7
9
  echo "Command failed - retrying with --no-index"
8
- pip install --no-index "$@"
10
+ pip3 install --no-index "$@"
9
11
  if [ $? -eq 0 ]; then
10
12
  echo "ERROR: pip install failed"
11
13
  fi
@@ -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,58 @@ 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
+ RESTRICTED:
256
+ summary: Marks this packet as restricted and will require approval if critical commanding is enabled
257
+ description: Used as one of the two types of critical commands (HAZARDOUS and RESTRICTED)
258
+ since: 5.20.0
259
+ VALIDATOR:
260
+ summary: Defines a validator class for a command
261
+ description: Validator class is used to validate the command success or failure with both a pre_check and post_check method.
262
+ parameters:
263
+ - name: Class Filename
264
+ required: true
265
+ description: The filename which contains the Ruby or Python class. The filename must
266
+ be named after the class such that the class is a CamelCase version of the
267
+ underscored filename. For example, 'command_validator.rb' should contain
268
+ 'class CommandValidator'.
269
+ values: .*
270
+ - name: Argument
271
+ required: false
272
+ description: Additional argument passed to the validator class constructor
273
+ values: .*
274
+ ruby_example: |
275
+ VALIDATOR custom_validator.rb
276
+
277
+ Defined in custom_validator.rb:
278
+
279
+ require 'openc3/packets/command_validator'
280
+ class CustomValidator < OpenC3::CommandValidator
281
+ def pre_check(packet)
282
+ if tlm("TGT PKT ITEM") == 0
283
+ return [false, "TGT PKT ITEM is 0"]
284
+ end
285
+ @cmd_acpt_cnt = tlm("TGT PKT CMD_ACPT_CNT")
286
+ return [true, nil]
287
+ end
288
+ def post_check(packet)
289
+ wait_check("TGT PKT CMD_ACPT_CNT > #{@cmd_acpt_cnt}", 10)
290
+ return [true, nil]
291
+ end
292
+ end
293
+
294
+ python_example: |
295
+ VALIDATOR custom_validator.rb
296
+
297
+ Defined in custom_validator.py:
298
+
299
+ class CustomValidator(CommandValidator):
300
+ def pre_check(self, command):
301
+ if tlm("TGT PKT ITEM") == 0:
302
+ return [False, "TGT PKT ITEM is 0"]
303
+ self.cmd_acpt_cnt = tlm("INST HEALTH_STATUS CMD_ACPT_CNT")
304
+ return [True, None]
305
+
306
+ def post_check(self, command):
307
+ wait_check(f"INST HEALTH_STATUS CMD_ACPT_CNT > {self.cmd_acpt_cnt}", 10)
308
+ return [True, None]
309
+ 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
@@ -1423,6 +1423,20 @@ Interactive Widgets:
1423
1423
  BUTTON 'Start Collect' 'var type = screen.getNamedWidget("COLLECT_TYPE").text();' +
1424
1424
  'api.cmd("INST COLLECT with TYPE "+type+", DURATION 10.0")'
1425
1425
  NAMED_WIDGET COLLECT_TYPE COMBOBOX NORMAL SPECIAL
1426
+ DATE:
1427
+ summary: Displays a date picker
1428
+ description:
1429
+ Note this is of limited use by itself and is primarily used in
1430
+ conjunction with NAMED_WIDGET.
1431
+ parameters:
1432
+ - name: Date label
1433
+ required: false
1434
+ description: Text to label the data selection ('Date' by default)
1435
+ values: .+
1436
+ example: |
1437
+ BUTTON 'Alert Date' 'var date = screen.getNamedWidget("DATE").text();' +
1438
+ 'alert("Date:"+date)'
1439
+ NAMED_WIDGET DATE DATE
1426
1440
  RADIOGROUP:
1427
1441
  summary: Creates a group of RADIOBUTTONs
1428
1442
  description: RADIOBUTTONs must be part of a group to enable selection logic
@@ -1464,6 +1478,20 @@ Interactive Widgets:
1464
1478
  NAMED_WIDGET DURATION TEXTFIELD 12 "10.0"
1465
1479
  BUTTON 'Start Collect' 'var dur = screen.getNamedWidget("DURATION").text();' +
1466
1480
  'api.cmd("INST COLLECT with TYPE NORMAL, DURATION "+dur+"")'
1481
+ TIME:
1482
+ summary: Displays a time picker
1483
+ description:
1484
+ Note this is of limited use by itself and is primarily used in
1485
+ conjunction with NAMED_WIDGET.
1486
+ parameters:
1487
+ - name: Time label
1488
+ required: false
1489
+ description: Text to label the time selection ('Time' by default)
1490
+ values: .+
1491
+ example: |
1492
+ BUTTON 'Alert Time' 'var time = screen.getNamedWidget("TIME").text();' +
1493
+ 'alert("Time:"+time)'
1494
+ NAMED_WIDGET TIME TIME
1467
1495
  Canvas Widgets:
1468
1496
  description:
1469
1497
  Canvas Widgets are used to draw custom displays into telemetry screens.
@@ -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
  {