openc3 5.17.1 → 5.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -4
  3. data/bin/cstol_converter +14 -14
  4. data/bin/openc3cli +190 -8
  5. data/data/config/_interfaces.yaml +5 -5
  6. data/data/config/command_modifiers.yaml +59 -0
  7. data/data/config/interface_modifiers.yaml +19 -9
  8. data/data/config/item_modifiers.yaml +34 -26
  9. data/data/config/microservice.yaml +4 -1
  10. data/data/config/param_item_modifiers.yaml +17 -1
  11. data/data/config/parameter_modifiers.yaml +30 -13
  12. data/data/config/plugins.yaml +9 -5
  13. data/data/config/screen.yaml +9 -9
  14. data/data/config/table_manager.yaml +2 -2
  15. data/data/config/telemetry_modifiers.yaml +9 -4
  16. data/data/config/tool.yaml +4 -1
  17. data/data/config/widgets.yaml +44 -17
  18. data/ext/openc3/ext/config_parser/config_parser.c +1 -1
  19. data/ext/openc3/ext/packet/packet.c +7 -1
  20. data/ext/openc3/ext/platform/platform.c +3 -3
  21. data/ext/openc3/ext/structure/structure.c +56 -76
  22. data/lib/openc3/accessors/accessor.rb +1 -0
  23. data/lib/openc3/accessors/binary_accessor.rb +174 -15
  24. data/lib/openc3/accessors/form_accessor.rb +2 -2
  25. data/lib/openc3/accessors/http_accessor.rb +1 -1
  26. data/lib/openc3/accessors/json_accessor.rb +6 -4
  27. data/lib/openc3/accessors/template_accessor.rb +6 -9
  28. data/lib/openc3/accessors/xml_accessor.rb +1 -1
  29. data/lib/openc3/api/cmd_api.rb +72 -44
  30. data/lib/openc3/api/config_api.rb +10 -10
  31. data/lib/openc3/api/interface_api.rb +28 -21
  32. data/lib/openc3/api/limits_api.rb +30 -30
  33. data/lib/openc3/api/metrics_api.rb +3 -3
  34. data/lib/openc3/api/offline_access_api.rb +5 -5
  35. data/lib/openc3/api/router_api.rb +25 -19
  36. data/lib/openc3/api/settings_api.rb +10 -10
  37. data/lib/openc3/api/stash_api.rb +10 -10
  38. data/lib/openc3/api/target_api.rb +10 -10
  39. data/lib/openc3/api/tlm_api.rb +44 -44
  40. data/lib/openc3/config/config_parser.rb +1 -1
  41. data/lib/openc3/conversions/bit_reverse_conversion.rb +60 -0
  42. data/lib/openc3/conversions/ip_read_conversion.rb +59 -0
  43. data/lib/openc3/conversions/ip_write_conversion.rb +61 -0
  44. data/lib/openc3/conversions/object_read_conversion.rb +88 -0
  45. data/lib/openc3/conversions/object_write_conversion.rb +38 -0
  46. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +7 -7
  47. data/lib/openc3/conversions.rb +6 -1
  48. data/lib/openc3/core_ext/array.rb +5 -5
  49. data/lib/openc3/core_ext/exception.rb +9 -2
  50. data/lib/openc3/core_ext/string.rb +2 -2
  51. data/lib/openc3/interfaces/http_server_interface.rb +1 -0
  52. data/lib/openc3/interfaces/interface.rb +1 -1
  53. data/lib/openc3/interfaces/linc_interface.rb +3 -3
  54. data/lib/openc3/io/json_api.rb +11 -6
  55. data/lib/openc3/io/json_drb.rb +19 -21
  56. data/lib/openc3/io/json_rpc.rb +15 -14
  57. data/lib/openc3/logs/buffered_packet_log_writer.rb +3 -3
  58. data/lib/openc3/logs/log_writer.rb +7 -8
  59. data/lib/openc3/logs/packet_log_writer.rb +7 -7
  60. data/lib/openc3/logs/text_log_writer.rb +4 -4
  61. data/lib/openc3/microservices/decom_microservice.rb +19 -4
  62. data/lib/openc3/microservices/interface_microservice.rb +41 -3
  63. data/lib/openc3/microservices/microservice.rb +11 -11
  64. data/lib/openc3/microservices/reaction_microservice.rb +2 -2
  65. data/lib/openc3/microservices/scope_cleanup_microservice.rb +1 -1
  66. data/lib/openc3/microservices/timeline_microservice.rb +70 -45
  67. data/lib/openc3/microservices/trigger_group_microservice.rb +3 -3
  68. data/lib/openc3/migrations/20240915000000_activity_uuid.rb +28 -0
  69. data/lib/openc3/models/activity_model.rb +124 -92
  70. data/lib/openc3/models/auth_model.rb +31 -2
  71. data/lib/openc3/models/cvt_model.rb +11 -5
  72. data/lib/openc3/models/gem_model.rb +8 -8
  73. data/lib/openc3/models/plugin_model.rb +3 -3
  74. data/lib/openc3/models/reducer_model.rb +2 -2
  75. data/lib/openc3/models/scope_model.rb +45 -14
  76. data/lib/openc3/models/sorted_model.rb +5 -5
  77. data/lib/openc3/models/target_model.rb +7 -4
  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 -2
  82. data/lib/openc3/operators/operator.rb +14 -12
  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 +49 -16
  86. data/lib/openc3/packets/packet_config.rb +47 -25
  87. data/lib/openc3/packets/packet_item.rb +5 -0
  88. data/lib/openc3/packets/parsers/packet_parser.rb +3 -3
  89. data/lib/openc3/packets/structure.rb +87 -15
  90. data/lib/openc3/packets/structure_item.rb +76 -53
  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 +2 -2
  94. data/lib/openc3/script/commands.rb +6 -4
  95. data/lib/openc3/script/extract.rb +5 -3
  96. data/lib/openc3/script/metadata.rb +2 -2
  97. data/lib/openc3/script/suite.rb +17 -17
  98. data/lib/openc3/script/web_socket_api.rb +11 -0
  99. data/lib/openc3/streams/serial_stream.rb +2 -3
  100. data/lib/openc3/streams/stream.rb +2 -2
  101. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +10 -10
  102. data/lib/openc3/tools/table_manager/table_manager_core.rb +11 -11
  103. data/lib/openc3/tools/table_manager/table_parser.rb +2 -3
  104. data/lib/openc3/topics/command_decom_topic.rb +2 -1
  105. data/lib/openc3/topics/command_topic.rb +3 -3
  106. data/lib/openc3/topics/decom_interface_topic.rb +4 -3
  107. data/lib/openc3/topics/system_events_topic.rb +40 -0
  108. data/lib/openc3/topics/telemetry_decom_topic.rb +1 -1
  109. data/lib/openc3/utilities/authentication.rb +2 -1
  110. data/lib/openc3/utilities/authorization.rb +4 -3
  111. data/lib/openc3/utilities/cli_generator.rb +15 -8
  112. data/lib/openc3/utilities/cosmos_rails_formatter.rb +60 -0
  113. data/lib/openc3/utilities/crc.rb +6 -6
  114. data/lib/openc3/utilities/local_mode.rb +2 -1
  115. data/lib/openc3/utilities/logger.rb +44 -34
  116. data/lib/openc3/utilities/metric.rb +1 -2
  117. data/lib/openc3/utilities/quaternion.rb +18 -18
  118. data/lib/openc3/utilities/target_file.rb +4 -4
  119. data/lib/openc3/version.rb +6 -6
  120. data/lib/openc3/win32/win32_main.rb +2 -2
  121. data/templates/tool_angular/package.json +22 -22
  122. data/templates/tool_react/package.json +13 -13
  123. data/templates/tool_svelte/package.json +14 -14
  124. data/templates/tool_svelte/src/services/openc3-api.js +17 -17
  125. data/templates/tool_vue/package.json +13 -13
  126. data/templates/widget/package.json +11 -12
  127. data/templates/widget/src/Widget.vue +0 -1
  128. metadata +25 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c70541c17780bda9cd1adb897963eb1daedfc459bc2637a511745180430e162
