openc3 5.17.1 → 5.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.
|