openc3 5.18.0 → 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.
- checksums.yaml +4 -4
- data/Gemfile +7 -4
- data/bin/cstol_converter +14 -14
- data/bin/openc3cli +189 -7
- data/data/config/_interfaces.yaml +1 -1
- data/data/config/command_modifiers.yaml +55 -0
- data/data/config/interface_modifiers.yaml +1 -1
- data/data/config/param_item_modifiers.yaml +1 -1
- data/data/config/parameter_modifiers.yaml +1 -1
- data/data/config/plugins.yaml +6 -2
- data/data/config/screen.yaml +2 -2
- data/data/config/table_manager.yaml +2 -2
- data/data/config/tool.yaml +4 -1
- data/data/config/widgets.yaml +3 -3
- data/ext/openc3/ext/config_parser/config_parser.c +1 -1
- data/ext/openc3/ext/packet/packet.c +1 -1
- data/ext/openc3/ext/platform/platform.c +3 -3
- data/ext/openc3/ext/structure/structure.c +56 -76
- data/lib/openc3/accessors/binary_accessor.rb +4 -4
- data/lib/openc3/accessors/form_accessor.rb +2 -2
- data/lib/openc3/accessors/http_accessor.rb +1 -1
- data/lib/openc3/accessors/json_accessor.rb +6 -4
- data/lib/openc3/accessors/template_accessor.rb +6 -9
- data/lib/openc3/accessors/xml_accessor.rb +1 -1
- data/lib/openc3/api/cmd_api.rb +35 -11
- data/lib/openc3/api/limits_api.rb +1 -1
- data/lib/openc3/config/config_parser.rb +1 -1
- data/lib/openc3/conversions/segmented_polynomial_conversion.rb +7 -7
- data/lib/openc3/core_ext/array.rb +5 -5
- data/lib/openc3/core_ext/exception.rb +9 -2
- data/lib/openc3/core_ext/string.rb +2 -2
- data/lib/openc3/interfaces/http_server_interface.rb +1 -0
- data/lib/openc3/interfaces/interface.rb +1 -1
- data/lib/openc3/interfaces/linc_interface.rb +3 -3
- data/lib/openc3/io/json_api.rb +11 -6
- data/lib/openc3/io/json_rpc.rb +1 -1
- data/lib/openc3/logs/buffered_packet_log_writer.rb +3 -3
- data/lib/openc3/logs/log_writer.rb +7 -8
- data/lib/openc3/logs/packet_log_writer.rb +7 -7
- data/lib/openc3/logs/text_log_writer.rb +4 -4
- data/lib/openc3/microservices/decom_microservice.rb +19 -4
- data/lib/openc3/microservices/interface_microservice.rb +41 -3
- data/lib/openc3/microservices/reaction_microservice.rb +2 -2
- data/lib/openc3/microservices/trigger_group_microservice.rb +3 -3
- data/lib/openc3/migrations/20240915000000_activity_uuid.rb +28 -0
- data/lib/openc3/models/activity_model.rb +109 -80
- data/lib/openc3/models/auth_model.rb +31 -2
- data/lib/openc3/models/cvt_model.rb +11 -5
- data/lib/openc3/models/gem_model.rb +8 -8
- data/lib/openc3/models/plugin_model.rb +3 -3
- data/lib/openc3/models/reducer_model.rb +2 -2
- data/lib/openc3/models/scope_model.rb +1 -1
- data/lib/openc3/models/sorted_model.rb +4 -4
- data/lib/openc3/models/target_model.rb +3 -3
- data/lib/openc3/models/tool_config_model.rb +1 -1
- data/lib/openc3/models/tool_model.rb +4 -4
- data/lib/openc3/models/widget_model.rb +11 -5
- data/lib/openc3/operators/operator.rb +5 -3
- data/lib/openc3/packets/command_validator.rb +48 -0
- data/lib/openc3/packets/commands.rb +6 -14
- data/lib/openc3/packets/packet.rb +31 -15
- data/lib/openc3/packets/packet_config.rb +10 -9
- data/lib/openc3/packets/parsers/packet_parser.rb +3 -3
- data/lib/openc3/packets/structure.rb +21 -13
- data/lib/openc3/packets/structure_item.rb +33 -47
- data/lib/openc3/packets/telemetry.rb +6 -27
- data/lib/openc3/script/api_shared.rb +7 -5
- data/lib/openc3/script/calendar.rb +2 -2
- data/lib/openc3/script/commands.rb +6 -4
- data/lib/openc3/script/metadata.rb +2 -2
- data/lib/openc3/script/suite.rb +17 -17
- data/lib/openc3/streams/serial_stream.rb +2 -3
- data/lib/openc3/streams/stream.rb +2 -2
- data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +10 -10
- data/lib/openc3/tools/table_manager/table_manager_core.rb +11 -11
- data/lib/openc3/tools/table_manager/table_parser.rb +2 -3
- data/lib/openc3/topics/command_decom_topic.rb +2 -1
- data/lib/openc3/topics/command_topic.rb +3 -3
- data/lib/openc3/topics/decom_interface_topic.rb +2 -2
- data/lib/openc3/topics/telemetry_decom_topic.rb +1 -1
- data/lib/openc3/utilities/authorization.rb +2 -1
- data/lib/openc3/utilities/cli_generator.rb +15 -8
- data/lib/openc3/utilities/cosmos_rails_formatter.rb +60 -0
- data/lib/openc3/utilities/crc.rb +6 -6
- data/lib/openc3/utilities/local_mode.rb +2 -1
- data/lib/openc3/utilities/logger.rb +44 -34
- data/lib/openc3/utilities/metric.rb +1 -2
- data/lib/openc3/utilities/quaternion.rb +18 -18
- data/lib/openc3/utilities/target_file.rb +4 -4
- data/lib/openc3/version.rb +5 -5
- data/lib/openc3/win32/win32_main.rb +2 -2
- data/templates/tool_angular/package.json +21 -21
- data/templates/tool_react/package.json +10 -10
- data/templates/tool_svelte/package.json +11 -11
- data/templates/tool_svelte/src/services/openc3-api.js +17 -17
- data/templates/tool_vue/package.json +9 -9
- data/templates/widget/package.json +6 -7
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65a5c8a4ee0645f9d129c7c6140b9e8863e61e035f5e0269a977e8cd7d722c15
|
|
4
|
+
data.tar.gz: a023d452a32050ab5d81a3edcdc8fccaf10eed1a81b481ef11d89c633d3f6cba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
#
|
|
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
|
|
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
|
-
#
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =>
|
|
1059
|
-
puts
|
|
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 |
|
|
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 |
|
|
164
|
-
File.open(
|
|
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
|
-
|
|
209
|
+
"# FIXME (no Qt): #{line.sub("<%", "< %").sub("%>", "% >")}"
|
|
197
210
|
elsif line.include?('Cosmos::')
|
|
198
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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:
|
data/data/config/plugins.yaml
CHANGED
|
@@ -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
|
|
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
|
|
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
|
data/data/config/screen.yaml
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
data/data/config/tool.yaml
CHANGED
|
@@ -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:
|
|
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
|
data/data/config/widgets.yaml
CHANGED
|
@@ -121,7 +121,7 @@ Layout Widgets:
|
|
|
121
121
|
parameters:
|
|
122
122
|
- name: Tab text
|
|
123
123
|
required: true
|
|
124
|
-
description: Text to
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
127
|
+
/* Only support linux segfault catching */
|
|
128
128
|
#else
|
|
129
129
|
signal(SIGSEGV, catch_sigsegv);
|
|
130
130
|
signal(SIGILL, catch_sigsegv);
|