4
- data.tar.gz: a6eb2e273a1bb9dd5d849b68e8910b7c8eaaca65cee41eed8bdb155891eec1db
3
+ metadata.gz: 65a5c8a4ee0645f9d129c7c6140b9e8863e61e035f5e0269a977e8cd7d722c15
4
+ data.tar.gz: a023d452a32050ab5d81a3edcdc8fccaf10eed1a81b481ef11d89c633d3f6cba
5
5
  SHA512:
6
- metadata.gz: 60e677a6a9b13c7d94d4f32b70b824509e2c87cf691bebb9285b96c544318a8016f2f98b119c657de1cee77b0ec197301bd040962ec9055afe0cd0ae3228ad74
7
- data.tar.gz: a8a9f67755a4768806f92134a42c50a44cd686a95ad55877f66fcc735ceb75741845ffe1e71aaa66f5ac5181cb929f999f808c86abc6a1b9408f44f122933892
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
@@ -425,7 +438,7 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
425
438
  unless OpenC3::ScopeModel.names.include?(scope)
426
439
  begin
427
440
  puts "Creating scope: #{scope}"
428
- scope_model = OpenC3::ScopeModel.new(name: scope, scope: scope)
441
+ scope_model = OpenC3::ScopeModel.new(name: scope)
429
442
  scope_model.create
