openc3 5.17.1 → 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 +190 -8
- data/data/config/_interfaces.yaml +5 -5
- data/data/config/command_modifiers.yaml +59 -0
- data/data/config/interface_modifiers.yaml +19 -9
- data/data/config/item_modifiers.yaml +34 -26
- data/data/config/microservice.yaml +4 -1
- data/data/config/param_item_modifiers.yaml +17 -1
- data/data/config/parameter_modifiers.yaml +30 -13
- data/data/config/plugins.yaml +9 -5
- data/data/config/screen.yaml +9 -9
- data/data/config/table_manager.yaml +2 -2
- data/data/config/telemetry_modifiers.yaml +9 -4
- data/data/config/tool.yaml +4 -1
- data/data/config/widgets.yaml +44 -17
- data/ext/openc3/ext/config_parser/config_parser.c +1 -1
- data/ext/openc3/ext/packet/packet.c +7 -1
- data/ext/openc3/ext/platform/platform.c +3 -3
- data/ext/openc3/ext/structure/structure.c +56 -76
- data/lib/openc3/accessors/accessor.rb +1 -0
- data/lib/openc3/accessors/binary_accessor.rb +174 -15
- 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 +72 -44
- data/lib/openc3/api/config_api.rb +10 -10
- data/lib/openc3/api/interface_api.rb +28 -21
- data/lib/openc3/api/limits_api.rb +30 -30
- data/lib/openc3/api/metrics_api.rb +3 -3
- data/lib/openc3/api/offline_access_api.rb +5 -5
- data/lib/openc3/api/router_api.rb +25 -19
- data/lib/openc3/api/settings_api.rb +10 -10
- data/lib/openc3/api/stash_api.rb +10 -10
- data/lib/openc3/api/target_api.rb +10 -10
- data/lib/openc3/api/tlm_api.rb +44 -44
- data/lib/openc3/config/config_parser.rb +1 -1
- data/lib/openc3/conversions/bit_reverse_conversion.rb +60 -0
- data/lib/openc3/conversions/ip_read_conversion.rb +59 -0
- data/lib/openc3/conversions/ip_write_conversion.rb +61 -0
- data/lib/openc3/conversions/object_read_conversion.rb +88 -0
- data/lib/openc3/conversions/object_write_conversion.rb +38 -0
- data/lib/openc3/conversions/segmented_polynomial_conversion.rb +7 -7
- data/lib/openc3/conversions.rb +6 -1
- 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_drb.rb +19 -21
- data/lib/openc3/io/json_rpc.rb +15 -14
- 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/microservice.rb +11 -11
- data/lib/openc3/microservices/reaction_microservice.rb +2 -2
- data/lib/openc3/microservices/scope_cleanup_microservice.rb +1 -1
- data/lib/openc3/microservices/timeline_microservice.rb +70 -45
- 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 +124 -92
- 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 +45 -14
- data/lib/openc3/models/sorted_model.rb +5 -5
- data/lib/openc3/models/target_model.rb +7 -4
- 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/microservice_operator.rb +2 -2
- data/lib/openc3/operators/operator.rb +14 -12
- data/lib/openc3/packets/command_validator.rb +48 -0
- data/lib/openc3/packets/commands.rb +6 -14
- data/lib/openc3/packets/packet.rb +49 -16
- data/lib/openc3/packets/packet_config.rb +47 -25
- data/lib/openc3/packets/packet_item.rb +5 -0
- data/lib/openc3/packets/parsers/packet_parser.rb +3 -3
- data/lib/openc3/packets/structure.rb +87 -15
- data/lib/openc3/packets/structure_item.rb +76 -53
- 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/extract.rb +5 -3
- data/lib/openc3/script/metadata.rb +2 -2
- data/lib/openc3/script/suite.rb +17 -17
- data/lib/openc3/script/web_socket_api.rb +11 -0
- 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 +4 -3
- data/lib/openc3/topics/system_events_topic.rb +40 -0
- data/lib/openc3/topics/telemetry_decom_topic.rb +1 -1
- data/lib/openc3/utilities/authentication.rb +2 -1
- data/lib/openc3/utilities/authorization.rb +4 -3
- 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 +6 -6
- data/lib/openc3/win32/win32_main.rb +2 -2
- data/templates/tool_angular/package.json +22 -22
- data/templates/tool_react/package.json +13 -13
- data/templates/tool_svelte/package.json +14 -14
- data/templates/tool_svelte/src/services/openc3-api.js +17 -17
- data/templates/tool_vue/package.json +13 -13
- data/templates/widget/package.json +11 -12
- data/templates/widget/src/Widget.vue +0 -1
- metadata +25 -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
|
@@ -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
|
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
|
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
|
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
|
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
|
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:
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
224
|
-
|
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.
|