openc3 5.18.0 → 5.19.0
Sign up to get free protection for your applications and to get access to all the features.
- 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);
|