430
443
  scope_model.deploy(".", {})
431
444
  rescue => e
@@ -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'
@@ -1,5 +1,5 @@
1
1
  ---
2
- tcpip_client_interface.rb:
2
+ tcpip_client_interface:
3
3
  parameters:
4
4
  - name: Host
5
5
  required: true
@@ -29,7 +29,7 @@ tcpip_client_interface.rb:
29
29
  # prettier-ignore
30
30
  values:
31
31
  <%= MetaConfigParser.load('protocols.yaml').to_meta_config_yaml(8) %>
32
- tcpip_server_interface.rb:
32
+ tcpip_server_interface:
33
33
  parameters:
34
34
  - name: Write Port
35
35
  required: true
@@ -54,7 +54,7 @@ tcpip_server_interface.rb:
54
54
  # prettier-ignore
55
55
  values:
56
56
  <%= MetaConfigParser.load('protocols.yaml').to_meta_config_yaml(8) %>
57
- udp_interface.rb:
57
+ udp_interface:
58
58
  description: The UDP interface uses UDP packets to send and receive telemetry
59
59
  from the target
60
60
  parameters:
@@ -76,7 +76,7 @@ udp_interface.rb:
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
@@ -100,7 +100,7 @@ udp_interface.rb:
100
100
  required: false
101
101
  description: Address to bind UDP ports to
102
102
  values: .+
103
- serial_interface.rb:
103
+ serial_interface:
104
104
  description: Connects to a target over a serial port. OpenC3 provides drivers
105
105
  for both Windows and POSIX drivers for UNIX based systems.
106
106
  parameters:
@@ -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
@@ -244,3 +248,58 @@ SCREEN:
244
248
  description: Screen Name of related telemetry screen
245
249
  values: .+
246
250
  since: 5.14.0
251
+ VIRTUAL:
252
+ summary: Marks this packet as virtual and not participating in identification
253
+ description: Used for packet definitions that can be used as structures for items with a given packet.
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
@@ -6,9 +6,12 @@ MAP_TARGET:
6
6
  required: true
7
7
  description: Target name to map to this interface
8
8
  values: .+
9
- example: |
9
+ ruby_example: |
10
10
  INTERFACE DATA_INT tcpip_client_interface.rb host.docker.internal 8080 8081 10.0 nil BURST
11
11
  MAP_TARGET DATA
12
+ python_example: |
13
+ INTERFACE DATA_INT openc3/interfaces/tcpip_client_interface.py host.docker.internal 8080 8081 10.0 nil BURST
14
+ MAP_TARGET DATA
12
15
  MAP_CMD_TARGET:
13
16
  summary: Maps a target name to an interface for commands only
14
17
  since: 5.2.0
@@ -17,9 +20,12 @@ MAP_CMD_TARGET:
17
20
  required: true
18
21
  description: Command target name to map to this interface
19
22
  values: .+
20
- example: |
23
+ ruby_example: |
21
24
  INTERFACE CMD_INT tcpip_client_interface.rb host.docker.internal 8080 8081 10.0 nil BURST
22
25
  MAP_CMD_TARGET DATA # Only DATA commands go on the CMD_INT interface
26
+ python_example: |
27
+ INTERFACE CMD_INT openc3/interfaces/tcpip_client_interface.py host.docker.internal 8080 8081 10.0 nil BURST
28
+ MAP_CMD_TARGET DATA # Only DATA commands go on the CMD_INT interface
23
29
  MAP_TLM_TARGET:
24
30
  summary: Maps a target name to an interface for telemetry only
25
31
  since: 5.2.0
@@ -28,9 +34,12 @@ MAP_TLM_TARGET:
28
34
  required: true
29
35
  description: Telemetry target name to map to this interface
30
36
  values: .+
31
- example: |
37
+ ruby_example: |
32
38
  INTERFACE TLM_INT tcpip_client_interface.rb host.docker.internal 8080 8081 10.0 nil BURST
33
39
  MAP_TLM_TARGET DATA # Only DATA telemetry received on TLM_INT interface
40
+ python_example: |
41
+ INTERFACE TLM_INT openc3/interfaces/tcpip_client_interface.py host.docker.internal 8080 8081 10.0 nil BURST
42
+ MAP_TLM_TARGET DATA # Only DATA telemetry received on TLM_INT interface
34
43
  DONT_CONNECT:
35
44
  summary: Server will not automatically try to connect to the interface at startup
36
45
  DONT_RECONNECT:
@@ -51,7 +60,7 @@ DISABLE_DISCONNECT:
51
60
  description:
52
61
  Use this keyword to prevent the user from disconnecting from the interface.
53
62
  This is typically used in a 'production' environment where you would not want
54
- the user to inadvertantly disconnect from a target.
63
+ the user to inadvertently disconnect from a target.
55
64
  LOG_RAW:
56
65
  summary: Deprecated, use LOG_STREAM
57
66
  LOG_STREAM:
@@ -102,17 +111,18 @@ PROTOCOL:
102
111
  values: ["READ", "WRITE", "READ_WRITE"]
103
112
  - name: Protocol Filename or Classname
104
113
  required: true
105
- description: Ruby filename or class name which implements the protocol
114
+ description: Ruby or Python filename or class name which implements the protocol
106
115
  values: .*
107
116
  - name: Protocol specific parameters
108
117
  required: false
109
118
  description: Additional parameters used by the protocol
110
- example: |
119
+ ruby_example: |
111
120
  INTERFACE DATA_INT tcpip_client_interface.rb host.docker.internal 8080 8081 10.0 nil nil
112
121
  MAP_TARGET DATA
113
122
  # Rather than defining the LENGTH protocol on the INTERFACE line we define it here
114
123
  PROTOCOL READ LengthProtocol 0 16 0 1 BIG_ENDIAN 4 0xBA5EBA11
115
- INTERFACE DATA_INT tcpip_client_interface.rb host.docker.internal 8080 8081 10.0 nil BURST
124
+ python_example: |
125
+ INTERFACE DATA_INT openc3/interfaces/tcpip_client_interface.py host.docker.internal 8080 8081 10.0 nil BURST
116
126
  MAP_TARGET DATA
117
127
  PROTOCOL READ IgnorePacketProtocol INST IMAGE # Drop all INST IMAGE packets
118
128
  OPTION:
@@ -220,8 +230,8 @@ CMD:
220
230
  required: true
221
231
  description: One or more arguments to exec to run the microservice.
222
232
  values: .+
223
- example: |
224
- CMD ruby interface_microservice.rb DEFAULT__INTERFACE__INT1
233
+ ruby_example: CMD ruby interface_microservice.rb DEFAULT__INTERFACE__INT1
234
+ python_example: CMD python interface_microservice.py DEFAULT__INTERFACE__INT1
225
235
  CONTAINER:
226
236
  summary: Docker Container
227
237
  description: Container to execute and run the microservice in. Only used in COSMOS Enterprise Edition.